@strapi/utils 5.51.1 → 5.52.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 (62) hide show
  1. package/dist/content-types.d.ts.map +1 -1
  2. package/dist/content-types.js +39 -2
  3. package/dist/content-types.js.map +1 -1
  4. package/dist/content-types.mjs +39 -2
  5. package/dist/content-types.mjs.map +1 -1
  6. package/dist/convert-query-params.js +4 -4
  7. package/dist/convert-query-params.js.map +1 -1
  8. package/dist/errors.d.ts +1 -1
  9. package/dist/errors.d.ts.map +1 -1
  10. package/dist/errors.js.map +1 -1
  11. package/dist/errors.mjs.map +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/install-id.js +1 -1
  14. package/dist/install-id.js.map +1 -1
  15. package/dist/install-id.mjs +1 -1
  16. package/dist/install-id.mjs.map +1 -1
  17. package/dist/pagination.d.ts +1 -1
  18. package/dist/pagination.d.ts.map +1 -1
  19. package/dist/pagination.js +12 -5
  20. package/dist/pagination.js.map +1 -1
  21. package/dist/pagination.mjs +12 -5
  22. package/dist/pagination.mjs.map +1 -1
  23. package/dist/parse-type.d.ts +13 -0
  24. package/dist/parse-type.d.ts.map +1 -1
  25. package/dist/parse-type.js +44 -15
  26. package/dist/parse-type.js.map +1 -1
  27. package/dist/parse-type.mjs +41 -15
  28. package/dist/parse-type.mjs.map +1 -1
  29. package/dist/policy.js +1 -1
  30. package/dist/policy.js.map +1 -1
  31. package/dist/policy.mjs +1 -1
  32. package/dist/policy.mjs.map +1 -1
  33. package/dist/route-serialization.d.ts +1 -1
  34. package/dist/route-serialization.d.ts.map +1 -1
  35. package/dist/route-serialization.js +1 -1
  36. package/dist/route-serialization.js.map +1 -1
  37. package/dist/route-serialization.mjs +1 -1
  38. package/dist/route-serialization.mjs.map +1 -1
  39. package/dist/sanitize/index.js +3 -1
  40. package/dist/sanitize/index.js.map +1 -1
  41. package/dist/sanitize/index.mjs +3 -1
  42. package/dist/sanitize/index.mjs.map +1 -1
  43. package/dist/sanitize/sanitizers.d.ts.map +1 -1
  44. package/dist/sanitize/sanitizers.js +5 -3
  45. package/dist/sanitize/sanitizers.js.map +1 -1
  46. package/dist/sanitize/sanitizers.mjs +5 -3
  47. package/dist/sanitize/sanitizers.mjs.map +1 -1
  48. package/dist/sanitize/visitors/remove-restricted-relations.d.ts.map +1 -1
  49. package/dist/sanitize/visitors/remove-restricted-relations.js +128 -86
  50. package/dist/sanitize/visitors/remove-restricted-relations.js.map +1 -1
  51. package/dist/sanitize/visitors/remove-restricted-relations.mjs +128 -86
  52. package/dist/sanitize/visitors/remove-restricted-relations.mjs.map +1 -1
  53. package/dist/traverse-entity.d.ts.map +1 -1
  54. package/dist/traverse-entity.js +9 -1
  55. package/dist/traverse-entity.js.map +1 -1
  56. package/dist/traverse-entity.mjs +9 -1
  57. package/dist/traverse-entity.mjs.map +1 -1
  58. package/dist/validate/validators.js +12 -19
  59. package/dist/validate/validators.js.map +1 -1
  60. package/dist/validate/validators.mjs +13 -20
  61. package/dist/validate/validators.mjs.map +1 -1
  62. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"sanitizers.d.ts","sourceRoot":"","sources":["../../src/sanitize/sanitizers.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,UAAU,OAAO;IACf,MAAM,EAAE,KAAK,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,QAAA,MAAM,iBAAiB,GAAI,KAAK,OAAO,MAAY,QAAQ,IAAI,kBAM9D,CAAC;AAEF,QAAA,MAAM,qBAAqB,GAAU,KAAK,OAAO,EAAE,QAAQ,IAAI,kBAa9D,CAAC;AAEF,QAAA,MAAM,sBAAsB,uEAsC1B,CAAC;AAEH,QAAA,MAAM,mBAAmB,uEAuCvB,CAAC;AAEH,QAAA,MAAM,qBAAqB,uEAyBzB,CAAC;AAEH,QAAA,MAAM,uBAAuB,uEAiC3B,CAAC;AAEH,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,GACxB,CAAC"}
1
+ {"version":3,"file":"sanitizers.d.ts","sourceRoot":"","sources":["../../src/sanitize/sanitizers.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,UAAU,OAAO;IACf,MAAM,EAAE,KAAK,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,QAAA,MAAM,iBAAiB,GAAI,KAAK,OAAO,MAAY,QAAQ,IAAI,kBAM9D,CAAC;AAEF,QAAA,MAAM,qBAAqB,GAAU,KAAK,OAAO,EAAE,QAAQ,IAAI,kBAe9D,CAAC;AAEF,QAAA,MAAM,sBAAsB,uEAsC1B,CAAC;AAEH,QAAA,MAAM,mBAAmB,uEAuCvB,CAAC;AAEH,QAAA,MAAM,qBAAqB,uEAyBzB,CAAC;AAEH,QAAA,MAAM,uBAAuB,uEAiC3B,CAAC;AAEH,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,GACxB,CAAC"}
@@ -27,9 +27,11 @@ const defaultSanitizeOutput = async (ctx, entity)=>{
27
27
  if (!ctx.schema) {
28
28
  throw new Error('Missing schema in defaultSanitizeOutput');
29
29
  }
30
- return traverseEntity((...args)=>{
31
- removePassword(...args);
32
- removePrivate(...args);
30
+ // Named parameters rather than a rest-spread: this runs once per key, per node, per
31
+ // entity, and `(...args)` allocated a fresh array on every call.
32
+ return traverseEntity((visitorOptions, visitorUtils)=>{
33
+ removePassword(visitorOptions, visitorUtils);
34
+ removePrivate(visitorOptions, visitorUtils);
33
35
  }, ctx, entity);
34
36
  };
35
37
  const defaultSanitizeFilters = fp.curry((ctx, filters)=>{
@@ -1 +1 @@
1
- {"version":3,"file":"sanitizers.js","sources":["../../src/sanitize/sanitizers.ts"],"sourcesContent":["import { curry, isEmpty, isNil, isArray, isPlainObject } from 'lodash/fp';\n\nimport { pipe as pipeAsync } from '../async';\nimport traverseEntity from '../traverse-entity';\nimport { isScalarAttribute, constants } from '../content-types';\n\nimport {\n traverseQueryFilters,\n traverseQuerySort,\n traverseQueryPopulate,\n traverseQueryFields,\n} from '../traverse';\n\nimport {\n removePassword,\n removePrivate,\n removeDynamicZones,\n removeMorphToRelations,\n expandWildcardPopulate,\n} from './visitors';\nimport { isOperator } from '../operators';\n\nimport type { Model, Data } from '../types';\nimport type { Parent } from '../traverse/factory';\n\ninterface Context {\n schema: Model;\n getModel: (model: string) => Model;\n parent?: Parent;\n}\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nconst sanitizePasswords = (ctx: Context) => async (entity: Data) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in sanitizePasswords');\n }\n\n return traverseEntity(removePassword, ctx, entity);\n};\n\nconst defaultSanitizeOutput = async (ctx: Context, entity: Data) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeOutput');\n }\n\n return traverseEntity(\n (...args) => {\n removePassword(...args);\n removePrivate(...args);\n },\n ctx,\n entity\n );\n};\n\nconst defaultSanitizeFilters = curry((ctx: Context, filters: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeFilters');\n }\n\n return pipeAsync(\n // Remove keys that are not attributes or valid operators\n traverseQueryFilters(({ key, attribute }, { remove }) => {\n const isAttribute = !!attribute;\n\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!isAttribute && !isOperator(key)) {\n remove(key);\n }\n }, ctx),\n // Remove dynamic zones from filters\n traverseQueryFilters(removeDynamicZones, ctx),\n // Remove morpTo relations from filters\n traverseQueryFilters(removeMorphToRelations, ctx),\n // Remove passwords from filters\n traverseQueryFilters(removePassword, ctx),\n // Remove private from filters\n traverseQueryFilters(removePrivate, ctx),\n // Remove empty plain objects and empty arrays. Do not use lodash isObject+isEmpty: built-ins with no\n // enumerable keys (Date, RegExp, boxed primitives, etc.) are \"empty\" and would wrongly drop valid operands.\n traverseQueryFilters(({ key, value }, { remove }) => {\n const isEmptyPlainObject = isPlainObject(value) && isEmpty(value);\n const isEmptyArrayOperand = isArray(value) && isEmpty(value);\n if (isEmptyPlainObject || isEmptyArrayOperand) {\n remove(key);\n }\n }, ctx)\n )(filters);\n});\n\nconst defaultSanitizeSort = curry((ctx: Context, sort: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeSort');\n }\n\n return pipeAsync(\n // Remove non attribute keys\n traverseQuerySort(({ key, attribute }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!attribute) {\n remove(key);\n }\n }, ctx),\n // Remove dynamic zones from sort\n traverseQuerySort(removeDynamicZones, ctx),\n // Remove morpTo relations from sort\n traverseQuerySort(removeMorphToRelations, ctx),\n // Remove private from sort\n traverseQuerySort(removePrivate, ctx),\n // Remove passwords from filters\n traverseQuerySort(removePassword, ctx),\n // Remove keys for empty non-scalar values\n traverseQuerySort(({ key, attribute, value }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not removing it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!isScalarAttribute(attribute) && isEmpty(value)) {\n remove(key);\n }\n }, ctx)\n )(sort);\n});\n\nconst defaultSanitizeFields = curry((ctx: Context, fields: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeFields');\n }\n\n return pipeAsync(\n // Only keep scalar attributes\n traverseQueryFields(({ key, attribute }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (isNil(attribute) || !isScalarAttribute(attribute)) {\n remove(key);\n }\n }, ctx),\n // Remove private fields\n traverseQueryFields(removePrivate, ctx),\n // Remove password fields\n traverseQueryFields(removePassword, ctx),\n // Remove nil values from fields array\n (value) => (isArray(value) ? value.filter((field) => !isNil(field)) : value)\n )(fields);\n});\n\nconst defaultSanitizePopulate = curry((ctx: Context, populate: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizePopulate');\n }\n\n return pipeAsync(\n traverseQueryPopulate(expandWildcardPopulate, ctx),\n traverseQueryPopulate(async ({ key, value, schema, attribute, getModel, path }, { set }) => {\n if (attribute) {\n return;\n }\n\n const parent = { key, path, schema, attribute } satisfies Parent;\n\n if (key === 'sort') {\n set(key, await defaultSanitizeSort({ schema, getModel, parent }, value));\n }\n\n if (key === 'filters') {\n set(key, await defaultSanitizeFilters({ schema, getModel, parent }, value));\n }\n\n if (key === 'fields') {\n set(key, await defaultSanitizeFields({ schema, getModel, parent }, value));\n }\n\n if (key === 'populate') {\n set(key, await defaultSanitizePopulate({ schema, getModel, parent }, value));\n }\n }, ctx),\n // Remove private fields\n traverseQueryPopulate(removePrivate, ctx)\n )(populate);\n});\n\nexport {\n sanitizePasswords,\n defaultSanitizeOutput,\n defaultSanitizeFilters,\n defaultSanitizeSort,\n defaultSanitizeFields,\n defaultSanitizePopulate,\n};\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","sanitizePasswords","ctx","entity","schema","Error","traverseEntity","removePassword","defaultSanitizeOutput","args","removePrivate","defaultSanitizeFilters","curry","filters","pipeAsync","traverseQueryFilters","key","attribute","remove","isAttribute","includes","isOperator","removeDynamicZones","removeMorphToRelations","value","isEmptyPlainObject","isPlainObject","isEmpty","isEmptyArrayOperand","isArray","defaultSanitizeSort","sort","traverseQuerySort","isScalarAttribute","defaultSanitizeFields","fields","traverseQueryFields","isNil","filter","field","defaultSanitizePopulate","populate","traverseQueryPopulate","expandWildcardPopulate","getModel","path","set","parent"],"mappings":"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"}
1
+ {"version":3,"file":"sanitizers.js","sources":["../../src/sanitize/sanitizers.ts"],"sourcesContent":["import { curry, isEmpty, isNil, isArray, isPlainObject } from 'lodash/fp';\n\nimport { pipe as pipeAsync } from '../async';\nimport traverseEntity from '../traverse-entity';\nimport { isScalarAttribute, constants } from '../content-types';\n\nimport {\n traverseQueryFilters,\n traverseQuerySort,\n traverseQueryPopulate,\n traverseQueryFields,\n} from '../traverse';\n\nimport {\n removePassword,\n removePrivate,\n removeDynamicZones,\n removeMorphToRelations,\n expandWildcardPopulate,\n} from './visitors';\nimport { isOperator } from '../operators';\n\nimport type { Model, Data } from '../types';\nimport type { Parent } from '../traverse/factory';\n\ninterface Context {\n schema: Model;\n getModel: (model: string) => Model;\n parent?: Parent;\n}\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nconst sanitizePasswords = (ctx: Context) => async (entity: Data) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in sanitizePasswords');\n }\n\n return traverseEntity(removePassword, ctx, entity);\n};\n\nconst defaultSanitizeOutput = async (ctx: Context, entity: Data) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeOutput');\n }\n\n // Named parameters rather than a rest-spread: this runs once per key, per node, per\n // entity, and `(...args)` allocated a fresh array on every call.\n return traverseEntity(\n (visitorOptions, visitorUtils) => {\n removePassword(visitorOptions, visitorUtils);\n removePrivate(visitorOptions, visitorUtils);\n },\n ctx,\n entity\n );\n};\n\nconst defaultSanitizeFilters = curry((ctx: Context, filters: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeFilters');\n }\n\n return pipeAsync(\n // Remove keys that are not attributes or valid operators\n traverseQueryFilters(({ key, attribute }, { remove }) => {\n const isAttribute = !!attribute;\n\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!isAttribute && !isOperator(key)) {\n remove(key);\n }\n }, ctx),\n // Remove dynamic zones from filters\n traverseQueryFilters(removeDynamicZones, ctx),\n // Remove morpTo relations from filters\n traverseQueryFilters(removeMorphToRelations, ctx),\n // Remove passwords from filters\n traverseQueryFilters(removePassword, ctx),\n // Remove private from filters\n traverseQueryFilters(removePrivate, ctx),\n // Remove empty plain objects and empty arrays. Do not use lodash isObject+isEmpty: built-ins with no\n // enumerable keys (Date, RegExp, boxed primitives, etc.) are \"empty\" and would wrongly drop valid operands.\n traverseQueryFilters(({ key, value }, { remove }) => {\n const isEmptyPlainObject = isPlainObject(value) && isEmpty(value);\n const isEmptyArrayOperand = isArray(value) && isEmpty(value);\n if (isEmptyPlainObject || isEmptyArrayOperand) {\n remove(key);\n }\n }, ctx)\n )(filters);\n});\n\nconst defaultSanitizeSort = curry((ctx: Context, sort: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeSort');\n }\n\n return pipeAsync(\n // Remove non attribute keys\n traverseQuerySort(({ key, attribute }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!attribute) {\n remove(key);\n }\n }, ctx),\n // Remove dynamic zones from sort\n traverseQuerySort(removeDynamicZones, ctx),\n // Remove morpTo relations from sort\n traverseQuerySort(removeMorphToRelations, ctx),\n // Remove private from sort\n traverseQuerySort(removePrivate, ctx),\n // Remove passwords from filters\n traverseQuerySort(removePassword, ctx),\n // Remove keys for empty non-scalar values\n traverseQuerySort(({ key, attribute, value }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not removing it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!isScalarAttribute(attribute) && isEmpty(value)) {\n remove(key);\n }\n }, ctx)\n )(sort);\n});\n\nconst defaultSanitizeFields = curry((ctx: Context, fields: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeFields');\n }\n\n return pipeAsync(\n // Only keep scalar attributes\n traverseQueryFields(({ key, attribute }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (isNil(attribute) || !isScalarAttribute(attribute)) {\n remove(key);\n }\n }, ctx),\n // Remove private fields\n traverseQueryFields(removePrivate, ctx),\n // Remove password fields\n traverseQueryFields(removePassword, ctx),\n // Remove nil values from fields array\n (value) => (isArray(value) ? value.filter((field) => !isNil(field)) : value)\n )(fields);\n});\n\nconst defaultSanitizePopulate = curry((ctx: Context, populate: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizePopulate');\n }\n\n return pipeAsync(\n traverseQueryPopulate(expandWildcardPopulate, ctx),\n traverseQueryPopulate(async ({ key, value, schema, attribute, getModel, path }, { set }) => {\n if (attribute) {\n return;\n }\n\n const parent = { key, path, schema, attribute } satisfies Parent;\n\n if (key === 'sort') {\n set(key, await defaultSanitizeSort({ schema, getModel, parent }, value));\n }\n\n if (key === 'filters') {\n set(key, await defaultSanitizeFilters({ schema, getModel, parent }, value));\n }\n\n if (key === 'fields') {\n set(key, await defaultSanitizeFields({ schema, getModel, parent }, value));\n }\n\n if (key === 'populate') {\n set(key, await defaultSanitizePopulate({ schema, getModel, parent }, value));\n }\n }, ctx),\n // Remove private fields\n traverseQueryPopulate(removePrivate, ctx)\n )(populate);\n});\n\nexport {\n sanitizePasswords,\n defaultSanitizeOutput,\n defaultSanitizeFilters,\n defaultSanitizeSort,\n defaultSanitizeFields,\n defaultSanitizePopulate,\n};\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","sanitizePasswords","ctx","entity","schema","Error","traverseEntity","removePassword","defaultSanitizeOutput","visitorOptions","visitorUtils","removePrivate","defaultSanitizeFilters","curry","filters","pipeAsync","traverseQueryFilters","key","attribute","remove","isAttribute","includes","isOperator","removeDynamicZones","removeMorphToRelations","value","isEmptyPlainObject","isPlainObject","isEmpty","isEmptyArrayOperand","isArray","defaultSanitizeSort","sort","traverseQuerySort","isScalarAttribute","defaultSanitizeFields","fields","traverseQueryFields","isNil","filter","field","defaultSanitizePopulate","populate","traverseQueryPopulate","expandWildcardPopulate","getModel","path","set","parent"],"mappings":";;;;;;;;;;;;;;;;;;AA+BA,MAAM,EAAEA,YAAY,EAAEC,gBAAgB,EAAE,GAAGC,sBAAAA;AAE3C,MAAMC,iBAAAA,GAAoB,CAACC,GAAAA,GAAiB,OAAOC,MAAAA,GAAAA;QACjD,IAAI,CAACD,GAAAA,CAAIE,MAAM,EAAE;AACf,YAAA,MAAM,IAAIC,KAAAA,CAAM,qCAAA,CAAA;AAClB,QAAA;QAEA,OAAOC,cAAAA,CAAeC,gBAAgBL,GAAAA,EAAKC,MAAAA,CAAAA;AAC7C,IAAA;AAEA,MAAMK,qBAAAA,GAAwB,OAAON,GAAAA,EAAcC,MAAAA,GAAAA;IACjD,IAAI,CAACD,GAAAA,CAAIE,MAAM,EAAE;AACf,QAAA,MAAM,IAAIC,KAAAA,CAAM,yCAAA,CAAA;AAClB,IAAA;;;IAIA,OAAOC,cAAAA,CACL,CAACG,cAAAA,EAAgBC,YAAAA,GAAAA;AACfH,QAAAA,cAAAA,CAAeE,cAAAA,EAAgBC,YAAAA,CAAAA;AAC/BC,QAAAA,aAAAA,CAAcF,cAAAA,EAAgBC,YAAAA,CAAAA;AAChC,IAAA,CAAA,EACAR,GAAAA,EACAC,MAAAA,CAAAA;AAEJ;AAEA,MAAMS,sBAAAA,GAAyBC,QAAAA,CAAM,CAACX,GAAAA,EAAcY,OAAAA,GAAAA;IAClD,IAAI,CAACZ,GAAAA,CAAIE,MAAM,EAAE;AACf,QAAA,MAAM,IAAIC,KAAAA,CAAM,0CAAA,CAAA;AAClB,IAAA;AAEA,IAAA,OAAOU;IAELC,YAAAA,CAAqB,CAAC,EAAEC,GAAG,EAAEC,SAAS,EAAE,EAAE,EAAEC,MAAM,EAAE,GAAA;QAClD,MAAMC,WAAAA,GAAc,CAAC,CAACF,SAAAA;;;QAItB,IAAI;AAACpB,YAAAA,YAAAA;AAAcC,YAAAA;SAAiB,CAACsB,QAAQ,CAACJ,GAAAA,CAAAA,EAAM;AAClD,YAAA;AACF,QAAA;AAEA,QAAA,IAAI,CAACG,WAAAA,IAAe,CAACE,oBAAAA,CAAWL,GAAAA,CAAAA,EAAM;YACpCE,MAAAA,CAAOF,GAAAA,CAAAA;AACT,QAAA;AACF,IAAA,CAAA,EAAGf;IAEHc,YAAAA,CAAqBO,kBAAAA,EAAoBrB;IAEzCc,YAAAA,CAAqBQ,sBAAAA,EAAwBtB;IAE7Cc,YAAAA,CAAqBT,cAAAA,EAAgBL;IAErCc,YAAAA,CAAqBL,aAAAA,EAAeT;;IAGpCc,YAAAA,CAAqB,CAAC,EAAEC,GAAG,EAAEQ,KAAK,EAAE,EAAE,EAAEN,MAAM,EAAE,GAAA;QAC9C,MAAMO,kBAAAA,GAAqBC,gBAAAA,CAAcF,KAAAA,CAAAA,IAAUG,UAAAA,CAAQH,KAAAA,CAAAA;QAC3D,MAAMI,mBAAAA,GAAsBC,UAAAA,CAAQL,KAAAA,CAAAA,IAAUG,UAAAA,CAAQH,KAAAA,CAAAA;AACtD,QAAA,IAAIC,sBAAsBG,mBAAAA,EAAqB;YAC7CV,MAAAA,CAAOF,GAAAA,CAAAA;AACT,QAAA;AACF,IAAA,CAAA,EAAGf,GAAAA,CAAAA,CAAAA,CACHY,OAAAA,CAAAA;AACJ,CAAA;AAEA,MAAMiB,mBAAAA,GAAsBlB,QAAAA,CAAM,CAACX,GAAAA,EAAc8B,IAAAA,GAAAA;IAC/C,IAAI,CAAC9B,GAAAA,CAAIE,MAAM,EAAE;AACf,QAAA,MAAM,IAAIC,KAAAA,CAAM,uCAAA,CAAA;AAClB,IAAA;AAEA,IAAA,OAAOU;IAELkB,SAAAA,CAAkB,CAAC,EAAEhB,GAAG,EAAEC,SAAS,EAAE,EAAE,EAAEC,MAAM,EAAE,GAAA;;;QAG/C,IAAI;AAACrB,YAAAA,YAAAA;AAAcC,YAAAA;SAAiB,CAACsB,QAAQ,CAACJ,GAAAA,CAAAA,EAAM;AAClD,YAAA;AACF,QAAA;AAEA,QAAA,IAAI,CAACC,SAAAA,EAAW;YACdC,MAAAA,CAAOF,GAAAA,CAAAA;AACT,QAAA;AACF,IAAA,CAAA,EAAGf;IAEH+B,SAAAA,CAAkBV,kBAAAA,EAAoBrB;IAEtC+B,SAAAA,CAAkBT,sBAAAA,EAAwBtB;IAE1C+B,SAAAA,CAAkBtB,aAAAA,EAAeT;IAEjC+B,SAAAA,CAAkB1B,cAAAA,EAAgBL;IAElC+B,SAAAA,CAAkB,CAAC,EAAEhB,GAAG,EAAEC,SAAS,EAAEO,KAAK,EAAE,EAAE,EAAEN,MAAM,EAAE,GAAA;;;QAGtD,IAAI;AAACrB,YAAAA,YAAAA;AAAcC,YAAAA;SAAiB,CAACsB,QAAQ,CAACJ,GAAAA,CAAAA,EAAM;AAClD,YAAA;AACF,QAAA;AAEA,QAAA,IAAI,CAACiB,8BAAAA,CAAkBhB,SAAAA,CAAAA,IAAcU,UAAAA,CAAQH,KAAAA,CAAAA,EAAQ;YACnDN,MAAAA,CAAOF,GAAAA,CAAAA;AACT,QAAA;AACF,IAAA,CAAA,EAAGf,GAAAA,CAAAA,CAAAA,CACH8B,IAAAA,CAAAA;AACJ,CAAA;AAEA,MAAMG,qBAAAA,GAAwBtB,QAAAA,CAAM,CAACX,GAAAA,EAAckC,MAAAA,GAAAA;IACjD,IAAI,CAAClC,GAAAA,CAAIE,MAAM,EAAE;AACf,QAAA,MAAM,IAAIC,KAAAA,CAAM,yCAAA,CAAA;AAClB,IAAA;AAEA,IAAA,OAAOU;IAELsB,WAAAA,CAAoB,CAAC,EAAEpB,GAAG,EAAEC,SAAS,EAAE,EAAE,EAAEC,MAAM,EAAE,GAAA;;;QAGjD,IAAI;AAACrB,YAAAA,YAAAA;AAAcC,YAAAA;SAAiB,CAACsB,QAAQ,CAACJ,GAAAA,CAAAA,EAAM;AAClD,YAAA;AACF,QAAA;AAEA,QAAA,IAAIqB,QAAAA,CAAMpB,SAAAA,CAAAA,IAAc,CAACgB,8BAAAA,CAAkBhB,SAAAA,CAAAA,EAAY;YACrDC,MAAAA,CAAOF,GAAAA,CAAAA;AACT,QAAA;AACF,IAAA,CAAA,EAAGf;IAEHmC,WAAAA,CAAoB1B,aAAAA,EAAeT;IAEnCmC,WAAAA,CAAoB9B,cAAAA,EAAgBL;IAEpC,CAACuB,KAAAA,GAAWK,UAAAA,CAAQL,KAAAA,CAAAA,GAASA,KAAAA,CAAMc,MAAM,CAAC,CAACC,KAAAA,GAAU,CAACF,QAAAA,CAAME,KAAAA,CAAAA,CAAAA,GAAUf,KAAAA,CAAAA,CACtEW,MAAAA,CAAAA;AACJ,CAAA;AAEA,MAAMK,uBAAAA,GAA0B5B,QAAAA,CAAM,CAACX,GAAAA,EAAcwC,QAAAA,GAAAA;IACnD,IAAI,CAACxC,GAAAA,CAAIE,MAAM,EAAE;AACf,QAAA,MAAM,IAAIC,KAAAA,CAAM,2CAAA,CAAA;AAClB,IAAA;IAEA,OAAOU,UAAAA,CACL4B,cAAsBC,sBAAAA,EAAwB1C,GAAAA,CAAAA,EAC9CyC,cAAsB,OAAO,EAAE1B,GAAG,EAAEQ,KAAK,EAAErB,MAAM,EAAEc,SAAS,EAAE2B,QAAQ,EAAEC,IAAI,EAAE,EAAE,EAAEC,GAAG,EAAE,GAAA;AACrF,QAAA,IAAI7B,SAAAA,EAAW;AACb,YAAA;AACF,QAAA;AAEA,QAAA,MAAM8B,MAAAA,GAAS;AAAE/B,YAAAA,GAAAA;AAAK6B,YAAAA,IAAAA;AAAM1C,YAAAA,MAAAA;AAAQc,YAAAA;AAAU,SAAA;AAE9C,QAAA,IAAID,QAAQ,MAAA,EAAQ;YAClB8B,GAAAA,CAAI9B,GAAAA,EAAK,MAAMc,mBAAAA,CAAoB;AAAE3B,gBAAAA,MAAAA;AAAQyC,gBAAAA,QAAAA;AAAUG,gBAAAA;aAAO,EAAGvB,KAAAA,CAAAA,CAAAA;AACnE,QAAA;AAEA,QAAA,IAAIR,QAAQ,SAAA,EAAW;YACrB8B,GAAAA,CAAI9B,GAAAA,EAAK,MAAML,sBAAAA,CAAuB;AAAER,gBAAAA,MAAAA;AAAQyC,gBAAAA,QAAAA;AAAUG,gBAAAA;aAAO,EAAGvB,KAAAA,CAAAA,CAAAA;AACtE,QAAA;AAEA,QAAA,IAAIR,QAAQ,QAAA,EAAU;YACpB8B,GAAAA,CAAI9B,GAAAA,EAAK,MAAMkB,qBAAAA,CAAsB;AAAE/B,gBAAAA,MAAAA;AAAQyC,gBAAAA,QAAAA;AAAUG,gBAAAA;aAAO,EAAGvB,KAAAA,CAAAA,CAAAA;AACrE,QAAA;AAEA,QAAA,IAAIR,QAAQ,UAAA,EAAY;YACtB8B,GAAAA,CAAI9B,GAAAA,EAAK,MAAMwB,uBAAAA,CAAwB;AAAErC,gBAAAA,MAAAA;AAAQyC,gBAAAA,QAAAA;AAAUG,gBAAAA;aAAO,EAAGvB,KAAAA,CAAAA,CAAAA;AACvE,QAAA;AACF,IAAA,CAAA,EAAGvB;AAEHyC,IAAAA,aAAAA,CAAsBhC,eAAeT,GAAAA,CAAAA,CAAAA,CACrCwC,QAAAA,CAAAA;AACJ,CAAA;;;;;;;;;"}
@@ -25,9 +25,11 @@ const defaultSanitizeOutput = async (ctx, entity)=>{
25
25
  if (!ctx.schema) {
26
26
  throw new Error('Missing schema in defaultSanitizeOutput');
27
27
  }
28
- return traverseEntity((...args)=>{
29
- visitor$1(...args);
30
- visitor(...args);
28
+ // Named parameters rather than a rest-spread: this runs once per key, per node, per
29
+ // entity, and `(...args)` allocated a fresh array on every call.
30
+ return traverseEntity((visitorOptions, visitorUtils)=>{
31
+ visitor$1(visitorOptions, visitorUtils);
32
+ visitor(visitorOptions, visitorUtils);
31
33
  }, ctx, entity);
32
34
  };
33
35
  const defaultSanitizeFilters = curry((ctx, filters)=>{
@@ -1 +1 @@
1
- {"version":3,"file":"sanitizers.mjs","sources":["../../src/sanitize/sanitizers.ts"],"sourcesContent":["import { curry, isEmpty, isNil, isArray, isPlainObject } from 'lodash/fp';\n\nimport { pipe as pipeAsync } from '../async';\nimport traverseEntity from '../traverse-entity';\nimport { isScalarAttribute, constants } from '../content-types';\n\nimport {\n traverseQueryFilters,\n traverseQuerySort,\n traverseQueryPopulate,\n traverseQueryFields,\n} from '../traverse';\n\nimport {\n removePassword,\n removePrivate,\n removeDynamicZones,\n removeMorphToRelations,\n expandWildcardPopulate,\n} from './visitors';\nimport { isOperator } from '../operators';\n\nimport type { Model, Data } from '../types';\nimport type { Parent } from '../traverse/factory';\n\ninterface Context {\n schema: Model;\n getModel: (model: string) => Model;\n parent?: Parent;\n}\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nconst sanitizePasswords = (ctx: Context) => async (entity: Data) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in sanitizePasswords');\n }\n\n return traverseEntity(removePassword, ctx, entity);\n};\n\nconst defaultSanitizeOutput = async (ctx: Context, entity: Data) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeOutput');\n }\n\n return traverseEntity(\n (...args) => {\n removePassword(...args);\n removePrivate(...args);\n },\n ctx,\n entity\n );\n};\n\nconst defaultSanitizeFilters = curry((ctx: Context, filters: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeFilters');\n }\n\n return pipeAsync(\n // Remove keys that are not attributes or valid operators\n traverseQueryFilters(({ key, attribute }, { remove }) => {\n const isAttribute = !!attribute;\n\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!isAttribute && !isOperator(key)) {\n remove(key);\n }\n }, ctx),\n // Remove dynamic zones from filters\n traverseQueryFilters(removeDynamicZones, ctx),\n // Remove morpTo relations from filters\n traverseQueryFilters(removeMorphToRelations, ctx),\n // Remove passwords from filters\n traverseQueryFilters(removePassword, ctx),\n // Remove private from filters\n traverseQueryFilters(removePrivate, ctx),\n // Remove empty plain objects and empty arrays. Do not use lodash isObject+isEmpty: built-ins with no\n // enumerable keys (Date, RegExp, boxed primitives, etc.) are \"empty\" and would wrongly drop valid operands.\n traverseQueryFilters(({ key, value }, { remove }) => {\n const isEmptyPlainObject = isPlainObject(value) && isEmpty(value);\n const isEmptyArrayOperand = isArray(value) && isEmpty(value);\n if (isEmptyPlainObject || isEmptyArrayOperand) {\n remove(key);\n }\n }, ctx)\n )(filters);\n});\n\nconst defaultSanitizeSort = curry((ctx: Context, sort: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeSort');\n }\n\n return pipeAsync(\n // Remove non attribute keys\n traverseQuerySort(({ key, attribute }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!attribute) {\n remove(key);\n }\n }, ctx),\n // Remove dynamic zones from sort\n traverseQuerySort(removeDynamicZones, ctx),\n // Remove morpTo relations from sort\n traverseQuerySort(removeMorphToRelations, ctx),\n // Remove private from sort\n traverseQuerySort(removePrivate, ctx),\n // Remove passwords from filters\n traverseQuerySort(removePassword, ctx),\n // Remove keys for empty non-scalar values\n traverseQuerySort(({ key, attribute, value }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not removing it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!isScalarAttribute(attribute) && isEmpty(value)) {\n remove(key);\n }\n }, ctx)\n )(sort);\n});\n\nconst defaultSanitizeFields = curry((ctx: Context, fields: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeFields');\n }\n\n return pipeAsync(\n // Only keep scalar attributes\n traverseQueryFields(({ key, attribute }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (isNil(attribute) || !isScalarAttribute(attribute)) {\n remove(key);\n }\n }, ctx),\n // Remove private fields\n traverseQueryFields(removePrivate, ctx),\n // Remove password fields\n traverseQueryFields(removePassword, ctx),\n // Remove nil values from fields array\n (value) => (isArray(value) ? value.filter((field) => !isNil(field)) : value)\n )(fields);\n});\n\nconst defaultSanitizePopulate = curry((ctx: Context, populate: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizePopulate');\n }\n\n return pipeAsync(\n traverseQueryPopulate(expandWildcardPopulate, ctx),\n traverseQueryPopulate(async ({ key, value, schema, attribute, getModel, path }, { set }) => {\n if (attribute) {\n return;\n }\n\n const parent = { key, path, schema, attribute } satisfies Parent;\n\n if (key === 'sort') {\n set(key, await defaultSanitizeSort({ schema, getModel, parent }, value));\n }\n\n if (key === 'filters') {\n set(key, await defaultSanitizeFilters({ schema, getModel, parent }, value));\n }\n\n if (key === 'fields') {\n set(key, await defaultSanitizeFields({ schema, getModel, parent }, value));\n }\n\n if (key === 'populate') {\n set(key, await defaultSanitizePopulate({ schema, getModel, parent }, value));\n }\n }, ctx),\n // Remove private fields\n traverseQueryPopulate(removePrivate, ctx)\n )(populate);\n});\n\nexport {\n sanitizePasswords,\n defaultSanitizeOutput,\n defaultSanitizeFilters,\n defaultSanitizeSort,\n defaultSanitizeFields,\n defaultSanitizePopulate,\n};\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","sanitizePasswords","ctx","entity","schema","Error","traverseEntity","removePassword","defaultSanitizeOutput","args","removePrivate","defaultSanitizeFilters","curry","filters","pipeAsync","traverseQueryFilters","key","attribute","remove","isAttribute","includes","isOperator","removeDynamicZones","removeMorphToRelations","value","isEmptyPlainObject","isPlainObject","isEmpty","isEmptyArrayOperand","isArray","defaultSanitizeSort","sort","traverseQuerySort","isScalarAttribute","defaultSanitizeFields","fields","traverseQueryFields","isNil","filter","field","defaultSanitizePopulate","populate","traverseQueryPopulate","expandWildcardPopulate","getModel","path","set","parent"],"mappings":"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"}
1
+ {"version":3,"file":"sanitizers.mjs","sources":["../../src/sanitize/sanitizers.ts"],"sourcesContent":["import { curry, isEmpty, isNil, isArray, isPlainObject } from 'lodash/fp';\n\nimport { pipe as pipeAsync } from '../async';\nimport traverseEntity from '../traverse-entity';\nimport { isScalarAttribute, constants } from '../content-types';\n\nimport {\n traverseQueryFilters,\n traverseQuerySort,\n traverseQueryPopulate,\n traverseQueryFields,\n} from '../traverse';\n\nimport {\n removePassword,\n removePrivate,\n removeDynamicZones,\n removeMorphToRelations,\n expandWildcardPopulate,\n} from './visitors';\nimport { isOperator } from '../operators';\n\nimport type { Model, Data } from '../types';\nimport type { Parent } from '../traverse/factory';\n\ninterface Context {\n schema: Model;\n getModel: (model: string) => Model;\n parent?: Parent;\n}\n\nconst { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants;\n\nconst sanitizePasswords = (ctx: Context) => async (entity: Data) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in sanitizePasswords');\n }\n\n return traverseEntity(removePassword, ctx, entity);\n};\n\nconst defaultSanitizeOutput = async (ctx: Context, entity: Data) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeOutput');\n }\n\n // Named parameters rather than a rest-spread: this runs once per key, per node, per\n // entity, and `(...args)` allocated a fresh array on every call.\n return traverseEntity(\n (visitorOptions, visitorUtils) => {\n removePassword(visitorOptions, visitorUtils);\n removePrivate(visitorOptions, visitorUtils);\n },\n ctx,\n entity\n );\n};\n\nconst defaultSanitizeFilters = curry((ctx: Context, filters: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeFilters');\n }\n\n return pipeAsync(\n // Remove keys that are not attributes or valid operators\n traverseQueryFilters(({ key, attribute }, { remove }) => {\n const isAttribute = !!attribute;\n\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!isAttribute && !isOperator(key)) {\n remove(key);\n }\n }, ctx),\n // Remove dynamic zones from filters\n traverseQueryFilters(removeDynamicZones, ctx),\n // Remove morpTo relations from filters\n traverseQueryFilters(removeMorphToRelations, ctx),\n // Remove passwords from filters\n traverseQueryFilters(removePassword, ctx),\n // Remove private from filters\n traverseQueryFilters(removePrivate, ctx),\n // Remove empty plain objects and empty arrays. Do not use lodash isObject+isEmpty: built-ins with no\n // enumerable keys (Date, RegExp, boxed primitives, etc.) are \"empty\" and would wrongly drop valid operands.\n traverseQueryFilters(({ key, value }, { remove }) => {\n const isEmptyPlainObject = isPlainObject(value) && isEmpty(value);\n const isEmptyArrayOperand = isArray(value) && isEmpty(value);\n if (isEmptyPlainObject || isEmptyArrayOperand) {\n remove(key);\n }\n }, ctx)\n )(filters);\n});\n\nconst defaultSanitizeSort = curry((ctx: Context, sort: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeSort');\n }\n\n return pipeAsync(\n // Remove non attribute keys\n traverseQuerySort(({ key, attribute }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!attribute) {\n remove(key);\n }\n }, ctx),\n // Remove dynamic zones from sort\n traverseQuerySort(removeDynamicZones, ctx),\n // Remove morpTo relations from sort\n traverseQuerySort(removeMorphToRelations, ctx),\n // Remove private from sort\n traverseQuerySort(removePrivate, ctx),\n // Remove passwords from filters\n traverseQuerySort(removePassword, ctx),\n // Remove keys for empty non-scalar values\n traverseQuerySort(({ key, attribute, value }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not removing it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (!isScalarAttribute(attribute) && isEmpty(value)) {\n remove(key);\n }\n }, ctx)\n )(sort);\n});\n\nconst defaultSanitizeFields = curry((ctx: Context, fields: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizeFields');\n }\n\n return pipeAsync(\n // Only keep scalar attributes\n traverseQueryFields(({ key, attribute }, { remove }) => {\n // ID is not an attribute per se, so we need to make\n // an extra check to ensure we're not checking it\n if ([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE].includes(key)) {\n return;\n }\n\n if (isNil(attribute) || !isScalarAttribute(attribute)) {\n remove(key);\n }\n }, ctx),\n // Remove private fields\n traverseQueryFields(removePrivate, ctx),\n // Remove password fields\n traverseQueryFields(removePassword, ctx),\n // Remove nil values from fields array\n (value) => (isArray(value) ? value.filter((field) => !isNil(field)) : value)\n )(fields);\n});\n\nconst defaultSanitizePopulate = curry((ctx: Context, populate: unknown) => {\n if (!ctx.schema) {\n throw new Error('Missing schema in defaultSanitizePopulate');\n }\n\n return pipeAsync(\n traverseQueryPopulate(expandWildcardPopulate, ctx),\n traverseQueryPopulate(async ({ key, value, schema, attribute, getModel, path }, { set }) => {\n if (attribute) {\n return;\n }\n\n const parent = { key, path, schema, attribute } satisfies Parent;\n\n if (key === 'sort') {\n set(key, await defaultSanitizeSort({ schema, getModel, parent }, value));\n }\n\n if (key === 'filters') {\n set(key, await defaultSanitizeFilters({ schema, getModel, parent }, value));\n }\n\n if (key === 'fields') {\n set(key, await defaultSanitizeFields({ schema, getModel, parent }, value));\n }\n\n if (key === 'populate') {\n set(key, await defaultSanitizePopulate({ schema, getModel, parent }, value));\n }\n }, ctx),\n // Remove private fields\n traverseQueryPopulate(removePrivate, ctx)\n )(populate);\n});\n\nexport {\n sanitizePasswords,\n defaultSanitizeOutput,\n defaultSanitizeFilters,\n defaultSanitizeSort,\n defaultSanitizeFields,\n defaultSanitizePopulate,\n};\n"],"names":["ID_ATTRIBUTE","DOC_ID_ATTRIBUTE","constants","sanitizePasswords","ctx","entity","schema","Error","traverseEntity","removePassword","defaultSanitizeOutput","visitorOptions","visitorUtils","removePrivate","defaultSanitizeFilters","curry","filters","pipeAsync","traverseQueryFilters","key","attribute","remove","isAttribute","includes","isOperator","removeDynamicZones","removeMorphToRelations","value","isEmptyPlainObject","isPlainObject","isEmpty","isEmptyArrayOperand","isArray","defaultSanitizeSort","sort","traverseQuerySort","isScalarAttribute","defaultSanitizeFields","fields","traverseQueryFields","isNil","filter","field","defaultSanitizePopulate","populate","traverseQueryPopulate","expandWildcardPopulate","getModel","path","set","parent"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"remove-restricted-relations.d.ts","sourceRoot":"","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;yBAStC,MAAM,OAAO,KAAG,OAAO;AAAvC,wBA0HI"}
1
+ {"version":3,"file":"remove-restricted-relations.d.ts","sourceRoot":"","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;yBAStC,MAAM,OAAO,KAAG,OAAO;AAAvC,wBAcI"}
@@ -8,108 +8,150 @@ const ACTIONS_TO_VERIFY = [
8
8
  'find'
9
9
  ];
10
10
  const { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypes.constants;
11
- var removeRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema }, { remove, set })=>{
12
- if (!attribute) {
11
+ var removeRestrictedRelations = ((auth)=>(visitorOptions, visitorUtils)=>{
12
+ const { attribute } = visitorOptions;
13
+ // Deliberately not an `async` function. This visitor is invoked for every key of
14
+ // every node of every entity, and only relation attributes need any work at all.
15
+ // Declaring it `async` meant each of those keys allocated a promise purely to
16
+ // resolve it immediately, and the traversal then awaited it. Returning early
17
+ // without a promise lets the traversal stay synchronous for the common case.
18
+ if (!attribute || attribute.type !== 'relation') {
13
19
  return;
14
20
  }
15
- const isRelation = attribute.type === 'relation';
16
- if (!isRelation) {
21
+ return visitRelationAttribute(auth, visitorOptions, visitorUtils);
22
+ });
23
+ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, { remove, set })=>{
24
+ // Narrowing only; the caller reaches this for relation attributes exclusively.
25
+ if (!attribute) {
26
+ return;
27
+ }
28
+ const handleMorphRelation = async ()=>{
29
+ const elements = data[key];
30
+ if (!elements) {
17
31
  return;
18
32
  }
19
- const handleMorphRelation = async ()=>{
20
- const elements = data[key];
21
- if (!elements) {
22
- return;
33
+ if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {
34
+ const newValue = {};
35
+ const connect = await handleMorphElements(elements.connect || []);
36
+ const relSet = await handleMorphElements(elements.set || []);
37
+ const disconnect = await handleMorphElements(elements.disconnect || []);
38
+ if (connect.length > 0) {
39
+ newValue.connect = connect;
23
40
  }
24
- if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {
25
- const newValue = {};
26
- const connect = await handleMorphElements(elements.connect || []);
27
- const relSet = await handleMorphElements(elements.set || []);
28
- const disconnect = await handleMorphElements(elements.disconnect || []);
29
- if (connect.length > 0) {
30
- newValue.connect = connect;
31
- }
32
- if (relSet.length > 0) {
33
- newValue.set = relSet;
34
- }
35
- if (disconnect.length > 0) {
36
- newValue.disconnect = disconnect;
37
- }
38
- // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here
39
- if ('options' in elements && typeof elements.options === 'object' && elements.options !== null) {
40
- const filteredOptions = {};
41
- // Iterate through the keys of elements.options
42
- Object.keys(elements.options).forEach((key)=>{
43
- const validator = relations.VALID_RELATION_ORDERING_KEYS[key];
44
- // Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it
45
- if (validator && validator(elements.options[key])) {
46
- filteredOptions[key] = elements.options[key];
47
- }
48
- });
49
- // Assign the filtered options back to newValue
50
- newValue.options = filteredOptions;
51
- } else {
52
- newValue.options = {};
53
- }
54
- set(key, newValue);
55
- } else {
56
- const newMorphValue = await handleMorphElements(elements);
57
- if (newMorphValue.length) {
58
- set(key, newMorphValue);
59
- }
41
+ if (relSet.length > 0) {
42
+ newValue.set = relSet;
60
43
  }
61
- };
62
- const handleMorphElements = async (elements)=>{
63
- const allowedElements = [];
64
- if (!fp.isArray(elements)) {
65
- return allowedElements;
44
+ if (disconnect.length > 0) {
45
+ newValue.disconnect = disconnect;
46
+ }
47
+ // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here
48
+ if ('options' in elements && typeof elements.options === 'object' && elements.options !== null) {
49
+ const filteredOptions = {};
50
+ // Iterate through the keys of elements.options
51
+ Object.keys(elements.options).forEach((key)=>{
52
+ const validator = relations.VALID_RELATION_ORDERING_KEYS[key];
53
+ // Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it
54
+ if (validator && validator(elements.options[key])) {
55
+ filteredOptions[key] = elements.options[key];
56
+ }
57
+ });
58
+ // Assign the filtered options back to newValue
59
+ newValue.options = filteredOptions;
60
+ } else {
61
+ newValue.options = {};
66
62
  }
67
- for (const element of elements){
68
- if (!fp.isObject(element) || !('__type' in element)) {
69
- continue;
70
- }
71
- const scopes = ACTIONS_TO_VERIFY.map((action)=>`${element.__type}.${action}`);
72
- const isAllowed = await hasAccessToSomeScopes(scopes, auth);
73
- if (isAllowed) {
74
- allowedElements.push(element);
75
- }
63
+ set(key, newValue);
64
+ } else {
65
+ const newMorphValue = await handleMorphElements(elements);
66
+ if (newMorphValue.length) {
67
+ set(key, newMorphValue);
76
68
  }
69
+ }
70
+ };
71
+ const handleMorphElements = async (elements)=>{
72
+ const allowedElements = [];
73
+ if (!fp.isArray(elements)) {
77
74
  return allowedElements;
78
- };
79
- const handleRegularRelation = async ()=>{
80
- const scopes = ACTIONS_TO_VERIFY.map((action)=>`${attribute.target}.${action}`);
75
+ }
76
+ for (const element of elements){
77
+ if (!fp.isObject(element) || !('__type' in element)) {
78
+ continue;
79
+ }
80
+ const scopes = ACTIONS_TO_VERIFY.map((action)=>`${element.__type}.${action}`);
81
81
  const isAllowed = await hasAccessToSomeScopes(scopes, auth);
82
- // If the authenticated user don't have access to any of the scopes, then remove the field
83
- if (!isAllowed) {
84
- remove(key);
82
+ if (isAllowed) {
83
+ allowedElements.push(element);
85
84
  }
86
- };
87
- const isCreatorRelation = [
88
- CREATED_BY_ATTRIBUTE,
89
- UPDATED_BY_ATTRIBUTE
90
- ].includes(key);
91
- // Polymorphic relations
92
- if (contentTypes.isMorphToRelationalAttribute(attribute)) {
93
- await handleMorphRelation();
94
- return;
95
85
  }
96
- // Creator relations
97
- if (isCreatorRelation && schema.options?.populateCreatorFields) {
98
- // do nothing
99
- return;
86
+ return allowedElements;
87
+ };
88
+ const handleRegularRelation = async ()=>{
89
+ const scopes = ACTIONS_TO_VERIFY.map((action)=>`${attribute.target}.${action}`);
90
+ const isAllowed = await hasAccessToSomeScopes(scopes, auth);
91
+ // If the authenticated user don't have access to any of the scopes, then remove the field
92
+ if (!isAllowed) {
93
+ remove(key);
100
94
  }
101
- // Regular relations
102
- await handleRegularRelation();
103
- });
95
+ };
96
+ const isCreatorRelation = [
97
+ CREATED_BY_ATTRIBUTE,
98
+ UPDATED_BY_ATTRIBUTE
99
+ ].includes(key);
100
+ // Polymorphic relations
101
+ if (contentTypes.isMorphToRelationalAttribute(attribute)) {
102
+ await handleMorphRelation();
103
+ return;
104
+ }
105
+ // Creator relations
106
+ if (isCreatorRelation && schema.options?.populateCreatorFields) {
107
+ // do nothing
108
+ return;
109
+ }
110
+ // Regular relations
111
+ await handleRegularRelation();
112
+ };
113
+ /**
114
+ * Per-`auth` memo of scope decisions.
115
+ *
116
+ * The decision for a given scope depends only on the ability carried by `auth`, which is
117
+ * built once per request and not mutated while the response is sanitized. Sanitizing a
118
+ * list re-asks the same question for every entity, so a 25-entity page with four
119
+ * relations asked ~100 identical questions; each denial also constructed an Error, and
120
+ * capturing a stack trace is by far the most expensive part of that.
121
+ *
122
+ * Keyed weakly on the `auth` object so entries are collected with the request.
123
+ */ const scopeDecisionCache = new WeakMap();
124
+ const canAccessScope = async (scope, auth)=>{
125
+ const cacheable = typeof auth === 'object' && auth !== null;
126
+ let decisions;
127
+ if (cacheable) {
128
+ decisions = scopeDecisionCache.get(auth);
129
+ if (decisions === undefined) {
130
+ decisions = new Map();
131
+ scopeDecisionCache.set(auth, decisions);
132
+ } else {
133
+ const cached = decisions.get(scope);
134
+ if (cached !== undefined) {
135
+ return cached;
136
+ }
137
+ }
138
+ }
139
+ let allowed;
140
+ try {
141
+ await strapi.auth.verify(auth, {
142
+ scope
143
+ });
144
+ allowed = true;
145
+ } catch {
146
+ allowed = false;
147
+ }
148
+ decisions?.set(scope, allowed);
149
+ return allowed;
150
+ };
104
151
  const hasAccessToSomeScopes = async (scopes, auth)=>{
105
152
  for (const scope of scopes){
106
- try {
107
- await strapi.auth.verify(auth, {
108
- scope
109
- });
153
+ if (await canAccessScope(scope, auth)) {
110
154
  return true;
111
- } catch {
112
- continue;
113
155
  }
114
156
  }
115
157
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"remove-restricted-relations.js","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Visitor } from '../../traverse/factory';\nimport { RelationOrderingOptions } from '../../types';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\n\nexport default (auth: unknown): Visitor =>\n async ({ data, key, attribute, schema }, { remove, set }) => {\n if (!attribute) {\n return;\n }\n\n const isRelation = attribute.type === 'relation';\n\n if (!isRelation) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements: any = (data as Record<string, MorphArray>)[key];\n\n if (!elements) {\n return;\n }\n\n if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n if (\n 'options' in elements &&\n typeof elements.options === 'object' &&\n elements.options !== null\n ) {\n const filteredOptions: RelationOrderingOptions = {};\n\n // Iterate through the keys of elements.options\n Object.keys(elements.options).forEach((key) => {\n const validator = VALID_RELATION_ORDERING_KEYS[key as keyof RelationOrderingOptions];\n\n // Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it\n if (validator && validator(elements.options[key])) {\n filteredOptions[key as keyof RelationOrderingOptions] = elements.options[key];\n }\n });\n\n // Assign the filtered options back to newValue\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (newMorphValue.length) {\n set(key, newMorphValue);\n }\n }\n };\n\n const handleMorphElements = async (elements: any[]) => {\n const allowedElements: Record<string, unknown>[] = [];\n\n if (!isArray(elements)) {\n return allowedElements;\n }\n\n for (const element of elements) {\n if (!isObject(element) || !('__type' in element)) {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n // If the authenticated user don't have access to any of the scopes, then remove the field\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n // Polymorphic relations\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n await handleMorphRelation();\n return;\n }\n\n // Creator relations\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n // do nothing\n return;\n }\n\n // Regular relations\n await handleRegularRelation();\n };\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n try {\n await strapi.auth.verify(auth, { scope });\n return true;\n } catch {\n continue;\n }\n }\n\n return false;\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","data","key","attribute","schema","remove","set","isRelation","type","handleMorphRelation","elements","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","validator","VALID_RELATION_ORDERING_KEYS","newMorphValue","allowedElements","isArray","element","isObject","scopes","map","action","__type","isAllowed","hasAccessToSomeScopes","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scope","strapi","verify"],"mappings":"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"}
1
+ {"version":3,"file":"remove-restricted-relations.js","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Visitor } from '../../traverse/factory';\nimport { RelationOrderingOptions } from '../../types';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\n\nexport default (auth: unknown): Visitor =>\n (visitorOptions, visitorUtils) => {\n const { attribute } = visitorOptions;\n\n // Deliberately not an `async` function. This visitor is invoked for every key of\n // every node of every entity, and only relation attributes need any work at all.\n // Declaring it `async` meant each of those keys allocated a promise purely to\n // resolve it immediately, and the traversal then awaited it. Returning early\n // without a promise lets the traversal stay synchronous for the common case.\n if (!attribute || attribute.type !== 'relation') {\n return;\n }\n\n return visitRelationAttribute(auth, visitorOptions, visitorUtils) as unknown as void;\n };\n\nconst visitRelationAttribute = async (\n auth: unknown,\n { data, key, attribute, schema }: Parameters<Visitor>[0],\n { remove, set }: Parameters<Visitor>[1]\n) => {\n // Narrowing only; the caller reaches this for relation attributes exclusively.\n if (!attribute) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements: any = (data as Record<string, MorphArray>)[key];\n\n if (!elements) {\n return;\n }\n\n if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n if (\n 'options' in elements &&\n typeof elements.options === 'object' &&\n elements.options !== null\n ) {\n const filteredOptions: RelationOrderingOptions = {};\n\n // Iterate through the keys of elements.options\n Object.keys(elements.options).forEach((key) => {\n const validator = VALID_RELATION_ORDERING_KEYS[key as keyof RelationOrderingOptions];\n\n // Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it\n if (validator && validator(elements.options[key])) {\n filteredOptions[key as keyof RelationOrderingOptions] = elements.options[key];\n }\n });\n\n // Assign the filtered options back to newValue\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (newMorphValue.length) {\n set(key, newMorphValue);\n }\n }\n };\n\n const handleMorphElements = async (elements: any[]) => {\n const allowedElements: Record<string, unknown>[] = [];\n\n if (!isArray(elements)) {\n return allowedElements;\n }\n\n for (const element of elements) {\n if (!isObject(element) || !('__type' in element)) {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n // If the authenticated user don't have access to any of the scopes, then remove the field\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n // Polymorphic relations\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n await handleMorphRelation();\n return;\n }\n\n // Creator relations\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n // do nothing\n return;\n }\n\n // Regular relations\n await handleRegularRelation();\n};\n\n/**\n * Per-`auth` memo of scope decisions.\n *\n * The decision for a given scope depends only on the ability carried by `auth`, which is\n * built once per request and not mutated while the response is sanitized. Sanitizing a\n * list re-asks the same question for every entity, so a 25-entity page with four\n * relations asked ~100 identical questions; each denial also constructed an Error, and\n * capturing a stack trace is by far the most expensive part of that.\n *\n * Keyed weakly on the `auth` object so entries are collected with the request.\n */\nconst scopeDecisionCache = new WeakMap<object, Map<string, boolean>>();\n\nconst canAccessScope = async (scope: string, auth: unknown): Promise<boolean> => {\n const cacheable = typeof auth === 'object' && auth !== null;\n\n let decisions: Map<string, boolean> | undefined;\n\n if (cacheable) {\n decisions = scopeDecisionCache.get(auth as object);\n\n if (decisions === undefined) {\n decisions = new Map();\n scopeDecisionCache.set(auth as object, decisions);\n } else {\n const cached = decisions.get(scope);\n if (cached !== undefined) {\n return cached;\n }\n }\n }\n\n let allowed: boolean;\n try {\n await strapi.auth.verify(auth, { scope });\n allowed = true;\n } catch {\n allowed = false;\n }\n\n decisions?.set(scope, allowed);\n\n return allowed;\n};\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n if (await canAccessScope(scope, auth)) {\n return true;\n }\n }\n\n return false;\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","visitorOptions","visitorUtils","attribute","type","visitRelationAttribute","data","key","schema","remove","set","handleMorphRelation","elements","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","validator","VALID_RELATION_ORDERING_KEYS","newMorphValue","allowedElements","isArray","element","isObject","scopes","map","action","__type","isAllowed","hasAccessToSomeScopes","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scopeDecisionCache","WeakMap","canAccessScope","scope","cacheable","decisions","get","undefined","Map","cached","allowed","strapi","verify"],"mappings":"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"}