@strapi/core 5.52.0 → 5.52.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/Strapi.d.ts +1 -0
  2. package/dist/Strapi.d.ts.map +1 -1
  3. package/dist/Strapi.js +5 -1
  4. package/dist/Strapi.js.map +1 -1
  5. package/dist/Strapi.mjs +5 -1
  6. package/dist/Strapi.mjs.map +1 -1
  7. package/dist/core-api/routes/validation/common.d.ts +3 -3
  8. package/dist/core-api/routes/validation/common.js +3 -3
  9. package/dist/core-api/routes/validation/common.js.map +1 -1
  10. package/dist/core-api/routes/validation/common.mjs +3 -3
  11. package/dist/core-api/routes/validation/common.mjs.map +1 -1
  12. package/dist/core-api/routes/validation/content-type.d.ts +3 -3
  13. package/dist/core-api/routes/validation/content-type.d.ts.map +1 -1
  14. package/dist/core-api/routes/validation/content-type.js +1 -1
  15. package/dist/core-api/routes/validation/content-type.js.map +1 -1
  16. package/dist/core-api/routes/validation/content-type.mjs +1 -1
  17. package/dist/core-api/routes/validation/content-type.mjs.map +1 -1
  18. package/dist/core-api/routes/validation/schema-registry.d.ts +21 -0
  19. package/dist/core-api/routes/validation/schema-registry.d.ts.map +1 -0
  20. package/dist/core-api/routes/validation/schema-registry.js +99 -0
  21. package/dist/core-api/routes/validation/schema-registry.js.map +1 -0
  22. package/dist/core-api/routes/validation/schema-registry.mjs +77 -0
  23. package/dist/core-api/routes/validation/schema-registry.mjs.map +1 -0
  24. package/dist/core-api/routes/validation/utils.d.ts +6 -6
  25. package/dist/core-api/routes/validation/utils.d.ts.map +1 -1
  26. package/dist/core-api/routes/validation/utils.js +34 -34
  27. package/dist/core-api/routes/validation/utils.js.map +1 -1
  28. package/dist/core-api/routes/validation/utils.mjs +34 -34
  29. package/dist/core-api/routes/validation/utils.mjs.map +1 -1
  30. package/dist/loaders/plugins/get-enabled-plugins.d.ts.map +1 -1
  31. package/dist/loaders/plugins/get-enabled-plugins.js +2 -7
  32. package/dist/loaders/plugins/get-enabled-plugins.js.map +1 -1
  33. package/dist/loaders/plugins/get-enabled-plugins.mjs +2 -3
  34. package/dist/loaders/plugins/get-enabled-plugins.mjs.map +1 -1
  35. package/dist/loaders/plugins/get-user-plugins-config.d.ts +6 -1
  36. package/dist/loaders/plugins/get-user-plugins-config.d.ts.map +1 -1
  37. package/dist/loaders/plugins/get-user-plugins-config.js.map +1 -1
  38. package/dist/loaders/plugins/get-user-plugins-config.mjs.map +1 -1
  39. package/dist/migrations/database/5.0.0-discard-drafts.d.ts.map +1 -1
  40. package/dist/migrations/database/5.0.0-discard-drafts.js +4 -0
  41. package/dist/migrations/database/5.0.0-discard-drafts.js.map +1 -1
  42. package/dist/migrations/database/5.0.0-discard-drafts.mjs +4 -0
  43. package/dist/migrations/database/5.0.0-discard-drafts.mjs.map +1 -1
  44. package/dist/migrations/database/serialize-json-columns.d.ts +12 -0
  45. package/dist/migrations/database/serialize-json-columns.d.ts.map +1 -0
  46. package/dist/migrations/database/serialize-json-columns.js +35 -0
  47. package/dist/migrations/database/serialize-json-columns.js.map +1 -0
  48. package/dist/migrations/database/serialize-json-columns.mjs +33 -0
  49. package/dist/migrations/database/serialize-json-columns.mjs.map +1 -0
  50. package/dist/package.json.js +14 -14
  51. package/dist/package.json.mjs +14 -14
  52. package/dist/services/content-api/index.d.ts.map +1 -1
  53. package/dist/services/content-api/index.js +33 -41
  54. package/dist/services/content-api/index.js.map +1 -1
  55. package/dist/services/content-api/index.mjs +33 -38
  56. package/dist/services/content-api/index.mjs.map +1 -1
  57. package/dist/services/content-api/permissions/index.js +2 -2
  58. package/dist/services/content-api/permissions/index.js.map +1 -1
  59. package/dist/services/content-api/permissions/index.mjs +2 -2
  60. package/dist/services/content-api/permissions/index.mjs.map +1 -1
  61. package/dist/services/server/register-routes.js +7 -7
  62. package/dist/services/server/register-routes.js.map +1 -1
  63. package/dist/services/server/register-routes.mjs +7 -7
  64. package/dist/services/server/register-routes.mjs.map +1 -1
  65. package/dist/services/session-manager.d.ts.map +1 -1
  66. package/dist/services/session-manager.js +9 -9
  67. package/dist/services/session-manager.js.map +1 -1
  68. package/dist/services/session-manager.mjs +9 -9
  69. package/dist/services/session-manager.mjs.map +1 -1
  70. package/dist/utils/zod.d.ts +23 -0
  71. package/dist/utils/zod.d.ts.map +1 -0
  72. package/dist/utils/zod.js +46 -0
  73. package/dist/utils/zod.js.map +1 -0
  74. package/dist/utils/zod.mjs +43 -0
  75. package/dist/utils/zod.mjs.map +1 -0
  76. package/package.json +14 -14
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Re-serializes JSON/blocks columns in a row before INSERT.
3
+ *
4
+ * mysql2 and node-pg parse JSON/JSONB columns on SELECT into JS objects; knex
5
+ * does not re-serialize those objects on INSERT. Stringify object/array values
6
+ * for json and blocks attributes. Values that are already strings (typical of
7
+ * SQLite) are left unchanged.
8
+ */
9
+ export declare const serializeJsonColumns: (row: Record<string, any>, meta: {
10
+ attributes?: Record<string, any>;
11
+ } | null | undefined) => Record<string, any>;
12
+ //# sourceMappingURL=serialize-json-columns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize-json-columns.d.ts","sourceRoot":"","sources":["../../../src/migrations/database/serialize-json-columns.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAC/B,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACxB,MAAM;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,KAC5D,MAAM,CAAC,MAAM,EAAE,GAAG,CAsBpB,CAAC"}
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ const JSON_ATTRIBUTE_TYPES = new Set([
4
+ 'json',
5
+ 'blocks'
6
+ ]);
7
+ /**
8
+ * Re-serializes JSON/blocks columns in a row before INSERT.
9
+ *
10
+ * mysql2 and node-pg parse JSON/JSONB columns on SELECT into JS objects; knex
11
+ * does not re-serialize those objects on INSERT. Stringify object/array values
12
+ * for json and blocks attributes. Values that are already strings (typical of
13
+ * SQLite) are left unchanged.
14
+ */ const serializeJsonColumns = (row, meta)=>{
15
+ if (!meta?.attributes) {
16
+ return row;
17
+ }
18
+ for (const attribute of Object.values(meta.attributes)){
19
+ if (!JSON_ATTRIBUTE_TYPES.has(attribute.type)) {
20
+ continue;
21
+ }
22
+ const columnName = attribute.columnName;
23
+ if (!columnName || !(columnName in row)) {
24
+ continue;
25
+ }
26
+ const value = row[columnName];
27
+ if (value != null && typeof value === 'object') {
28
+ row[columnName] = JSON.stringify(value);
29
+ }
30
+ }
31
+ return row;
32
+ };
33
+
34
+ exports.serializeJsonColumns = serializeJsonColumns;
35
+ //# sourceMappingURL=serialize-json-columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize-json-columns.js","sources":["../../../src/migrations/database/serialize-json-columns.ts"],"sourcesContent":["const JSON_ATTRIBUTE_TYPES = new Set(['json', 'blocks']);\n\n/**\n * Re-serializes JSON/blocks columns in a row before INSERT.\n *\n * mysql2 and node-pg parse JSON/JSONB columns on SELECT into JS objects; knex\n * does not re-serialize those objects on INSERT. Stringify object/array values\n * for json and blocks attributes. Values that are already strings (typical of\n * SQLite) are left unchanged.\n */\nexport const serializeJsonColumns = (\n row: Record<string, any>,\n meta: { attributes?: Record<string, any> } | null | undefined\n): Record<string, any> => {\n if (!meta?.attributes) {\n return row;\n }\n\n for (const attribute of Object.values(meta.attributes) as any[]) {\n if (!JSON_ATTRIBUTE_TYPES.has(attribute.type)) {\n continue;\n }\n\n const columnName = attribute.columnName;\n if (!columnName || !(columnName in row)) {\n continue;\n }\n\n const value = row[columnName];\n if (value != null && typeof value === 'object') {\n row[columnName] = JSON.stringify(value);\n }\n }\n\n return row;\n};\n"],"names":["JSON_ATTRIBUTE_TYPES","Set","serializeJsonColumns","row","meta","attributes","attribute","Object","values","has","type","columnName","value","JSON","stringify"],"mappings":";;AAAA,MAAMA,oBAAAA,GAAuB,IAAIC,GAAAA,CAAI;AAAC,IAAA,MAAA;AAAQ,IAAA;AAAS,CAAA,CAAA;AAEvD;;;;;;;AAOC,IACM,MAAMC,oBAAAA,GAAuB,CAClCC,GAAAA,EACAC,IAAAA,GAAAA;IAEA,IAAI,CAACA,MAAMC,UAAAA,EAAY;QACrB,OAAOF,GAAAA;AACT,IAAA;AAEA,IAAA,KAAK,MAAMG,SAAAA,IAAaC,MAAAA,CAAOC,MAAM,CAACJ,IAAAA,CAAKC,UAAU,CAAA,CAAY;AAC/D,QAAA,IAAI,CAACL,oBAAAA,CAAqBS,GAAG,CAACH,SAAAA,CAAUI,IAAI,CAAA,EAAG;AAC7C,YAAA;AACF,QAAA;QAEA,MAAMC,UAAAA,GAAaL,UAAUK,UAAU;AACvC,QAAA,IAAI,CAACA,UAAAA,IAAc,EAAEA,UAAAA,IAAcR,GAAE,CAAA,EAAI;AACvC,YAAA;AACF,QAAA;QAEA,MAAMS,KAAAA,GAAQT,GAAG,CAACQ,UAAAA,CAAW;AAC7B,QAAA,IAAIC,KAAAA,IAAS,IAAA,IAAQ,OAAOA,KAAAA,KAAU,QAAA,EAAU;AAC9CT,YAAAA,GAAG,CAACQ,UAAAA,CAAW,GAAGE,IAAAA,CAAKC,SAAS,CAACF,KAAAA,CAAAA;AACnC,QAAA;AACF,IAAA;IAEA,OAAOT,GAAAA;AACT;;;;"}
@@ -0,0 +1,33 @@
1
+ const JSON_ATTRIBUTE_TYPES = new Set([
2
+ 'json',
3
+ 'blocks'
4
+ ]);
5
+ /**
6
+ * Re-serializes JSON/blocks columns in a row before INSERT.
7
+ *
8
+ * mysql2 and node-pg parse JSON/JSONB columns on SELECT into JS objects; knex
9
+ * does not re-serialize those objects on INSERT. Stringify object/array values
10
+ * for json and blocks attributes. Values that are already strings (typical of
11
+ * SQLite) are left unchanged.
12
+ */ const serializeJsonColumns = (row, meta)=>{
13
+ if (!meta?.attributes) {
14
+ return row;
15
+ }
16
+ for (const attribute of Object.values(meta.attributes)){
17
+ if (!JSON_ATTRIBUTE_TYPES.has(attribute.type)) {
18
+ continue;
19
+ }
20
+ const columnName = attribute.columnName;
21
+ if (!columnName || !(columnName in row)) {
22
+ continue;
23
+ }
24
+ const value = row[columnName];
25
+ if (value != null && typeof value === 'object') {
26
+ row[columnName] = JSON.stringify(value);
27
+ }
28
+ }
29
+ return row;
30
+ };
31
+
32
+ export { serializeJsonColumns };
33
+ //# sourceMappingURL=serialize-json-columns.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize-json-columns.mjs","sources":["../../../src/migrations/database/serialize-json-columns.ts"],"sourcesContent":["const JSON_ATTRIBUTE_TYPES = new Set(['json', 'blocks']);\n\n/**\n * Re-serializes JSON/blocks columns in a row before INSERT.\n *\n * mysql2 and node-pg parse JSON/JSONB columns on SELECT into JS objects; knex\n * does not re-serialize those objects on INSERT. Stringify object/array values\n * for json and blocks attributes. Values that are already strings (typical of\n * SQLite) are left unchanged.\n */\nexport const serializeJsonColumns = (\n row: Record<string, any>,\n meta: { attributes?: Record<string, any> } | null | undefined\n): Record<string, any> => {\n if (!meta?.attributes) {\n return row;\n }\n\n for (const attribute of Object.values(meta.attributes) as any[]) {\n if (!JSON_ATTRIBUTE_TYPES.has(attribute.type)) {\n continue;\n }\n\n const columnName = attribute.columnName;\n if (!columnName || !(columnName in row)) {\n continue;\n }\n\n const value = row[columnName];\n if (value != null && typeof value === 'object') {\n row[columnName] = JSON.stringify(value);\n }\n }\n\n return row;\n};\n"],"names":["JSON_ATTRIBUTE_TYPES","Set","serializeJsonColumns","row","meta","attributes","attribute","Object","values","has","type","columnName","value","JSON","stringify"],"mappings":"AAAA,MAAMA,oBAAAA,GAAuB,IAAIC,GAAAA,CAAI;AAAC,IAAA,MAAA;AAAQ,IAAA;AAAS,CAAA,CAAA;AAEvD;;;;;;;AAOC,IACM,MAAMC,oBAAAA,GAAuB,CAClCC,GAAAA,EACAC,IAAAA,GAAAA;IAEA,IAAI,CAACA,MAAMC,UAAAA,EAAY;QACrB,OAAOF,GAAAA;AACT,IAAA;AAEA,IAAA,KAAK,MAAMG,SAAAA,IAAaC,MAAAA,CAAOC,MAAM,CAACJ,IAAAA,CAAKC,UAAU,CAAA,CAAY;AAC/D,QAAA,IAAI,CAACL,oBAAAA,CAAqBS,GAAG,CAACH,SAAAA,CAAUI,IAAI,CAAA,EAAG;AAC7C,YAAA;AACF,QAAA;QAEA,MAAMC,UAAAA,GAAaL,UAAUK,UAAU;AACvC,QAAA,IAAI,CAACA,UAAAA,IAAc,EAAEA,UAAAA,IAAcR,GAAE,CAAA,EAAI;AACvC,YAAA;AACF,QAAA;QAEA,MAAMS,KAAAA,GAAQT,GAAG,CAACQ,UAAAA,CAAW;AAC7B,QAAA,IAAIC,KAAAA,IAAS,IAAA,IAAQ,OAAOA,KAAAA,KAAU,QAAA,EAAU;AAC9CT,YAAAA,GAAG,CAACQ,UAAAA,CAAW,GAAGE,IAAAA,CAAKC,SAAS,CAACF,KAAAA,CAAAA;AACnC,QAAA;AACF,IAAA;IAEA,OAAOT,GAAAA;AACT;;;;"}
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var name = "@strapi/core";
6
- var version = "5.52.0";
6
+ var version = "5.52.2";
7
7
  var description = "Core of Strapi";
8
8
  var homepage = "https://strapi.io";
9
9
  var bugs = {
@@ -65,15 +65,15 @@ var dependencies = {
65
65
  "@koa/router": "12.0.2",
66
66
  "@modelcontextprotocol/sdk": "1.30.0",
67
67
  "@paralleldrive/cuid2": "2.2.2",
68
- "@strapi/admin": "5.52.0",
69
- "@strapi/database": "5.52.0",
70
- "@strapi/generators": "5.52.0",
71
- "@strapi/logger": "5.52.0",
72
- "@strapi/openapi": "5.52.0",
73
- "@strapi/permissions": "5.52.0",
74
- "@strapi/types": "5.52.0",
75
- "@strapi/typescript-utils": "5.52.0",
76
- "@strapi/utils": "5.52.0",
68
+ "@strapi/admin": "5.52.2",
69
+ "@strapi/database": "5.52.2",
70
+ "@strapi/generators": "5.52.2",
71
+ "@strapi/logger": "5.52.2",
72
+ "@strapi/openapi": "5.52.2",
73
+ "@strapi/permissions": "5.52.2",
74
+ "@strapi/types": "5.52.2",
75
+ "@strapi/typescript-utils": "5.52.2",
76
+ "@strapi/utils": "5.52.2",
77
77
  "@vercel/stega": "0.1.2",
78
78
  bcryptjs: "2.4.3",
79
79
  boxen: "5.1.2",
@@ -117,7 +117,7 @@ var dependencies = {
117
117
  typescript: "5.9.3",
118
118
  undici: "6.28.0",
119
119
  yup: "0.32.9",
120
- zod: "3.25.76"
120
+ zod: "4.4.3"
121
121
  };
122
122
  var devDependencies = {
123
123
  "@swc/core": "1.13.5",
@@ -137,12 +137,12 @@ var devDependencies = {
137
137
  "@types/mime-types": "2.1.1",
138
138
  "@types/node": "20.19.41",
139
139
  "@types/node-schedule": "2.1.7",
140
- "eslint-config-custom": "5.52.0",
140
+ "eslint-config-custom": "5.52.2",
141
141
  jest: "29.6.0",
142
142
  supertest: "7.2.2",
143
- tsconfig: "5.52.0",
143
+ tsconfig: "5.52.2",
144
144
  vitest: "catalog:",
145
- "vitest-config": "5.52.0"
145
+ "vitest-config": "5.52.2"
146
146
  };
147
147
  var engines = {
148
148
  node: ">=20.0.0 <=26.x.x",
@@ -1,5 +1,5 @@
1
1
  var name = "@strapi/core";
2
- var version = "5.52.0";
2
+ var version = "5.52.2";
3
3
  var description = "Core of Strapi";
4
4
  var homepage = "https://strapi.io";
5
5
  var bugs = {
@@ -61,15 +61,15 @@ var dependencies = {
61
61
  "@koa/router": "12.0.2",
62
62
  "@modelcontextprotocol/sdk": "1.30.0",
63
63
  "@paralleldrive/cuid2": "2.2.2",
64
- "@strapi/admin": "5.52.0",
65
- "@strapi/database": "5.52.0",
66
- "@strapi/generators": "5.52.0",
67
- "@strapi/logger": "5.52.0",
68
- "@strapi/openapi": "5.52.0",
69
- "@strapi/permissions": "5.52.0",
70
- "@strapi/types": "5.52.0",
71
- "@strapi/typescript-utils": "5.52.0",
72
- "@strapi/utils": "5.52.0",
64
+ "@strapi/admin": "5.52.2",
65
+ "@strapi/database": "5.52.2",
66
+ "@strapi/generators": "5.52.2",
67
+ "@strapi/logger": "5.52.2",
68
+ "@strapi/openapi": "5.52.2",
69
+ "@strapi/permissions": "5.52.2",
70
+ "@strapi/types": "5.52.2",
71
+ "@strapi/typescript-utils": "5.52.2",
72
+ "@strapi/utils": "5.52.2",
73
73
  "@vercel/stega": "0.1.2",
74
74
  bcryptjs: "2.4.3",
75
75
  boxen: "5.1.2",
@@ -113,7 +113,7 @@ var dependencies = {
113
113
  typescript: "5.9.3",
114
114
  undici: "6.28.0",
115
115
  yup: "0.32.9",
116
- zod: "3.25.76"
116
+ zod: "4.4.3"
117
117
  };
118
118
  var devDependencies = {
119
119
  "@swc/core": "1.13.5",
@@ -133,12 +133,12 @@ var devDependencies = {
133
133
  "@types/mime-types": "2.1.1",
134
134
  "@types/node": "20.19.41",
135
135
  "@types/node-schedule": "2.1.7",
136
- "eslint-config-custom": "5.52.0",
136
+ "eslint-config-custom": "5.52.2",
137
137
  jest: "29.6.0",
138
138
  supertest: "7.2.2",
139
- tsconfig: "5.52.0",
139
+ tsconfig: "5.52.2",
140
140
  vitest: "catalog:",
141
- "vitest-config": "5.52.0"
141
+ "vitest-config": "5.52.2"
142
142
  };
143
143
  var engines = {
144
144
  node: ">=20.0.0 <=26.x.x",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/content-api/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EACR,QAAQ,EAIT,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAO,MAAM,eAAe,CAAC;AAiHxD;;GAEG;AACH,QAAA,MAAM,gBAAgB,GAAI,QAAQ,IAAI,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+HAhFhB,CAAC;;;;;;;;+HAZ6C,CAAA;;;;;;8BAsIxC,OAAO,CAAC,UAAU,CAAC,qBAAqB;8BAQxC,OAAO,CAAC,UAAU,CAAC,qBAAqB;uCAK/B,IAAI,CAAC,KAAK,EAAE,KAAG,IAAI;CA2F9D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/content-api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EAIT,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAO,MAAM,eAAe,CAAC;AA8HxD;;GAEG;AACH,QAAA,MAAM,gBAAgB,GAAI,QAAQ,IAAI,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+HAxFL,CAAA;;;;;;;;+HAJjC,CAAC;;;;;;8BAsI2B,OAAO,CAAC,UAAU,CAAC,qBAAqB;8BAQxC,OAAO,CAAC,UAAU,CAAC,qBAAqB;uCAK/B,IAAI,CAAC,KAAK,EAAE,KAAG,IAAI;CAqF9D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1,12 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var _ = require('lodash');
4
3
  var strapiUtils = require('@strapi/utils');
5
4
  var z = require('zod/v4');
5
+ var zod = require('../../utils/zod.js');
6
6
  var index = require('./permissions/index.js');
7
7
 
8
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
-
10
8
  function _interopNamespace(e) {
11
9
  if (e && e.__esModule) return e;
12
10
  var n = Object.create(null);
@@ -25,7 +23,6 @@ function _interopNamespace(e) {
25
23
  return Object.freeze(n);
26
24
  }
27
25
 
28
- var ___default = /*#__PURE__*/_interopDefault(_);
29
26
  var z__namespace = /*#__PURE__*/_interopNamespace(z);
30
27
 
31
28
  const transformRoutePrefixFor = (pluginName)=>(route)=>{
@@ -41,33 +38,35 @@ const filterContentAPI = (route)=>route.info.type === 'content-api';
41
38
  * Runtime check for addQueryParams: we only allow scalar or array-of-scalar schemas (no nested objects).
42
39
  * We keep this in addition to the ZodQueryParamSchema type because: (1) TypeScript can be bypassed (JS,
43
40
  * any, or schema from another Zod instance); (2) it gives a clear, immediate error at registration
44
- * time instead of a later failure in validate/sanitize. This list is intentionally tied to Zod v4
45
- * constructor names; if Zod changes internals, this may need updating.
46
- * Compatibility: Zod 3 and Zod 4 Classic (zod/v4) both use these constructor names and
47
- * expose ._def with .innerType / .element for Optional/Default/Array. Zod 4 Core/Mini use
48
- * ._zod.def instead; we only accept schemas from the same zod/v4 instance used here.
49
- */ const ALLOWED_QUERY_SCHEMA_NAMES = new Set([
50
- 'ZodString',
51
- 'ZodNumber',
52
- 'ZodBoolean',
53
- 'ZodEnum',
54
- 'ZodOptional',
55
- 'ZodDefault',
56
- 'ZodArray'
41
+ * time instead of a later failure in validate/sanitize.
42
+ */ const ALLOWED_QUERY_SCHEMA_TYPES = new Set([
43
+ 'string',
44
+ 'number',
45
+ 'boolean',
46
+ 'enum'
57
47
  ]);
48
+ const formatZodSchemaName = (type)=>{
49
+ if (type.length === 0) {
50
+ return '';
51
+ }
52
+ return `Zod${type.split('_').map((part)=>`${part.charAt(0).toUpperCase()}${part.slice(1)}`).join('')}`;
53
+ };
58
54
  function assertQueryParamSchema(schema, param) {
59
- const name = schema?.constructor?.name ?? '';
60
- if (!ALLOWED_QUERY_SCHEMA_NAMES.has(name)) {
55
+ if (!zod.isZodType(schema)) {
56
+ const name = typeof schema === 'object' && schema !== null ? schema.constructor?.name ?? 'Object' : typeof schema;
61
57
  throw new Error(`contentAPI.addQueryParams: param "${param}" schema must be a scalar (string, number, boolean, enum) or array of scalars; got ${name}. Use addInputParams for nested objects.`);
62
58
  }
63
- if (name === 'ZodOptional' || name === 'ZodDefault') {
64
- const inner = schema?._def?.innerType;
65
- if (inner) assertQueryParamSchema(inner, param);
59
+ const inspection = zod.inspectZodSchema(schema);
60
+ if (inspection.type === 'optional' || inspection.type === 'default') {
61
+ assertQueryParamSchema(inspection.innerType, param);
62
+ return;
63
+ }
64
+ if (inspection.type === 'array') {
65
+ assertQueryParamSchema(inspection.element, param);
66
66
  return;
67
67
  }
68
- if (name === 'ZodArray') {
69
- const element = schema?._def?.element;
70
- if (element) assertQueryParamSchema(element, param);
68
+ if (!ALLOWED_QUERY_SCHEMA_TYPES.has(inspection.type)) {
69
+ throw new Error(`contentAPI.addQueryParams: param "${param}" schema must be a scalar (string, number, boolean, enum) or array of scalars; got ${formatZodSchemaName(inspection.type)}. Use addInputParams for nested objects.`);
71
70
  }
72
71
  }
73
72
  function resolveSchema(schemaOrFactory) {
@@ -183,37 +182,30 @@ const mergeOneInputParamIntoRoute = (route, param, schema, matchRoute)=>{
183
182
  };
184
183
  const getRoutesMap = async ()=>{
185
184
  const routesMap = {};
186
- ___default.default.forEach(strapi.apis, (api, apiName)=>{
187
- const routes = ___default.default.flatMap(api.routes, (route)=>{
188
- if ('routes' in route) {
189
- return route.routes;
190
- }
191
- return route;
192
- }).filter(filterContentAPI);
185
+ for (const [apiName, api] of Object.entries(strapi.apis)){
186
+ const routes = Object.values(api.routes).flatMap((route)=>'routes' in route ? route.routes : route).filter(filterContentAPI);
193
187
  if (routes.length === 0) {
194
- return;
188
+ continue;
195
189
  }
196
190
  const apiPrefix = strapi.config.get('api.rest.prefix');
197
191
  routesMap[`api::${apiName}`] = routes.map((route)=>({
198
192
  ...route,
199
193
  path: `${apiPrefix}${route.path}`
200
194
  }));
201
- });
202
- ___default.default.forEach(strapi.plugins, (plugin, pluginName)=>{
195
+ }
196
+ for (const [pluginName, plugin] of Object.entries(strapi.plugins)){
203
197
  const transformPrefix = transformRoutePrefixFor(pluginName);
204
- if (Array.isArray(plugin.routes)) {
205
- return plugin.routes.map(transformPrefix).filter(filterContentAPI);
206
- }
207
- const routes = ___default.default.flatMap(plugin.routes, (route)=>route.routes.map(transformPrefix)).filter(filterContentAPI);
198
+ const pluginRoutes = Array.isArray(plugin.routes) ? plugin.routes : Object.values(plugin.routes).flatMap((route)=>route.routes);
199
+ const routes = pluginRoutes.map(transformPrefix).filter(filterContentAPI);
208
200
  if (routes.length === 0) {
209
- return;
201
+ continue;
210
202
  }
211
203
  const apiPrefix = strapi.config.get('api.rest.prefix');
212
204
  routesMap[`plugin::${pluginName}`] = routes.map((route)=>({
213
205
  ...route,
214
206
  path: `${apiPrefix}${route.path}`
215
207
  }));
216
- });
208
+ }
217
209
  return strapiUtils.sanitizeRoutesMapForSerialization(routesMap);
218
210
  };
219
211
  const sanitizer = strapiUtils.sanitize.createAPISanitizers({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/services/content-api/index.ts"],"sourcesContent":["import _ from 'lodash';\nimport {\n sanitize,\n validate,\n sanitizeRoutesMapForSerialization,\n ALLOWED_QUERY_PARAM_KEYS,\n RESERVED_INPUT_PARAM_KEYS,\n} from '@strapi/utils';\nimport * as z from 'zod/v4';\n\nimport type { Core, Modules, UID } from '@strapi/types';\n\nimport instantiatePermissionsUtilities from './permissions';\n\nconst transformRoutePrefixFor = (pluginName: string) => (route: Core.Route) => {\n const prefix = route.config && route.config.prefix;\n const path = prefix !== undefined ? `${prefix}${route.path}` : `/${pluginName}${route.path}`;\n\n return {\n ...route,\n path,\n };\n};\n\nconst filterContentAPI = (route: Core.Route) => route.info.type === 'content-api';\n\n/**\n * Runtime check for addQueryParams: we only allow scalar or array-of-scalar schemas (no nested objects).\n * We keep this in addition to the ZodQueryParamSchema type because: (1) TypeScript can be bypassed (JS,\n * any, or schema from another Zod instance); (2) it gives a clear, immediate error at registration\n * time instead of a later failure in validate/sanitize. This list is intentionally tied to Zod v4\n * constructor names; if Zod changes internals, this may need updating.\n * Compatibility: Zod 3 and Zod 4 Classic (zod/v4) both use these constructor names and\n * expose ._def with .innerType / .element for Optional/Default/Array. Zod 4 Core/Mini use\n * ._zod.def instead; we only accept schemas from the same zod/v4 instance used here.\n */\nconst ALLOWED_QUERY_SCHEMA_NAMES = new Set([\n 'ZodString',\n 'ZodNumber',\n 'ZodBoolean',\n 'ZodEnum',\n 'ZodOptional',\n 'ZodDefault',\n 'ZodArray',\n]);\n\nfunction assertQueryParamSchema(schema: unknown, param: string): void {\n const name = (schema as { constructor?: { name?: string } })?.constructor?.name ?? '';\n if (!ALLOWED_QUERY_SCHEMA_NAMES.has(name)) {\n throw new Error(\n `contentAPI.addQueryParams: param \"${param}\" schema must be a scalar (string, number, boolean, enum) or array of scalars; got ${name}. Use addInputParams for nested objects.`\n );\n }\n if (name === 'ZodOptional' || name === 'ZodDefault') {\n const inner = (schema as { _def?: { innerType?: unknown } })?._def?.innerType;\n if (inner) assertQueryParamSchema(inner, param);\n return;\n }\n if (name === 'ZodArray') {\n const element = (schema as { _def?: { element?: unknown } })?._def?.element;\n if (element) assertQueryParamSchema(element, param);\n }\n}\n\nfunction resolveSchema<T>(schemaOrFactory: T | ((zInstance: typeof z) => T)): T {\n if (typeof schemaOrFactory === 'function') {\n return (schemaOrFactory as (zInstance: typeof z) => T)(z);\n }\n return schemaOrFactory;\n}\n\nconst mergeOneQueryParamIntoRoute = (\n route: Core.Route,\n param: string,\n schema: z.ZodType,\n matchRoute?: (route: Core.Route) => boolean\n): void => {\n if (matchRoute && !matchRoute(route)) return;\n const query = { ...route.request?.query };\n if (param in query) {\n throw new Error(\n `contentAPI.addQueryParams: param \"${param}\" already exists on route ${route.method} ${route.path}`\n );\n }\n route.request = { ...route.request, query: { ...query, [param]: schema } };\n};\n\nconst mergeOneInputParamIntoRoute = (\n route: Core.Route,\n param: string,\n schema: z.ZodType,\n matchRoute?: (route: Core.Route) => boolean\n): void => {\n if (matchRoute && !matchRoute(route)) return;\n const jsonKey = 'application/json';\n type RouteBody = NonNullable<NonNullable<Core.Route['request']>['body']>;\n const body: RouteBody = route.request?.body ? { ...route.request.body } : ({} as RouteBody);\n const existing = body[jsonKey];\n const base =\n existing && typeof existing === 'object' && 'shape' in existing\n ? (existing as { shape: Record<string, z.ZodType> }).shape\n : {};\n if (param in base) {\n throw new Error(\n `contentAPI.addInputParams: param \"${param}\" already exists on route ${route.method} ${route.path}`\n );\n }\n body[jsonKey] = z.object({ ...base, [param]: schema }) as RouteBody[keyof RouteBody];\n route.request = { ...route.request, body };\n};\n\n/** Stored options with schema always resolved (never a function). */\ntype ResolvedQueryParamEntry = {\n param: string;\n schema: z.ZodType;\n matchRoute?: (route: Core.Route) => boolean;\n};\ntype ResolvedInputParamEntry = {\n param: string;\n schema: z.ZodType;\n matchRoute?: (route: Core.Route) => boolean;\n};\n\n/**\n * Create a content API container that holds logic, tools and utils. (eg: permissions, ...)\n */\nconst createContentAPI = (strapi: Core.Strapi) => {\n const extraQueryParams: ResolvedQueryParamEntry[] = [];\n const extraInputParams: ResolvedInputParamEntry[] = [];\n\n const addQueryParam = (options: Modules.ContentAPI.QueryParamEntry & { param: string }) => {\n const { param, schema: schemaOrFactory, matchRoute } = options;\n const schema = resolveSchema(schemaOrFactory);\n assertQueryParamSchema(schema, param);\n if ((ALLOWED_QUERY_PARAM_KEYS as readonly string[]).includes(param)) {\n throw new Error(\n `contentAPI.addQueryParams: param \"${param}\" is reserved by Strapi; use a different name`\n );\n }\n if (extraQueryParams.some((o) => o.param === param)) {\n throw new Error(`contentAPI.addQueryParams: param \"${param}\" has already been added`);\n }\n extraQueryParams.push({ param, schema, matchRoute });\n // Params are merged into routes when initRouting() runs (applyExtraParamsToRoutes).\n // We do not merge here: at register() time routes may not exist yet (lazy creation), and\n // merging here would cause double-merge when initRouting runs and 400 \"invalid param\" or\n // \"param already exists\" errors.\n };\n\n const addInputParam = (options: Modules.ContentAPI.InputParamEntry & { param: string }) => {\n const { param, schema: schemaOrFactory, matchRoute } = options;\n const schema = resolveSchema(schemaOrFactory);\n if ((RESERVED_INPUT_PARAM_KEYS as readonly string[]).includes(param)) {\n throw new Error(\n `contentAPI.addInputParams: param \"${param}\" is reserved by Strapi; use a different name`\n );\n }\n if (extraInputParams.some((o) => o.param === param)) {\n throw new Error(`contentAPI.addInputParams: param \"${param}\" has already been added`);\n }\n extraInputParams.push({ param, schema, matchRoute });\n // Params are merged into routes when initRouting() runs (applyExtraParamsToRoutes).\n };\n\n /**\n * Register extra query params. Keys = param names; values = { schema, matchRoute? }.\n * Schemas must be Zod scalar or array-of-scalars (enforced at runtime via assertQueryParamSchema).\n */\n const addQueryParams = (options: Modules.ContentAPI.AddQueryParamsOptions) => {\n Object.entries(options).forEach(([param, rest]) => addQueryParam({ param, ...rest }));\n };\n\n /**\n * Register extra input params (root-level body.data). Keys = param names; values = { schema, matchRoute? }.\n * Any Zod type allowed; enforced at registration time.\n */\n const addInputParams = (options: Modules.ContentAPI.AddInputParamsOptions) => {\n Object.entries(options).forEach(([param, rest]) => addInputParam({ param, ...rest }));\n };\n\n /** Merge all registered extra params into the given routes (mutates in place). Called at route registration. Throws if a param key already exists. */\n const applyExtraParamsToRoutes = (routes: Core.Route[]): void => {\n routes.forEach((route) => {\n for (const { param, schema, matchRoute } of extraQueryParams) {\n mergeOneQueryParamIntoRoute(route, param, schema, matchRoute);\n }\n for (const { param, schema, matchRoute } of extraInputParams) {\n mergeOneInputParamIntoRoute(route, param, schema, matchRoute);\n }\n });\n };\n\n const getRoutesMap = async () => {\n const routesMap: Record<string, Core.Route[]> = {};\n\n _.forEach(strapi.apis, (api, apiName) => {\n const routes = _.flatMap(api.routes, (route) => {\n if ('routes' in route) {\n return route.routes;\n }\n\n return route;\n }).filter(filterContentAPI);\n\n if (routes.length === 0) {\n return;\n }\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n routesMap[`api::${apiName}`] = routes.map((route) => ({\n ...route,\n path: `${apiPrefix}${route.path}`,\n }));\n });\n\n _.forEach(strapi.plugins, (plugin, pluginName) => {\n const transformPrefix = transformRoutePrefixFor(pluginName);\n\n if (Array.isArray(plugin.routes)) {\n return plugin.routes.map(transformPrefix).filter(filterContentAPI);\n }\n\n const routes = _.flatMap(plugin.routes, (route) => route.routes.map(transformPrefix)).filter(\n filterContentAPI\n );\n\n if (routes.length === 0) {\n return;\n }\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n routesMap[`plugin::${pluginName}`] = routes.map((route) => ({\n ...route,\n path: `${apiPrefix}${route.path}`,\n }));\n });\n\n return sanitizeRoutesMapForSerialization(routesMap);\n };\n\n const sanitizer = sanitize.createAPISanitizers({\n getModel(uid: string) {\n return strapi.getModel(uid as UID.Schema);\n },\n get sanitizers() {\n return {\n input: strapi.sanitizers.get('content-api.input'),\n output: strapi.sanitizers.get('content-api.output'),\n };\n },\n });\n\n const validator = validate.createAPIValidators({\n getModel(uid: string) {\n return strapi.getModel(uid as UID.Schema);\n },\n get validators() {\n return {\n input: strapi.validators.get('content-api.input'),\n };\n },\n });\n\n return {\n permissions: instantiatePermissionsUtilities(strapi),\n getRoutesMap,\n sanitize: sanitizer,\n validate: validator,\n addQueryParams,\n addInputParams,\n applyExtraParamsToRoutes,\n };\n};\n\nexport default createContentAPI;\n"],"names":["transformRoutePrefixFor","pluginName","route","prefix","config","path","undefined","filterContentAPI","info","type","ALLOWED_QUERY_SCHEMA_NAMES","Set","assertQueryParamSchema","schema","param","name","has","Error","inner","_def","innerType","element","resolveSchema","schemaOrFactory","z","mergeOneQueryParamIntoRoute","matchRoute","query","request","method","mergeOneInputParamIntoRoute","jsonKey","body","existing","base","shape","object","createContentAPI","strapi","extraQueryParams","extraInputParams","addQueryParam","options","ALLOWED_QUERY_PARAM_KEYS","includes","some","o","push","addInputParam","RESERVED_INPUT_PARAM_KEYS","addQueryParams","Object","entries","forEach","rest","addInputParams","applyExtraParamsToRoutes","routes","getRoutesMap","routesMap","_","apis","api","apiName","flatMap","filter","length","apiPrefix","get","map","plugins","plugin","transformPrefix","Array","isArray","sanitizeRoutesMapForSerialization","sanitizer","sanitize","createAPISanitizers","getModel","uid","sanitizers","input","output","validator","validate","createAPIValidators","validators","permissions","instantiatePermissionsUtilities"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/services/content-api/index.ts"],"sourcesContent":["import {\n sanitize,\n validate,\n sanitizeRoutesMapForSerialization,\n ALLOWED_QUERY_PARAM_KEYS,\n RESERVED_INPUT_PARAM_KEYS,\n} from '@strapi/utils';\nimport * as z from 'zod/v4';\n\nimport type { Core, Modules, UID } from '@strapi/types';\n\nimport { inspectZodSchema, isZodType } from '../../utils/zod';\n\nimport instantiatePermissionsUtilities from './permissions';\n\nconst transformRoutePrefixFor = (pluginName: string) => (route: Core.Route) => {\n const prefix = route.config && route.config.prefix;\n const path = prefix !== undefined ? `${prefix}${route.path}` : `/${pluginName}${route.path}`;\n\n return {\n ...route,\n path,\n };\n};\n\nconst filterContentAPI = (route: Core.Route) => route.info.type === 'content-api';\n\n/**\n * Runtime check for addQueryParams: we only allow scalar or array-of-scalar schemas (no nested objects).\n * We keep this in addition to the ZodQueryParamSchema type because: (1) TypeScript can be bypassed (JS,\n * any, or schema from another Zod instance); (2) it gives a clear, immediate error at registration\n * time instead of a later failure in validate/sanitize.\n */\nconst ALLOWED_QUERY_SCHEMA_TYPES = new Set(['string', 'number', 'boolean', 'enum']);\n\nconst formatZodSchemaName = (type: string): string => {\n if (type.length === 0) {\n return '';\n }\n\n return `Zod${type\n .split('_')\n .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)\n .join('')}`;\n};\n\nfunction assertQueryParamSchema(schema: unknown, param: string): void {\n if (!isZodType(schema)) {\n const name =\n typeof schema === 'object' && schema !== null\n ? ((schema as { constructor?: { name?: string } }).constructor?.name ?? 'Object')\n : typeof schema;\n throw new Error(\n `contentAPI.addQueryParams: param \"${param}\" schema must be a scalar (string, number, boolean, enum) or array of scalars; got ${name}. Use addInputParams for nested objects.`\n );\n }\n\n const inspection = inspectZodSchema(schema);\n\n if (inspection.type === 'optional' || inspection.type === 'default') {\n assertQueryParamSchema(inspection.innerType, param);\n return;\n }\n\n if (inspection.type === 'array') {\n assertQueryParamSchema(inspection.element, param);\n return;\n }\n\n if (!ALLOWED_QUERY_SCHEMA_TYPES.has(inspection.type)) {\n throw new Error(\n `contentAPI.addQueryParams: param \"${param}\" schema must be a scalar (string, number, boolean, enum) or array of scalars; got ${formatZodSchemaName(inspection.type)}. Use addInputParams for nested objects.`\n );\n }\n}\n\nfunction resolveSchema<T>(schemaOrFactory: T | ((zInstance: typeof z) => T)): T {\n if (typeof schemaOrFactory === 'function') {\n return (schemaOrFactory as (zInstance: typeof z) => T)(z);\n }\n return schemaOrFactory;\n}\n\nconst mergeOneQueryParamIntoRoute = (\n route: Core.Route,\n param: string,\n schema: z.ZodType,\n matchRoute?: (route: Core.Route) => boolean\n): void => {\n if (matchRoute && !matchRoute(route)) return;\n const query = { ...route.request?.query };\n if (param in query) {\n throw new Error(\n `contentAPI.addQueryParams: param \"${param}\" already exists on route ${route.method} ${route.path}`\n );\n }\n route.request = { ...route.request, query: { ...query, [param]: schema } };\n};\n\nconst mergeOneInputParamIntoRoute = (\n route: Core.Route,\n param: string,\n schema: z.ZodType,\n matchRoute?: (route: Core.Route) => boolean\n): void => {\n if (matchRoute && !matchRoute(route)) return;\n const jsonKey = 'application/json';\n type RouteBody = NonNullable<NonNullable<Core.Route['request']>['body']>;\n const body: RouteBody = route.request?.body ? { ...route.request.body } : ({} as RouteBody);\n const existing = body[jsonKey];\n const base =\n existing && typeof existing === 'object' && 'shape' in existing\n ? (existing as { shape: Record<string, z.ZodType> }).shape\n : {};\n if (param in base) {\n throw new Error(\n `contentAPI.addInputParams: param \"${param}\" already exists on route ${route.method} ${route.path}`\n );\n }\n body[jsonKey] = z.object({ ...base, [param]: schema }) as RouteBody[keyof RouteBody];\n route.request = { ...route.request, body };\n};\n\n/** Stored options with schema always resolved (never a function). */\ntype ResolvedQueryParamEntry = {\n param: string;\n schema: z.ZodType;\n matchRoute?: (route: Core.Route) => boolean;\n};\ntype ResolvedInputParamEntry = {\n param: string;\n schema: z.ZodType;\n matchRoute?: (route: Core.Route) => boolean;\n};\n\n/**\n * Create a content API container that holds logic, tools and utils. (eg: permissions, ...)\n */\nconst createContentAPI = (strapi: Core.Strapi) => {\n const extraQueryParams: ResolvedQueryParamEntry[] = [];\n const extraInputParams: ResolvedInputParamEntry[] = [];\n\n const addQueryParam = (options: Modules.ContentAPI.QueryParamEntry & { param: string }) => {\n const { param, schema: schemaOrFactory, matchRoute } = options;\n const schema = resolveSchema(schemaOrFactory);\n assertQueryParamSchema(schema, param);\n if ((ALLOWED_QUERY_PARAM_KEYS as readonly string[]).includes(param)) {\n throw new Error(\n `contentAPI.addQueryParams: param \"${param}\" is reserved by Strapi; use a different name`\n );\n }\n if (extraQueryParams.some((o) => o.param === param)) {\n throw new Error(`contentAPI.addQueryParams: param \"${param}\" has already been added`);\n }\n extraQueryParams.push({ param, schema, matchRoute });\n // Params are merged into routes when initRouting() runs (applyExtraParamsToRoutes).\n // We do not merge here: at register() time routes may not exist yet (lazy creation), and\n // merging here would cause double-merge when initRouting runs and 400 \"invalid param\" or\n // \"param already exists\" errors.\n };\n\n const addInputParam = (options: Modules.ContentAPI.InputParamEntry & { param: string }) => {\n const { param, schema: schemaOrFactory, matchRoute } = options;\n const schema = resolveSchema(schemaOrFactory);\n if ((RESERVED_INPUT_PARAM_KEYS as readonly string[]).includes(param)) {\n throw new Error(\n `contentAPI.addInputParams: param \"${param}\" is reserved by Strapi; use a different name`\n );\n }\n if (extraInputParams.some((o) => o.param === param)) {\n throw new Error(`contentAPI.addInputParams: param \"${param}\" has already been added`);\n }\n extraInputParams.push({ param, schema, matchRoute });\n // Params are merged into routes when initRouting() runs (applyExtraParamsToRoutes).\n };\n\n /**\n * Register extra query params. Keys = param names; values = { schema, matchRoute? }.\n * Schemas must be Zod scalar or array-of-scalars (enforced at runtime via assertQueryParamSchema).\n */\n const addQueryParams = (options: Modules.ContentAPI.AddQueryParamsOptions) => {\n Object.entries(options).forEach(([param, rest]) => addQueryParam({ param, ...rest }));\n };\n\n /**\n * Register extra input params (root-level body.data). Keys = param names; values = { schema, matchRoute? }.\n * Any Zod type allowed; enforced at registration time.\n */\n const addInputParams = (options: Modules.ContentAPI.AddInputParamsOptions) => {\n Object.entries(options).forEach(([param, rest]) => addInputParam({ param, ...rest }));\n };\n\n /** Merge all registered extra params into the given routes (mutates in place). Called at route registration. Throws if a param key already exists. */\n const applyExtraParamsToRoutes = (routes: Core.Route[]): void => {\n routes.forEach((route) => {\n for (const { param, schema, matchRoute } of extraQueryParams) {\n mergeOneQueryParamIntoRoute(route, param, schema, matchRoute);\n }\n for (const { param, schema, matchRoute } of extraInputParams) {\n mergeOneInputParamIntoRoute(route, param, schema, matchRoute);\n }\n });\n };\n\n const getRoutesMap = async () => {\n const routesMap: Record<string, Core.Route[]> = {};\n\n for (const [apiName, api] of Object.entries(strapi.apis)) {\n const routes = Object.values(api.routes)\n .flatMap((route) => ('routes' in route ? route.routes : route))\n .filter(filterContentAPI);\n\n if (routes.length === 0) {\n continue;\n }\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n routesMap[`api::${apiName}`] = routes.map((route) => ({\n ...route,\n path: `${apiPrefix}${route.path}`,\n }));\n }\n\n for (const [pluginName, plugin] of Object.entries(strapi.plugins)) {\n const transformPrefix = transformRoutePrefixFor(pluginName);\n\n const pluginRoutes = Array.isArray(plugin.routes)\n ? plugin.routes\n : Object.values(plugin.routes).flatMap((route) => route.routes);\n\n const routes = pluginRoutes.map(transformPrefix).filter(filterContentAPI);\n\n if (routes.length === 0) {\n continue;\n }\n\n const apiPrefix = strapi.config.get('api.rest.prefix');\n routesMap[`plugin::${pluginName}`] = routes.map((route) => ({\n ...route,\n path: `${apiPrefix}${route.path}`,\n }));\n }\n\n return sanitizeRoutesMapForSerialization(routesMap);\n };\n\n const sanitizer = sanitize.createAPISanitizers({\n getModel(uid: string) {\n return strapi.getModel(uid as UID.Schema);\n },\n get sanitizers() {\n return {\n input: strapi.sanitizers.get('content-api.input'),\n output: strapi.sanitizers.get('content-api.output'),\n };\n },\n });\n\n const validator = validate.createAPIValidators({\n getModel(uid: string) {\n return strapi.getModel(uid as UID.Schema);\n },\n get validators() {\n return {\n input: strapi.validators.get('content-api.input'),\n };\n },\n });\n\n return {\n permissions: instantiatePermissionsUtilities(strapi),\n getRoutesMap,\n sanitize: sanitizer,\n validate: validator,\n addQueryParams,\n addInputParams,\n applyExtraParamsToRoutes,\n };\n};\n\nexport default createContentAPI;\n"],"names":["transformRoutePrefixFor","pluginName","route","prefix","config","path","undefined","filterContentAPI","info","type","ALLOWED_QUERY_SCHEMA_TYPES","Set","formatZodSchemaName","length","split","map","part","charAt","toUpperCase","slice","join","assertQueryParamSchema","schema","param","isZodType","name","Error","inspection","inspectZodSchema","innerType","element","has","resolveSchema","schemaOrFactory","z","mergeOneQueryParamIntoRoute","matchRoute","query","request","method","mergeOneInputParamIntoRoute","jsonKey","body","existing","base","shape","object","createContentAPI","strapi","extraQueryParams","extraInputParams","addQueryParam","options","ALLOWED_QUERY_PARAM_KEYS","includes","some","o","push","addInputParam","RESERVED_INPUT_PARAM_KEYS","addQueryParams","Object","entries","forEach","rest","addInputParams","applyExtraParamsToRoutes","routes","getRoutesMap","routesMap","apiName","api","apis","values","flatMap","filter","apiPrefix","get","plugin","plugins","transformPrefix","pluginRoutes","Array","isArray","sanitizeRoutesMapForSerialization","sanitizer","sanitize","createAPISanitizers","getModel","uid","sanitizers","input","output","validator","validate","createAPIValidators","validators","permissions","instantiatePermissionsUtilities"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import _ from 'lodash';
2
1
  import { sanitize, validate, sanitizeRoutesMapForSerialization, RESERVED_INPUT_PARAM_KEYS, ALLOWED_QUERY_PARAM_KEYS } from '@strapi/utils';
3
2
  import * as z from 'zod/v4';
3
+ import { isZodType, inspectZodSchema } from '../../utils/zod.mjs';
4
4
  import instantiatePermissionsUtilities from './permissions/index.mjs';
5
5
 
6
6
  const transformRoutePrefixFor = (pluginName)=>(route)=>{
@@ -16,33 +16,35 @@ const filterContentAPI = (route)=>route.info.type === 'content-api';
16
16
  * Runtime check for addQueryParams: we only allow scalar or array-of-scalar schemas (no nested objects).
17
17
  * We keep this in addition to the ZodQueryParamSchema type because: (1) TypeScript can be bypassed (JS,
18
18
  * any, or schema from another Zod instance); (2) it gives a clear, immediate error at registration
19
- * time instead of a later failure in validate/sanitize. This list is intentionally tied to Zod v4
20
- * constructor names; if Zod changes internals, this may need updating.
21
- * Compatibility: Zod 3 and Zod 4 Classic (zod/v4) both use these constructor names and
22
- * expose ._def with .innerType / .element for Optional/Default/Array. Zod 4 Core/Mini use
23
- * ._zod.def instead; we only accept schemas from the same zod/v4 instance used here.
24
- */ const ALLOWED_QUERY_SCHEMA_NAMES = new Set([
25
- 'ZodString',
26
- 'ZodNumber',
27
- 'ZodBoolean',
28
- 'ZodEnum',
29
- 'ZodOptional',
30
- 'ZodDefault',
31
- 'ZodArray'
19
+ * time instead of a later failure in validate/sanitize.
20
+ */ const ALLOWED_QUERY_SCHEMA_TYPES = new Set([
21
+ 'string',
22
+ 'number',
23
+ 'boolean',
24
+ 'enum'
32
25
  ]);
26
+ const formatZodSchemaName = (type)=>{
27
+ if (type.length === 0) {
28
+ return '';
29
+ }
30
+ return `Zod${type.split('_').map((part)=>`${part.charAt(0).toUpperCase()}${part.slice(1)}`).join('')}`;
31
+ };
33
32
  function assertQueryParamSchema(schema, param) {
34
- const name = schema?.constructor?.name ?? '';
35
- if (!ALLOWED_QUERY_SCHEMA_NAMES.has(name)) {
33
+ if (!isZodType(schema)) {
34
+ const name = typeof schema === 'object' && schema !== null ? schema.constructor?.name ?? 'Object' : typeof schema;
36
35
  throw new Error(`contentAPI.addQueryParams: param "${param}" schema must be a scalar (string, number, boolean, enum) or array of scalars; got ${name}. Use addInputParams for nested objects.`);
37
36
  }
38
- if (name === 'ZodOptional' || name === 'ZodDefault') {
39
- const inner = schema?._def?.innerType;
40
- if (inner) assertQueryParamSchema(inner, param);
37
+ const inspection = inspectZodSchema(schema);
38
+ if (inspection.type === 'optional' || inspection.type === 'default') {
39
+ assertQueryParamSchema(inspection.innerType, param);
41
40
  return;
42
41
  }
43
- if (name === 'ZodArray') {
44
- const element = schema?._def?.element;
45
- if (element) assertQueryParamSchema(element, param);
42
+ if (inspection.type === 'array') {
43
+ assertQueryParamSchema(inspection.element, param);
44
+ return;
45
+ }
46
+ if (!ALLOWED_QUERY_SCHEMA_TYPES.has(inspection.type)) {
47
+ throw new Error(`contentAPI.addQueryParams: param "${param}" schema must be a scalar (string, number, boolean, enum) or array of scalars; got ${formatZodSchemaName(inspection.type)}. Use addInputParams for nested objects.`);
46
48
  }
47
49
  }
48
50
  function resolveSchema(schemaOrFactory) {
@@ -158,37 +160,30 @@ const mergeOneInputParamIntoRoute = (route, param, schema, matchRoute)=>{
158
160
  };
159
161
  const getRoutesMap = async ()=>{
160
162
  const routesMap = {};
161
- _.forEach(strapi.apis, (api, apiName)=>{
162
- const routes = _.flatMap(api.routes, (route)=>{
163
- if ('routes' in route) {
164
- return route.routes;
165
- }
166
- return route;
167
- }).filter(filterContentAPI);
163
+ for (const [apiName, api] of Object.entries(strapi.apis)){
164
+ const routes = Object.values(api.routes).flatMap((route)=>'routes' in route ? route.routes : route).filter(filterContentAPI);
168
165
  if (routes.length === 0) {
169
- return;
166
+ continue;
170
167
  }
171
168
  const apiPrefix = strapi.config.get('api.rest.prefix');
172
169
  routesMap[`api::${apiName}`] = routes.map((route)=>({
173
170
  ...route,
174
171
  path: `${apiPrefix}${route.path}`
175
172
  }));
176
- });
177
- _.forEach(strapi.plugins, (plugin, pluginName)=>{
173
+ }
174
+ for (const [pluginName, plugin] of Object.entries(strapi.plugins)){
178
175
  const transformPrefix = transformRoutePrefixFor(pluginName);
179
- if (Array.isArray(plugin.routes)) {
180
- return plugin.routes.map(transformPrefix).filter(filterContentAPI);
181
- }
182
- const routes = _.flatMap(plugin.routes, (route)=>route.routes.map(transformPrefix)).filter(filterContentAPI);
176
+ const pluginRoutes = Array.isArray(plugin.routes) ? plugin.routes : Object.values(plugin.routes).flatMap((route)=>route.routes);
177
+ const routes = pluginRoutes.map(transformPrefix).filter(filterContentAPI);
183
178
  if (routes.length === 0) {
184
- return;
179
+ continue;
185
180
  }
186
181
  const apiPrefix = strapi.config.get('api.rest.prefix');
187
182
  routesMap[`plugin::${pluginName}`] = routes.map((route)=>({
188
183
  ...route,
189
184
  path: `${apiPrefix}${route.path}`
190
185
  }));
191
- });
186
+ }
192
187
  return sanitizeRoutesMapForSerialization(routesMap);
193
188
  };
194
189
  const sanitizer = sanitize.createAPISanitizers({