@strapi/utils 5.36.1 → 5.37.0

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.
Files changed (72) hide show
  1. package/dist/content-api-constants.d.ts +17 -0
  2. package/dist/content-api-constants.d.ts.map +1 -0
  3. package/dist/content-api-constants.js +43 -0
  4. package/dist/content-api-constants.js.map +1 -0
  5. package/dist/content-api-constants.mjs +39 -0
  6. package/dist/content-api-constants.mjs.map +1 -0
  7. package/dist/content-api-route-params.d.ts +17 -0
  8. package/dist/content-api-route-params.d.ts.map +1 -0
  9. package/dist/content-api-route-params.js +21 -0
  10. package/dist/content-api-route-params.js.map +1 -0
  11. package/dist/content-api-route-params.mjs +18 -0
  12. package/dist/content-api-route-params.mjs.map +1 -0
  13. package/dist/content-types.d.ts +10 -2
  14. package/dist/content-types.d.ts.map +1 -1
  15. package/dist/content-types.js +22 -2
  16. package/dist/content-types.js.map +1 -1
  17. package/dist/content-types.mjs +19 -3
  18. package/dist/content-types.mjs.map +1 -1
  19. package/dist/convert-query-params.d.ts +5 -0
  20. package/dist/convert-query-params.d.ts.map +1 -1
  21. package/dist/convert-query-params.js +5 -4
  22. package/dist/convert-query-params.js.map +1 -1
  23. package/dist/convert-query-params.mjs +5 -4
  24. package/dist/convert-query-params.mjs.map +1 -1
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +9 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +2 -0
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/sanitize/index.d.ts +15 -1
  32. package/dist/sanitize/index.d.ts.map +1 -1
  33. package/dist/sanitize/index.js +73 -3
  34. package/dist/sanitize/index.js.map +1 -1
  35. package/dist/sanitize/index.mjs +74 -4
  36. package/dist/sanitize/index.mjs.map +1 -1
  37. package/dist/sanitize/visitors/index.d.ts +1 -0
  38. package/dist/sanitize/visitors/index.d.ts.map +1 -1
  39. package/dist/sanitize/visitors/index.js +2 -0
  40. package/dist/sanitize/visitors/index.js.map +1 -1
  41. package/dist/sanitize/visitors/index.mjs +1 -0
  42. package/dist/sanitize/visitors/index.mjs.map +1 -1
  43. package/dist/sanitize/visitors/remove-unrecognized-fields.d.ts +4 -0
  44. package/dist/sanitize/visitors/remove-unrecognized-fields.d.ts.map +1 -0
  45. package/dist/sanitize/visitors/remove-unrecognized-fields.js +43 -0
  46. package/dist/sanitize/visitors/remove-unrecognized-fields.js.map +1 -0
  47. package/dist/sanitize/visitors/remove-unrecognized-fields.mjs +41 -0
  48. package/dist/sanitize/visitors/remove-unrecognized-fields.mjs.map +1 -0
  49. package/dist/traverse-entity.d.ts +4 -0
  50. package/dist/traverse-entity.d.ts.map +1 -1
  51. package/dist/traverse-entity.js +13 -7
  52. package/dist/traverse-entity.js.map +1 -1
  53. package/dist/traverse-entity.mjs +13 -7
  54. package/dist/traverse-entity.mjs.map +1 -1
  55. package/dist/validate/index.d.ts +14 -1
  56. package/dist/validate/index.d.ts.map +1 -1
  57. package/dist/validate/index.js +83 -7
  58. package/dist/validate/index.js.map +1 -1
  59. package/dist/validate/index.mjs +83 -7
  60. package/dist/validate/index.mjs.map +1 -1
  61. package/dist/validate/validators.js +0 -1
  62. package/dist/validate/validators.js.map +1 -1
  63. package/dist/validate/validators.mjs +0 -1
  64. package/dist/validate/validators.mjs.map +1 -1
  65. package/dist/validate/visitors/throw-unrecognized-fields.d.ts.map +1 -1
  66. package/dist/validate/visitors/throw-unrecognized-fields.js +16 -32
  67. package/dist/validate/visitors/throw-unrecognized-fields.js.map +1 -1
  68. package/dist/validate/visitors/throw-unrecognized-fields.mjs +17 -33
  69. package/dist/validate/visitors/throw-unrecognized-fields.mjs.map +1 -1
  70. package/dist/zod-schema.d.ts +17 -0
  71. package/dist/zod-schema.d.ts.map +1 -0
  72. package/package.json +4 -4
@@ -21,7 +21,7 @@ const traverseEntity = async (visitor, options, entity)=>{
21
21
  raw: null,
22
22
  attribute: null,
23
23
  rawWithIndices: null
24
- }, schema, getModel } = options;
24
+ }, schema, getModel, allowedExtraRootKeys } = options;
25
25
  let parent = options.parent;
26
26
  const traverseMorphRelationTarget = async (visitor, path, entry)=>{
27
27
  const targetSchema = getModel(entry.__type);
@@ -29,7 +29,8 @@ const traverseEntity = async (visitor, options, entity)=>{
29
29
  schema: targetSchema,
30
30
  path,
31
31
  getModel,
32
- parent
32
+ parent,
33
+ allowedExtraRootKeys
33
34
  };
34
35
  return traverseEntity(visitor, traverseOptions, entry);
35
36
  };
@@ -38,7 +39,8 @@ const traverseEntity = async (visitor, options, entity)=>{
38
39
  schema,
39
40
  path,
40
41
  getModel,
41
- parent
42
+ parent,
43
+ allowedExtraRootKeys
42
44
  };
43
45
  return traverseEntity(visitor, traverseOptions, entry);
44
46
  };
@@ -49,7 +51,8 @@ const traverseEntity = async (visitor, options, entity)=>{
49
51
  schema: targetSchema,
50
52
  path,
51
53
  getModel,
52
- parent
54
+ parent,
55
+ allowedExtraRootKeys
53
56
  };
54
57
  return traverseEntity(visitor, traverseOptions, entry);
55
58
  };
@@ -58,7 +61,8 @@ const traverseEntity = async (visitor, options, entity)=>{
58
61
  schema,
59
62
  path,
60
63
  getModel,
61
- parent
64
+ parent,
65
+ allowedExtraRootKeys
62
66
  };
63
67
  return traverseEntity(visitor, traverseOptions, entry);
64
68
  };
@@ -68,7 +72,8 @@ const traverseEntity = async (visitor, options, entity)=>{
68
72
  schema: targetSchema,
69
73
  path,
70
74
  getModel,
71
- parent
75
+ parent,
76
+ allowedExtraRootKeys
72
77
  };
73
78
  return traverseEntity(visitor, traverseOptions, entry);
74
79
  };
@@ -104,7 +109,8 @@ const traverseEntity = async (visitor, options, entity)=>{
104
109
  attribute,
105
110
  path: newPath,
106
111
  getModel,
107
- parent
112
+ parent,
113
+ allowedExtraRootKeys
108
114
  };
109
115
  await visitor(visitorOptions, visitorUtils);
110
116
  // Extract the value for the current key (after calling the visitor)
@@ -1 +1 @@
1
- {"version":3,"file":"traverse-entity.js","sources":["../src/traverse-entity.ts"],"sourcesContent":["import { clone, isObject, isArray, isNil, curry } from 'lodash/fp';\n\nimport type { Attribute, AnyAttribute, Model, Data } from './types';\nimport { isRelationalAttribute, isMediaAttribute } from './content-types';\n\n/**\n * Execute promises in parallel but throw errors in array index order.\n */\nconst parallelWithOrderedErrors = async <T>(promises: Promise<T>[]): Promise<T[]> => {\n const results = await Promise.allSettled(promises);\n\n // Throw first error in array index order (matches sequential behavior)\n for (let i = 0; i < results.length; i += 1) {\n const result = results[i];\n if (result.status === 'rejected') {\n throw result.reason;\n }\n }\n\n return results.map((r) => (r as PromiseFulfilledResult<T>).value);\n};\n\nexport type VisitorUtils = ReturnType<typeof createVisitorUtils>;\n\nexport interface VisitorOptions {\n data: Record<string, unknown>;\n schema: Model;\n key: string;\n value: Data[keyof Data];\n attribute?: AnyAttribute;\n path: Path;\n getModel(uid: string): Model;\n parent?: Parent;\n}\n\nexport type Visitor = (visitorOptions: VisitorOptions, visitorUtils: VisitorUtils) => void;\n\nexport interface Path {\n raw: string | null;\n attribute: string | null;\n rawWithIndices?: string | null;\n}\n\nexport interface TraverseOptions {\n schema: Model;\n path?: Path;\n parent?: Parent;\n getModel(uid: string): Model;\n}\n\nexport interface Parent {\n attribute?: Attribute;\n key: string | null;\n path: Path;\n schema: Model;\n}\n\nconst traverseEntity = async (visitor: Visitor, options: TraverseOptions, entity: Data) => {\n const { path = { raw: null, attribute: null, rawWithIndices: null }, schema, getModel } = options;\n\n let parent = options.parent;\n\n const traverseMorphRelationTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__type!);\n\n const traverseOptions: TraverseOptions = { schema: targetSchema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseRelationTarget =\n (schema: Model) => async (visitor: Visitor, path: Path, entry: Data) => {\n const traverseOptions: TraverseOptions = { schema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseMediaTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchemaUID = 'plugin::upload.file';\n const targetSchema = getModel(targetSchemaUID);\n\n const traverseOptions: TraverseOptions = { schema: targetSchema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseComponent = async (visitor: Visitor, path: Path, schema: Model, entry: Data) => {\n const traverseOptions: TraverseOptions = { schema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const visitDynamicZoneEntry = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__component!);\n const traverseOptions: TraverseOptions = { schema: targetSchema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n // End recursion\n if (!isObject(entity) || isNil(schema)) {\n return entity;\n }\n\n // Don't mutate the original entity object\n // only clone at 1st level as the next level will get clone when traversed\n const copy = clone(entity);\n const visitorUtils = createVisitorUtils({ data: copy });\n\n const keys = Object.keys(copy);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n // Retrieve the attribute definition associated to the key from the schema\n const attribute = schema.attributes[key] as AnyAttribute | undefined;\n\n const newPath = { ...path };\n\n newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;\n newPath.rawWithIndices = isNil(path.rawWithIndices) ? key : `${path.rawWithIndices}.${key}`;\n\n if (!isNil(attribute)) {\n newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;\n }\n\n // Visit the current attribute\n const visitorOptions: VisitorOptions = {\n data: copy,\n schema,\n key,\n value: copy[key],\n attribute,\n path: newPath,\n getModel,\n parent,\n };\n\n await visitor(visitorOptions, visitorUtils);\n\n // Extract the value for the current key (after calling the visitor)\n const value = copy[key];\n\n // Ignore Nil values or attributes\n if (isNil(value) || isNil(attribute)) {\n continue;\n }\n\n if (isRelationalAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');\n\n const method = isMorphRelation\n ? traverseMorphRelationTarget\n : traverseRelationTarget(getModel(attribute.target!));\n\n if (isArray(value)) {\n // Process array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return method(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await method(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (isMediaAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n\n if (isArray(value)) {\n // Process media array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseMediaTarget(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await traverseMediaTarget(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'component') {\n parent = { schema, key, attribute, path: newPath };\n const targetSchema = getModel(attribute.component);\n\n if (isArray(value)) {\n // Process component array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseComponent(visitor, arrayPath, targetSchema, item);\n })\n );\n } else {\n copy[key] = await traverseComponent(visitor, newPath, targetSchema, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'dynamiczone' && isArray(value)) {\n parent = { schema, key, attribute, path: newPath };\n\n // Process dynamic zone items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return visitDynamicZoneEntry(visitor, arrayPath, item);\n })\n );\n\n continue;\n }\n }\n\n return copy;\n};\n\nconst createVisitorUtils = ({ data }: { data: Data }) => ({\n remove(key: string) {\n delete data[key];\n },\n\n set(key: string, value: Data) {\n data[key] = value;\n },\n});\n\nexport default curry(traverseEntity);\n"],"names":["parallelWithOrderedErrors","promises","results","Promise","allSettled","i","length","result","status","reason","map","r","value","traverseEntity","visitor","options","entity","path","raw","attribute","rawWithIndices","schema","getModel","parent","traverseMorphRelationTarget","entry","targetSchema","__type","traverseOptions","traverseRelationTarget","traverseMediaTarget","targetSchemaUID","traverseComponent","visitDynamicZoneEntry","__component","isObject","isNil","copy","clone","visitorUtils","createVisitorUtils","data","keys","Object","key","attributes","newPath","visitorOptions","isRelationalAttribute","isMorphRelation","relation","toLowerCase","startsWith","method","target","isArray","item","arrayPath","isMediaAttribute","type","component","remove","set","curry"],"mappings":"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"}
1
+ {"version":3,"file":"traverse-entity.js","sources":["../src/traverse-entity.ts"],"sourcesContent":["import { clone, isObject, isArray, isNil, curry } from 'lodash/fp';\n\nimport type { Attribute, AnyAttribute, Model, Data } from './types';\nimport { isRelationalAttribute, isMediaAttribute } from './content-types';\n\n/**\n * Execute promises in parallel but throw errors in array index order.\n */\nconst parallelWithOrderedErrors = async <T>(promises: Promise<T>[]): Promise<T[]> => {\n const results = await Promise.allSettled(promises);\n\n // Throw first error in array index order (matches sequential behavior)\n for (let i = 0; i < results.length; i += 1) {\n const result = results[i];\n if (result.status === 'rejected') {\n throw result.reason;\n }\n }\n\n return results.map((r) => (r as PromiseFulfilledResult<T>).value);\n};\n\nexport type VisitorUtils = ReturnType<typeof createVisitorUtils>;\n\nexport interface VisitorOptions {\n data: Record<string, unknown>;\n schema: Model;\n key: string;\n value: Data[keyof Data];\n attribute?: AnyAttribute;\n path: Path;\n getModel(uid: string): Model;\n parent?: Parent;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport type Visitor = (visitorOptions: VisitorOptions, visitorUtils: VisitorUtils) => void;\n\nexport interface Path {\n raw: string | null;\n attribute: string | null;\n rawWithIndices?: string | null;\n}\n\nexport interface TraverseOptions {\n schema: Model;\n path?: Path;\n parent?: Parent;\n getModel(uid: string): Model;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport interface Parent {\n attribute?: Attribute;\n key: string | null;\n path: Path;\n schema: Model;\n}\n\nconst traverseEntity = async (visitor: Visitor, options: TraverseOptions, entity: Data) => {\n const {\n path = { raw: null, attribute: null, rawWithIndices: null },\n schema,\n getModel,\n allowedExtraRootKeys,\n } = options;\n\n let parent = options.parent;\n\n const traverseMorphRelationTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__type!);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseRelationTarget =\n (schema: Model) => async (visitor: Visitor, path: Path, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseMediaTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchemaUID = 'plugin::upload.file';\n const targetSchema = getModel(targetSchemaUID);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseComponent = async (visitor: Visitor, path: Path, schema: Model, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const visitDynamicZoneEntry = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__component!);\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n // End recursion\n if (!isObject(entity) || isNil(schema)) {\n return entity;\n }\n\n // Don't mutate the original entity object\n // only clone at 1st level as the next level will get clone when traversed\n const copy = clone(entity);\n const visitorUtils = createVisitorUtils({ data: copy });\n\n const keys = Object.keys(copy);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n // Retrieve the attribute definition associated to the key from the schema\n const attribute = schema.attributes[key] as AnyAttribute | undefined;\n\n const newPath = { ...path };\n\n newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;\n newPath.rawWithIndices = isNil(path.rawWithIndices) ? key : `${path.rawWithIndices}.${key}`;\n\n if (!isNil(attribute)) {\n newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;\n }\n\n // Visit the current attribute\n const visitorOptions: VisitorOptions = {\n data: copy,\n schema,\n key,\n value: copy[key],\n attribute,\n path: newPath,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n await visitor(visitorOptions, visitorUtils);\n\n // Extract the value for the current key (after calling the visitor)\n const value = copy[key];\n\n // Ignore Nil values or attributes\n if (isNil(value) || isNil(attribute)) {\n continue;\n }\n\n if (isRelationalAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');\n\n const method = isMorphRelation\n ? traverseMorphRelationTarget\n : traverseRelationTarget(getModel(attribute.target!));\n\n if (isArray(value)) {\n // Process array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return method(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await method(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (isMediaAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n\n if (isArray(value)) {\n // Process media array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseMediaTarget(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await traverseMediaTarget(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'component') {\n parent = { schema, key, attribute, path: newPath };\n const targetSchema = getModel(attribute.component);\n\n if (isArray(value)) {\n // Process component array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseComponent(visitor, arrayPath, targetSchema, item);\n })\n );\n } else {\n copy[key] = await traverseComponent(visitor, newPath, targetSchema, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'dynamiczone' && isArray(value)) {\n parent = { schema, key, attribute, path: newPath };\n\n // Process dynamic zone items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return visitDynamicZoneEntry(visitor, arrayPath, item);\n })\n );\n\n continue;\n }\n }\n\n return copy;\n};\n\nconst createVisitorUtils = ({ data }: { data: Data }) => ({\n remove(key: string) {\n delete data[key];\n },\n\n set(key: string, value: Data) {\n data[key] = value;\n },\n});\n\nexport default curry(traverseEntity);\n"],"names":["parallelWithOrderedErrors","promises","results","Promise","allSettled","i","length","result","status","reason","map","r","value","traverseEntity","visitor","options","entity","path","raw","attribute","rawWithIndices","schema","getModel","allowedExtraRootKeys","parent","traverseMorphRelationTarget","entry","targetSchema","__type","traverseOptions","traverseRelationTarget","traverseMediaTarget","targetSchemaUID","traverseComponent","visitDynamicZoneEntry","__component","isObject","isNil","copy","clone","visitorUtils","createVisitorUtils","data","keys","Object","key","attributes","newPath","visitorOptions","isRelationalAttribute","isMorphRelation","relation","toLowerCase","startsWith","method","target","isArray","item","arrayPath","isMediaAttribute","type","component","remove","set","curry"],"mappings":"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"}
@@ -19,7 +19,7 @@ const traverseEntity = async (visitor, options, entity)=>{
19
19
  raw: null,
20
20
  attribute: null,
21
21
  rawWithIndices: null
22
- }, schema, getModel } = options;
22
+ }, schema, getModel, allowedExtraRootKeys } = options;
23
23
  let parent = options.parent;
24
24
  const traverseMorphRelationTarget = async (visitor, path, entry)=>{
25
25
  const targetSchema = getModel(entry.__type);
@@ -27,7 +27,8 @@ const traverseEntity = async (visitor, options, entity)=>{
27
27
  schema: targetSchema,
28
28
  path,
29
29
  getModel,
30
- parent
30
+ parent,
31
+ allowedExtraRootKeys
31
32
  };
32
33
  return traverseEntity(visitor, traverseOptions, entry);
33
34
  };
@@ -36,7 +37,8 @@ const traverseEntity = async (visitor, options, entity)=>{
36
37
  schema,
37
38
  path,
38
39
  getModel,
39
- parent
40
+ parent,
41
+ allowedExtraRootKeys
40
42
  };
41
43
  return traverseEntity(visitor, traverseOptions, entry);
42
44
  };
@@ -47,7 +49,8 @@ const traverseEntity = async (visitor, options, entity)=>{
47
49
  schema: targetSchema,
48
50
  path,
49
51
  getModel,
50
- parent
52
+ parent,
53
+ allowedExtraRootKeys
51
54
  };
52
55
  return traverseEntity(visitor, traverseOptions, entry);
53
56
  };
@@ -56,7 +59,8 @@ const traverseEntity = async (visitor, options, entity)=>{
56
59
  schema,
57
60
  path,
58
61
  getModel,
59
- parent
62
+ parent,
63
+ allowedExtraRootKeys
60
64
  };
61
65
  return traverseEntity(visitor, traverseOptions, entry);
62
66
  };
@@ -66,7 +70,8 @@ const traverseEntity = async (visitor, options, entity)=>{
66
70
  schema: targetSchema,
67
71
  path,
68
72
  getModel,
69
- parent
73
+ parent,
74
+ allowedExtraRootKeys
70
75
  };
71
76
  return traverseEntity(visitor, traverseOptions, entry);
72
77
  };
@@ -102,7 +107,8 @@ const traverseEntity = async (visitor, options, entity)=>{
102
107
  attribute,
103
108
  path: newPath,
104
109
  getModel,
105
- parent
110
+ parent,
111
+ allowedExtraRootKeys
106
112
  };
107
113
  await visitor(visitorOptions, visitorUtils);
108
114
  // Extract the value for the current key (after calling the visitor)
@@ -1 +1 @@
1
- {"version":3,"file":"traverse-entity.mjs","sources":["../src/traverse-entity.ts"],"sourcesContent":["import { clone, isObject, isArray, isNil, curry } from 'lodash/fp';\n\nimport type { Attribute, AnyAttribute, Model, Data } from './types';\nimport { isRelationalAttribute, isMediaAttribute } from './content-types';\n\n/**\n * Execute promises in parallel but throw errors in array index order.\n */\nconst parallelWithOrderedErrors = async <T>(promises: Promise<T>[]): Promise<T[]> => {\n const results = await Promise.allSettled(promises);\n\n // Throw first error in array index order (matches sequential behavior)\n for (let i = 0; i < results.length; i += 1) {\n const result = results[i];\n if (result.status === 'rejected') {\n throw result.reason;\n }\n }\n\n return results.map((r) => (r as PromiseFulfilledResult<T>).value);\n};\n\nexport type VisitorUtils = ReturnType<typeof createVisitorUtils>;\n\nexport interface VisitorOptions {\n data: Record<string, unknown>;\n schema: Model;\n key: string;\n value: Data[keyof Data];\n attribute?: AnyAttribute;\n path: Path;\n getModel(uid: string): Model;\n parent?: Parent;\n}\n\nexport type Visitor = (visitorOptions: VisitorOptions, visitorUtils: VisitorUtils) => void;\n\nexport interface Path {\n raw: string | null;\n attribute: string | null;\n rawWithIndices?: string | null;\n}\n\nexport interface TraverseOptions {\n schema: Model;\n path?: Path;\n parent?: Parent;\n getModel(uid: string): Model;\n}\n\nexport interface Parent {\n attribute?: Attribute;\n key: string | null;\n path: Path;\n schema: Model;\n}\n\nconst traverseEntity = async (visitor: Visitor, options: TraverseOptions, entity: Data) => {\n const { path = { raw: null, attribute: null, rawWithIndices: null }, schema, getModel } = options;\n\n let parent = options.parent;\n\n const traverseMorphRelationTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__type!);\n\n const traverseOptions: TraverseOptions = { schema: targetSchema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseRelationTarget =\n (schema: Model) => async (visitor: Visitor, path: Path, entry: Data) => {\n const traverseOptions: TraverseOptions = { schema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseMediaTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchemaUID = 'plugin::upload.file';\n const targetSchema = getModel(targetSchemaUID);\n\n const traverseOptions: TraverseOptions = { schema: targetSchema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseComponent = async (visitor: Visitor, path: Path, schema: Model, entry: Data) => {\n const traverseOptions: TraverseOptions = { schema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const visitDynamicZoneEntry = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__component!);\n const traverseOptions: TraverseOptions = { schema: targetSchema, path, getModel, parent };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n // End recursion\n if (!isObject(entity) || isNil(schema)) {\n return entity;\n }\n\n // Don't mutate the original entity object\n // only clone at 1st level as the next level will get clone when traversed\n const copy = clone(entity);\n const visitorUtils = createVisitorUtils({ data: copy });\n\n const keys = Object.keys(copy);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n // Retrieve the attribute definition associated to the key from the schema\n const attribute = schema.attributes[key] as AnyAttribute | undefined;\n\n const newPath = { ...path };\n\n newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;\n newPath.rawWithIndices = isNil(path.rawWithIndices) ? key : `${path.rawWithIndices}.${key}`;\n\n if (!isNil(attribute)) {\n newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;\n }\n\n // Visit the current attribute\n const visitorOptions: VisitorOptions = {\n data: copy,\n schema,\n key,\n value: copy[key],\n attribute,\n path: newPath,\n getModel,\n parent,\n };\n\n await visitor(visitorOptions, visitorUtils);\n\n // Extract the value for the current key (after calling the visitor)\n const value = copy[key];\n\n // Ignore Nil values or attributes\n if (isNil(value) || isNil(attribute)) {\n continue;\n }\n\n if (isRelationalAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');\n\n const method = isMorphRelation\n ? traverseMorphRelationTarget\n : traverseRelationTarget(getModel(attribute.target!));\n\n if (isArray(value)) {\n // Process array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return method(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await method(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (isMediaAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n\n if (isArray(value)) {\n // Process media array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseMediaTarget(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await traverseMediaTarget(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'component') {\n parent = { schema, key, attribute, path: newPath };\n const targetSchema = getModel(attribute.component);\n\n if (isArray(value)) {\n // Process component array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseComponent(visitor, arrayPath, targetSchema, item);\n })\n );\n } else {\n copy[key] = await traverseComponent(visitor, newPath, targetSchema, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'dynamiczone' && isArray(value)) {\n parent = { schema, key, attribute, path: newPath };\n\n // Process dynamic zone items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return visitDynamicZoneEntry(visitor, arrayPath, item);\n })\n );\n\n continue;\n }\n }\n\n return copy;\n};\n\nconst createVisitorUtils = ({ data }: { data: Data }) => ({\n remove(key: string) {\n delete data[key];\n },\n\n set(key: string, value: Data) {\n data[key] = value;\n },\n});\n\nexport default curry(traverseEntity);\n"],"names":["parallelWithOrderedErrors","promises","results","Promise","allSettled","i","length","result","status","reason","map","r","value","traverseEntity","visitor","options","entity","path","raw","attribute","rawWithIndices","schema","getModel","parent","traverseMorphRelationTarget","entry","targetSchema","__type","traverseOptions","traverseRelationTarget","traverseMediaTarget","targetSchemaUID","traverseComponent","visitDynamicZoneEntry","__component","isObject","isNil","copy","clone","visitorUtils","createVisitorUtils","data","keys","Object","key","attributes","newPath","visitorOptions","isRelationalAttribute","isMorphRelation","relation","toLowerCase","startsWith","method","target","isArray","item","arrayPath","isMediaAttribute","type","component","remove","set","curry"],"mappings":"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"}
1
+ {"version":3,"file":"traverse-entity.mjs","sources":["../src/traverse-entity.ts"],"sourcesContent":["import { clone, isObject, isArray, isNil, curry } from 'lodash/fp';\n\nimport type { Attribute, AnyAttribute, Model, Data } from './types';\nimport { isRelationalAttribute, isMediaAttribute } from './content-types';\n\n/**\n * Execute promises in parallel but throw errors in array index order.\n */\nconst parallelWithOrderedErrors = async <T>(promises: Promise<T>[]): Promise<T[]> => {\n const results = await Promise.allSettled(promises);\n\n // Throw first error in array index order (matches sequential behavior)\n for (let i = 0; i < results.length; i += 1) {\n const result = results[i];\n if (result.status === 'rejected') {\n throw result.reason;\n }\n }\n\n return results.map((r) => (r as PromiseFulfilledResult<T>).value);\n};\n\nexport type VisitorUtils = ReturnType<typeof createVisitorUtils>;\n\nexport interface VisitorOptions {\n data: Record<string, unknown>;\n schema: Model;\n key: string;\n value: Data[keyof Data];\n attribute?: AnyAttribute;\n path: Path;\n getModel(uid: string): Model;\n parent?: Parent;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport type Visitor = (visitorOptions: VisitorOptions, visitorUtils: VisitorUtils) => void;\n\nexport interface Path {\n raw: string | null;\n attribute: string | null;\n rawWithIndices?: string | null;\n}\n\nexport interface TraverseOptions {\n schema: Model;\n path?: Path;\n parent?: Parent;\n getModel(uid: string): Model;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport interface Parent {\n attribute?: Attribute;\n key: string | null;\n path: Path;\n schema: Model;\n}\n\nconst traverseEntity = async (visitor: Visitor, options: TraverseOptions, entity: Data) => {\n const {\n path = { raw: null, attribute: null, rawWithIndices: null },\n schema,\n getModel,\n allowedExtraRootKeys,\n } = options;\n\n let parent = options.parent;\n\n const traverseMorphRelationTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__type!);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseRelationTarget =\n (schema: Model) => async (visitor: Visitor, path: Path, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseMediaTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchemaUID = 'plugin::upload.file';\n const targetSchema = getModel(targetSchemaUID);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseComponent = async (visitor: Visitor, path: Path, schema: Model, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const visitDynamicZoneEntry = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__component!);\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n // End recursion\n if (!isObject(entity) || isNil(schema)) {\n return entity;\n }\n\n // Don't mutate the original entity object\n // only clone at 1st level as the next level will get clone when traversed\n const copy = clone(entity);\n const visitorUtils = createVisitorUtils({ data: copy });\n\n const keys = Object.keys(copy);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n // Retrieve the attribute definition associated to the key from the schema\n const attribute = schema.attributes[key] as AnyAttribute | undefined;\n\n const newPath = { ...path };\n\n newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;\n newPath.rawWithIndices = isNil(path.rawWithIndices) ? key : `${path.rawWithIndices}.${key}`;\n\n if (!isNil(attribute)) {\n newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;\n }\n\n // Visit the current attribute\n const visitorOptions: VisitorOptions = {\n data: copy,\n schema,\n key,\n value: copy[key],\n attribute,\n path: newPath,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n await visitor(visitorOptions, visitorUtils);\n\n // Extract the value for the current key (after calling the visitor)\n const value = copy[key];\n\n // Ignore Nil values or attributes\n if (isNil(value) || isNil(attribute)) {\n continue;\n }\n\n if (isRelationalAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');\n\n const method = isMorphRelation\n ? traverseMorphRelationTarget\n : traverseRelationTarget(getModel(attribute.target!));\n\n if (isArray(value)) {\n // Process array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return method(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await method(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (isMediaAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n\n if (isArray(value)) {\n // Process media array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseMediaTarget(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await traverseMediaTarget(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'component') {\n parent = { schema, key, attribute, path: newPath };\n const targetSchema = getModel(attribute.component);\n\n if (isArray(value)) {\n // Process component array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseComponent(visitor, arrayPath, targetSchema, item);\n })\n );\n } else {\n copy[key] = await traverseComponent(visitor, newPath, targetSchema, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'dynamiczone' && isArray(value)) {\n parent = { schema, key, attribute, path: newPath };\n\n // Process dynamic zone items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return visitDynamicZoneEntry(visitor, arrayPath, item);\n })\n );\n\n continue;\n }\n }\n\n return copy;\n};\n\nconst createVisitorUtils = ({ data }: { data: Data }) => ({\n remove(key: string) {\n delete data[key];\n },\n\n set(key: string, value: Data) {\n data[key] = value;\n },\n});\n\nexport default curry(traverseEntity);\n"],"names":["parallelWithOrderedErrors","promises","results","Promise","allSettled","i","length","result","status","reason","map","r","value","traverseEntity","visitor","options","entity","path","raw","attribute","rawWithIndices","schema","getModel","allowedExtraRootKeys","parent","traverseMorphRelationTarget","entry","targetSchema","__type","traverseOptions","traverseRelationTarget","traverseMediaTarget","targetSchemaUID","traverseComponent","visitDynamicZoneEntry","__component","isObject","isNil","copy","clone","visitorUtils","createVisitorUtils","data","keys","Object","key","attributes","newPath","visitorOptions","isRelationalAttribute","isMorphRelation","relation","toLowerCase","startsWith","method","target","isArray","item","arrayPath","isMediaAttribute","type","component","remove","set","curry"],"mappings":"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"}
@@ -1,9 +1,20 @@
1
1
  import { CurriedFunction1 } from 'lodash';
2
+ import { type RouteLike } from '../content-api-route-params';
2
3
  import * as visitors from './visitors';
3
4
  import * as validators from './validators';
4
5
  import { Model, Data } from '../types';
5
6
  export interface Options {
6
7
  auth?: unknown;
8
+ /**
9
+ * If true, validateQuery throws when the query has any top-level key not in the allowed list.
10
+ * Defaults to false for backward compatibility.
11
+ */
12
+ strictParams?: boolean;
13
+ /**
14
+ * When set, extra query/input params are derived from the route's request schema (and validated with Zod).
15
+ * When absent, no extra params are allowed in strict mode.
16
+ */
17
+ route?: RouteLike;
7
18
  }
8
19
  export interface Validator {
9
20
  (schema: Model): CurriedFunction1<Data, Promise<Data>>;
@@ -11,6 +22,8 @@ export interface Validator {
11
22
  export interface ValidateFunc {
12
23
  (data: unknown, schema: Model, options?: Options): Promise<void>;
13
24
  }
25
+ export type ValidateQueryParamHandler = (value: unknown, schema: Model, options?: Options) => Promise<void>;
26
+ export type ValidateBodyParamHandler = (value: unknown, schema: Model, options?: Options) => Promise<void>;
14
27
  interface APIOptions {
15
28
  validators?: Validators;
16
29
  getModel: (model: string) => Model;
@@ -20,7 +33,7 @@ export interface Validators {
20
33
  }
21
34
  declare const createAPIValidators: (opts: APIOptions) => {
22
35
  input: ValidateFunc;
23
- query: (query: Record<string, unknown>, schema: Model, { auth }?: Options) => Promise<void>;
36
+ query: (query: Record<string, unknown>, schema: Model, { auth, strictParams, route }?: Options) => Promise<void>;
24
37
  filters: ValidateFunc;
25
38
  sort: ValidateFunc;
26
39
  fields: ValidateFunc;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAO1C,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAK3C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAKvC,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,MAAM,EAAE,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACxD;AACD,MAAM,WAAW,YAAY;IAC3B,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED,UAAU,UAAU;IAClB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,QAAA,MAAM,mBAAmB,SAAU,UAAU;;mBA0DlC,OAAO,MAAM,EAAE,OAAO,CAAC,UACtB,KAAK,aACH,OAAO;;;;;CAqIpB,CAAC;AAEF,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAM1C,OAAO,EACL,KAAK,SAAS,EAGf,MAAM,6BAA6B,CAAC;AAIrC,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAK3C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAKvC,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,MAAM,EAAE,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACxD;AACD,MAAM,WAAW,YAAY;IAC3B,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED,MAAM,MAAM,yBAAyB,GAAG,CACtC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,KAAK,EACb,OAAO,CAAC,EAAE,OAAO,KACd,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,MAAM,wBAAwB,GAAG,CACrC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,KAAK,EACb,OAAO,CAAC,EAAE,OAAO,KACd,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,UAAU,UAAU;IAClB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,QAAA,MAAM,mBAAmB,SAAU,UAAU;;mBA8FlC,OAAO,MAAM,EAAE,OAAO,CAAC,UACtB,KAAK,kCAC0B,OAAO;;;;;CA0KjD,CAAC;AAEF,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
@@ -2,6 +2,8 @@
2
2
 
3
3
  var fp = require('lodash/fp');
4
4
  var contentTypes = require('../content-types.js');
5
+ var contentApiConstants = require('../content-api-constants.js');
6
+ var contentApiRouteParams = require('../content-api-route-params.js');
5
7
  var async = require('../async.js');
6
8
  var utils = require('./utils.js');
7
9
  var index = require('./visitors/index.js');
@@ -19,16 +21,16 @@ var throwRestrictedRelations = require('./visitors/throw-restricted-relations.js
19
21
  const { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = contentTypes.constants;
20
22
  const createAPIValidators = (opts)=>{
21
23
  const { getModel } = opts || {};
22
- const validateInput = async (data, schema, { auth } = {})=>{
24
+ const validateInput = async (data, schema, options = {})=>{
25
+ const { auth, route } = options;
23
26
  if (!schema) {
24
27
  throw new Error('Missing schema in validateInput');
25
28
  }
26
29
  if (fp.isArray(data)) {
27
- await Promise.all(data.map((entry)=>validateInput(entry, schema, {
28
- auth
29
- })));
30
+ await Promise.all(data.map((entry)=>validateInput(entry, schema, options)));
30
31
  return;
31
32
  }
33
+ const allowedExtraRootKeys = contentApiRouteParams.getExtraRootKeysFromRouteBody(route);
32
34
  const nonWritableAttributes = contentTypes.getNonWritableAttributes(schema);
33
35
  const transforms = [
34
36
  (data)=>{
@@ -51,10 +53,11 @@ const createAPIValidators = (opts)=>{
51
53
  schema,
52
54
  getModel
53
55
  }),
54
- // unrecognized attributes
56
+ // unrecognized attributes (allowedExtraRootKeys = registered input param keys)
55
57
  traverseEntity(throwUnrecognizedFields, {
56
58
  schema,
57
- getModel
59
+ getModel,
60
+ allowedExtraRootKeys
58
61
  })
59
62
  ];
60
63
  if (auth) {
@@ -68,6 +71,36 @@ const createAPIValidators = (opts)=>{
68
71
  opts?.validators?.input?.forEach((validator)=>transforms.push(validator(schema)));
69
72
  try {
70
73
  await async.pipe(...transforms)(data);
74
+ // Validate extra root keys from route's body schema with Zod (throw on failure).
75
+ //
76
+ // Content-api sends the document payload as body.data; the controller calls validateInput(body.data, ctx),
77
+ // so the input we receive here is the inner payload (keys like "relatedMedia", "name"), not the full body.
78
+ // The route's body schema is z.object({ data: ... }), so its shape includes "data". We skip "data" because
79
+ // the main document payload is already validated above by traverseEntity (throwUnrecognizedFields, etc.);
80
+ // relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run
81
+ // Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).
82
+ if (fp.isObject(data) && route?.request?.body?.['application/json']) {
83
+ const bodySchema = route.request.body['application/json'];
84
+ if (typeof bodySchema === 'object' && 'shape' in bodySchema) {
85
+ const shape = bodySchema.shape;
86
+ const dataObj = data;
87
+ for (const key of Object.keys(shape)){
88
+ if (key === 'data' || !(key in dataObj)) continue;
89
+ const zodSchema = shape[key];
90
+ if (zodSchema && typeof zodSchema.parse === 'function') {
91
+ const result = zodSchema.safeParse(dataObj[key]);
92
+ if (!result.success) {
93
+ throw new errors.ValidationError(result.error?.message ?? 'Validation failed', {
94
+ key,
95
+ path: null,
96
+ source: 'body',
97
+ param: key
98
+ });
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
71
104
  } catch (e) {
72
105
  if (e instanceof errors.ValidationError) {
73
106
  e.details.source = 'body';
@@ -75,10 +108,53 @@ const createAPIValidators = (opts)=>{
75
108
  throw e;
76
109
  }
77
110
  };
78
- const validateQuery = async (query, schema, { auth } = {})=>{
111
+ const validateQuery = async (query, schema, { auth, strictParams = false, route } = {})=>{
79
112
  if (!schema) {
80
113
  throw new Error('Missing schema in validateQuery');
81
114
  }
115
+ if (strictParams) {
116
+ const extraQueryKeys = contentApiRouteParams.getExtraQueryKeysFromRoute(route);
117
+ const allowedKeys = [
118
+ ...contentApiConstants.ALLOWED_QUERY_PARAM_KEYS,
119
+ ...extraQueryKeys
120
+ ];
121
+ for (const key of Object.keys(query)){
122
+ if (!allowedKeys.includes(key)) {
123
+ try {
124
+ utils.throwInvalidKey({
125
+ key,
126
+ path: null
127
+ });
128
+ } catch (e) {
129
+ if (e instanceof errors.ValidationError) {
130
+ e.details.source = 'query';
131
+ e.details.param = key;
132
+ }
133
+ throw e;
134
+ }
135
+ }
136
+ }
137
+ // Validate extra query keys from route's request schema with Zod (throw on failure)
138
+ const routeQuerySchema = route?.request?.query;
139
+ if (routeQuerySchema) {
140
+ for (const key of extraQueryKeys){
141
+ if (key in query) {
142
+ const zodSchema = routeQuerySchema[key];
143
+ if (zodSchema && typeof zodSchema.parse === 'function') {
144
+ const result = zodSchema.safeParse(query[key]);
145
+ if (!result.success) {
146
+ throw new errors.ValidationError(result.error?.message ?? 'Invalid query param', {
147
+ key,
148
+ path: null,
149
+ source: 'query',
150
+ param: key
151
+ });
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
82
158
  const { filters, sort, fields, populate } = query;
83
159
  if (filters) {
84
160
  await validateFilters(filters, schema, {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/validate/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, isObject } from 'lodash/fp';\n\nimport { getNonWritableAttributes, constants } from '../content-types';\nimport { pipe as pipeAsync } from '../async';\nimport { throwInvalidKey } from './utils';\n\nimport * as visitors from './visitors';\nimport * as validators from './validators';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\n\nimport { Model, Data } from '../types';\nimport { ValidationError } from '../errors';\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nexport interface Options {\n auth?: unknown;\n}\n\nexport interface Validator {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface ValidateFunc {\n (data: unknown, schema: Model, options?: Options): Promise<void>;\n}\n\ninterface APIOptions {\n validators?: Validators;\n getModel: (model: string) => Model;\n}\n\nexport interface Validators {\n input?: Validator[];\n}\n\nconst createAPIValidators = (opts: APIOptions) => {\n const { getModel } = opts || {};\n\n const validateInput: ValidateFunc = async (data: unknown, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateInput');\n }\n\n if (isArray(data)) {\n await Promise.all(data.map((entry) => validateInput(entry, schema, { auth })));\n return;\n }\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n (data: unknown) => {\n if (isObject(data)) {\n if (ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: ID_ATTRIBUTE });\n }\n\n if (DOC_ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: DOC_ID_ATTRIBUTE });\n }\n }\n return data;\n },\n // non-writable attributes\n traverseEntity(visitors.throwRestrictedFields(nonWritableAttributes), { schema, getModel }),\n // unrecognized attributes\n traverseEntity(visitors.throwUnrecognizedFields, { schema, getModel }),\n ];\n\n if (auth) {\n // restricted relations\n transforms.push(\n traverseEntity(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n // Apply validators from registry if exists\n opts?.validators?.input?.forEach((validator: Validator) => transforms.push(validator(schema)));\n\n try {\n await pipeAsync(...transforms)(data as Data);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'body';\n }\n throw e;\n }\n };\n\n const validateQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in validateQuery');\n }\n const { filters, sort, fields, populate } = query;\n\n if (filters) {\n await validateFilters(filters, schema, { auth });\n }\n\n if (sort) {\n await validateSort(sort, schema, { auth });\n }\n\n if (fields) {\n await validateFields(fields, schema);\n }\n\n // a wildcard is always valid; its conversion will be handled by the entity service and can be optimized with sanitizer\n if (populate && populate !== '*') {\n await validatePopulate(populate, schema);\n }\n };\n\n const validateFilters: ValidateFunc = async (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateFilters');\n }\n if (isArray(filters)) {\n await Promise.all(filters.map((filter) => validateFilters(filter, schema, { auth })));\n return;\n }\n\n const transforms = [validators.defaultValidateFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(filters);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'filters';\n }\n throw e;\n }\n };\n\n const validateSort: ValidateFunc = async (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateSort');\n }\n const transforms = [validators.defaultValidateSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(sort);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'sort';\n }\n throw e;\n }\n };\n\n const validateFields: ValidateFunc = async (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in validateFields');\n }\n const transforms = [validators.defaultValidateFields({ schema, getModel })];\n\n try {\n await pipeAsync(...transforms)(fields);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'fields';\n }\n throw e;\n }\n };\n\n const validatePopulate: ValidateFunc = async (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [validators.defaultValidatePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(populate);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'populate';\n }\n throw e;\n }\n };\n\n return {\n input: validateInput,\n query: validateQuery,\n filters: validateFilters,\n sort: validateSort,\n fields: validateFields,\n populate: validatePopulate,\n };\n};\n\nexport { createAPIValidators, validators, visitors };\n\nexport type APIValidators = ReturnType<typeof createAPIValidators>;\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","createAPIValidators","opts","getModel","validateInput","data","schema","auth","Error","isArray","Promise","all","map","entry","nonWritableAttributes","getNonWritableAttributes","transforms","isObject","throwInvalidKey","key","traverseEntity","visitors","push","validators","input","forEach","validator","pipeAsync","e","ValidationError","details","source","validateQuery","query","filters","sort","fields","populate","validateFilters","validateSort","validateFields","validatePopulate","filter","traverseQueryFilters","param","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/validate/index.ts"],"sourcesContent":["import { CurriedFunction1 } from 'lodash';\nimport { isArray, isObject } from 'lodash/fp';\nimport type { z } from 'zod/v4';\n\nimport { getNonWritableAttributes, constants } from '../content-types';\nimport { ALLOWED_QUERY_PARAM_KEYS } from '../content-api-constants';\nimport {\n type RouteLike,\n getExtraQueryKeysFromRoute,\n getExtraRootKeysFromRouteBody,\n} from '../content-api-route-params';\nimport { pipe as pipeAsync } from '../async';\nimport { throwInvalidKey } from './utils';\n\nimport * as visitors from './visitors';\nimport * as validators from './validators';\nimport traverseEntity from '../traverse-entity';\n\nimport { traverseQueryFilters, traverseQuerySort, traverseQueryPopulate } from '../traverse';\n\nimport { Model, Data } from '../types';\nimport { ValidationError } from '../errors';\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nexport interface Options {\n auth?: unknown;\n /**\n * If true, validateQuery throws when the query has any top-level key not in the allowed list.\n * Defaults to false for backward compatibility.\n */\n strictParams?: boolean;\n /**\n * When set, extra query/input params are derived from the route's request schema (and validated with Zod).\n * When absent, no extra params are allowed in strict mode.\n */\n route?: RouteLike;\n}\n\nexport interface Validator {\n (schema: Model): CurriedFunction1<Data, Promise<Data>>;\n}\nexport interface ValidateFunc {\n (data: unknown, schema: Model, options?: Options): Promise<void>;\n}\n\nexport type ValidateQueryParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\nexport type ValidateBodyParamHandler = (\n value: unknown,\n schema: Model,\n options?: Options\n) => Promise<void>;\n\ninterface APIOptions {\n validators?: Validators;\n getModel: (model: string) => Model;\n}\n\nexport interface Validators {\n input?: Validator[];\n}\n\nconst createAPIValidators = (opts: APIOptions) => {\n const { getModel } = opts || {};\n\n const validateInput: ValidateFunc = async (data: unknown, schema: Model, options = {}) => {\n const { auth, route } = options;\n if (!schema) {\n throw new Error('Missing schema in validateInput');\n }\n\n if (isArray(data)) {\n await Promise.all(data.map((entry) => validateInput(entry, schema, options)));\n return;\n }\n\n const allowedExtraRootKeys = getExtraRootKeysFromRouteBody(route);\n\n const nonWritableAttributes = getNonWritableAttributes(schema);\n\n const transforms = [\n (data: unknown) => {\n if (isObject(data)) {\n if (ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: ID_ATTRIBUTE });\n }\n\n if (DOC_ID_ATTRIBUTE in data) {\n throwInvalidKey({ key: DOC_ID_ATTRIBUTE });\n }\n }\n return data;\n },\n // non-writable attributes\n traverseEntity(visitors.throwRestrictedFields(nonWritableAttributes), { schema, getModel }),\n // unrecognized attributes (allowedExtraRootKeys = registered input param keys)\n traverseEntity(visitors.throwUnrecognizedFields, {\n schema,\n getModel,\n allowedExtraRootKeys,\n }),\n ];\n\n if (auth) {\n // restricted relations\n transforms.push(\n traverseEntity(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n // Apply validators from registry if exists\n opts?.validators?.input?.forEach((validator: Validator) => transforms.push(validator(schema)));\n\n try {\n await pipeAsync(...transforms)(data as Data);\n\n // Validate extra root keys from route's body schema with Zod (throw on failure).\n //\n // Content-api sends the document payload as body.data; the controller calls validateInput(body.data, ctx),\n // so the input we receive here is the inner payload (keys like \"relatedMedia\", \"name\"), not the full body.\n // The route's body schema is z.object({ data: ... }), so its shape includes \"data\". We skip \"data\" because\n // the main document payload is already validated above by traverseEntity (throwUnrecognizedFields, etc.);\n // relation ops (connect/disconnect/set) are handled there, not by the route's Zod schema. We only run\n // Zod here for truly extra root keys added via addInputParams (e.g. clientMutationId).\n if (isObject(data) && route?.request?.body?.['application/json']) {\n const bodySchema = route.request.body['application/json'];\n if (typeof bodySchema === 'object' && 'shape' in bodySchema) {\n const shape = (bodySchema as { shape: Record<string, z.ZodTypeAny> }).shape;\n const dataObj = data as Record<string, unknown>;\n for (const key of Object.keys(shape)) {\n if (key === 'data' || !(key in dataObj)) continue;\n const zodSchema = shape[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(dataObj[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Validation failed',\n { key, path: null, source: 'body', param: key }\n );\n }\n }\n }\n }\n }\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'body';\n }\n throw e;\n }\n };\n\n const validateQuery = async (\n query: Record<string, unknown>,\n schema: Model,\n { auth, strictParams = false, route }: Options = {}\n ) => {\n if (!schema) {\n throw new Error('Missing schema in validateQuery');\n }\n\n if (strictParams) {\n const extraQueryKeys = getExtraQueryKeysFromRoute(route);\n const allowedKeys = [...ALLOWED_QUERY_PARAM_KEYS, ...extraQueryKeys];\n for (const key of Object.keys(query)) {\n if (!allowedKeys.includes(key)) {\n try {\n throwInvalidKey({ key, path: null });\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = key;\n }\n throw e;\n }\n }\n }\n // Validate extra query keys from route's request schema with Zod (throw on failure)\n const routeQuerySchema = route?.request?.query;\n if (routeQuerySchema) {\n for (const key of extraQueryKeys) {\n if (key in query) {\n const zodSchema = routeQuerySchema[key];\n if (zodSchema && typeof (zodSchema as z.ZodTypeAny).parse === 'function') {\n const result = (zodSchema as z.ZodTypeAny).safeParse(query[key]);\n if (!result.success) {\n throw new ValidationError(\n (result.error?.message as string) ?? 'Invalid query param',\n { key, path: null, source: 'query', param: key }\n );\n }\n }\n }\n }\n }\n }\n\n const { filters, sort, fields, populate } = query;\n\n if (filters) {\n await validateFilters(filters, schema, { auth });\n }\n\n if (sort) {\n await validateSort(sort, schema, { auth });\n }\n\n if (fields) {\n await validateFields(fields, schema);\n }\n\n // a wildcard is always valid; its conversion will be handled by the entity service and can be optimized with sanitizer\n if (populate && populate !== '*') {\n await validatePopulate(populate, schema);\n }\n };\n\n const validateFilters: ValidateFunc = async (filters, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateFilters');\n }\n if (isArray(filters)) {\n await Promise.all(filters.map((filter) => validateFilters(filter, schema, { auth })));\n return;\n }\n\n const transforms = [validators.defaultValidateFilters({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryFilters(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(filters);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'filters';\n }\n throw e;\n }\n };\n\n const validateSort: ValidateFunc = async (sort, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in validateSort');\n }\n const transforms = [validators.defaultValidateSort({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQuerySort(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(sort);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'sort';\n }\n throw e;\n }\n };\n\n const validateFields: ValidateFunc = async (fields, schema: Model) => {\n if (!schema) {\n throw new Error('Missing schema in validateFields');\n }\n const transforms = [validators.defaultValidateFields({ schema, getModel })];\n\n try {\n await pipeAsync(...transforms)(fields);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'fields';\n }\n throw e;\n }\n };\n\n const validatePopulate: ValidateFunc = async (populate, schema: Model, { auth } = {}) => {\n if (!schema) {\n throw new Error('Missing schema in sanitizePopulate');\n }\n const transforms = [validators.defaultValidatePopulate({ schema, getModel })];\n\n if (auth) {\n transforms.push(\n traverseQueryPopulate(visitors.throwRestrictedRelations(auth), {\n schema,\n getModel,\n })\n );\n }\n\n try {\n await pipeAsync(...transforms)(populate);\n } catch (e) {\n if (e instanceof ValidationError) {\n e.details.source = 'query';\n e.details.param = 'populate';\n }\n throw e;\n }\n };\n\n return {\n input: validateInput,\n query: validateQuery,\n filters: validateFilters,\n sort: validateSort,\n fields: validateFields,\n populate: validatePopulate,\n };\n};\n\nexport { createAPIValidators, validators, visitors };\n\nexport type APIValidators = ReturnType<typeof createAPIValidators>;\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","createAPIValidators","opts","getModel","validateInput","data","schema","options","auth","route","Error","isArray","Promise","all","map","entry","allowedExtraRootKeys","getExtraRootKeysFromRouteBody","nonWritableAttributes","getNonWritableAttributes","transforms","isObject","throwInvalidKey","key","traverseEntity","visitors","push","validators","input","forEach","validator","pipeAsync","request","body","bodySchema","shape","dataObj","Object","keys","zodSchema","parse","result","safeParse","success","ValidationError","error","message","path","source","param","e","details","validateQuery","query","strictParams","extraQueryKeys","getExtraQueryKeysFromRoute","allowedKeys","ALLOWED_QUERY_PARAM_KEYS","includes","routeQuerySchema","filters","sort","fields","populate","validateFilters","validateSort","validateFields","validatePopulate","filter","traverseQueryFilters","traverseQuerySort","traverseQueryPopulate"],"mappings":"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"}