omni-rest 0.4.6 → 0.4.8

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,5 +1,5 @@
1
1
  import { PrismaClient } from '@prisma/client';
2
- import { P as PrismaRestOptions } from '../types-BURph8D5.mjs';
2
+ import { P as PrismaRestOptions } from '../types-CLcDS1BU.mjs';
3
3
 
4
4
  /**
5
5
  * Express adapter for omni-rest.
@@ -1,5 +1,5 @@
1
1
  import { PrismaClient } from '@prisma/client';
2
- import { P as PrismaRestOptions } from '../types-BURph8D5.js';
2
+ import { P as PrismaRestOptions } from '../types-CLcDS1BU.js';
3
3
 
4
4
  /**
5
5
  * Express adapter for omni-rest.
@@ -272,7 +272,9 @@ function createRouter(prisma, options = {}) {
272
272
  maxLimit = 100,
273
273
  softDelete = false,
274
274
  softDeleteField,
275
- envelope = true
275
+ envelope = true,
276
+ fieldGuards = {},
277
+ rateLimit
276
278
  } = options;
277
279
  const models = getModels(prisma);
278
280
  const modelMap = buildModelMap(models, allow);
@@ -287,6 +289,12 @@ function createRouter(prisma, options = {}) {
287
289
  }
288
290
  };
289
291
  }
292
+ if (rateLimit) {
293
+ const rateLimitError = await rateLimit({ model: meta.name, method, id });
294
+ if (rateLimitError) {
295
+ return { status: 429, data: { error: rateLimitError } };
296
+ }
297
+ }
290
298
  const guardError = await runGuard(guards, meta.routeName, method, {
291
299
  id,
292
300
  body
@@ -309,7 +317,8 @@ function createRouter(prisma, options = {}) {
309
317
  operation,
310
318
  softDelete,
311
319
  softDeleteField,
312
- envelope
320
+ envelope,
321
+ fieldGuards[meta.routeName]
313
322
  );
314
323
  } catch (e) {
315
324
  return handlePrismaError(e);
@@ -325,7 +334,7 @@ function createRouter(prisma, options = {}) {
325
334
  }
326
335
  return { handle, modelMap, models };
327
336
  }
328
- async function executeOperation(prisma, meta, method, id, body, searchParams, defaultLimit, maxLimit, operation, softDelete = false, softDeleteField, envelope = true) {
337
+ async function executeOperation(prisma, meta, method, id, body, searchParams, defaultLimit, maxLimit, operation, softDelete = false, softDeleteField, envelope = true, fg) {
329
338
  const delegate = getDelegate(prisma, meta);
330
339
  const { where, orderBy, skip, take, include, select } = buildQuery(
331
340
  searchParams,
@@ -336,6 +345,7 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
336
345
  const includeArg = Object.keys(include).length > 0 ? include : void 0;
337
346
  const selectArg = select && Object.keys(select).length > 0 ? select : void 0;
338
347
  const projection = selectArg ? { select: selectArg } : includeArg ? { include: includeArg } : {};
348
+ const safeBody = sanitizeBody(body, fg);
339
349
  if (method === "PATCH" && operation === "bulk-update") {
340
350
  if (!Array.isArray(body) || body.length === 0) {
341
351
  return {
@@ -400,17 +410,18 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
400
410
  delegate.findMany({ where: listWhere, orderBy, skip, take, ...projection }),
401
411
  delegate.count({ where: listWhere })
402
412
  ]);
413
+ const safeData = data.map((r) => stripResponse(r, fg));
403
414
  if (!envelope) {
404
415
  return {
405
416
  status: 200,
406
- data,
417
+ data: safeData,
407
418
  headers: { "X-Total-Count": String(total) }
408
419
  };
409
420
  }
410
421
  return {
411
422
  status: 200,
412
423
  data: {
413
- data,
424
+ data: safeData,
414
425
  meta: {
415
426
  total,
416
427
  page: Math.floor(skip / take) + 1,
@@ -428,18 +439,18 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
428
439
  if (!record) {
429
440
  return { status: 404, data: { error: `${meta.name} with id "${id}" not found.` } };
430
441
  }
431
- return { status: 200, data: record };
442
+ return { status: 200, data: stripResponse(record, fg) };
432
443
  }
433
444
  if (method === "POST" && !id) {
434
- const record = await delegate.create({ data: body });
435
- return { status: 201, data: record };
445
+ const record = await delegate.create({ data: safeBody });
446
+ return { status: 201, data: stripResponse(record, fg) };
436
447
  }
437
448
  if ((method === "PUT" || method === "PATCH") && id) {
438
449
  const record = await delegate.update({
439
450
  where: { [meta.idField]: coerceId(id) },
440
- data: body
451
+ data: safeBody
441
452
  });
442
- return { status: 200, data: record };
453
+ return { status: 200, data: stripResponse(record, fg) };
443
454
  }
444
455
  if (method === "DELETE" && id) {
445
456
  const softDeleteInfo = softDelete ? detectSoftDeleteField(meta.fields, softDeleteField) : null;
@@ -457,6 +468,26 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
457
468
  }
458
469
  return { status: 405, data: { error: `Method ${method} not allowed.` } };
459
470
  }
471
+ function stripResponse(record, fg) {
472
+ if (!fg || !record || typeof record !== "object") return record;
473
+ const blocked = /* @__PURE__ */ new Set([...fg.hidden ?? [], ...fg.writeOnly ?? []]);
474
+ if (blocked.size === 0) return record;
475
+ const out = {};
476
+ for (const [k, v] of Object.entries(record)) {
477
+ if (!blocked.has(k)) out[k] = v;
478
+ }
479
+ return out;
480
+ }
481
+ function sanitizeBody(body, fg) {
482
+ if (!fg || !body || typeof body !== "object" || Array.isArray(body)) return body;
483
+ const readOnly = new Set(fg.readOnly ?? []);
484
+ if (readOnly.size === 0) return body;
485
+ const out = {};
486
+ for (const [k, v] of Object.entries(body)) {
487
+ if (!readOnly.has(k)) out[k] = v;
488
+ }
489
+ return out;
490
+ }
460
491
  function coerceId(id) {
461
492
  const n = Number(id);
462
493
  return isNaN(n) ? id : n;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/introspect.ts","../../src/query-builder.ts","../../src/middleware-helpers.ts","../../src/router.ts","../../src/adapters/express.ts"],"names":["f","sortedOps","id"],"mappings":"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","file":"express.js","sourcesContent":["import type { ModelMeta, FieldMeta } from \"./types\";\r\n\r\n/**\r\n * Reads Prisma's DMMF (Data Model Meta Format) at runtime\r\n * and returns structured metadata for every model in your schema.\r\n *\r\n * No file reading. No code generation. Pure runtime introspection.\r\n */\r\nexport function getModels(prisma?: any): ModelMeta[] {\r\n let raw: any[] | undefined;\r\n\r\n // Try to get from PrismaClient instance first (new v5 format)\r\n if (prisma?._runtimeDataModel?.models) {\r\n const modelsObj = prisma._runtimeDataModel.models;\r\n // Convert object to array, adding name from key\r\n raw = Object.entries(modelsObj).map(([name, model]: [string, any]) => ({\r\n name,\r\n ...model,\r\n fields: (model.fields || []).map((f: any) => ({\r\n ...f,\r\n relationName: f.kind === \"object\" ? f.name : undefined,\r\n })),\r\n }));\r\n }\r\n\r\n // Fallback to DMMF from @prisma/client module — use plain require so\r\n // vi.mock(\"@prisma/client\") can intercept it in tests. The CLI handles\r\n // the global-install path resolution separately before calling getModels.\r\n if (!raw) {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-var-requires\r\n const prismaModule = require(\"@prisma/client\");\r\n const dmmfModels = prismaModule?.Prisma?.dmmf?.datamodel?.models;\r\n if (dmmfModels) {\r\n raw = dmmfModels;\r\n }\r\n } catch {\r\n // @prisma/client not available or not generated — handled below\r\n }\r\n }\r\n\r\n if (!raw) {\r\n throw new Error(\r\n \"[omni-rest] Could not find Prisma DMMF. Ensure Prisma client is generated and you're passing a PrismaClient instance to omni-rest.\"\r\n );\r\n }\r\n\r\n if (!Array.isArray(raw)) {\r\n throw new Error(\r\n `[omni-rest] Expected models to be an array, got ${typeof raw}. Debug: prisma._runtimeDataModel.models=${!!prisma?._runtimeDataModel?.models}, raw value=${JSON.stringify(raw).slice(0, 100)}`\r\n );\r\n }\r\n\r\n return raw.map((model: any) => {\r\n const fields: FieldMeta[] = model.fields.map((f: any) => ({\r\n name: f.name,\r\n type: f.type,\r\n isId: f.isId,\r\n isRequired: f.isRequired,\r\n isList: f.isList,\r\n isRelation: !!f.relationName,\r\n hasDefaultValue: !!f.hasDefaultValue || !!f.default,\r\n isUpdatedAt: !!f.isUpdatedAt,\r\n }));\r\n\r\n const idField =\r\n model.fields.find((f: any) => f.isId)?.name ?? \"id\";\r\n\r\n return {\r\n name: model.name,\r\n routeName: toRouteName(model.name),\r\n fields,\r\n idField,\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * Converts a Prisma model name to a URL-safe route segment.\r\n * \"UserProfile\" → \"userprofile\"\r\n * \"OrderItem\" → \"orderitem\"\r\n */\r\nexport function toRouteName(modelName: string): string {\r\n return modelName.toLowerCase();\r\n}\r\n\r\n/**\r\n * Returns a map of routeName → ModelMeta for O(1) lookups.\r\n */\r\nexport function buildModelMap(\r\n models: ModelMeta[],\r\n allowList?: string[]\r\n): Record<string, ModelMeta> {\r\n const filtered = allowList\r\n ? models.filter((m) => allowList.includes(m.routeName))\r\n : models;\r\n\r\n return Object.fromEntries(filtered.map((m) => [m.routeName, m]));\r\n}\r\n\r\n/**\r\n * Auto-detects the soft-delete field for a model.\r\n * Returns the field name and the value to set, or null if not found.\r\n *\r\n * Priority:\r\n * 1. Explicit `softDeleteField` option\r\n * 2. A field named \"deletedAt\" with type DateTime\r\n * 3. A field named \"isActive\" with type Boolean\r\n */\r\nexport function detectSoftDeleteField(\r\n fields: FieldMeta[],\r\n explicitField?: string\r\n): { field: string; value: Date | boolean } | null {\r\n if (explicitField) {\r\n const f = fields.find((f) => f.name === explicitField);\r\n if (!f) return null;\r\n // Infer value from type\r\n const value = f.type === \"Boolean\" ? false : new Date();\r\n return { field: explicitField, value };\r\n }\r\n\r\n const deletedAt = fields.find((f) => f.name === \"deletedAt\" && f.type === \"DateTime\");\r\n if (deletedAt) return { field: \"deletedAt\", value: new Date() };\r\n\r\n const isActive = fields.find((f) => f.name === \"isActive\" && f.type === \"Boolean\");\r\n if (isActive) return { field: \"isActive\", value: false };\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Gets the Prisma client delegate for a model.\r\n * prisma[\"userProfile\"] or prisma[\"user\"] — handles camelCase.\r\n */\r\nexport function getDelegate(prisma: any, meta: ModelMeta): any {\r\n // Prisma client properties are camelCase of model name\r\n // \"UserProfile\" → \"userProfile\"\r\n const key =\r\n meta.name.charAt(0).toLowerCase() + meta.name.slice(1);\r\n const delegate = prisma[key];\r\n\r\n if (!delegate) {\r\n throw new Error(\r\n `Could not find Prisma delegate for model \"${meta.name}\". ` +\r\n `Expected prisma.${key} to exist.`\r\n );\r\n }\r\n\r\n return delegate;\r\n}","import type { ParsedQuery, FieldMeta } from \"./types\";\r\n\r\n/**\r\n * Supported filter suffixes and their Prisma equivalents.\r\n *\r\n * Usage in URL:\r\n * ?name_contains=john\r\n * ?age_gte=18\r\n * ?status_in=active,inactive\r\n * ?email_not=test@test.com\r\n */\r\nconst FILTER_OPERATORS: Record<string, string> = {\r\n _gte: \"gte\",\r\n _lte: \"lte\",\r\n _gt: \"gt\",\r\n _lt: \"lt\",\r\n _contains: \"contains\",\r\n _icontains: \"contains\", // case-insensitive version (mode: insensitive)\r\n _startsWith: \"startsWith\",\r\n _endsWith: \"endsWith\",\r\n _in: \"in\",\r\n _notIn: \"notIn\",\r\n _not: \"not\",\r\n};\r\n\r\n/**\r\n * Reserved query keys — these are NOT treated as filters.\r\n */\r\nconst RESERVED_KEYS = new Set([\r\n \"page\",\r\n \"limit\",\r\n \"sort\",\r\n \"include\",\r\n \"select\",\r\n \"fields\",\r\n \"search\",\r\n]);\r\n\r\n/**\r\n * Parses URLSearchParams into a full Prisma query object.\r\n *\r\n * Supports:\r\n * Filtering → ?name=John ?age_gte=18 ?status_in=a,b\r\n * Relation filter → ?employees.isActive=true ?author.name_contains=john\r\n * Sorting → ?sort=createdAt:desc or ?sort=name:asc\r\n * Pagination → ?page=2&limit=10\r\n * Relations → ?include=posts,profile\r\n * Fields → ?select=id,name,email or ?fields=id,name,email\r\n * Search → ?search=eng (queries all String fields with OR)\r\n *\r\n * Dot-notation relation filtering:\r\n * ?relation.field=value → isList → { relation: { some: { field: value } } }\r\n * → singular → { relation: { field: value } }\r\n * Operator suffixes work inside dot notation:\r\n * ?author.name_contains=john → { author: { name: { contains: \"john\" } } }\r\n *\r\n * NOTE: Only one level of nesting is supported (relation.field).\r\n * Deeply nested filters (relation.nested.field) are not supported and will\r\n * be treated as a regular (non-relation) filter key.\r\n */\r\nexport function buildQuery(\r\n searchParams: URLSearchParams,\r\n defaultLimit = 20,\r\n maxLimit = 100,\r\n modelFields?: FieldMeta[]\r\n): ParsedQuery {\r\n const where: Record<string, any> = {};\r\n const orderBy: Record<string, any> = {};\r\n let include: Record<string, boolean> = {};\r\n let select: Record<string, boolean> | null = null;\r\n\r\n // ─── Pagination ────────────────────────────────────────────────────────────\r\n const page = Math.max(1, parseInt(searchParams.get(\"page\") ?? \"1\"));\r\n const rawLimit = parseInt(searchParams.get(\"limit\") ?? String(defaultLimit));\r\n const take = Math.min(rawLimit, maxLimit);\r\n const skip = (page - 1) * take;\r\n\r\n // ─── Sort ──────────────────────────────────────────────────────────────────\r\n const sortParam = searchParams.get(\"sort\");\r\n if (sortParam) {\r\n // Supports multiple sorts: ?sort=name:asc,createdAt:desc,_count.posts:desc\r\n for (const part of sortParam.split(\",\")) {\r\n const [field, dir] = part.trim().split(\":\");\r\n if (!field) continue;\r\n const direction = dir === \"desc\" ? \"desc\" : \"asc\";\r\n\r\n // _count.relation:dir → { relation: { _count: dir } }\r\n if (field.startsWith(\"_count.\")) {\r\n const relation = field.slice(\"_count.\".length);\r\n if (relation) {\r\n orderBy[relation] = { _count: direction };\r\n }\r\n } else {\r\n orderBy[field] = direction;\r\n }\r\n }\r\n }\r\n\r\n // ─── Include (relations) ───────────────────────────────────────────────────\r\n const includeParam = searchParams.get(\"include\");\r\n if (includeParam) {\r\n for (const rel of includeParam.split(\",\")) {\r\n if (rel.trim()) include[rel.trim()] = true;\r\n }\r\n }\r\n\r\n // ─── Select (fields) — ?select= or ?fields= alias ────────────────────────\r\n const selectParam = searchParams.get(\"select\") ?? searchParams.get(\"fields\");\r\n if (selectParam) {\r\n select = {};\r\n for (const field of selectParam.split(\",\")) {\r\n if (field.trim()) select[field.trim()] = true;\r\n }\r\n }\r\n\r\n // ─── Global search (?search=) ─────────────────────────────────────────────\r\n const searchValue = searchParams.get(\"search\");\r\n if (searchValue && modelFields) {\r\n const stringFields = modelFields.filter(\r\n (f) => f.type === \"String\" && !f.isRelation\r\n );\r\n if (stringFields.length > 0) {\r\n const orClauses = stringFields.map((f) => ({\r\n [f.name]: { contains: searchValue, mode: \"insensitive\" },\r\n }));\r\n // Merge with any existing where via AND so other filters still apply\r\n where[\"OR\"] = orClauses;\r\n }\r\n }\r\n\r\n // ─── Filters ───────────────────────────────────────────────────────────────\r\n for (const [key, value] of searchParams.entries()) {\r\n if (RESERVED_KEYS.has(key)) continue;\r\n\r\n // ── Dot-notation relation filter (?relation.field[_op]=value) ──────────\r\n // Only handle a single dot (one level deep). Deeper nesting falls through.\r\n const dotIndex = key.indexOf(\".\");\r\n if (dotIndex > 0 && key.indexOf(\".\", dotIndex + 1) === -1 && modelFields) {\r\n const relationName = key.slice(0, dotIndex);\r\n const fieldPart = key.slice(dotIndex + 1); // e.g. \"isActive\" or \"name_contains\"\r\n\r\n const relationMeta = modelFields.find(\r\n (f) => f.name === relationName && f.isRelation\r\n );\r\n\r\n if (relationMeta) {\r\n // Parse the field part for operator suffix\r\n const sortedOps = Object.keys(FILTER_OPERATORS).sort(\r\n (a, b) => b.length - a.length\r\n );\r\n let fieldName = fieldPart;\r\n let fieldFilter: any;\r\n\r\n let opMatched = false;\r\n for (const suffix of sortedOps) {\r\n if (fieldPart.endsWith(suffix)) {\r\n fieldName = fieldPart.slice(0, -suffix.length);\r\n const prismaOp = FILTER_OPERATORS[suffix];\r\n let parsedValue: any = value;\r\n\r\n if (prismaOp === \"in\" || prismaOp === \"notIn\") {\r\n parsedValue = value.split(\",\").map((v) => v.trim());\r\n } else if (!isNaN(Number(parsedValue)) && typeof parsedValue === \"string\") {\r\n parsedValue = Number(parsedValue);\r\n }\r\n\r\n const extra = suffix === \"_icontains\" ? { mode: \"insensitive\" } : {};\r\n fieldFilter = { [prismaOp]: parsedValue, ...extra };\r\n opMatched = true;\r\n break;\r\n }\r\n }\r\n\r\n if (!opMatched) {\r\n // Exact match — coerce value\r\n let parsedValue: any = value;\r\n if (value === \"true\") parsedValue = true;\r\n else if (value === \"false\") parsedValue = false;\r\n else if (!isNaN(Number(value)) && value !== \"\") parsedValue = Number(value);\r\n fieldFilter = parsedValue;\r\n }\r\n\r\n // isList relation → some: { field: filter }\r\n // singular relation → direct: { field: filter }\r\n const nested = { [fieldName]: fieldFilter };\r\n where[relationName] = relationMeta.isList\r\n ? { some: nested }\r\n : nested;\r\n\r\n continue; // skip regular filter processing for this key\r\n }\r\n }\r\n\r\n // ── Regular filters ────────────────────────────────────────────────────\r\n // Check operator suffixes (longest match first)\r\n let matched = false;\r\n const sortedOps = Object.keys(FILTER_OPERATORS).sort(\r\n (a, b) => b.length - a.length\r\n );\r\n\r\n for (const suffix of sortedOps) {\r\n if (key.endsWith(suffix)) {\r\n const field = key.slice(0, -suffix.length);\r\n const prismaOp = FILTER_OPERATORS[suffix];\r\n\r\n let parsedValue: any = value;\r\n\r\n // Parse arrays\r\n if (prismaOp === \"in\" || prismaOp === \"notIn\") {\r\n parsedValue = value.split(\",\").map((v) => v.trim());\r\n }\r\n\r\n // Attempt numeric coercion\r\n if (!isNaN(Number(parsedValue)) && typeof parsedValue === \"string\") {\r\n parsedValue = Number(parsedValue);\r\n }\r\n\r\n // Case-insensitive flag\r\n const extra = suffix === \"_icontains\" ? { mode: \"insensitive\" } : {};\r\n\r\n where[field] = { [prismaOp]: parsedValue, ...extra };\r\n matched = true;\r\n break;\r\n }\r\n }\r\n\r\n // No operator — exact match\r\n if (!matched) {\r\n let parsedValue: any = value;\r\n\r\n // Coerce booleans\r\n if (value === \"true\") parsedValue = true;\r\n else if (value === \"false\") parsedValue = false;\r\n // Coerce numbers\r\n else if (!isNaN(Number(value)) && value !== \"\") {\r\n parsedValue = Number(value);\r\n }\r\n\r\n where[key] = parsedValue;\r\n }\r\n }\r\n\r\n return { where, orderBy, skip, take, include, select };\r\n}","import type { GuardMap, HookFn, HookContext } from \"./types\";\r\n\r\n/**\r\n * Runs the guard for the given model+method combo.\r\n * Returns an error string if blocked, null if allowed.\r\n */\r\nexport async function runGuard(\r\n guards: GuardMap,\r\n model: string,\r\n method: string,\r\n ctx: { id?: string | null; body?: any }\r\n): Promise<string | null> {\r\n const modelGuards = guards[model];\r\n if (!modelGuards) return null;\r\n\r\n const fn = modelGuards[method as keyof typeof modelGuards];\r\n if (!fn) return null;\r\n\r\n return fn({ ...ctx, method });\r\n}\r\n\r\n/**\r\n * Runs a lifecycle hook (beforeOperation / afterOperation).\r\n * Silently swallows errors so hooks never crash the main flow.\r\n */\r\nexport async function runHook(\r\n hook: HookFn | undefined,\r\n ctx: HookContext\r\n): Promise<void> {\r\n if (!hook) return;\r\n try {\r\n await hook(ctx);\r\n } catch (e) {\r\n // Hooks should not crash the request\r\n console.error(\"[omni-rest] Hook error:\", e);\r\n }\r\n}","import { PrismaClient } from \"@prisma/client\";\r\nimport { getModels, buildModelMap, getDelegate, detectSoftDeleteField } from \"./introspect\";\r\nimport { buildQuery } from \"./query-builder\";\r\nimport { runGuard, runHook } from \"./middleware-helpers\";\r\nimport type {\r\n PrismaRestOptions,\r\n HandlerResult,\r\n RouterInstance,\r\n ModelMeta,\r\n} from \"./types\";\r\n\r\n/**\r\n * Creates a framework-agnostic CRUD router powered by Prisma DMMF.\r\n *\r\n * All adapters (Express, Next.js, Fastify) use this under the hood.\r\n */\r\nexport function createRouter(\r\n prisma: PrismaClient,\r\n options: PrismaRestOptions = {}\r\n): RouterInstance {\r\n const {\r\n allow,\r\n guards = {},\r\n beforeOperation,\r\n afterOperation,\r\n defaultLimit = 20,\r\n maxLimit = 100,\r\n softDelete = false,\r\n softDeleteField,\r\n envelope = true,\r\n } = options;\r\n\r\n // Introspect schema once at startup\r\n const models = getModels(prisma);\r\n const modelMap = buildModelMap(models, allow);\r\n\r\n async function handle(\r\n method: string,\r\n modelName: string,\r\n id: string | null,\r\n body: any,\r\n searchParams: URLSearchParams,\r\n operation?: string\r\n ): Promise<HandlerResult> {\r\n // ── 1. Resolve model ───────────────────────────────────────────────────\r\n const meta = modelMap[modelName.toLowerCase()];\r\n if (!meta) {\r\n return {\r\n status: 404,\r\n data: {\r\n error: `Model \"${modelName}\" not found or not exposed.`,\r\n available: Object.keys(modelMap),\r\n },\r\n };\r\n }\r\n\r\n // ── 2. Guard check ─────────────────────────────────────────────────────\r\n const guardError = await runGuard(guards, meta.routeName, method, {\r\n id,\r\n body,\r\n });\r\n if (guardError) {\r\n return { status: 403, data: { error: guardError } };\r\n }\r\n\r\n // ── 3. Before hook ─────────────────────────────────────────────────────\r\n await runHook(beforeOperation, { model: meta.name, method, id, body });\r\n\r\n // ── 4. Execute operation ───────────────────────────────────────────────\r\n let result: HandlerResult;\r\n\r\n try {\r\n result = await executeOperation(\r\n prisma,\r\n meta,\r\n method,\r\n id,\r\n body,\r\n searchParams,\r\n defaultLimit,\r\n maxLimit,\r\n operation,\r\n softDelete,\r\n softDeleteField,\r\n envelope\r\n );\r\n } catch (e: any) {\r\n return handlePrismaError(e);\r\n }\r\n\r\n // ── 5. After hook ──────────────────────────────────────────────────────\r\n await runHook(afterOperation, {\r\n model: meta.name,\r\n method,\r\n id,\r\n body,\r\n result: result.data,\r\n });\r\n\r\n return result;\r\n }\r\n\r\n return { handle, modelMap, models };\r\n}\r\n\r\n// ─── Operation executor ────────────────────────────────────────────────────────\r\n\r\nasync function executeOperation(\r\n prisma: PrismaClient,\r\n meta: ModelMeta,\r\n method: string,\r\n id: string | null,\r\n body: any,\r\n searchParams: URLSearchParams,\r\n defaultLimit: number,\r\n maxLimit: number,\r\n operation?: string,\r\n softDelete = false,\r\n softDeleteField?: string,\r\n envelope = true\r\n): Promise<HandlerResult> {\r\n const delegate = getDelegate(prisma, meta);\r\n const { where, orderBy, skip, take, include, select } = buildQuery(\r\n searchParams,\r\n defaultLimit,\r\n maxLimit,\r\n meta.fields\r\n );\r\n\r\n // Build include/select args (mutually exclusive in Prisma)\r\n const includeArg =\r\n Object.keys(include).length > 0 ? include : undefined;\r\n const selectArg =\r\n select && Object.keys(select).length > 0 ? select : undefined;\r\n const projection = selectArg ? { select: selectArg } : includeArg ? { include: includeArg } : {};\r\n\r\n // ── PATCH /model/bulk/update ────────────────────────────────────────────────\r\n if (method === \"PATCH\" && operation === \"bulk-update\") {\r\n if (!Array.isArray(body) || body.length === 0) {\r\n return {\r\n status: 400,\r\n data: { error: \"Request body must be a non-empty array of update records\" },\r\n };\r\n }\r\n\r\n // Validate that each item has an id\r\n for (const item of body) {\r\n if (!item[meta.idField]) {\r\n return {\r\n status: 400,\r\n data: { error: `Each record must have an ${meta.idField} field` },\r\n };\r\n }\r\n }\r\n\r\n // Execute updates in parallel\r\n const results = await Promise.all(\r\n body.map((item) => {\r\n const id = item[meta.idField];\r\n const updateData = { ...item };\r\n delete updateData[meta.idField]; // Remove id from update data\r\n\r\n return delegate.update({\r\n where: { [meta.idField]: coerceId(id) },\r\n data: updateData,\r\n ...projection,\r\n });\r\n })\r\n );\r\n\r\n return {\r\n status: 200,\r\n data: {\r\n updated: results.length,\r\n records: results,\r\n },\r\n };\r\n }\r\n\r\n // ── DELETE /model/bulk/delete ──────────────────────────────────────────────\r\n if (method === \"DELETE\" && operation === \"bulk-delete\") {\r\n if (!Array.isArray(body) || body.length === 0) {\r\n return {\r\n status: 400,\r\n data: { error: \"Request body must be a non-empty array of IDs\" },\r\n };\r\n }\r\n\r\n // Handle both array of IDs and array of objects with id field\r\n const ids = body.map((item: any) =>\r\n typeof item === \"object\" ? item[meta.idField] : item\r\n );\r\n\r\n // Use deleteMany with a where clause\r\n const result = await delegate.deleteMany({\r\n where: {\r\n [meta.idField]: { in: ids.map(coerceId) },\r\n },\r\n });\r\n\r\n return {\r\n status: 200,\r\n data: {\r\n deleted: result.count,\r\n },\r\n };\r\n }\r\n\r\n // ── GET /model ─────────────────────────────────────────────────────────────\r\n if (method === \"GET\" && !id) {\r\n // Exclude soft-deleted records from list when soft delete is active\r\n const softDeleteInfo = softDelete\r\n ? detectSoftDeleteField(meta.fields, softDeleteField)\r\n : null;\r\n const listWhere = softDeleteInfo\r\n ? { ...where, [softDeleteInfo.field]: softDeleteInfo.field === \"isActive\" ? true : null }\r\n : where;\r\n\r\n const [data, total] = await (prisma as any).$transaction([\r\n delegate.findMany({ where: listWhere, orderBy, skip, take, ...projection }),\r\n delegate.count({ where: listWhere }),\r\n ]);\r\n\r\n if (!envelope) {\r\n return {\r\n status: 200,\r\n data,\r\n headers: { \"X-Total-Count\": String(total) },\r\n };\r\n }\r\n\r\n return {\r\n status: 200,\r\n data: {\r\n data,\r\n meta: {\r\n total,\r\n page: Math.floor(skip / take) + 1,\r\n limit: take,\r\n totalPages: Math.ceil(total / take),\r\n },\r\n },\r\n };\r\n }\r\n\r\n // ── GET /model/:id ─────────────────────────────────────────────────────────\r\n if (method === \"GET\" && id) {\r\n const record = await delegate.findUnique({\r\n where: { [meta.idField]: coerceId(id) },\r\n ...projection,\r\n });\r\n\r\n if (!record) {\r\n return { status: 404, data: { error: `${meta.name} with id \"${id}\" not found.` } };\r\n }\r\n\r\n return { status: 200, data: record };\r\n }\r\n\r\n // ── POST /model ────────────────────────────────────────────────────────────\r\n if (method === \"POST\" && !id) {\r\n const record = await delegate.create({ data: body });\r\n return { status: 201, data: record };\r\n }\r\n\r\n // ── PUT /model/:id ─────────────────────────────────────────────────────────\r\n if ((method === \"PUT\" || method === \"PATCH\") && id) {\r\n const record = await delegate.update({\r\n where: { [meta.idField]: coerceId(id) },\r\n data: body,\r\n });\r\n return { status: 200, data: record };\r\n }\r\n\r\n // ── DELETE /model/:id ──────────────────────────────────────────────────────\r\n if (method === \"DELETE\" && id) {\r\n const softDeleteInfo = softDelete\r\n ? detectSoftDeleteField(meta.fields, softDeleteField)\r\n : null;\r\n\r\n if (softDeleteInfo) {\r\n const record = await delegate.update({\r\n where: { [meta.idField]: coerceId(id) },\r\n data: { [softDeleteInfo.field]: softDeleteInfo.value },\r\n });\r\n return { status: 200, data: record };\r\n }\r\n\r\n await delegate.delete({\r\n where: { [meta.idField]: coerceId(id) },\r\n });\r\n return { status: 204, data: null };\r\n }\r\n\r\n return { status: 405, data: { error: `Method ${method} not allowed.` } };\r\n}\r\n\r\n// ─── Helpers ──────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Coerces an ID string to number when possible (common with Int PKs).\r\n */\r\nfunction coerceId(id: string): string | number {\r\n const n = Number(id);\r\n return isNaN(n) ? id : n;\r\n}\r\n\r\n/**\r\n * Maps Prisma error codes to meaningful HTTP responses.\r\n */\r\nfunction handlePrismaError(e: any): HandlerResult {\r\n const code = e?.code;\r\n\r\n if (code === \"P2025\") {\r\n return { status: 404, data: { error: \"Record not found.\" } };\r\n }\r\n if (code === \"P2002\") {\r\n const fields = e?.meta?.target ?? \"unknown fields\";\r\n return {\r\n status: 409,\r\n data: { error: `Unique constraint failed on: ${fields}` },\r\n };\r\n }\r\n if (code === \"P2003\") {\r\n return { status: 400, data: { error: \"Foreign key constraint failed.\" } };\r\n }\r\n if (code === \"P2014\") {\r\n return { status: 400, data: { error: \"Relation violation.\" } };\r\n }\r\n\r\n return { status: 500, data: { error: e?.message ?? \"Internal server error.\" } };\r\n}","import { PrismaClient } from \"@prisma/client\";\r\nimport { createRouter } from \"../router\";\r\nimport type { PrismaRestOptions } from \"../types\";\r\n\r\n/**\r\n * Express adapter for omni-rest.\r\n *\r\n * @example\r\n * ```ts\r\n * import express from \"express\";\r\n * import { PrismaClient } from \"@prisma/client\";\r\n * import { expressAdapter } from \"omni-rest/express\";\r\n *\r\n * const app = express();\r\n * const prisma = new PrismaClient();\r\n *\r\n * app.use(express.json());\r\n * app.use(\"/api\", expressAdapter(prisma, {\r\n * allow: [\"department\", \"category\", \"city\"],\r\n * }));\r\n *\r\n * // Auto-generates:\r\n * // GET /api/department\r\n * // POST /api/department\r\n * // GET /api/department/:id\r\n * // PUT /api/department/:id\r\n * // PATCH /api/department/:id\r\n * // DELETE /api/department/:id\r\n * // PATCH /api/department/bulk/update\r\n * // DELETE /api/department/bulk/delete\r\n * ```\r\n */\r\nexport function expressAdapter(\r\n prisma: PrismaClient,\r\n options: PrismaRestOptions = {}\r\n) {\r\n // Lazy require so express stays optional peer dep\r\n // eslint-disable-next-line @typescript-eslint/no-var-requires\r\n const { Router } = require(\"express\");\r\n const router = Router();\r\n const { handle } = createRouter(prisma, options);\r\n\r\n // PATCH + DELETE /model/bulk/update and /model/bulk/delete\r\n router.patch(\"/:model/bulk/update\", async (req: any, res: any) => {\r\n try {\r\n const { status, data, headers } = await handle(\r\n \"PATCH\",\r\n req.params.model,\r\n null,\r\n req.body ?? [],\r\n new URLSearchParams(\r\n Object.entries(req.query as Record<string, string>)\r\n .map(([k, v]) => `${k}=${v}`)\r\n .join(\"&\")\r\n ),\r\n \"bulk-update\"\r\n );\r\n if (headers) Object.entries(headers).forEach(([k, v]) => res.setHeader(k, v));\r\n if (status === 204) return res.sendStatus(204);\r\n return res.status(status).json(data);\r\n } catch (e: any) {\r\n return res.status(500).json({ error: e.message });\r\n }\r\n });\r\n\r\n router.delete(\"/:model/bulk/delete\", async (req: any, res: any) => {\r\n try {\r\n const { status, data, headers } = await handle(\r\n \"DELETE\",\r\n req.params.model,\r\n null,\r\n req.body ?? [],\r\n new URLSearchParams(\r\n Object.entries(req.query as Record<string, string>)\r\n .map(([k, v]) => `${k}=${v}`)\r\n .join(\"&\")\r\n ),\r\n \"bulk-delete\"\r\n );\r\n if (headers) Object.entries(headers).forEach(([k, v]) => res.setHeader(k, v));\r\n if (status === 204) return res.sendStatus(204);\r\n return res.status(status).json(data);\r\n } catch (e: any) {\r\n return res.status(500).json({ error: e.message });\r\n }\r\n });\r\n\r\n // GET + POST /model\r\n router.route(\"/:model\").get(handler).post(handler);\r\n\r\n // GET + PUT + PATCH + DELETE /model/:id\r\n router\r\n .route(\"/:model/:id\")\r\n .get(handler)\r\n .put(handler)\r\n .patch(handler)\r\n .delete(handler);\r\n\r\n async function handler(req: any, res: any) {\r\n try {\r\n const { status, data, headers } = await handle(\r\n req.method,\r\n req.params.model,\r\n req.params.id ?? null,\r\n req.body ?? {},\r\n new URLSearchParams(\r\n Object.entries(req.query as Record<string, string>)\r\n .map(([k, v]) => `${k}=${v}`)\r\n .join(\"&\")\r\n )\r\n );\r\n\r\n if (headers) Object.entries(headers).forEach(([k, v]) => res.setHeader(k, v));\r\n if (status === 204) return res.sendStatus(204);\r\n return res.status(status).json(data);\r\n } catch (e: any) {\r\n return res.status(500).json({ error: e.message });\r\n }\r\n }\r\n\r\n return router;\r\n}\r\n\r\n/**\r\n * Express error-handling middleware that maps Prisma error codes to clean\r\n * JSON responses. Mount it after all routes to catch Prisma errors from\r\n * both omni-rest and your own custom routes.\r\n *\r\n * @example\r\n * ```ts\r\n * import { expressAdapter, omniRestErrorHandler } from \"omni-rest/express\";\r\n *\r\n * app.use(\"/api\", expressAdapter(prisma));\r\n * app.use(\"/custom\", myCustomRoutes);\r\n * app.use(omniRestErrorHandler());\r\n * ```\r\n */\r\nexport function omniRestErrorHandler() {\r\n // Standard Express 4-argument error middleware\r\n return function (err: any, _req: any, res: any, next: any) {\r\n const code = err?.code;\r\n\r\n if (code === \"P2025\") {\r\n return res.status(404).json({ error: \"Record not found.\" });\r\n }\r\n if (code === \"P2002\") {\r\n const fields = err?.meta?.target ?? \"unknown fields\";\r\n return res.status(409).json({ error: `Unique constraint failed on: ${fields}` });\r\n }\r\n if (code === \"P2003\") {\r\n return res.status(400).json({ error: \"Foreign key constraint failed.\" });\r\n }\r\n if (code === \"P2014\") {\r\n return res.status(400).json({ error: \"Relation violation.\" });\r\n }\r\n\r\n // Not a known Prisma error — pass through to next error handler\r\n if (!code) {\r\n return next(err);\r\n }\r\n\r\n return res.status(500).json({ error: err?.message ?? \"Internal server error.\" });\r\n };\r\n}"]}
1
+ {"version":3,"sources":["../../src/introspect.ts","../../src/query-builder.ts","../../src/middleware-helpers.ts","../../src/router.ts","../../src/adapters/express.ts"],"names":["f","sortedOps","id"],"mappings":";;;;;;;;;;AAQO,SAAS,UAAU,MAAA,EAA2B;AACnD,EAAA,IAAI,GAAA;AAGJ,EAAA,IAAI,MAAA,EAAQ,mBAAmB,MAAA,EAAQ;AACrC,IAAA,MAAM,SAAA,GAAY,OAAO,iBAAA,CAAkB,MAAA;AAE3C,IAAA,GAAA,GAAM,MAAA,CAAO,QAAQ,SAAS,CAAA,CAAE,IAAI,CAAC,CAAC,IAAA,EAAM,KAAK,CAAA,MAAsB;AAAA,MACrE,IAAA;AAAA,MACA,GAAG,KAAA;AAAA,MACH,SAAS,KAAA,CAAM,MAAA,IAAU,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAY;AAAA,QAC5C,GAAG,CAAA;AAAA,QACH,YAAA,EAAc,CAAA,CAAE,IAAA,KAAS,QAAA,GAAW,EAAE,IAAA,GAAO;AAAA,OAC/C,CAAE;AAAA,KACJ,CAAE,CAAA;AAAA,EACJ;AAKA,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,IAAI;AAEF,MAAA,MAAM,YAAA,GAAe,UAAQ,gBAAgB,CAAA;AAC7C,MAAA,MAAM,UAAA,GAAa,YAAA,EAAc,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,MAAA;AAC1D,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,GAAA,GAAM,UAAA;AAAA,MACR;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACvB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,mDAAmD,OAAO,GAAG,CAAA,yCAAA,EAA4C,CAAC,CAAC,MAAA,EAAQ,iBAAA,EAAmB,MAAM,CAAA,YAAA,EAAe,KAAK,SAAA,CAAU,GAAG,EAAE,KAAA,CAAM,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA,KAC9L;AAAA,EACF;AAEA,EAAA,OAAO,GAAA,CAAI,GAAA,CAAI,CAAC,KAAA,KAAe;AAC7B,IAAA,MAAM,MAAA,GAAsB,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAY;AAAA,MACxD,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,YAAY,CAAA,CAAE,UAAA;AAAA,MACd,QAAQ,CAAA,CAAE,MAAA;AAAA,MACV,UAAA,EAAY,CAAC,CAAC,CAAA,CAAE,YAAA;AAAA,MAChB,iBAAiB,CAAC,CAAC,EAAE,eAAA,IAAmB,CAAC,CAAC,CAAA,CAAE,OAAA;AAAA,MAC5C,WAAA,EAAa,CAAC,CAAC,CAAA,CAAE;AAAA,KACnB,CAAE,CAAA;AAEF,IAAA,MAAM,OAAA,GACJ,MAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAW,CAAA,CAAE,IAAI,CAAA,EAAG,IAAA,IAAQ,IAAA;AAEjD,IAAA,OAAO;AAAA,MACL,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,SAAA,EAAW,WAAA,CAAY,KAAA,CAAM,IAAI,CAAA;AAAA,MACjC,MAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAOO,SAAS,YAAY,SAAA,EAA2B;AACrD,EAAA,OAAO,UAAU,WAAA,EAAY;AAC/B;AAKO,SAAS,aAAA,CACd,QACA,SAAA,EAC2B;AAC3B,EAAA,MAAM,QAAA,GAAW,SAAA,GACb,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAAM,SAAA,CAAU,QAAA,CAAS,CAAA,CAAE,SAAS,CAAC,CAAA,GACpD,MAAA;AAEJ,EAAA,OAAO,MAAA,CAAO,WAAA,CAAY,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,SAAA,EAAW,CAAC,CAAC,CAAC,CAAA;AACjE;AAWO,SAAS,qBAAA,CACd,QACA,aAAA,EACiD;AACjD,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,MAAM,IAAI,MAAA,CAAO,IAAA,CAAK,CAACA,EAAAA,KAAMA,EAAAA,CAAE,SAAS,aAAa,CAAA;AACrD,IAAA,IAAI,CAAC,GAAG,OAAO,IAAA;AAEf,IAAA,MAAM,QAAQ,CAAA,CAAE,IAAA,KAAS,SAAA,GAAY,KAAA,uBAAY,IAAA,EAAK;AACtD,IAAA,OAAO,EAAE,KAAA,EAAO,aAAA,EAAe,KAAA,EAAM;AAAA,EACvC;AAEA,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,IAAA,KAAS,WAAA,IAAe,CAAA,CAAE,IAAA,KAAS,UAAU,CAAA;AACpF,EAAA,IAAI,SAAA,SAAkB,EAAE,KAAA,EAAO,aAAa,KAAA,kBAAO,IAAI,MAAK,EAAE;AAE9D,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,IAAA,KAAS,UAAA,IAAc,CAAA,CAAE,IAAA,KAAS,SAAS,CAAA;AACjF,EAAA,IAAI,UAAU,OAAO,EAAE,KAAA,EAAO,UAAA,EAAY,OAAO,KAAA,EAAM;AAEvD,EAAA,OAAO,IAAA;AACT;AAMO,SAAS,WAAA,CAAY,QAAa,IAAA,EAAsB;AAG7D,EAAA,MAAM,GAAA,GACJ,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAAY,GAAI,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA;AACvD,EAAA,MAAM,QAAA,GAAW,OAAO,GAAG,CAAA;AAE3B,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,0CAAA,EAA6C,IAAA,CAAK,IAAI,CAAA,mBAAA,EACjC,GAAG,CAAA,UAAA;AAAA,KAC1B;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;;;AC1IA,IAAM,gBAAA,GAA2C;AAAA,EAC/C,IAAA,EAAM,KAAA;AAAA,EACN,IAAA,EAAM,KAAA;AAAA,EACN,GAAA,EAAK,IAAA;AAAA,EACL,GAAA,EAAK,IAAA;AAAA,EACL,SAAA,EAAW,UAAA;AAAA,EACX,UAAA,EAAY,UAAA;AAAA;AAAA,EACZ,WAAA,EAAa,YAAA;AAAA,EACb,SAAA,EAAW,UAAA;AAAA,EACX,GAAA,EAAK,IAAA;AAAA,EACL,MAAA,EAAQ,OAAA;AAAA,EACR,IAAA,EAAM;AACR,CAAA;AAKA,IAAM,aAAA,uBAAoB,GAAA,CAAI;AAAA,EAC5B,MAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAC,CAAA;AAwBM,SAAS,WACd,YAAA,EACA,YAAA,GAAe,EAAA,EACf,QAAA,GAAW,KACX,WAAA,EACa;AACb,EAAA,MAAM,QAA6B,EAAC;AACpC,EAAA,MAAM,UAA+B,EAAC;AACtC,EAAA,IAAI,UAAmC,EAAC;AACxC,EAAA,IAAI,MAAA,GAAyC,IAAA;AAG7C,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAA,CAAS,aAAa,GAAA,CAAI,MAAM,CAAA,IAAK,GAAG,CAAC,CAAA;AAClE,EAAA,MAAM,QAAA,GAAW,SAAS,YAAA,CAAa,GAAA,CAAI,OAAO,CAAA,IAAK,MAAA,CAAO,YAAY,CAAC,CAAA;AAC3E,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,QAAQ,CAAA;AACxC,EAAA,MAAM,IAAA,GAAA,CAAQ,OAAO,CAAA,IAAK,IAAA;AAG1B,EAAA,MAAM,SAAA,GAAY,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AACzC,EAAA,IAAI,SAAA,EAAW;AAEb,IAAA,KAAA,MAAW,IAAA,IAAQ,SAAA,CAAU,KAAA,CAAM,GAAG,CAAA,EAAG;AACvC,MAAA,MAAM,CAAC,OAAO,GAAG,CAAA,GAAI,KAAK,IAAA,EAAK,CAAE,MAAM,GAAG,CAAA;AAC1C,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,MAAM,SAAA,GAAY,GAAA,KAAQ,MAAA,GAAS,MAAA,GAAS,KAAA;AAG5C,MAAA,IAAI,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA,EAAG;AAC/B,QAAA,MAAM,QAAA,GAAW,KAAA,CAAM,KAAA,CAAM,SAAA,CAAU,MAAM,CAAA;AAC7C,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,OAAA,CAAQ,QAAQ,CAAA,GAAI,EAAE,MAAA,EAAQ,SAAA,EAAU;AAAA,QAC1C;AAAA,MACF,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,KAAK,CAAA,GAAI,SAAA;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,YAAA,GAAe,YAAA,CAAa,GAAA,CAAI,SAAS,CAAA;AAC/C,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,KAAA,MAAW,GAAA,IAAO,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA,EAAG;AACzC,MAAA,IAAI,IAAI,IAAA,EAAK,UAAW,GAAA,CAAI,IAAA,EAAM,CAAA,GAAI,IAAA;AAAA,IACxC;AAAA,EACF;AAGA,EAAA,MAAM,cAAc,YAAA,CAAa,GAAA,CAAI,QAAQ,CAAA,IAAK,YAAA,CAAa,IAAI,QAAQ,CAAA;AAC3E,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,MAAA,GAAS,EAAC;AACV,IAAA,KAAA,MAAW,KAAA,IAAS,WAAA,CAAY,KAAA,CAAM,GAAG,CAAA,EAAG;AAC1C,MAAA,IAAI,MAAM,IAAA,EAAK,SAAU,KAAA,CAAM,IAAA,EAAM,CAAA,GAAI,IAAA;AAAA,IAC3C;AAAA,EACF;AAGA,EAAA,MAAM,WAAA,GAAc,YAAA,CAAa,GAAA,CAAI,QAAQ,CAAA;AAC7C,EAAA,IAAI,eAAe,WAAA,EAAa;AAC9B,IAAA,MAAM,eAAe,WAAA,CAAY,MAAA;AAAA,MAC/B,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,QAAA,IAAY,CAAC,CAAA,CAAE;AAAA,KACnC;AACA,IAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,MAAA,MAAM,SAAA,GAAY,YAAA,CAAa,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,QACzC,CAAC,EAAE,IAAI,GAAG,EAAE,QAAA,EAAU,WAAA,EAAa,MAAM,aAAA;AAAc,OACzD,CAAE,CAAA;AAEF,MAAA,KAAA,CAAM,IAAI,CAAA,GAAI,SAAA;AAAA,IAChB;AAAA,EACF;AAGA,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,YAAA,CAAa,SAAQ,EAAG;AACjD,IAAA,IAAI,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA,EAAG;AAI5B,IAAA,MAAM,QAAA,GAAW,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAChC,IAAA,IAAI,QAAA,GAAW,KAAK,GAAA,CAAI,OAAA,CAAQ,KAAK,QAAA,GAAW,CAAC,CAAA,KAAM,EAAA,IAAM,WAAA,EAAa;AACxE,MAAA,MAAM,YAAA,GAAe,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAA;AAC1C,MAAA,MAAM,SAAA,GAAY,GAAA,CAAI,KAAA,CAAM,QAAA,GAAW,CAAC,CAAA;AAExC,MAAA,MAAM,eAAe,WAAA,CAAY,IAAA;AAAA,QAC/B,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,gBAAgB,CAAA,CAAE;AAAA,OACtC;AAEA,MAAA,IAAI,YAAA,EAAc;AAEhB,QAAA,MAAMC,UAAAA,GAAY,MAAA,CAAO,IAAA,CAAK,gBAAgB,CAAA,CAAE,IAAA;AAAA,UAC9C,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAS,CAAA,CAAE;AAAA,SACzB;AACA,QAAA,IAAI,SAAA,GAAY,SAAA;AAChB,QAAA,IAAI,WAAA;AAEJ,QAAA,IAAI,SAAA,GAAY,KAAA;AAChB,QAAA,KAAA,MAAW,UAAUA,UAAAA,EAAW;AAC9B,UAAA,IAAI,SAAA,CAAU,QAAA,CAAS,MAAM,CAAA,EAAG;AAC9B,YAAA,SAAA,GAAY,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,CAAC,OAAO,MAAM,CAAA;AAC7C,YAAA,MAAM,QAAA,GAAW,iBAAiB,MAAM,CAAA;AACxC,YAAA,IAAI,WAAA,GAAmB,KAAA;AAEvB,YAAA,IAAI,QAAA,KAAa,IAAA,IAAQ,QAAA,KAAa,OAAA,EAAS;AAC7C,cAAA,WAAA,GAAc,KAAA,CAAM,MAAM,GAAG,CAAA,CAAE,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAA;AAAA,YACpD,CAAA,MAAA,IAAW,CAAC,KAAA,CAAM,MAAA,CAAO,WAAW,CAAC,CAAA,IAAK,OAAO,WAAA,KAAgB,QAAA,EAAU;AACzE,cAAA,WAAA,GAAc,OAAO,WAAW,CAAA;AAAA,YAClC;AAEA,YAAA,MAAM,QAAQ,MAAA,KAAW,YAAA,GAAe,EAAE,IAAA,EAAM,aAAA,KAAkB,EAAC;AACnE,YAAA,WAAA,GAAc,EAAE,CAAC,QAAQ,GAAG,WAAA,EAAa,GAAG,KAAA,EAAM;AAClD,YAAA,SAAA,GAAY,IAAA;AACZ,YAAA;AAAA,UACF;AAAA,QACF;AAEA,QAAA,IAAI,CAAC,SAAA,EAAW;AAEd,UAAA,IAAI,WAAA,GAAmB,KAAA;AACvB,UAAA,IAAI,KAAA,KAAU,QAAQ,WAAA,GAAc,IAAA;AAAA,eAAA,IAC3B,KAAA,KAAU,SAAS,WAAA,GAAc,KAAA;AAAA,eAAA,IACjC,CAAC,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,KAAK,KAAA,KAAU,EAAA,EAAI,WAAA,GAAc,MAAA,CAAO,KAAK,CAAA;AAC1E,UAAA,WAAA,GAAc,WAAA;AAAA,QAChB;AAIA,QAAA,MAAM,MAAA,GAAS,EAAE,CAAC,SAAS,GAAG,WAAA,EAAY;AAC1C,QAAA,KAAA,CAAM,YAAY,CAAA,GAAI,YAAA,CAAa,SAC/B,EAAE,IAAA,EAAM,QAAO,GACf,MAAA;AAEJ,QAAA;AAAA,MACF;AAAA,IACF;AAIA,IAAA,IAAI,OAAA,GAAU,KAAA;AACd,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,IAAA,CAAK,gBAAgB,CAAA,CAAE,IAAA;AAAA,MAC9C,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAS,CAAA,CAAE;AAAA,KACzB;AAEA,IAAA,KAAA,MAAW,UAAU,SAAA,EAAW;AAC9B,MAAA,IAAI,GAAA,CAAI,QAAA,CAAS,MAAM,CAAA,EAAG;AACxB,QAAA,MAAM,QAAQ,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,CAAC,OAAO,MAAM,CAAA;AACzC,QAAA,MAAM,QAAA,GAAW,iBAAiB,MAAM,CAAA;AAExC,QAAA,IAAI,WAAA,GAAmB,KAAA;AAGvB,QAAA,IAAI,QAAA,KAAa,IAAA,IAAQ,QAAA,KAAa,OAAA,EAAS;AAC7C,UAAA,WAAA,GAAc,KAAA,CAAM,MAAM,GAAG,CAAA,CAAE,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAA;AAAA,QACpD;AAGA,QAAA,IAAI,CAAC,MAAM,MAAA,CAAO,WAAW,CAAC,CAAA,IAAK,OAAO,gBAAgB,QAAA,EAAU;AAClE,UAAA,WAAA,GAAc,OAAO,WAAW,CAAA;AAAA,QAClC;AAGA,QAAA,MAAM,QAAQ,MAAA,KAAW,YAAA,GAAe,EAAE,IAAA,EAAM,aAAA,KAAkB,EAAC;AAEnE,QAAA,KAAA,CAAM,KAAK,IAAI,EAAE,CAAC,QAAQ,GAAG,WAAA,EAAa,GAAG,KAAA,EAAM;AACnD,QAAA,OAAA,GAAU,IAAA;AACV,QAAA;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,IAAI,WAAA,GAAmB,KAAA;AAGvB,MAAA,IAAI,KAAA,KAAU,QAAQ,WAAA,GAAc,IAAA;AAAA,WAAA,IAC3B,KAAA,KAAU,SAAS,WAAA,GAAc,KAAA;AAAA,WAAA,IAEjC,CAAC,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA,IAAK,UAAU,EAAA,EAAI;AAC9C,QAAA,WAAA,GAAc,OAAO,KAAK,CAAA;AAAA,MAC5B;AAEA,MAAA,KAAA,CAAM,GAAG,CAAA,GAAI,WAAA;AAAA,IACf;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,KAAA,EAAO,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,SAAS,MAAA,EAAO;AACvD;;;AC7OA,eAAsB,QAAA,CACpB,MAAA,EACA,KAAA,EACA,MAAA,EACA,GAAA,EACwB;AACxB,EAAA,MAAM,WAAA,GAAc,OAAO,KAAK,CAAA;AAChC,EAAA,IAAI,CAAC,aAAa,OAAO,IAAA;AAEzB,EAAA,MAAM,EAAA,GAAK,YAAY,MAAkC,CAAA;AACzD,EAAA,IAAI,CAAC,IAAI,OAAO,IAAA;AAEhB,EAAA,OAAO,EAAA,CAAG,EAAE,GAAG,GAAA,EAAK,QAAQ,CAAA;AAC9B;AAMA,eAAsB,OAAA,CACpB,MACA,GAAA,EACe;AACf,EAAA,IAAI,CAAC,IAAA,EAAM;AACX,EAAA,IAAI;AACF,IAAA,MAAM,KAAK,GAAG,CAAA;AAAA,EAChB,SAAS,CAAA,EAAG;AAEV,IAAA,OAAA,CAAQ,KAAA,CAAM,2BAA2B,CAAC,CAAA;AAAA,EAC5C;AACF;;;AClBO,SAAS,YAAA,CACd,MAAA,EACA,OAAA,GAA6B,EAAC,EACd;AAChB,EAAA,MAAM;AAAA,IACJ,KAAA;AAAA,IACA,SAAS,EAAC;AAAA,IACV,eAAA;AAAA,IACA,cAAA;AAAA,IACA,YAAA,GAAe,EAAA;AAAA,IACf,QAAA,GAAW,GAAA;AAAA,IACX,UAAA,GAAa,KAAA;AAAA,IACb,eAAA;AAAA,IACA,QAAA,GAAW,IAAA;AAAA,IACX,cAAc,EAAC;AAAA,IACf;AAAA,GACF,GAAI,OAAA;AAGJ,EAAA,MAAM,MAAA,GAAS,UAAU,MAAM,CAAA;AAC/B,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,MAAA,EAAQ,KAAK,CAAA;AAE5C,EAAA,eAAe,OACb,MAAA,EACA,SAAA,EACA,EAAA,EACA,IAAA,EACA,cACA,SAAA,EACwB;AAExB,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,SAAA,CAAU,WAAA,EAAa,CAAA;AAC7C,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,GAAA;AAAA,QACR,IAAA,EAAM;AAAA,UACJ,KAAA,EAAO,UAAU,SAAS,CAAA,2BAAA,CAAA;AAAA,UAC1B,SAAA,EAAW,MAAA,CAAO,IAAA,CAAK,QAAQ;AAAA;AACjC,OACF;AAAA,IACF;AAGA,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,MAAM,cAAA,GAAiB,MAAM,SAAA,CAAU,EAAE,OAAO,IAAA,CAAK,IAAA,EAAM,MAAA,EAAQ,EAAA,EAAI,CAAA;AACvE,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,MAAM,EAAE,KAAA,EAAO,gBAAe,EAAE;AAAA,MACxD;AAAA,IACF;AAGA,IAAA,MAAM,aAAa,MAAM,QAAA,CAAS,MAAA,EAAQ,IAAA,CAAK,WAAW,MAAA,EAAQ;AAAA,MAChE,EAAA;AAAA,MACA;AAAA,KACD,CAAA;AACD,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,MAAM,EAAE,KAAA,EAAO,YAAW,EAAE;AAAA,IACpD;AAGA,IAAA,MAAM,OAAA,CAAQ,iBAAiB,EAAE,KAAA,EAAO,KAAK,IAAA,EAAM,MAAA,EAAQ,EAAA,EAAI,IAAA,EAAM,CAAA;AAGrE,IAAA,IAAI,MAAA;AAEJ,IAAA,IAAI;AACF,MAAA,MAAA,GAAS,MAAM,gBAAA;AAAA,QACb,MAAA;AAAA,QACA,IAAA;AAAA,QACA,MAAA;AAAA,QACA,EAAA;AAAA,QACA,IAAA;AAAA,QACA,YAAA;AAAA,QACA,YAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA,UAAA;AAAA,QACA,eAAA;AAAA,QACA,QAAA;AAAA,QACA,WAAA,CAAY,KAAK,SAAS;AAAA,OAC5B;AAAA,IACF,SAAS,CAAA,EAAQ;AACf,MAAA,OAAO,kBAAkB,CAAC,CAAA;AAAA,IAC5B;AAGA,IAAA,MAAM,QAAQ,cAAA,EAAgB;AAAA,MAC5B,OAAO,IAAA,CAAK,IAAA;AAAA,MACZ,MAAA;AAAA,MACA,EAAA;AAAA,MACA,IAAA;AAAA,MACA,QAAQ,MAAA,CAAO;AAAA,KAChB,CAAA;AAED,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAO;AACpC;AAIA,eAAe,gBAAA,CACb,MAAA,EACA,IAAA,EACA,MAAA,EACA,IACA,IAAA,EACA,YAAA,EACA,YAAA,EACA,QAAA,EACA,WACA,UAAA,GAAa,KAAA,EACb,eAAA,EACA,QAAA,GAAW,MACX,EAAA,EACwB;AACxB,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,MAAA,EAAQ,IAAI,CAAA;AACzC,EAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAS,MAAM,IAAA,EAAM,OAAA,EAAS,QAAO,GAAI,UAAA;AAAA,IACtD,YAAA;AAAA,IACA,YAAA;AAAA,IACA,QAAA;AAAA,IACA,IAAA,CAAK;AAAA,GACP;AAGA,EAAA,MAAM,aACJ,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,MAAA,GAAS,IAAI,OAAA,GAAU,MAAA;AAC9C,EAAA,MAAM,SAAA,GACJ,UAAU,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,MAAA,GAAS,IAAI,MAAA,GAAS,MAAA;AACtD,EAAA,MAAM,UAAA,GAAa,SAAA,GAAY,EAAE,MAAA,EAAQ,SAAA,EAAU,GAAI,UAAA,GAAa,EAAE,OAAA,EAAS,UAAA,EAAW,GAAI,EAAC;AAG/F,EAAA,MAAM,QAAA,GAAW,YAAA,CAAa,IAAA,EAAM,EAAE,CAAA;AAGtC,EAAA,IAAI,MAAA,KAAW,OAAA,IAAW,SAAA,KAAc,aAAA,EAAe;AACrD,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,IAAK,IAAA,CAAK,WAAW,CAAA,EAAG;AAC7C,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,GAAA;AAAA,QACR,IAAA,EAAM,EAAE,KAAA,EAAO,0DAAA;AAA2D,OAC5E;AAAA,IACF;AAGA,IAAA,KAAA,MAAW,QAAQ,IAAA,EAAM;AACvB,MAAA,IAAI,CAAC,IAAA,CAAK,IAAA,CAAK,OAAO,CAAA,EAAG;AACvB,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ,GAAA;AAAA,UACR,MAAM,EAAE,KAAA,EAAO,CAAA,yBAAA,EAA4B,IAAA,CAAK,OAAO,CAAA,MAAA,CAAA;AAAS,SAClE;AAAA,MACF;AAAA,IACF;AAGA,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,GAAA;AAAA,MAC5B,IAAA,CAAK,GAAA,CAAI,CAAC,IAAA,KAAS;AACjB,QAAA,MAAMC,GAAAA,GAAK,IAAA,CAAK,IAAA,CAAK,OAAO,CAAA;AAC5B,QAAA,MAAM,UAAA,GAAa,EAAE,GAAG,IAAA,EAAK;AAC7B,QAAA,OAAO,UAAA,CAAW,KAAK,OAAO,CAAA;AAE9B,QAAA,OAAO,SAAS,MAAA,CAAO;AAAA,UACrB,KAAA,EAAO,EAAE,CAAC,IAAA,CAAK,OAAO,GAAG,QAAA,CAASA,GAAE,CAAA,EAAE;AAAA,UACtC,IAAA,EAAM,UAAA;AAAA,UACN,GAAG;AAAA,SACJ,CAAA;AAAA,MACH,CAAC;AAAA,KACH;AAEA,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ,GAAA;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,SAAS,OAAA,CAAQ,MAAA;AAAA,QACjB,OAAA,EAAS;AAAA;AACX,KACF;AAAA,EACF;AAGA,EAAA,IAAI,MAAA,KAAW,QAAA,IAAY,SAAA,KAAc,aAAA,EAAe;AACtD,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,IAAK,IAAA,CAAK,WAAW,CAAA,EAAG;AAC7C,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,GAAA;AAAA,QACR,IAAA,EAAM,EAAE,KAAA,EAAO,+CAAA;AAAgD,OACjE;AAAA,IACF;AAGA,IAAA,MAAM,MAAM,IAAA,CAAK,GAAA;AAAA,MAAI,CAAC,SACpB,OAAO,IAAA,KAAS,WAAW,IAAA,CAAK,IAAA,CAAK,OAAO,CAAA,GAAI;AAAA,KAClD;AAGA,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,UAAA,CAAW;AAAA,MACvC,KAAA,EAAO;AAAA,QACL,CAAC,KAAK,OAAO,GAAG,EAAE,EAAA,EAAI,GAAA,CAAI,GAAA,CAAI,QAAQ,CAAA;AAAE;AAC1C,KACD,CAAA;AAED,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ,GAAA;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,SAAS,MAAA,CAAO;AAAA;AAClB,KACF;AAAA,EACF;AAGA,EAAA,IAAI,MAAA,KAAW,KAAA,IAAS,CAAC,EAAA,EAAI;AAC3B,IAAA,MAAM,iBAAiB,UAAA,GACnB,qBAAA,CAAsB,IAAA,CAAK,MAAA,EAAQ,eAAe,CAAA,GAClD,IAAA;AACJ,IAAA,MAAM,SAAA,GAAY,cAAA,GACd,EAAE,GAAG,OAAO,CAAC,cAAA,CAAe,KAAK,GAAG,cAAA,CAAe,KAAA,KAAU,UAAA,GAAa,IAAA,GAAO,MAAK,GACtF,KAAA;AAEJ,IAAA,MAAM,CAAC,IAAA,EAAM,KAAK,CAAA,GAAI,MAAO,OAAe,YAAA,CAAa;AAAA,MACvD,QAAA,CAAS,QAAA,CAAS,EAAE,KAAA,EAAO,SAAA,EAAW,SAAS,IAAA,EAAM,IAAA,EAAM,GAAG,UAAA,EAAY,CAAA;AAAA,MAC1E,QAAA,CAAS,KAAA,CAAM,EAAE,KAAA,EAAO,WAAW;AAAA,KACpC,CAAA;AAED,IAAA,MAAM,QAAA,GAAY,KAAe,GAAA,CAAI,CAAC,MAAW,aAAA,CAAc,CAAA,EAAG,EAAE,CAAC,CAAA;AAErE,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,GAAA;AAAA,QACR,IAAA,EAAM,QAAA;AAAA,QACN,OAAA,EAAS,EAAE,eAAA,EAAiB,MAAA,CAAO,KAAK,CAAA;AAAE,OAC5C;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ,GAAA;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM;AAAA,UACJ,KAAA;AAAA,UACA,IAAA,EAAM,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,IAAI,CAAA,GAAI,CAAA;AAAA,UAChC,KAAA,EAAO,IAAA;AAAA,UACP,UAAA,EAAY,IAAA,CAAK,IAAA,CAAK,KAAA,GAAQ,IAAI;AAAA;AACpC;AACF,KACF;AAAA,EACF;AAGA,EAAA,IAAI,MAAA,KAAW,SAAS,EAAA,EAAI;AAC1B,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,UAAA,CAAW;AAAA,MACvC,KAAA,EAAO,EAAE,CAAC,IAAA,CAAK,OAAO,GAAG,QAAA,CAAS,EAAE,CAAA,EAAE;AAAA,MACtC,GAAG;AAAA,KACJ,CAAA;AAED,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,IAAA,EAAM,EAAE,KAAA,EAAO,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,UAAA,EAAa,EAAE,CAAA,YAAA,CAAA,EAAe,EAAE;AAAA,IACnF;AAEA,IAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,MAAM,aAAA,CAAc,MAAA,EAAQ,EAAE,CAAA,EAAE;AAAA,EACxD;AAGA,EAAA,IAAI,MAAA,KAAW,MAAA,IAAU,CAAC,EAAA,EAAI;AAC5B,IAAA,MAAM,SAAS,MAAM,QAAA,CAAS,OAAO,EAAE,IAAA,EAAM,UAAU,CAAA;AACvD,IAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,MAAM,aAAA,CAAc,MAAA,EAAQ,EAAE,CAAA,EAAE;AAAA,EACxD;AAGA,EAAA,IAAA,CAAK,MAAA,KAAW,KAAA,IAAS,MAAA,KAAW,OAAA,KAAY,EAAA,EAAI;AAClD,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,MAAA,CAAO;AAAA,MACnC,KAAA,EAAO,EAAE,CAAC,IAAA,CAAK,OAAO,GAAG,QAAA,CAAS,EAAE,CAAA,EAAE;AAAA,MACtC,IAAA,EAAM;AAAA,KACP,CAAA;AACD,IAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,MAAM,aAAA,CAAc,MAAA,EAAQ,EAAE,CAAA,EAAE;AAAA,EACxD;AAGA,EAAA,IAAI,MAAA,KAAW,YAAY,EAAA,EAAI;AAC7B,IAAA,MAAM,iBAAiB,UAAA,GACnB,qBAAA,CAAsB,IAAA,CAAK,MAAA,EAAQ,eAAe,CAAA,GAClD,IAAA;AAEJ,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,MAAA,CAAO;AAAA,QACnC,KAAA,EAAO,EAAE,CAAC,IAAA,CAAK,OAAO,GAAG,QAAA,CAAS,EAAE,CAAA,EAAE;AAAA,QACtC,MAAM,EAAE,CAAC,eAAe,KAAK,GAAG,eAAe,KAAA;AAAM,OACtD,CAAA;AACD,MAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,IAAA,EAAM,MAAA,EAAO;AAAA,IACrC;AAEA,IAAA,MAAM,SAAS,MAAA,CAAO;AAAA,MACpB,KAAA,EAAO,EAAE,CAAC,IAAA,CAAK,OAAO,GAAG,QAAA,CAAS,EAAE,CAAA;AAAE,KACvC,CAAA;AACD,IAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,IAAA,EAAM,IAAA,EAAK;AAAA,EACnC;AAEA,EAAA,OAAO,EAAE,QAAQ,GAAA,EAAK,IAAA,EAAM,EAAE,KAAA,EAAO,CAAA,OAAA,EAAU,MAAM,CAAA,aAAA,CAAA,EAAgB,EAAE;AACzE;AAQA,SAAS,aAAA,CAAc,QAAa,EAAA,EAA4B;AAC9D,EAAA,IAAI,CAAC,EAAA,IAAM,CAAC,UAAU,OAAO,MAAA,KAAW,UAAU,OAAO,MAAA;AACzD,EAAA,MAAM,OAAA,mBAAU,IAAI,GAAA,CAAI,CAAC,GAAI,EAAA,CAAG,MAAA,IAAU,EAAC,EAAI,GAAI,EAAA,CAAG,SAAA,IAAa,EAAG,CAAC,CAAA;AACvE,EAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,CAAA,EAAG,OAAO,MAAA;AAC/B,EAAA,MAAM,MAA2B,EAAC;AAClC,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC3C,IAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,EAAG,GAAA,CAAI,CAAC,CAAA,GAAI,CAAA;AAAA,EAChC;AACA,EAAA,OAAO,GAAA;AACT;AAKA,SAAS,YAAA,CAAa,MAAW,EAAA,EAA4B;AAC3D,EAAA,IAAI,CAAC,EAAA,IAAM,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,IAAY,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG,OAAO,IAAA;AAC5E,EAAA,MAAM,WAAW,IAAI,GAAA,CAAI,EAAA,CAAG,QAAA,IAAY,EAAE,CAAA;AAC1C,EAAA,IAAI,QAAA,CAAS,IAAA,KAAS,CAAA,EAAG,OAAO,IAAA;AAChC,EAAA,MAAM,MAA2B,EAAC;AAClC,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG;AACzC,IAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,EAAG,GAAA,CAAI,CAAC,CAAA,GAAI,CAAA;AAAA,EACjC;AACA,EAAA,OAAO,GAAA;AACT;AAKA,SAAS,SAAS,EAAA,EAA6B;AAC7C,EAAA,MAAM,CAAA,GAAI,OAAO,EAAE,CAAA;AACnB,EAAA,OAAO,KAAA,CAAM,CAAC,CAAA,GAAI,EAAA,GAAK,CAAA;AACzB;AAKA,SAAS,kBAAkB,CAAA,EAAuB;AAChD,EAAA,MAAM,OAAO,CAAA,EAAG,IAAA;AAEhB,EAAA,IAAI,SAAS,OAAA,EAAS;AACpB,IAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,MAAM,EAAE,KAAA,EAAO,qBAAoB,EAAE;AAAA,EAC7D;AACA,EAAA,IAAI,SAAS,OAAA,EAAS;AACpB,IAAA,MAAM,MAAA,GAAS,CAAA,EAAG,IAAA,EAAM,MAAA,IAAU,gBAAA;AAClC,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ,GAAA;AAAA,MACR,IAAA,EAAM,EAAE,KAAA,EAAO,CAAA,6BAAA,EAAgC,MAAM,CAAA,CAAA;AAAG,KAC1D;AAAA,EACF;AACA,EAAA,IAAI,SAAS,OAAA,EAAS;AACpB,IAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,MAAM,EAAE,KAAA,EAAO,kCAAiC,EAAE;AAAA,EAC1E;AACA,EAAA,IAAI,SAAS,OAAA,EAAS;AACpB,IAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,MAAM,EAAE,KAAA,EAAO,uBAAsB,EAAE;AAAA,EAC/D;AAEA,EAAA,OAAO,EAAE,QAAQ,GAAA,EAAK,IAAA,EAAM,EAAE,KAAA,EAAO,CAAA,EAAG,OAAA,IAAW,wBAAA,EAAyB,EAAE;AAChF;;;AC1VO,SAAS,cAAA,CACd,MAAA,EACA,OAAA,GAA6B,EAAC,EAC9B;AAGA,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,CAAQ,SAAS,CAAA;AACpC,EAAA,MAAM,SAAS,MAAA,EAAO;AACtB,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,YAAA,CAAa,QAAQ,OAAO,CAAA;AAG/C,EAAA,MAAA,CAAO,KAAA,CAAM,qBAAA,EAAuB,OAAO,GAAA,EAAU,GAAA,KAAa;AAChE,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,OAAA,KAAY,MAAM,MAAA;AAAA,QACtC,OAAA;AAAA,QACA,IAAI,MAAA,CAAO,KAAA;AAAA,QACX,IAAA;AAAA,QACA,GAAA,CAAI,QAAQ,EAAC;AAAA,QACb,IAAI,eAAA;AAAA,UACF,OAAO,OAAA,CAAQ,GAAA,CAAI,KAA+B,CAAA,CAC/C,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,GAAG,CAAC,CAAA,CAAA,EAAI,CAAC,CAAA,CAAE,CAAA,CAC3B,KAAK,GAAG;AAAA,SACb;AAAA,QACA;AAAA,OACF;AACA,MAAA,IAAI,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,OAAO,EAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAC,CAAA;AAC5E,MAAA,IAAI,MAAA,KAAW,GAAA,EAAK,OAAO,GAAA,CAAI,WAAW,GAAG,CAAA;AAC7C,MAAA,OAAO,GAAA,CAAI,MAAA,CAAO,MAAM,CAAA,CAAE,KAAK,IAAI,CAAA;AAAA,IACrC,SAAS,CAAA,EAAQ;AACf,MAAA,OAAO,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,CAAA,CAAE,OAAA,EAAS,CAAA;AAAA,IAClD;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,MAAA,CAAO,qBAAA,EAAuB,OAAO,GAAA,EAAU,GAAA,KAAa;AACjE,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,OAAA,KAAY,MAAM,MAAA;AAAA,QACtC,QAAA;AAAA,QACA,IAAI,MAAA,CAAO,KAAA;AAAA,QACX,IAAA;AAAA,QACA,GAAA,CAAI,QAAQ,EAAC;AAAA,QACb,IAAI,eAAA;AAAA,UACF,OAAO,OAAA,CAAQ,GAAA,CAAI,KAA+B,CAAA,CAC/C,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,GAAG,CAAC,CAAA,CAAA,EAAI,CAAC,CAAA,CAAE,CAAA,CAC3B,KAAK,GAAG;AAAA,SACb;AAAA,QACA;AAAA,OACF;AACA,MAAA,IAAI,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,OAAO,EAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAC,CAAA;AAC5E,MAAA,IAAI,MAAA,KAAW,GAAA,EAAK,OAAO,GAAA,CAAI,WAAW,GAAG,CAAA;AAC7C,MAAA,OAAO,GAAA,CAAI,MAAA,CAAO,MAAM,CAAA,CAAE,KAAK,IAAI,CAAA;AAAA,IACrC,SAAS,CAAA,EAAQ;AACf,MAAA,OAAO,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,CAAA,CAAE,OAAA,EAAS,CAAA;AAAA,IAClD;AAAA,EACF,CAAC,CAAA;AAGD,EAAA,MAAA,CAAO,MAAM,SAAS,CAAA,CAAE,IAAI,OAAO,CAAA,CAAE,KAAK,OAAO,CAAA;AAGjD,EAAA,MAAA,CACG,KAAA,CAAM,aAAa,CAAA,CACnB,GAAA,CAAI,OAAO,CAAA,CACX,GAAA,CAAI,OAAO,CAAA,CACX,KAAA,CAAM,OAAO,CAAA,CACb,OAAO,OAAO,CAAA;AAEjB,EAAA,eAAe,OAAA,CAAQ,KAAU,GAAA,EAAU;AACzC,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,OAAA,KAAY,MAAM,MAAA;AAAA,QACtC,GAAA,CAAI,MAAA;AAAA,QACJ,IAAI,MAAA,CAAO,KAAA;AAAA,QACX,GAAA,CAAI,OAAO,EAAA,IAAM,IAAA;AAAA,QACjB,GAAA,CAAI,QAAQ,EAAC;AAAA,QACb,IAAI,eAAA;AAAA,UACF,OAAO,OAAA,CAAQ,GAAA,CAAI,KAA+B,CAAA,CAC/C,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,GAAG,CAAC,CAAA,CAAA,EAAI,CAAC,CAAA,CAAE,CAAA,CAC3B,KAAK,GAAG;AAAA;AACb,OACF;AAEA,MAAA,IAAI,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,OAAO,EAAE,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAC,CAAA;AAC5E,MAAA,IAAI,MAAA,KAAW,GAAA,EAAK,OAAO,GAAA,CAAI,WAAW,GAAG,CAAA;AAC7C,MAAA,OAAO,GAAA,CAAI,MAAA,CAAO,MAAM,CAAA,CAAE,KAAK,IAAI,CAAA;AAAA,IACrC,SAAS,CAAA,EAAQ;AACf,MAAA,OAAO,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,CAAA,CAAE,OAAA,EAAS,CAAA;AAAA,IAClD;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAgBO,SAAS,oBAAA,GAAuB;AAErC,EAAA,OAAO,SAAU,GAAA,EAAU,IAAA,EAAW,GAAA,EAAU,IAAA,EAAW;AACzD,IAAA,MAAM,OAAO,GAAA,EAAK,IAAA;AAElB,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,OAAO,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,qBAAqB,CAAA;AAAA,IAC5D;AACA,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,MAAM,MAAA,GAAS,GAAA,EAAK,IAAA,EAAM,MAAA,IAAU,gBAAA;AACpC,MAAA,OAAO,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,CAAA,6BAAA,EAAgC,MAAM,CAAA,CAAA,EAAI,CAAA;AAAA,IACjF;AACA,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,OAAO,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,kCAAkC,CAAA;AAAA,IACzE;AACA,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,OAAO,GAAA,CAAI,OAAO,GAAG,CAAA,CAAE,KAAK,EAAE,KAAA,EAAO,uBAAuB,CAAA;AAAA,IAC9D;AAGA,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,OAAO,KAAK,GAAG,CAAA;AAAA,IACjB;AAEA,IAAA,OAAO,GAAA,CAAI,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,CAAK,EAAE,KAAA,EAAO,GAAA,EAAK,OAAA,IAAW,wBAAA,EAA0B,CAAA;AAAA,EACjF,CAAA;AACF","file":"express.js","sourcesContent":["import type { ModelMeta, FieldMeta } from \"./types\";\r\n\r\n/**\r\n * Reads Prisma's DMMF (Data Model Meta Format) at runtime\r\n * and returns structured metadata for every model in your schema.\r\n *\r\n * No file reading. No code generation. Pure runtime introspection.\r\n */\r\nexport function getModels(prisma?: any): ModelMeta[] {\r\n let raw: any[] | undefined;\r\n\r\n // Try to get from PrismaClient instance first (new v5 format)\r\n if (prisma?._runtimeDataModel?.models) {\r\n const modelsObj = prisma._runtimeDataModel.models;\r\n // Convert object to array, adding name from key\r\n raw = Object.entries(modelsObj).map(([name, model]: [string, any]) => ({\r\n name,\r\n ...model,\r\n fields: (model.fields || []).map((f: any) => ({\r\n ...f,\r\n relationName: f.kind === \"object\" ? f.name : undefined,\r\n })),\r\n }));\r\n }\r\n\r\n // Fallback to DMMF from @prisma/client module — use plain require so\r\n // vi.mock(\"@prisma/client\") can intercept it in tests. The CLI handles\r\n // the global-install path resolution separately before calling getModels.\r\n if (!raw) {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-var-requires\r\n const prismaModule = require(\"@prisma/client\");\r\n const dmmfModels = prismaModule?.Prisma?.dmmf?.datamodel?.models;\r\n if (dmmfModels) {\r\n raw = dmmfModels;\r\n }\r\n } catch {\r\n // @prisma/client not available or not generated — handled below\r\n }\r\n }\r\n\r\n if (!raw) {\r\n throw new Error(\r\n \"[omni-rest] Could not find Prisma DMMF. Ensure Prisma client is generated and you're passing a PrismaClient instance to omni-rest.\"\r\n );\r\n }\r\n\r\n if (!Array.isArray(raw)) {\r\n throw new Error(\r\n `[omni-rest] Expected models to be an array, got ${typeof raw}. Debug: prisma._runtimeDataModel.models=${!!prisma?._runtimeDataModel?.models}, raw value=${JSON.stringify(raw).slice(0, 100)}`\r\n );\r\n }\r\n\r\n return raw.map((model: any) => {\r\n const fields: FieldMeta[] = model.fields.map((f: any) => ({\r\n name: f.name,\r\n type: f.type,\r\n isId: f.isId,\r\n isRequired: f.isRequired,\r\n isList: f.isList,\r\n isRelation: !!f.relationName,\r\n hasDefaultValue: !!f.hasDefaultValue || !!f.default,\r\n isUpdatedAt: !!f.isUpdatedAt,\r\n }));\r\n\r\n const idField =\r\n model.fields.find((f: any) => f.isId)?.name ?? \"id\";\r\n\r\n return {\r\n name: model.name,\r\n routeName: toRouteName(model.name),\r\n fields,\r\n idField,\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * Converts a Prisma model name to a URL-safe route segment.\r\n * \"UserProfile\" → \"userprofile\"\r\n * \"OrderItem\" → \"orderitem\"\r\n */\r\nexport function toRouteName(modelName: string): string {\r\n return modelName.toLowerCase();\r\n}\r\n\r\n/**\r\n * Returns a map of routeName → ModelMeta for O(1) lookups.\r\n */\r\nexport function buildModelMap(\r\n models: ModelMeta[],\r\n allowList?: string[]\r\n): Record<string, ModelMeta> {\r\n const filtered = allowList\r\n ? models.filter((m) => allowList.includes(m.routeName))\r\n : models;\r\n\r\n return Object.fromEntries(filtered.map((m) => [m.routeName, m]));\r\n}\r\n\r\n/**\r\n * Auto-detects the soft-delete field for a model.\r\n * Returns the field name and the value to set, or null if not found.\r\n *\r\n * Priority:\r\n * 1. Explicit `softDeleteField` option\r\n * 2. A field named \"deletedAt\" with type DateTime\r\n * 3. A field named \"isActive\" with type Boolean\r\n */\r\nexport function detectSoftDeleteField(\r\n fields: FieldMeta[],\r\n explicitField?: string\r\n): { field: string; value: Date | boolean } | null {\r\n if (explicitField) {\r\n const f = fields.find((f) => f.name === explicitField);\r\n if (!f) return null;\r\n // Infer value from type\r\n const value = f.type === \"Boolean\" ? false : new Date();\r\n return { field: explicitField, value };\r\n }\r\n\r\n const deletedAt = fields.find((f) => f.name === \"deletedAt\" && f.type === \"DateTime\");\r\n if (deletedAt) return { field: \"deletedAt\", value: new Date() };\r\n\r\n const isActive = fields.find((f) => f.name === \"isActive\" && f.type === \"Boolean\");\r\n if (isActive) return { field: \"isActive\", value: false };\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Gets the Prisma client delegate for a model.\r\n * prisma[\"userProfile\"] or prisma[\"user\"] — handles camelCase.\r\n */\r\nexport function getDelegate(prisma: any, meta: ModelMeta): any {\r\n // Prisma client properties are camelCase of model name\r\n // \"UserProfile\" → \"userProfile\"\r\n const key =\r\n meta.name.charAt(0).toLowerCase() + meta.name.slice(1);\r\n const delegate = prisma[key];\r\n\r\n if (!delegate) {\r\n throw new Error(\r\n `Could not find Prisma delegate for model \"${meta.name}\". ` +\r\n `Expected prisma.${key} to exist.`\r\n );\r\n }\r\n\r\n return delegate;\r\n}","import type { ParsedQuery, FieldMeta } from \"./types\";\r\n\r\n/**\r\n * Supported filter suffixes and their Prisma equivalents.\r\n *\r\n * Usage in URL:\r\n * ?name_contains=john\r\n * ?age_gte=18\r\n * ?status_in=active,inactive\r\n * ?email_not=test@test.com\r\n */\r\nconst FILTER_OPERATORS: Record<string, string> = {\r\n _gte: \"gte\",\r\n _lte: \"lte\",\r\n _gt: \"gt\",\r\n _lt: \"lt\",\r\n _contains: \"contains\",\r\n _icontains: \"contains\", // case-insensitive version (mode: insensitive)\r\n _startsWith: \"startsWith\",\r\n _endsWith: \"endsWith\",\r\n _in: \"in\",\r\n _notIn: \"notIn\",\r\n _not: \"not\",\r\n};\r\n\r\n/**\r\n * Reserved query keys — these are NOT treated as filters.\r\n */\r\nconst RESERVED_KEYS = new Set([\r\n \"page\",\r\n \"limit\",\r\n \"sort\",\r\n \"include\",\r\n \"select\",\r\n \"fields\",\r\n \"search\",\r\n]);\r\n\r\n/**\r\n * Parses URLSearchParams into a full Prisma query object.\r\n *\r\n * Supports:\r\n * Filtering → ?name=John ?age_gte=18 ?status_in=a,b\r\n * Relation filter → ?employees.isActive=true ?author.name_contains=john\r\n * Sorting → ?sort=createdAt:desc or ?sort=name:asc\r\n * Pagination → ?page=2&limit=10\r\n * Relations → ?include=posts,profile\r\n * Fields → ?select=id,name,email or ?fields=id,name,email\r\n * Search → ?search=eng (queries all String fields with OR)\r\n *\r\n * Dot-notation relation filtering:\r\n * ?relation.field=value → isList → { relation: { some: { field: value } } }\r\n * → singular → { relation: { field: value } }\r\n * Operator suffixes work inside dot notation:\r\n * ?author.name_contains=john → { author: { name: { contains: \"john\" } } }\r\n *\r\n * NOTE: Only one level of nesting is supported (relation.field).\r\n * Deeply nested filters (relation.nested.field) are not supported and will\r\n * be treated as a regular (non-relation) filter key.\r\n */\r\nexport function buildQuery(\r\n searchParams: URLSearchParams,\r\n defaultLimit = 20,\r\n maxLimit = 100,\r\n modelFields?: FieldMeta[]\r\n): ParsedQuery {\r\n const where: Record<string, any> = {};\r\n const orderBy: Record<string, any> = {};\r\n let include: Record<string, boolean> = {};\r\n let select: Record<string, boolean> | null = null;\r\n\r\n // ─── Pagination ────────────────────────────────────────────────────────────\r\n const page = Math.max(1, parseInt(searchParams.get(\"page\") ?? \"1\"));\r\n const rawLimit = parseInt(searchParams.get(\"limit\") ?? String(defaultLimit));\r\n const take = Math.min(rawLimit, maxLimit);\r\n const skip = (page - 1) * take;\r\n\r\n // ─── Sort ──────────────────────────────────────────────────────────────────\r\n const sortParam = searchParams.get(\"sort\");\r\n if (sortParam) {\r\n // Supports multiple sorts: ?sort=name:asc,createdAt:desc,_count.posts:desc\r\n for (const part of sortParam.split(\",\")) {\r\n const [field, dir] = part.trim().split(\":\");\r\n if (!field) continue;\r\n const direction = dir === \"desc\" ? \"desc\" : \"asc\";\r\n\r\n // _count.relation:dir → { relation: { _count: dir } }\r\n if (field.startsWith(\"_count.\")) {\r\n const relation = field.slice(\"_count.\".length);\r\n if (relation) {\r\n orderBy[relation] = { _count: direction };\r\n }\r\n } else {\r\n orderBy[field] = direction;\r\n }\r\n }\r\n }\r\n\r\n // ─── Include (relations) ───────────────────────────────────────────────────\r\n const includeParam = searchParams.get(\"include\");\r\n if (includeParam) {\r\n for (const rel of includeParam.split(\",\")) {\r\n if (rel.trim()) include[rel.trim()] = true;\r\n }\r\n }\r\n\r\n // ─── Select (fields) — ?select= or ?fields= alias ────────────────────────\r\n const selectParam = searchParams.get(\"select\") ?? searchParams.get(\"fields\");\r\n if (selectParam) {\r\n select = {};\r\n for (const field of selectParam.split(\",\")) {\r\n if (field.trim()) select[field.trim()] = true;\r\n }\r\n }\r\n\r\n // ─── Global search (?search=) ─────────────────────────────────────────────\r\n const searchValue = searchParams.get(\"search\");\r\n if (searchValue && modelFields) {\r\n const stringFields = modelFields.filter(\r\n (f) => f.type === \"String\" && !f.isRelation\r\n );\r\n if (stringFields.length > 0) {\r\n const orClauses = stringFields.map((f) => ({\r\n [f.name]: { contains: searchValue, mode: \"insensitive\" },\r\n }));\r\n // Merge with any existing where via AND so other filters still apply\r\n where[\"OR\"] = orClauses;\r\n }\r\n }\r\n\r\n // ─── Filters ───────────────────────────────────────────────────────────────\r\n for (const [key, value] of searchParams.entries()) {\r\n if (RESERVED_KEYS.has(key)) continue;\r\n\r\n // ── Dot-notation relation filter (?relation.field[_op]=value) ──────────\r\n // Only handle a single dot (one level deep). Deeper nesting falls through.\r\n const dotIndex = key.indexOf(\".\");\r\n if (dotIndex > 0 && key.indexOf(\".\", dotIndex + 1) === -1 && modelFields) {\r\n const relationName = key.slice(0, dotIndex);\r\n const fieldPart = key.slice(dotIndex + 1); // e.g. \"isActive\" or \"name_contains\"\r\n\r\n const relationMeta = modelFields.find(\r\n (f) => f.name === relationName && f.isRelation\r\n );\r\n\r\n if (relationMeta) {\r\n // Parse the field part for operator suffix\r\n const sortedOps = Object.keys(FILTER_OPERATORS).sort(\r\n (a, b) => b.length - a.length\r\n );\r\n let fieldName = fieldPart;\r\n let fieldFilter: any;\r\n\r\n let opMatched = false;\r\n for (const suffix of sortedOps) {\r\n if (fieldPart.endsWith(suffix)) {\r\n fieldName = fieldPart.slice(0, -suffix.length);\r\n const prismaOp = FILTER_OPERATORS[suffix];\r\n let parsedValue: any = value;\r\n\r\n if (prismaOp === \"in\" || prismaOp === \"notIn\") {\r\n parsedValue = value.split(\",\").map((v) => v.trim());\r\n } else if (!isNaN(Number(parsedValue)) && typeof parsedValue === \"string\") {\r\n parsedValue = Number(parsedValue);\r\n }\r\n\r\n const extra = suffix === \"_icontains\" ? { mode: \"insensitive\" } : {};\r\n fieldFilter = { [prismaOp]: parsedValue, ...extra };\r\n opMatched = true;\r\n break;\r\n }\r\n }\r\n\r\n if (!opMatched) {\r\n // Exact match — coerce value\r\n let parsedValue: any = value;\r\n if (value === \"true\") parsedValue = true;\r\n else if (value === \"false\") parsedValue = false;\r\n else if (!isNaN(Number(value)) && value !== \"\") parsedValue = Number(value);\r\n fieldFilter = parsedValue;\r\n }\r\n\r\n // isList relation → some: { field: filter }\r\n // singular relation → direct: { field: filter }\r\n const nested = { [fieldName]: fieldFilter };\r\n where[relationName] = relationMeta.isList\r\n ? { some: nested }\r\n : nested;\r\n\r\n continue; // skip regular filter processing for this key\r\n }\r\n }\r\n\r\n // ── Regular filters ────────────────────────────────────────────────────\r\n // Check operator suffixes (longest match first)\r\n let matched = false;\r\n const sortedOps = Object.keys(FILTER_OPERATORS).sort(\r\n (a, b) => b.length - a.length\r\n );\r\n\r\n for (const suffix of sortedOps) {\r\n if (key.endsWith(suffix)) {\r\n const field = key.slice(0, -suffix.length);\r\n const prismaOp = FILTER_OPERATORS[suffix];\r\n\r\n let parsedValue: any = value;\r\n\r\n // Parse arrays\r\n if (prismaOp === \"in\" || prismaOp === \"notIn\") {\r\n parsedValue = value.split(\",\").map((v) => v.trim());\r\n }\r\n\r\n // Attempt numeric coercion\r\n if (!isNaN(Number(parsedValue)) && typeof parsedValue === \"string\") {\r\n parsedValue = Number(parsedValue);\r\n }\r\n\r\n // Case-insensitive flag\r\n const extra = suffix === \"_icontains\" ? { mode: \"insensitive\" } : {};\r\n\r\n where[field] = { [prismaOp]: parsedValue, ...extra };\r\n matched = true;\r\n break;\r\n }\r\n }\r\n\r\n // No operator — exact match\r\n if (!matched) {\r\n let parsedValue: any = value;\r\n\r\n // Coerce booleans\r\n if (value === \"true\") parsedValue = true;\r\n else if (value === \"false\") parsedValue = false;\r\n // Coerce numbers\r\n else if (!isNaN(Number(value)) && value !== \"\") {\r\n parsedValue = Number(value);\r\n }\r\n\r\n where[key] = parsedValue;\r\n }\r\n }\r\n\r\n return { where, orderBy, skip, take, include, select };\r\n}","import type { GuardMap, HookFn, HookContext } from \"./types\";\r\n\r\n/**\r\n * Runs the guard for the given model+method combo.\r\n * Returns an error string if blocked, null if allowed.\r\n */\r\nexport async function runGuard(\r\n guards: GuardMap,\r\n model: string,\r\n method: string,\r\n ctx: { id?: string | null; body?: any }\r\n): Promise<string | null> {\r\n const modelGuards = guards[model];\r\n if (!modelGuards) return null;\r\n\r\n const fn = modelGuards[method as keyof typeof modelGuards];\r\n if (!fn) return null;\r\n\r\n return fn({ ...ctx, method });\r\n}\r\n\r\n/**\r\n * Runs a lifecycle hook (beforeOperation / afterOperation).\r\n * Silently swallows errors so hooks never crash the main flow.\r\n */\r\nexport async function runHook(\r\n hook: HookFn | undefined,\r\n ctx: HookContext\r\n): Promise<void> {\r\n if (!hook) return;\r\n try {\r\n await hook(ctx);\r\n } catch (e) {\r\n // Hooks should not crash the request\r\n console.error(\"[omni-rest] Hook error:\", e);\r\n }\r\n}","import { PrismaClient } from \"@prisma/client\";\r\nimport { getModels, buildModelMap, getDelegate, detectSoftDeleteField } from \"./introspect\";\r\nimport { buildQuery } from \"./query-builder\";\r\nimport { runGuard, runHook } from \"./middleware-helpers\";\r\nimport type {\r\n PrismaRestOptions,\r\n HandlerResult,\r\n RouterInstance,\r\n ModelMeta,\r\n FieldGuardConfig,\r\n RateLimitFn,\r\n} from \"./types\";\r\n\r\n/**\r\n * Creates a framework-agnostic CRUD router powered by Prisma DMMF.\r\n *\r\n * All adapters (Express, Next.js, Fastify) use this under the hood.\r\n */\r\nexport function createRouter(\r\n prisma: PrismaClient,\r\n options: PrismaRestOptions = {}\r\n): RouterInstance {\r\n const {\r\n allow,\r\n guards = {},\r\n beforeOperation,\r\n afterOperation,\r\n defaultLimit = 20,\r\n maxLimit = 100,\r\n softDelete = false,\r\n softDeleteField,\r\n envelope = true,\r\n fieldGuards = {},\r\n rateLimit,\r\n } = options;\r\n\r\n // Introspect schema once at startup\r\n const models = getModels(prisma);\r\n const modelMap = buildModelMap(models, allow);\r\n\r\n async function handle(\r\n method: string,\r\n modelName: string,\r\n id: string | null,\r\n body: any,\r\n searchParams: URLSearchParams,\r\n operation?: string\r\n ): Promise<HandlerResult> {\r\n // ── 1. Resolve model ───────────────────────────────────────────────────\r\n const meta = modelMap[modelName.toLowerCase()];\r\n if (!meta) {\r\n return {\r\n status: 404,\r\n data: {\r\n error: `Model \"${modelName}\" not found or not exposed.`,\r\n available: Object.keys(modelMap),\r\n },\r\n };\r\n }\r\n\r\n // ── 2. Rate limit check ────────────────────────────────────────────────\r\n if (rateLimit) {\r\n const rateLimitError = await rateLimit({ model: meta.name, method, id });\r\n if (rateLimitError) {\r\n return { status: 429, data: { error: rateLimitError } };\r\n }\r\n }\r\n\r\n // ── 3. Guard check ─────────────────────────────────────────────────────\r\n const guardError = await runGuard(guards, meta.routeName, method, {\r\n id,\r\n body,\r\n });\r\n if (guardError) {\r\n return { status: 403, data: { error: guardError } };\r\n }\r\n\r\n // ── 4. Before hook ─────────────────────────────────────────────────────\r\n await runHook(beforeOperation, { model: meta.name, method, id, body });\r\n\r\n // ── 5. Execute operation ───────────────────────────────────────────────\r\n let result: HandlerResult;\r\n\r\n try {\r\n result = await executeOperation(\r\n prisma,\r\n meta,\r\n method,\r\n id,\r\n body,\r\n searchParams,\r\n defaultLimit,\r\n maxLimit,\r\n operation,\r\n softDelete,\r\n softDeleteField,\r\n envelope,\r\n fieldGuards[meta.routeName]\r\n );\r\n } catch (e: any) {\r\n return handlePrismaError(e);\r\n }\r\n\r\n // ── 6. After hook ──────────────────────────────────────────────────────\r\n await runHook(afterOperation, {\r\n model: meta.name,\r\n method,\r\n id,\r\n body,\r\n result: result.data,\r\n });\r\n\r\n return result;\r\n }\r\n\r\n return { handle, modelMap, models };\r\n}\r\n\r\n// ─── Operation executor ────────────────────────────────────────────────────────\r\n\r\nasync function executeOperation(\r\n prisma: PrismaClient,\r\n meta: ModelMeta,\r\n method: string,\r\n id: string | null,\r\n body: any,\r\n searchParams: URLSearchParams,\r\n defaultLimit: number,\r\n maxLimit: number,\r\n operation?: string,\r\n softDelete = false,\r\n softDeleteField?: string,\r\n envelope = true,\r\n fg?: FieldGuardConfig\r\n): Promise<HandlerResult> {\r\n const delegate = getDelegate(prisma, meta);\r\n const { where, orderBy, skip, take, include, select } = buildQuery(\r\n searchParams,\r\n defaultLimit,\r\n maxLimit,\r\n meta.fields\r\n );\r\n\r\n // Build include/select args (mutually exclusive in Prisma)\r\n const includeArg =\r\n Object.keys(include).length > 0 ? include : undefined;\r\n const selectArg =\r\n select && Object.keys(select).length > 0 ? select : undefined;\r\n const projection = selectArg ? { select: selectArg } : includeArg ? { include: includeArg } : {};\r\n\r\n // Sanitize write body — strip readOnly fields before passing to Prisma\r\n const safeBody = sanitizeBody(body, fg);\r\n\r\n // ── PATCH /model/bulk/update ────────────────────────────────────────────────\r\n if (method === \"PATCH\" && operation === \"bulk-update\") {\r\n if (!Array.isArray(body) || body.length === 0) {\r\n return {\r\n status: 400,\r\n data: { error: \"Request body must be a non-empty array of update records\" },\r\n };\r\n }\r\n\r\n // Validate that each item has an id\r\n for (const item of body) {\r\n if (!item[meta.idField]) {\r\n return {\r\n status: 400,\r\n data: { error: `Each record must have an ${meta.idField} field` },\r\n };\r\n }\r\n }\r\n\r\n // Execute updates in parallel\r\n const results = await Promise.all(\r\n body.map((item) => {\r\n const id = item[meta.idField];\r\n const updateData = { ...item };\r\n delete updateData[meta.idField]; // Remove id from update data\r\n\r\n return delegate.update({\r\n where: { [meta.idField]: coerceId(id) },\r\n data: updateData,\r\n ...projection,\r\n });\r\n })\r\n );\r\n\r\n return {\r\n status: 200,\r\n data: {\r\n updated: results.length,\r\n records: results,\r\n },\r\n };\r\n }\r\n\r\n // ── DELETE /model/bulk/delete ──────────────────────────────────────────────\r\n if (method === \"DELETE\" && operation === \"bulk-delete\") {\r\n if (!Array.isArray(body) || body.length === 0) {\r\n return {\r\n status: 400,\r\n data: { error: \"Request body must be a non-empty array of IDs\" },\r\n };\r\n }\r\n\r\n // Handle both array of IDs and array of objects with id field\r\n const ids = body.map((item: any) =>\r\n typeof item === \"object\" ? item[meta.idField] : item\r\n );\r\n\r\n // Use deleteMany with a where clause\r\n const result = await delegate.deleteMany({\r\n where: {\r\n [meta.idField]: { in: ids.map(coerceId) },\r\n },\r\n });\r\n\r\n return {\r\n status: 200,\r\n data: {\r\n deleted: result.count,\r\n },\r\n };\r\n }\r\n\r\n // ── GET /model ─────────────────────────────────────────────────────────────\r\n if (method === \"GET\" && !id) {\r\n const softDeleteInfo = softDelete\r\n ? detectSoftDeleteField(meta.fields, softDeleteField)\r\n : null;\r\n const listWhere = softDeleteInfo\r\n ? { ...where, [softDeleteInfo.field]: softDeleteInfo.field === \"isActive\" ? true : null }\r\n : where;\r\n\r\n const [data, total] = await (prisma as any).$transaction([\r\n delegate.findMany({ where: listWhere, orderBy, skip, take, ...projection }),\r\n delegate.count({ where: listWhere }),\r\n ]);\r\n\r\n const safeData = (data as any[]).map((r: any) => stripResponse(r, fg));\r\n\r\n if (!envelope) {\r\n return {\r\n status: 200,\r\n data: safeData,\r\n headers: { \"X-Total-Count\": String(total) },\r\n };\r\n }\r\n\r\n return {\r\n status: 200,\r\n data: {\r\n data: safeData,\r\n meta: {\r\n total,\r\n page: Math.floor(skip / take) + 1,\r\n limit: take,\r\n totalPages: Math.ceil(total / take),\r\n },\r\n },\r\n };\r\n }\r\n\r\n // ── GET /model/:id ─────────────────────────────────────────────────────────\r\n if (method === \"GET\" && id) {\r\n const record = await delegate.findUnique({\r\n where: { [meta.idField]: coerceId(id) },\r\n ...projection,\r\n });\r\n\r\n if (!record) {\r\n return { status: 404, data: { error: `${meta.name} with id \"${id}\" not found.` } };\r\n }\r\n\r\n return { status: 200, data: stripResponse(record, fg) };\r\n }\r\n\r\n // ── POST /model ────────────────────────────────────────────────────────────\r\n if (method === \"POST\" && !id) {\r\n const record = await delegate.create({ data: safeBody });\r\n return { status: 201, data: stripResponse(record, fg) };\r\n }\r\n\r\n // ── PUT /model/:id ─────────────────────────────────────────────────────────\r\n if ((method === \"PUT\" || method === \"PATCH\") && id) {\r\n const record = await delegate.update({\r\n where: { [meta.idField]: coerceId(id) },\r\n data: safeBody,\r\n });\r\n return { status: 200, data: stripResponse(record, fg) };\r\n }\r\n\r\n // ── DELETE /model/:id ──────────────────────────────────────────────────────\r\n if (method === \"DELETE\" && id) {\r\n const softDeleteInfo = softDelete\r\n ? detectSoftDeleteField(meta.fields, softDeleteField)\r\n : null;\r\n\r\n if (softDeleteInfo) {\r\n const record = await delegate.update({\r\n where: { [meta.idField]: coerceId(id) },\r\n data: { [softDeleteInfo.field]: softDeleteInfo.value },\r\n });\r\n return { status: 200, data: record };\r\n }\r\n\r\n await delegate.delete({\r\n where: { [meta.idField]: coerceId(id) },\r\n });\r\n return { status: 204, data: null };\r\n }\r\n\r\n return { status: 405, data: { error: `Method ${method} not allowed.` } };\r\n}\r\n\r\n// ─── Helpers ──────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Strips hidden and writeOnly fields from a response record.\r\n * Works on plain objects; passes through null/non-objects unchanged.\r\n */\r\nfunction stripResponse(record: any, fg?: FieldGuardConfig): any {\r\n if (!fg || !record || typeof record !== \"object\") return record;\r\n const blocked = new Set([...(fg.hidden ?? []), ...(fg.writeOnly ?? [])]);\r\n if (blocked.size === 0) return record;\r\n const out: Record<string, any> = {};\r\n for (const [k, v] of Object.entries(record)) {\r\n if (!blocked.has(k)) out[k] = v;\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Strips readOnly fields from a write body before passing to Prisma.\r\n */\r\nfunction sanitizeBody(body: any, fg?: FieldGuardConfig): any {\r\n if (!fg || !body || typeof body !== \"object\" || Array.isArray(body)) return body;\r\n const readOnly = new Set(fg.readOnly ?? []);\r\n if (readOnly.size === 0) return body;\r\n const out: Record<string, any> = {};\r\n for (const [k, v] of Object.entries(body)) {\r\n if (!readOnly.has(k)) out[k] = v;\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Coerces an ID string to number when possible (common with Int PKs).\r\n */\r\nfunction coerceId(id: string): string | number {\r\n const n = Number(id);\r\n return isNaN(n) ? id : n;\r\n}\r\n\r\n/**\r\n * Maps Prisma error codes to meaningful HTTP responses.\r\n */\r\nfunction handlePrismaError(e: any): HandlerResult {\r\n const code = e?.code;\r\n\r\n if (code === \"P2025\") {\r\n return { status: 404, data: { error: \"Record not found.\" } };\r\n }\r\n if (code === \"P2002\") {\r\n const fields = e?.meta?.target ?? \"unknown fields\";\r\n return {\r\n status: 409,\r\n data: { error: `Unique constraint failed on: ${fields}` },\r\n };\r\n }\r\n if (code === \"P2003\") {\r\n return { status: 400, data: { error: \"Foreign key constraint failed.\" } };\r\n }\r\n if (code === \"P2014\") {\r\n return { status: 400, data: { error: \"Relation violation.\" } };\r\n }\r\n\r\n return { status: 500, data: { error: e?.message ?? \"Internal server error.\" } };\r\n}","import { PrismaClient } from \"@prisma/client\";\r\nimport { createRouter } from \"../router\";\r\nimport type { PrismaRestOptions } from \"../types\";\r\n\r\n/**\r\n * Express adapter for omni-rest.\r\n *\r\n * @example\r\n * ```ts\r\n * import express from \"express\";\r\n * import { PrismaClient } from \"@prisma/client\";\r\n * import { expressAdapter } from \"omni-rest/express\";\r\n *\r\n * const app = express();\r\n * const prisma = new PrismaClient();\r\n *\r\n * app.use(express.json());\r\n * app.use(\"/api\", expressAdapter(prisma, {\r\n * allow: [\"department\", \"category\", \"city\"],\r\n * }));\r\n *\r\n * // Auto-generates:\r\n * // GET /api/department\r\n * // POST /api/department\r\n * // GET /api/department/:id\r\n * // PUT /api/department/:id\r\n * // PATCH /api/department/:id\r\n * // DELETE /api/department/:id\r\n * // PATCH /api/department/bulk/update\r\n * // DELETE /api/department/bulk/delete\r\n * ```\r\n */\r\nexport function expressAdapter(\r\n prisma: PrismaClient,\r\n options: PrismaRestOptions = {}\r\n) {\r\n // Lazy require so express stays optional peer dep\r\n // eslint-disable-next-line @typescript-eslint/no-var-requires\r\n const { Router } = require(\"express\");\r\n const router = Router();\r\n const { handle } = createRouter(prisma, options);\r\n\r\n // PATCH + DELETE /model/bulk/update and /model/bulk/delete\r\n router.patch(\"/:model/bulk/update\", async (req: any, res: any) => {\r\n try {\r\n const { status, data, headers } = await handle(\r\n \"PATCH\",\r\n req.params.model,\r\n null,\r\n req.body ?? [],\r\n new URLSearchParams(\r\n Object.entries(req.query as Record<string, string>)\r\n .map(([k, v]) => `${k}=${v}`)\r\n .join(\"&\")\r\n ),\r\n \"bulk-update\"\r\n );\r\n if (headers) Object.entries(headers).forEach(([k, v]) => res.setHeader(k, v));\r\n if (status === 204) return res.sendStatus(204);\r\n return res.status(status).json(data);\r\n } catch (e: any) {\r\n return res.status(500).json({ error: e.message });\r\n }\r\n });\r\n\r\n router.delete(\"/:model/bulk/delete\", async (req: any, res: any) => {\r\n try {\r\n const { status, data, headers } = await handle(\r\n \"DELETE\",\r\n req.params.model,\r\n null,\r\n req.body ?? [],\r\n new URLSearchParams(\r\n Object.entries(req.query as Record<string, string>)\r\n .map(([k, v]) => `${k}=${v}`)\r\n .join(\"&\")\r\n ),\r\n \"bulk-delete\"\r\n );\r\n if (headers) Object.entries(headers).forEach(([k, v]) => res.setHeader(k, v));\r\n if (status === 204) return res.sendStatus(204);\r\n return res.status(status).json(data);\r\n } catch (e: any) {\r\n return res.status(500).json({ error: e.message });\r\n }\r\n });\r\n\r\n // GET + POST /model\r\n router.route(\"/:model\").get(handler).post(handler);\r\n\r\n // GET + PUT + PATCH + DELETE /model/:id\r\n router\r\n .route(\"/:model/:id\")\r\n .get(handler)\r\n .put(handler)\r\n .patch(handler)\r\n .delete(handler);\r\n\r\n async function handler(req: any, res: any) {\r\n try {\r\n const { status, data, headers } = await handle(\r\n req.method,\r\n req.params.model,\r\n req.params.id ?? null,\r\n req.body ?? {},\r\n new URLSearchParams(\r\n Object.entries(req.query as Record<string, string>)\r\n .map(([k, v]) => `${k}=${v}`)\r\n .join(\"&\")\r\n )\r\n );\r\n\r\n if (headers) Object.entries(headers).forEach(([k, v]) => res.setHeader(k, v));\r\n if (status === 204) return res.sendStatus(204);\r\n return res.status(status).json(data);\r\n } catch (e: any) {\r\n return res.status(500).json({ error: e.message });\r\n }\r\n }\r\n\r\n return router;\r\n}\r\n\r\n/**\r\n * Express error-handling middleware that maps Prisma error codes to clean\r\n * JSON responses. Mount it after all routes to catch Prisma errors from\r\n * both omni-rest and your own custom routes.\r\n *\r\n * @example\r\n * ```ts\r\n * import { expressAdapter, omniRestErrorHandler } from \"omni-rest/express\";\r\n *\r\n * app.use(\"/api\", expressAdapter(prisma));\r\n * app.use(\"/custom\", myCustomRoutes);\r\n * app.use(omniRestErrorHandler());\r\n * ```\r\n */\r\nexport function omniRestErrorHandler() {\r\n // Standard Express 4-argument error middleware\r\n return function (err: any, _req: any, res: any, next: any) {\r\n const code = err?.code;\r\n\r\n if (code === \"P2025\") {\r\n return res.status(404).json({ error: \"Record not found.\" });\r\n }\r\n if (code === \"P2002\") {\r\n const fields = err?.meta?.target ?? \"unknown fields\";\r\n return res.status(409).json({ error: `Unique constraint failed on: ${fields}` });\r\n }\r\n if (code === \"P2003\") {\r\n return res.status(400).json({ error: \"Foreign key constraint failed.\" });\r\n }\r\n if (code === \"P2014\") {\r\n return res.status(400).json({ error: \"Relation violation.\" });\r\n }\r\n\r\n // Not a known Prisma error — pass through to next error handler\r\n if (!code) {\r\n return next(err);\r\n }\r\n\r\n return res.status(500).json({ error: err?.message ?? \"Internal server error.\" });\r\n };\r\n}"]}
@@ -270,7 +270,9 @@ function createRouter(prisma, options = {}) {
270
270
  maxLimit = 100,
271
271
  softDelete = false,
272
272
  softDeleteField,
273
- envelope = true
273
+ envelope = true,
274
+ fieldGuards = {},
275
+ rateLimit
274
276
  } = options;
275
277
  const models = getModels(prisma);
276
278
  const modelMap = buildModelMap(models, allow);
@@ -285,6 +287,12 @@ function createRouter(prisma, options = {}) {
285
287
  }
286
288
  };
287
289
  }
290
+ if (rateLimit) {
291
+ const rateLimitError = await rateLimit({ model: meta.name, method, id });
292
+ if (rateLimitError) {
293
+ return { status: 429, data: { error: rateLimitError } };
294
+ }
295
+ }
288
296
  const guardError = await runGuard(guards, meta.routeName, method, {
289
297
  id,
290
298
  body
@@ -307,7 +315,8 @@ function createRouter(prisma, options = {}) {
307
315
  operation,
308
316
  softDelete,
309
317
  softDeleteField,
310
- envelope
318
+ envelope,
319
+ fieldGuards[meta.routeName]
311
320
  );
312
321
  } catch (e) {
313
322
  return handlePrismaError(e);
@@ -323,7 +332,7 @@ function createRouter(prisma, options = {}) {
323
332
  }
324
333
  return { handle, modelMap, models };
325
334
  }
326
- async function executeOperation(prisma, meta, method, id, body, searchParams, defaultLimit, maxLimit, operation, softDelete = false, softDeleteField, envelope = true) {
335
+ async function executeOperation(prisma, meta, method, id, body, searchParams, defaultLimit, maxLimit, operation, softDelete = false, softDeleteField, envelope = true, fg) {
327
336
  const delegate = getDelegate(prisma, meta);
328
337
  const { where, orderBy, skip, take, include, select } = buildQuery(
329
338
  searchParams,
@@ -334,6 +343,7 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
334
343
  const includeArg = Object.keys(include).length > 0 ? include : void 0;
335
344
  const selectArg = select && Object.keys(select).length > 0 ? select : void 0;
336
345
  const projection = selectArg ? { select: selectArg } : includeArg ? { include: includeArg } : {};
346
+ const safeBody = sanitizeBody(body, fg);
337
347
  if (method === "PATCH" && operation === "bulk-update") {
338
348
  if (!Array.isArray(body) || body.length === 0) {
339
349
  return {
@@ -398,17 +408,18 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
398
408
  delegate.findMany({ where: listWhere, orderBy, skip, take, ...projection }),
399
409
  delegate.count({ where: listWhere })
400
410
  ]);
411
+ const safeData = data.map((r) => stripResponse(r, fg));
401
412
  if (!envelope) {
402
413
  return {
403
414
  status: 200,
404
- data,
415
+ data: safeData,
405
416
  headers: { "X-Total-Count": String(total) }
406
417
  };
407
418
  }
408
419
  return {
409
420
  status: 200,
410
421
  data: {
411
- data,
422
+ data: safeData,
412
423
  meta: {
413
424
  total,
414
425
  page: Math.floor(skip / take) + 1,
@@ -426,18 +437,18 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
426
437
  if (!record) {
427
438
  return { status: 404, data: { error: `${meta.name} with id "${id}" not found.` } };
428
439
  }
429
- return { status: 200, data: record };
440
+ return { status: 200, data: stripResponse(record, fg) };
430
441
  }
431
442
  if (method === "POST" && !id) {
432
- const record = await delegate.create({ data: body });
433
- return { status: 201, data: record };
443
+ const record = await delegate.create({ data: safeBody });
444
+ return { status: 201, data: stripResponse(record, fg) };
434
445
  }
435
446
  if ((method === "PUT" || method === "PATCH") && id) {
436
447
  const record = await delegate.update({
437
448
  where: { [meta.idField]: coerceId(id) },
438
- data: body
449
+ data: safeBody
439
450
  });
440
- return { status: 200, data: record };
451
+ return { status: 200, data: stripResponse(record, fg) };
441
452
  }
442
453
  if (method === "DELETE" && id) {
443
454
  const softDeleteInfo = softDelete ? detectSoftDeleteField(meta.fields, softDeleteField) : null;
@@ -455,6 +466,26 @@ async function executeOperation(prisma, meta, method, id, body, searchParams, de
455
466
  }
456
467
  return { status: 405, data: { error: `Method ${method} not allowed.` } };
457
468
  }
469
+ function stripResponse(record, fg) {
470
+ if (!fg || !record || typeof record !== "object") return record;
471
+ const blocked = /* @__PURE__ */ new Set([...fg.hidden ?? [], ...fg.writeOnly ?? []]);
472
+ if (blocked.size === 0) return record;
473
+ const out = {};
474
+ for (const [k, v] of Object.entries(record)) {
475
+ if (!blocked.has(k)) out[k] = v;
476
+ }
477
+ return out;
478
+ }
479
+ function sanitizeBody(body, fg) {
480
+ if (!fg || !body || typeof body !== "object" || Array.isArray(body)) return body;
481
+ const readOnly = new Set(fg.readOnly ?? []);
482
+ if (readOnly.size === 0) return body;
483
+ const out = {};
484
+ for (const [k, v] of Object.entries(body)) {
485
+ if (!readOnly.has(k)) out[k] = v;
486
+ }
487
+ return out;
488
+ }
458
489
  function coerceId(id) {
459
490
  const n = Number(id);
460
491
  return isNaN(n) ? id : n;