@strapi/core 5.57.0 → 5.58.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 (48) hide show
  1. package/dist/Strapi.d.ts +28 -0
  2. package/dist/Strapi.d.ts.map +1 -1
  3. package/dist/Strapi.js +4 -0
  4. package/dist/Strapi.js.map +1 -1
  5. package/dist/Strapi.mjs +4 -0
  6. package/dist/Strapi.mjs.map +1 -1
  7. package/dist/ee/entitlements.d.ts +44 -0
  8. package/dist/ee/entitlements.d.ts.map +1 -0
  9. package/dist/ee/entitlements.js +54 -0
  10. package/dist/ee/entitlements.js.map +1 -0
  11. package/dist/ee/entitlements.mjs +51 -0
  12. package/dist/ee/entitlements.mjs.map +1 -0
  13. package/dist/ee/index.d.ts +33 -0
  14. package/dist/ee/index.d.ts.map +1 -1
  15. package/dist/ee/index.js +70 -3
  16. package/dist/ee/index.js.map +1 -1
  17. package/dist/ee/index.mjs +71 -4
  18. package/dist/ee/index.mjs.map +1 -1
  19. package/dist/ee/license.d.ts +10 -0
  20. package/dist/ee/license.d.ts.map +1 -1
  21. package/dist/ee/license.js +25 -1
  22. package/dist/ee/license.js.map +1 -1
  23. package/dist/ee/license.mjs +25 -2
  24. package/dist/ee/license.mjs.map +1 -1
  25. package/dist/package.json.js +16 -16
  26. package/dist/package.json.mjs +16 -16
  27. package/dist/services/document-service/transform/fields.d.ts.map +1 -1
  28. package/dist/services/document-service/transform/fields.js +7 -1
  29. package/dist/services/document-service/transform/fields.js.map +1 -1
  30. package/dist/services/document-service/transform/fields.mjs +7 -1
  31. package/dist/services/document-service/transform/fields.mjs.map +1 -1
  32. package/dist/services/document-service/transform/populate.d.ts.map +1 -1
  33. package/dist/services/document-service/transform/populate.js +8 -1
  34. package/dist/services/document-service/transform/populate.js.map +1 -1
  35. package/dist/services/document-service/transform/populate.mjs +8 -1
  36. package/dist/services/document-service/transform/populate.mjs.map +1 -1
  37. package/dist/services/document-service/transform/relations/utils/map-relation.d.ts.map +1 -1
  38. package/dist/services/document-service/transform/relations/utils/map-relation.js +10 -4
  39. package/dist/services/document-service/transform/relations/utils/map-relation.js.map +1 -1
  40. package/dist/services/document-service/transform/relations/utils/map-relation.mjs +10 -4
  41. package/dist/services/document-service/transform/relations/utils/map-relation.mjs.map +1 -1
  42. package/dist/utils/detect-customizations.d.ts +31 -0
  43. package/dist/utils/detect-customizations.d.ts.map +1 -0
  44. package/dist/utils/detect-customizations.js +173 -0
  45. package/dist/utils/detect-customizations.js.map +1 -0
  46. package/dist/utils/detect-customizations.mjs +165 -0
  47. package/dist/utils/detect-customizations.mjs.map +1 -0
  48. package/package.json +16 -16
@@ -62,6 +62,27 @@ const DEFAULT_FEATURES = {
62
62
  }
63
63
  ]
64
64
  };
65
+ /**
66
+ * Features that can appear on a plan, in the order the admin panel lists them.
67
+ * Used to render entitlement rows for features a plan supports but this license
68
+ * does not include. `cms-ai` is deliberately absent: AI usage has its own panel.
69
+ */ const PLAN_FEATURE_CATALOG = {
70
+ bronze: [],
71
+ silver: [
72
+ 'sso',
73
+ 'cms-advanced-preview',
74
+ 'cms-content-releases',
75
+ 'cms-content-history'
76
+ ],
77
+ gold: [
78
+ 'sso',
79
+ 'cms-advanced-preview',
80
+ 'cms-content-releases',
81
+ 'review-workflows',
82
+ 'cms-content-history',
83
+ 'audit-logs'
84
+ ]
85
+ };
65
86
  const LICENSE_REGISTRY_URI = 'https://license.strapi.io';
66
87
  // Shape of a successful (200) response from the license registry.
67
88
  const licenseSuccessSchema = z__namespace.object({
@@ -136,7 +157,9 @@ const fetchLicense = async ({ strapi }, key, projectId)=>{
136
157
  }).catch(throwShouldFallbackError);
137
158
  const contentType = response.headers.get('Content-Type');
138
159
  if (contentType?.includes('application/json')) {
139
- const body = await response.json();
160
+ // A body that does not parse is an outage like any other malformed response, so it keeps
161
+ // the stored license instead of reading as a rejection.
162
+ const body = await response.json().catch(throwShouldFallbackError);
140
163
  switch(response.status){
141
164
  case 200:
142
165
  {
@@ -162,6 +185,7 @@ const fetchLicense = async ({ strapi }, key, projectId)=>{
162
185
 
163
186
  exports.LICENSE_REGISTRY_URI = LICENSE_REGISTRY_URI;
164
187
  exports.LicenseCheckError = LicenseCheckError;
188
+ exports.PLAN_FEATURE_CATALOG = PLAN_FEATURE_CATALOG;
165
189
  exports.fetchLicense = fetchLicense;
166
190
  exports.readLicense = readLicense;
167
191
  exports.verifyLicense = verifyLicense;
@@ -1 +1 @@
1
- {"version":3,"file":"license.js","sources":["../../src/ee/license.ts"],"sourcesContent":["import fs from 'fs';\nimport { join, resolve } from 'path';\nimport crypto from 'crypto';\nimport * as z from 'zod/v4';\nimport type { Core } from '@strapi/types';\n\nimport { generateInstallId } from '@strapi/utils';\n\ninterface LicenseInfo {\n type: 'bronze' | 'silver' | 'gold';\n isTrial: boolean;\n expireAt?: string;\n seats?: number;\n features?: Array<{ name: string; options?: Record<string, unknown> }>;\n subscriptionId?: string;\n planPriceId?: string;\n}\n\nconst DEFAULT_FEATURES = {\n bronze: [],\n silver: [],\n gold: [\n { name: 'sso' },\n // Set a null retention duration to allow the user to override it\n // The default of 90 days is set in the audit logs service\n { name: 'audit-logs', options: { retentionDays: null } },\n { name: 'review-workflows' },\n { name: 'cms-content-releases' },\n { name: 'cms-content-history', options: { retentionDays: 99999 } },\n { name: 'cms-advanced-preview' },\n ],\n};\n\nconst LICENSE_REGISTRY_URI = 'https://license.strapi.io';\n\n// Shape of a successful (200) response from the license registry.\nconst licenseSuccessSchema = z.object({\n data: z.object({\n license: z.string(),\n }),\n});\n\n// Shape of a bad-request (400) response from the license registry.\nconst licenseErrorSchema = z.object({\n error: z.object({\n message: z.string(),\n }),\n});\n\nconst publicKey = fs.readFileSync(resolve(__dirname, '../../resources/key.pub'));\n\nclass LicenseCheckError extends Error {\n shouldFallback = false;\n\n constructor(message: string, shouldFallback = false) {\n super(message);\n\n this.shouldFallback = shouldFallback;\n }\n}\n\nconst readLicense = (directory: string) => {\n try {\n const path = join(directory, 'license.txt');\n return fs.readFileSync(path).toString();\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'code' in error && error.code !== 'ENOENT') {\n throw Error('License file not readable, review its format and access rules.');\n }\n }\n};\n\nconst verifyLicense = (license: string) => {\n const [signature, base64Content] = Buffer.from(license, 'base64').toString().split('\\n');\n\n if (!signature || !base64Content) {\n throw new Error('Invalid license.');\n }\n\n const stringifiedContent = Buffer.from(base64Content, 'base64').toString();\n\n const verify = crypto.createVerify('RSA-SHA256');\n verify.update(stringifiedContent);\n verify.end();\n\n const verified = verify.verify(publicKey, signature, 'base64');\n\n if (!verified) {\n throw new Error('Invalid license.');\n }\n\n const licenseInfo: LicenseInfo = JSON.parse(stringifiedContent);\n\n if (!licenseInfo.features) {\n licenseInfo.features = DEFAULT_FEATURES[licenseInfo.type];\n }\n\n if (!licenseInfo.isTrial) {\n licenseInfo.isTrial = false;\n }\n\n Object.freeze(licenseInfo.features);\n return licenseInfo;\n};\n\nconst createShouldFallbackError = () => {\n return new LicenseCheckError('Could not proceed to the online validation of your license.', true);\n};\n\nconst throwShouldFallbackError = () => {\n throw createShouldFallbackError();\n};\n\nconst fetchLicense = async (\n { strapi }: { strapi: Core.Strapi },\n key: string,\n projectId: string\n) => {\n const { installId: installIdFromPackageJson } = strapi.config;\n\n const response = await strapi\n .fetch(`${LICENSE_REGISTRY_URI}/api/licenses/validate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n key,\n projectId,\n deviceId: generateInstallId(projectId, installIdFromPackageJson),\n }), // NOTE: Doing nothing on the LR with the installId\n })\n .catch(throwShouldFallbackError);\n\n const contentType = response.headers.get('Content-Type');\n\n if (contentType?.includes('application/json')) {\n const body = await response.json();\n\n switch (response.status) {\n case 200: {\n const result = licenseSuccessSchema.safeParse(body);\n if (!result.success) throw createShouldFallbackError();\n return result.data.data.license;\n }\n case 400: {\n const result = licenseErrorSchema.safeParse(body);\n if (!result.success) throw createShouldFallbackError();\n throw new LicenseCheckError(result.data.error.message);\n }\n case 404:\n throw new LicenseCheckError('The license used does not exists.');\n default:\n throw createShouldFallbackError();\n }\n } else {\n throw createShouldFallbackError();\n }\n};\n\nexport { readLicense, verifyLicense, fetchLicense, LicenseCheckError, LICENSE_REGISTRY_URI };\n"],"names":["DEFAULT_FEATURES","bronze","silver","gold","name","options","retentionDays","LICENSE_REGISTRY_URI","licenseSuccessSchema","z","object","data","license","string","licenseErrorSchema","error","message","publicKey","fs","readFileSync","resolve","__dirname","LicenseCheckError","Error","shouldFallback","readLicense","directory","path","join","toString","code","verifyLicense","signature","base64Content","Buffer","from","split","stringifiedContent","verify","crypto","createVerify","update","end","verified","licenseInfo","JSON","parse","features","type","isTrial","Object","freeze","createShouldFallbackError","throwShouldFallbackError","fetchLicense","strapi","key","projectId","installId","installIdFromPackageJson","config","response","fetch","method","headers","body","stringify","deviceId","generateInstallId","catch","contentType","get","includes","json","status","result","safeParse","success"],"mappings":"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"}
1
+ {"version":3,"file":"license.js","sources":["../../src/ee/license.ts"],"sourcesContent":["import fs from 'fs';\nimport { join, resolve } from 'path';\nimport crypto from 'crypto';\nimport * as z from 'zod/v4';\nimport type { Core } from '@strapi/types';\n\nimport { generateInstallId } from '@strapi/utils';\n\ninterface LicenseInfo {\n type: 'bronze' | 'silver' | 'gold';\n isTrial: boolean;\n expireAt?: string;\n seats?: number;\n features?: Array<{ name: string; options?: Record<string, unknown> }>;\n subscriptionId?: string;\n planPriceId?: string;\n /** End of the subscription term, epoch milliseconds. */\n // The registry sends an ISO date string; kept open to a timestamp\n renewalDate?: string | number;\n // Present on the payload at runtime; declared here so the type reflects reality.\n licenseKey?: string;\n customerId?: string;\n}\n\nconst DEFAULT_FEATURES = {\n bronze: [],\n silver: [],\n gold: [\n { name: 'sso' },\n // Set a null retention duration to allow the user to override it\n // The default of 90 days is set in the audit logs service\n { name: 'audit-logs', options: { retentionDays: null } },\n { name: 'review-workflows' },\n { name: 'cms-content-releases' },\n { name: 'cms-content-history', options: { retentionDays: 99999 } },\n { name: 'cms-advanced-preview' },\n ],\n};\n\n/**\n * Features that can appear on a plan, in the order the admin panel lists them.\n * Used to render entitlement rows for features a plan supports but this license\n * does not include. `cms-ai` is deliberately absent: AI usage has its own panel.\n */\nexport const PLAN_FEATURE_CATALOG: Record<'bronze' | 'silver' | 'gold', string[]> = {\n bronze: [],\n silver: ['sso', 'cms-advanced-preview', 'cms-content-releases', 'cms-content-history'],\n gold: [\n 'sso',\n 'cms-advanced-preview',\n 'cms-content-releases',\n 'review-workflows',\n 'cms-content-history',\n 'audit-logs',\n ],\n};\n\nconst LICENSE_REGISTRY_URI = 'https://license.strapi.io';\n\n// Shape of a successful (200) response from the license registry.\nconst licenseSuccessSchema = z.object({\n data: z.object({\n license: z.string(),\n }),\n});\n\n// Shape of a bad-request (400) response from the license registry.\nconst licenseErrorSchema = z.object({\n error: z.object({\n message: z.string(),\n }),\n});\n\nconst publicKey = fs.readFileSync(resolve(__dirname, '../../resources/key.pub'));\n\nclass LicenseCheckError extends Error {\n shouldFallback = false;\n\n constructor(message: string, shouldFallback = false) {\n super(message);\n\n this.shouldFallback = shouldFallback;\n }\n}\n\nconst readLicense = (directory: string) => {\n try {\n const path = join(directory, 'license.txt');\n return fs.readFileSync(path).toString();\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'code' in error && error.code !== 'ENOENT') {\n throw Error('License file not readable, review its format and access rules.');\n }\n }\n};\n\nconst verifyLicense = (license: string) => {\n const [signature, base64Content] = Buffer.from(license, 'base64').toString().split('\\n');\n\n if (!signature || !base64Content) {\n throw new Error('Invalid license.');\n }\n\n const stringifiedContent = Buffer.from(base64Content, 'base64').toString();\n\n const verify = crypto.createVerify('RSA-SHA256');\n verify.update(stringifiedContent);\n verify.end();\n\n const verified = verify.verify(publicKey, signature, 'base64');\n\n if (!verified) {\n throw new Error('Invalid license.');\n }\n\n const licenseInfo: LicenseInfo = JSON.parse(stringifiedContent);\n\n if (!licenseInfo.features) {\n licenseInfo.features = DEFAULT_FEATURES[licenseInfo.type];\n }\n\n if (!licenseInfo.isTrial) {\n licenseInfo.isTrial = false;\n }\n\n Object.freeze(licenseInfo.features);\n return licenseInfo;\n};\n\nconst createShouldFallbackError = () => {\n return new LicenseCheckError('Could not proceed to the online validation of your license.', true);\n};\n\nconst throwShouldFallbackError = () => {\n throw createShouldFallbackError();\n};\n\nconst fetchLicense = async (\n { strapi }: { strapi: Core.Strapi },\n key: string,\n projectId: string\n) => {\n const { installId: installIdFromPackageJson } = strapi.config;\n\n const response = await strapi\n .fetch(`${LICENSE_REGISTRY_URI}/api/licenses/validate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n key,\n projectId,\n deviceId: generateInstallId(projectId, installIdFromPackageJson),\n }), // NOTE: Doing nothing on the LR with the installId\n })\n .catch(throwShouldFallbackError);\n\n const contentType = response.headers.get('Content-Type');\n\n if (contentType?.includes('application/json')) {\n // A body that does not parse is an outage like any other malformed response, so it keeps\n // the stored license instead of reading as a rejection.\n const body = await response.json().catch(throwShouldFallbackError);\n\n switch (response.status) {\n case 200: {\n const result = licenseSuccessSchema.safeParse(body);\n if (!result.success) throw createShouldFallbackError();\n return result.data.data.license;\n }\n case 400: {\n const result = licenseErrorSchema.safeParse(body);\n if (!result.success) throw createShouldFallbackError();\n throw new LicenseCheckError(result.data.error.message);\n }\n case 404:\n throw new LicenseCheckError('The license used does not exists.');\n default:\n throw createShouldFallbackError();\n }\n } else {\n throw createShouldFallbackError();\n }\n};\n\nexport { readLicense, verifyLicense, fetchLicense, LicenseCheckError, LICENSE_REGISTRY_URI };\n"],"names":["DEFAULT_FEATURES","bronze","silver","gold","name","options","retentionDays","PLAN_FEATURE_CATALOG","LICENSE_REGISTRY_URI","licenseSuccessSchema","z","object","data","license","string","licenseErrorSchema","error","message","publicKey","fs","readFileSync","resolve","__dirname","LicenseCheckError","Error","shouldFallback","readLicense","directory","path","join","toString","code","verifyLicense","signature","base64Content","Buffer","from","split","stringifiedContent","verify","crypto","createVerify","update","end","verified","licenseInfo","JSON","parse","features","type","isTrial","Object","freeze","createShouldFallbackError","throwShouldFallbackError","fetchLicense","strapi","key","projectId","installId","installIdFromPackageJson","config","response","fetch","method","headers","body","stringify","deviceId","generateInstallId","catch","contentType","get","includes","json","status","result","safeParse","success"],"mappings":"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"}
@@ -36,6 +36,27 @@ const DEFAULT_FEATURES = {
36
36
  }
37
37
  ]
38
38
  };
39
+ /**
40
+ * Features that can appear on a plan, in the order the admin panel lists them.
41
+ * Used to render entitlement rows for features a plan supports but this license
42
+ * does not include. `cms-ai` is deliberately absent: AI usage has its own panel.
43
+ */ const PLAN_FEATURE_CATALOG = {
44
+ bronze: [],
45
+ silver: [
46
+ 'sso',
47
+ 'cms-advanced-preview',
48
+ 'cms-content-releases',
49
+ 'cms-content-history'
50
+ ],
51
+ gold: [
52
+ 'sso',
53
+ 'cms-advanced-preview',
54
+ 'cms-content-releases',
55
+ 'review-workflows',
56
+ 'cms-content-history',
57
+ 'audit-logs'
58
+ ]
59
+ };
39
60
  const LICENSE_REGISTRY_URI = 'https://license.strapi.io';
40
61
  // Shape of a successful (200) response from the license registry.
41
62
  const licenseSuccessSchema = z.object({
@@ -110,7 +131,9 @@ const fetchLicense = async ({ strapi }, key, projectId)=>{
110
131
  }).catch(throwShouldFallbackError);
111
132
  const contentType = response.headers.get('Content-Type');
112
133
  if (contentType?.includes('application/json')) {
113
- const body = await response.json();
134
+ // A body that does not parse is an outage like any other malformed response, so it keeps
135
+ // the stored license instead of reading as a rejection.
136
+ const body = await response.json().catch(throwShouldFallbackError);
114
137
  switch(response.status){
115
138
  case 200:
116
139
  {
@@ -134,5 +157,5 @@ const fetchLicense = async ({ strapi }, key, projectId)=>{
134
157
  }
135
158
  };
136
159
 
137
- export { LICENSE_REGISTRY_URI, LicenseCheckError, fetchLicense, readLicense, verifyLicense };
160
+ export { LICENSE_REGISTRY_URI, LicenseCheckError, PLAN_FEATURE_CATALOG, fetchLicense, readLicense, verifyLicense };
138
161
  //# sourceMappingURL=license.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"license.mjs","sources":["../../src/ee/license.ts"],"sourcesContent":["import fs from 'fs';\nimport { join, resolve } from 'path';\nimport crypto from 'crypto';\nimport * as z from 'zod/v4';\nimport type { Core } from '@strapi/types';\n\nimport { generateInstallId } from '@strapi/utils';\n\ninterface LicenseInfo {\n type: 'bronze' | 'silver' | 'gold';\n isTrial: boolean;\n expireAt?: string;\n seats?: number;\n features?: Array<{ name: string; options?: Record<string, unknown> }>;\n subscriptionId?: string;\n planPriceId?: string;\n}\n\nconst DEFAULT_FEATURES = {\n bronze: [],\n silver: [],\n gold: [\n { name: 'sso' },\n // Set a null retention duration to allow the user to override it\n // The default of 90 days is set in the audit logs service\n { name: 'audit-logs', options: { retentionDays: null } },\n { name: 'review-workflows' },\n { name: 'cms-content-releases' },\n { name: 'cms-content-history', options: { retentionDays: 99999 } },\n { name: 'cms-advanced-preview' },\n ],\n};\n\nconst LICENSE_REGISTRY_URI = 'https://license.strapi.io';\n\n// Shape of a successful (200) response from the license registry.\nconst licenseSuccessSchema = z.object({\n data: z.object({\n license: z.string(),\n }),\n});\n\n// Shape of a bad-request (400) response from the license registry.\nconst licenseErrorSchema = z.object({\n error: z.object({\n message: z.string(),\n }),\n});\n\nconst publicKey = fs.readFileSync(resolve(__dirname, '../../resources/key.pub'));\n\nclass LicenseCheckError extends Error {\n shouldFallback = false;\n\n constructor(message: string, shouldFallback = false) {\n super(message);\n\n this.shouldFallback = shouldFallback;\n }\n}\n\nconst readLicense = (directory: string) => {\n try {\n const path = join(directory, 'license.txt');\n return fs.readFileSync(path).toString();\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'code' in error && error.code !== 'ENOENT') {\n throw Error('License file not readable, review its format and access rules.');\n }\n }\n};\n\nconst verifyLicense = (license: string) => {\n const [signature, base64Content] = Buffer.from(license, 'base64').toString().split('\\n');\n\n if (!signature || !base64Content) {\n throw new Error('Invalid license.');\n }\n\n const stringifiedContent = Buffer.from(base64Content, 'base64').toString();\n\n const verify = crypto.createVerify('RSA-SHA256');\n verify.update(stringifiedContent);\n verify.end();\n\n const verified = verify.verify(publicKey, signature, 'base64');\n\n if (!verified) {\n throw new Error('Invalid license.');\n }\n\n const licenseInfo: LicenseInfo = JSON.parse(stringifiedContent);\n\n if (!licenseInfo.features) {\n licenseInfo.features = DEFAULT_FEATURES[licenseInfo.type];\n }\n\n if (!licenseInfo.isTrial) {\n licenseInfo.isTrial = false;\n }\n\n Object.freeze(licenseInfo.features);\n return licenseInfo;\n};\n\nconst createShouldFallbackError = () => {\n return new LicenseCheckError('Could not proceed to the online validation of your license.', true);\n};\n\nconst throwShouldFallbackError = () => {\n throw createShouldFallbackError();\n};\n\nconst fetchLicense = async (\n { strapi }: { strapi: Core.Strapi },\n key: string,\n projectId: string\n) => {\n const { installId: installIdFromPackageJson } = strapi.config;\n\n const response = await strapi\n .fetch(`${LICENSE_REGISTRY_URI}/api/licenses/validate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n key,\n projectId,\n deviceId: generateInstallId(projectId, installIdFromPackageJson),\n }), // NOTE: Doing nothing on the LR with the installId\n })\n .catch(throwShouldFallbackError);\n\n const contentType = response.headers.get('Content-Type');\n\n if (contentType?.includes('application/json')) {\n const body = await response.json();\n\n switch (response.status) {\n case 200: {\n const result = licenseSuccessSchema.safeParse(body);\n if (!result.success) throw createShouldFallbackError();\n return result.data.data.license;\n }\n case 400: {\n const result = licenseErrorSchema.safeParse(body);\n if (!result.success) throw createShouldFallbackError();\n throw new LicenseCheckError(result.data.error.message);\n }\n case 404:\n throw new LicenseCheckError('The license used does not exists.');\n default:\n throw createShouldFallbackError();\n }\n } else {\n throw createShouldFallbackError();\n }\n};\n\nexport { readLicense, verifyLicense, fetchLicense, LicenseCheckError, LICENSE_REGISTRY_URI };\n"],"names":["DEFAULT_FEATURES","bronze","silver","gold","name","options","retentionDays","LICENSE_REGISTRY_URI","licenseSuccessSchema","z","object","data","license","string","licenseErrorSchema","error","message","publicKey","fs","readFileSync","resolve","__dirname","LicenseCheckError","Error","shouldFallback","readLicense","directory","path","join","toString","code","verifyLicense","signature","base64Content","Buffer","from","split","stringifiedContent","verify","crypto","createVerify","update","end","verified","licenseInfo","JSON","parse","features","type","isTrial","Object","freeze","createShouldFallbackError","throwShouldFallbackError","fetchLicense","strapi","key","projectId","installId","installIdFromPackageJson","config","response","fetch","method","headers","body","stringify","deviceId","generateInstallId","catch","contentType","get","includes","json","status","result","safeParse","success"],"mappings":";;;;;;AAkBA,MAAMA,gBAAAA,GAAmB;AACvBC,IAAAA,MAAAA,EAAQ,EAAE;AACVC,IAAAA,MAAAA,EAAQ,EAAE;IACVC,IAAAA,EAAM;AACJ,QAAA;YAAEC,IAAAA,EAAM;AAAM,SAAA;;;AAGd,QAAA;YAAEA,IAAAA,EAAM,YAAA;YAAcC,OAAAA,EAAS;gBAAEC,aAAAA,EAAe;AAAK;AAAE,SAAA;AACvD,QAAA;YAAEF,IAAAA,EAAM;AAAmB,SAAA;AAC3B,QAAA;YAAEA,IAAAA,EAAM;AAAuB,SAAA;AAC/B,QAAA;YAAEA,IAAAA,EAAM,qBAAA;YAAuBC,OAAAA,EAAS;gBAAEC,aAAAA,EAAe;AAAM;AAAE,SAAA;AACjE,QAAA;YAAEF,IAAAA,EAAM;AAAuB;AAChC;AACH,CAAA;AAEA,MAAMG,oBAAAA,GAAuB;AAE7B;AACA,MAAMC,oBAAAA,GAAuBC,CAAAA,CAAEC,MAAM,CAAC;IACpCC,IAAAA,EAAMF,CAAAA,CAAEC,MAAM,CAAC;AACbE,QAAAA,OAAAA,EAASH,EAAEI,MAAM;AACnB,KAAA;AACF,CAAA,CAAA;AAEA;AACA,MAAMC,kBAAAA,GAAqBL,CAAAA,CAAEC,MAAM,CAAC;IAClCK,KAAAA,EAAON,CAAAA,CAAEC,MAAM,CAAC;AACdM,QAAAA,OAAAA,EAASP,EAAEI,MAAM;AACnB,KAAA;AACF,CAAA,CAAA;AAEA,MAAMI,SAAAA,GAAYC,EAAAA,CAAGC,YAAY,CAACC,QAAQC,SAAAA,EAAW,yBAAA,CAAA,CAAA;AAErD,MAAMC,iBAAAA,SAA0BC,KAAAA,CAAAA;AAG9B,IAAA,WAAA,CAAYP,OAAe,EAAEQ,cAAAA,GAAiB,KAAK,CAAE;QACnD,KAAK,CAACR,eAHRQ,cAAAA,GAAiB,KAAA;QAKf,IAAI,CAACA,cAAc,GAAGA,cAAAA;AACxB,IAAA;AACF;AAEA,MAAMC,cAAc,CAACC,SAAAA,GAAAA;IACnB,IAAI;QACF,MAAMC,IAAAA,GAAOC,KAAKF,SAAAA,EAAW,aAAA,CAAA;AAC7B,QAAA,OAAOR,EAAAA,CAAGC,YAAY,CAACQ,IAAAA,CAAAA,CAAME,QAAQ,EAAA;AACvC,IAAA,CAAA,CAAE,OAAOd,KAAAA,EAAO;QACd,IAAI,OAAOA,KAAAA,KAAU,QAAA,IAAYA,KAAAA,KAAU,IAAA,IAAQ,UAAUA,KAAAA,IAASA,KAAAA,CAAMe,IAAI,KAAK,QAAA,EAAU;AAC7F,YAAA,MAAMP,KAAAA,CAAM,gEAAA,CAAA;AACd,QAAA;AACF,IAAA;AACF;AAEA,MAAMQ,gBAAgB,CAACnB,OAAAA,GAAAA;AACrB,IAAA,MAAM,CAACoB,SAAAA,EAAWC,aAAAA,CAAc,GAAGC,MAAAA,CAAOC,IAAI,CAACvB,OAAAA,EAAS,QAAA,CAAA,CAAUiB,QAAQ,EAAA,CAAGO,KAAK,CAAC,IAAA,CAAA;IAEnF,IAAI,CAACJ,SAAAA,IAAa,CAACC,aAAAA,EAAe;AAChC,QAAA,MAAM,IAAIV,KAAAA,CAAM,kBAAA,CAAA;AAClB,IAAA;AAEA,IAAA,MAAMc,qBAAqBH,MAAAA,CAAOC,IAAI,CAACF,aAAAA,EAAe,UAAUJ,QAAQ,EAAA;IAExE,MAAMS,MAAAA,GAASC,MAAAA,CAAOC,YAAY,CAAC,YAAA,CAAA;AACnCF,IAAAA,MAAAA,CAAOG,MAAM,CAACJ,kBAAAA,CAAAA;AACdC,IAAAA,MAAAA,CAAOI,GAAG,EAAA;AAEV,IAAA,MAAMC,QAAAA,GAAWL,MAAAA,CAAOA,MAAM,CAACrB,WAAWe,SAAAA,EAAW,QAAA,CAAA;AAErD,IAAA,IAAI,CAACW,QAAAA,EAAU;AACb,QAAA,MAAM,IAAIpB,KAAAA,CAAM,kBAAA,CAAA;AAClB,IAAA;IAEA,MAAMqB,WAAAA,GAA2BC,IAAAA,CAAKC,KAAK,CAACT,kBAAAA,CAAAA;IAE5C,IAAI,CAACO,WAAAA,CAAYG,QAAQ,EAAE;AACzBH,QAAAA,WAAAA,CAAYG,QAAQ,GAAG/C,gBAAgB,CAAC4C,WAAAA,CAAYI,IAAI,CAAC;AAC3D,IAAA;IAEA,IAAI,CAACJ,WAAAA,CAAYK,OAAO,EAAE;AACxBL,QAAAA,WAAAA,CAAYK,OAAO,GAAG,KAAA;AACxB,IAAA;IAEAC,MAAAA,CAAOC,MAAM,CAACP,WAAAA,CAAYG,QAAQ,CAAA;IAClC,OAAOH,WAAAA;AACT;AAEA,MAAMQ,yBAAAA,GAA4B,IAAA;IAChC,OAAO,IAAI9B,kBAAkB,6DAAA,EAA+D,IAAA,CAAA;AAC9F,CAAA;AAEA,MAAM+B,wBAAAA,GAA2B,IAAA;IAC/B,MAAMD,yBAAAA,EAAAA;AACR,CAAA;AAEA,MAAME,eAAe,OACnB,EAAEC,MAAM,EAA2B,EACnCC,GAAAA,EACAC,SAAAA,GAAAA;AAEA,IAAA,MAAM,EAAEC,SAAAA,EAAWC,wBAAwB,EAAE,GAAGJ,OAAOK,MAAM;IAE7D,MAAMC,QAAAA,GAAW,MAAMN,MAAAA,CACpBO,KAAK,CAAC,CAAA,EAAGvD,oBAAAA,CAAqB,sBAAsB,CAAC,EAAE;QACtDwD,MAAAA,EAAQ,MAAA;QACRC,OAAAA,EAAS;YAAE,cAAA,EAAgB;AAAmB,SAAA;QAC9CC,IAAAA,EAAMpB,IAAAA,CAAKqB,SAAS,CAAC;AACnBV,YAAAA,GAAAA;AACAC,YAAAA,SAAAA;AACAU,YAAAA,QAAAA,EAAUC,kBAAkBX,SAAAA,EAAWE,wBAAAA;AACzC,SAAA;AACF,KAAA,CAAA,CACCU,KAAK,CAAChB,wBAAAA,CAAAA;AAET,IAAA,MAAMiB,WAAAA,GAAcT,QAAAA,CAASG,OAAO,CAACO,GAAG,CAAC,cAAA,CAAA;IAEzC,IAAID,WAAAA,EAAaE,SAAS,kBAAA,CAAA,EAAqB;QAC7C,MAAMP,IAAAA,GAAO,MAAMJ,QAAAA,CAASY,IAAI,EAAA;AAEhC,QAAA,OAAQZ,SAASa,MAAM;YACrB,KAAK,GAAA;AAAK,gBAAA;oBACR,MAAMC,MAAAA,GAASnE,oBAAAA,CAAqBoE,SAAS,CAACX,IAAAA,CAAAA;AAC9C,oBAAA,IAAI,CAACU,MAAAA,CAAOE,OAAO,EAAE,MAAMzB,yBAAAA,EAAAA;AAC3B,oBAAA,OAAOuB,MAAAA,CAAOhE,IAAI,CAACA,IAAI,CAACC,OAAO;AACjC,gBAAA;YACA,KAAK,GAAA;AAAK,gBAAA;oBACR,MAAM+D,MAAAA,GAAS7D,kBAAAA,CAAmB8D,SAAS,CAACX,IAAAA,CAAAA;AAC5C,oBAAA,IAAI,CAACU,MAAAA,CAAOE,OAAO,EAAE,MAAMzB,yBAAAA,EAAAA;AAC3B,oBAAA,MAAM,IAAI9B,iBAAAA,CAAkBqD,MAAAA,CAAOhE,IAAI,CAACI,KAAK,CAACC,OAAO,CAAA;AACvD,gBAAA;YACA,KAAK,GAAA;AACH,gBAAA,MAAM,IAAIM,iBAAAA,CAAkB,mCAAA,CAAA;AAC9B,YAAA;gBACE,MAAM8B,yBAAAA,EAAAA;AACV;IACF,CAAA,MAAO;QACL,MAAMA,yBAAAA,EAAAA;AACR,IAAA;AACF;;;;"}
1
+ {"version":3,"file":"license.mjs","sources":["../../src/ee/license.ts"],"sourcesContent":["import fs from 'fs';\nimport { join, resolve } from 'path';\nimport crypto from 'crypto';\nimport * as z from 'zod/v4';\nimport type { Core } from '@strapi/types';\n\nimport { generateInstallId } from '@strapi/utils';\n\ninterface LicenseInfo {\n type: 'bronze' | 'silver' | 'gold';\n isTrial: boolean;\n expireAt?: string;\n seats?: number;\n features?: Array<{ name: string; options?: Record<string, unknown> }>;\n subscriptionId?: string;\n planPriceId?: string;\n /** End of the subscription term, epoch milliseconds. */\n // The registry sends an ISO date string; kept open to a timestamp\n renewalDate?: string | number;\n // Present on the payload at runtime; declared here so the type reflects reality.\n licenseKey?: string;\n customerId?: string;\n}\n\nconst DEFAULT_FEATURES = {\n bronze: [],\n silver: [],\n gold: [\n { name: 'sso' },\n // Set a null retention duration to allow the user to override it\n // The default of 90 days is set in the audit logs service\n { name: 'audit-logs', options: { retentionDays: null } },\n { name: 'review-workflows' },\n { name: 'cms-content-releases' },\n { name: 'cms-content-history', options: { retentionDays: 99999 } },\n { name: 'cms-advanced-preview' },\n ],\n};\n\n/**\n * Features that can appear on a plan, in the order the admin panel lists them.\n * Used to render entitlement rows for features a plan supports but this license\n * does not include. `cms-ai` is deliberately absent: AI usage has its own panel.\n */\nexport const PLAN_FEATURE_CATALOG: Record<'bronze' | 'silver' | 'gold', string[]> = {\n bronze: [],\n silver: ['sso', 'cms-advanced-preview', 'cms-content-releases', 'cms-content-history'],\n gold: [\n 'sso',\n 'cms-advanced-preview',\n 'cms-content-releases',\n 'review-workflows',\n 'cms-content-history',\n 'audit-logs',\n ],\n};\n\nconst LICENSE_REGISTRY_URI = 'https://license.strapi.io';\n\n// Shape of a successful (200) response from the license registry.\nconst licenseSuccessSchema = z.object({\n data: z.object({\n license: z.string(),\n }),\n});\n\n// Shape of a bad-request (400) response from the license registry.\nconst licenseErrorSchema = z.object({\n error: z.object({\n message: z.string(),\n }),\n});\n\nconst publicKey = fs.readFileSync(resolve(__dirname, '../../resources/key.pub'));\n\nclass LicenseCheckError extends Error {\n shouldFallback = false;\n\n constructor(message: string, shouldFallback = false) {\n super(message);\n\n this.shouldFallback = shouldFallback;\n }\n}\n\nconst readLicense = (directory: string) => {\n try {\n const path = join(directory, 'license.txt');\n return fs.readFileSync(path).toString();\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'code' in error && error.code !== 'ENOENT') {\n throw Error('License file not readable, review its format and access rules.');\n }\n }\n};\n\nconst verifyLicense = (license: string) => {\n const [signature, base64Content] = Buffer.from(license, 'base64').toString().split('\\n');\n\n if (!signature || !base64Content) {\n throw new Error('Invalid license.');\n }\n\n const stringifiedContent = Buffer.from(base64Content, 'base64').toString();\n\n const verify = crypto.createVerify('RSA-SHA256');\n verify.update(stringifiedContent);\n verify.end();\n\n const verified = verify.verify(publicKey, signature, 'base64');\n\n if (!verified) {\n throw new Error('Invalid license.');\n }\n\n const licenseInfo: LicenseInfo = JSON.parse(stringifiedContent);\n\n if (!licenseInfo.features) {\n licenseInfo.features = DEFAULT_FEATURES[licenseInfo.type];\n }\n\n if (!licenseInfo.isTrial) {\n licenseInfo.isTrial = false;\n }\n\n Object.freeze(licenseInfo.features);\n return licenseInfo;\n};\n\nconst createShouldFallbackError = () => {\n return new LicenseCheckError('Could not proceed to the online validation of your license.', true);\n};\n\nconst throwShouldFallbackError = () => {\n throw createShouldFallbackError();\n};\n\nconst fetchLicense = async (\n { strapi }: { strapi: Core.Strapi },\n key: string,\n projectId: string\n) => {\n const { installId: installIdFromPackageJson } = strapi.config;\n\n const response = await strapi\n .fetch(`${LICENSE_REGISTRY_URI}/api/licenses/validate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n key,\n projectId,\n deviceId: generateInstallId(projectId, installIdFromPackageJson),\n }), // NOTE: Doing nothing on the LR with the installId\n })\n .catch(throwShouldFallbackError);\n\n const contentType = response.headers.get('Content-Type');\n\n if (contentType?.includes('application/json')) {\n // A body that does not parse is an outage like any other malformed response, so it keeps\n // the stored license instead of reading as a rejection.\n const body = await response.json().catch(throwShouldFallbackError);\n\n switch (response.status) {\n case 200: {\n const result = licenseSuccessSchema.safeParse(body);\n if (!result.success) throw createShouldFallbackError();\n return result.data.data.license;\n }\n case 400: {\n const result = licenseErrorSchema.safeParse(body);\n if (!result.success) throw createShouldFallbackError();\n throw new LicenseCheckError(result.data.error.message);\n }\n case 404:\n throw new LicenseCheckError('The license used does not exists.');\n default:\n throw createShouldFallbackError();\n }\n } else {\n throw createShouldFallbackError();\n }\n};\n\nexport { readLicense, verifyLicense, fetchLicense, LicenseCheckError, LICENSE_REGISTRY_URI };\n"],"names":["DEFAULT_FEATURES","bronze","silver","gold","name","options","retentionDays","PLAN_FEATURE_CATALOG","LICENSE_REGISTRY_URI","licenseSuccessSchema","z","object","data","license","string","licenseErrorSchema","error","message","publicKey","fs","readFileSync","resolve","__dirname","LicenseCheckError","Error","shouldFallback","readLicense","directory","path","join","toString","code","verifyLicense","signature","base64Content","Buffer","from","split","stringifiedContent","verify","crypto","createVerify","update","end","verified","licenseInfo","JSON","parse","features","type","isTrial","Object","freeze","createShouldFallbackError","throwShouldFallbackError","fetchLicense","strapi","key","projectId","installId","installIdFromPackageJson","config","response","fetch","method","headers","body","stringify","deviceId","generateInstallId","catch","contentType","get","includes","json","status","result","safeParse","success"],"mappings":"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"}
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var name = "@strapi/core";
6
- var version = "5.57.0";
6
+ var version = "5.58.0";
7
7
  var description = "Core of Strapi";
8
8
  var homepage = "https://strapi.io";
9
9
  var bugs = {
@@ -63,18 +63,18 @@ var dependencies = {
63
63
  "@casl/ability": "6.7.5",
64
64
  "@koa/cors": "5.0.0",
65
65
  "@koa/router": "12.0.2",
66
- "@modelcontextprotocol/node": "2.0.0",
67
- "@modelcontextprotocol/server": "2.0.0",
66
+ "@modelcontextprotocol/node": "2.1.1",
67
+ "@modelcontextprotocol/server": "2.3.1",
68
68
  "@paralleldrive/cuid2": "2.2.2",
69
- "@strapi/admin": "5.57.0",
70
- "@strapi/database": "5.57.0",
71
- "@strapi/generators": "5.57.0",
72
- "@strapi/logger": "5.57.0",
73
- "@strapi/openapi": "5.57.0",
74
- "@strapi/permissions": "5.57.0",
75
- "@strapi/types": "5.57.0",
76
- "@strapi/typescript-utils": "5.57.0",
77
- "@strapi/utils": "5.57.0",
69
+ "@strapi/admin": "5.58.0",
70
+ "@strapi/database": "5.58.0",
71
+ "@strapi/generators": "5.58.0",
72
+ "@strapi/logger": "5.58.0",
73
+ "@strapi/openapi": "5.58.0",
74
+ "@strapi/permissions": "5.58.0",
75
+ "@strapi/types": "5.58.0",
76
+ "@strapi/typescript-utils": "5.58.0",
77
+ "@strapi/utils": "5.58.0",
78
78
  "@vercel/stega": "0.1.2",
79
79
  bcryptjs: "2.4.3",
80
80
  boxen: "5.1.2",
@@ -121,7 +121,7 @@ var dependencies = {
121
121
  zod: "4.4.3"
122
122
  };
123
123
  var devDependencies = {
124
- "@modelcontextprotocol/client": "2.0.0",
124
+ "@modelcontextprotocol/client": "2.3.1",
125
125
  "@swc/core": "1.13.5",
126
126
  "@types/bcryptjs": "2.4.3",
127
127
  "@types/configstore": "5.0.1",
@@ -140,12 +140,12 @@ var devDependencies = {
140
140
  "@types/node": "20.19.41",
141
141
  ajv: "8.20.0",
142
142
  "ajv-formats": "3.0.1",
143
- "eslint-config-custom": "5.57.0",
143
+ "eslint-config-custom": "5.58.0",
144
144
  jest: "29.6.0",
145
145
  supertest: "7.2.2",
146
- tsconfig: "5.57.0",
146
+ tsconfig: "5.58.0",
147
147
  vitest: "catalog:",
148
- "vitest-config": "5.57.0"
148
+ "vitest-config": "5.58.0"
149
149
  };
150
150
  var engines = {
151
151
  node: ">=20.0.0 <=26.x.x",
@@ -1,5 +1,5 @@
1
1
  var name = "@strapi/core";
2
- var version = "5.57.0";
2
+ var version = "5.58.0";
3
3
  var description = "Core of Strapi";
4
4
  var homepage = "https://strapi.io";
5
5
  var bugs = {
@@ -59,18 +59,18 @@ var dependencies = {
59
59
  "@casl/ability": "6.7.5",
60
60
  "@koa/cors": "5.0.0",
61
61
  "@koa/router": "12.0.2",
62
- "@modelcontextprotocol/node": "2.0.0",
63
- "@modelcontextprotocol/server": "2.0.0",
62
+ "@modelcontextprotocol/node": "2.1.1",
63
+ "@modelcontextprotocol/server": "2.3.1",
64
64
  "@paralleldrive/cuid2": "2.2.2",
65
- "@strapi/admin": "5.57.0",
66
- "@strapi/database": "5.57.0",
67
- "@strapi/generators": "5.57.0",
68
- "@strapi/logger": "5.57.0",
69
- "@strapi/openapi": "5.57.0",
70
- "@strapi/permissions": "5.57.0",
71
- "@strapi/types": "5.57.0",
72
- "@strapi/typescript-utils": "5.57.0",
73
- "@strapi/utils": "5.57.0",
65
+ "@strapi/admin": "5.58.0",
66
+ "@strapi/database": "5.58.0",
67
+ "@strapi/generators": "5.58.0",
68
+ "@strapi/logger": "5.58.0",
69
+ "@strapi/openapi": "5.58.0",
70
+ "@strapi/permissions": "5.58.0",
71
+ "@strapi/types": "5.58.0",
72
+ "@strapi/typescript-utils": "5.58.0",
73
+ "@strapi/utils": "5.58.0",
74
74
  "@vercel/stega": "0.1.2",
75
75
  bcryptjs: "2.4.3",
76
76
  boxen: "5.1.2",
@@ -117,7 +117,7 @@ var dependencies = {
117
117
  zod: "4.4.3"
118
118
  };
119
119
  var devDependencies = {
120
- "@modelcontextprotocol/client": "2.0.0",
120
+ "@modelcontextprotocol/client": "2.3.1",
121
121
  "@swc/core": "1.13.5",
122
122
  "@types/bcryptjs": "2.4.3",
123
123
  "@types/configstore": "5.0.1",
@@ -136,12 +136,12 @@ var devDependencies = {
136
136
  "@types/node": "20.19.41",
137
137
  ajv: "8.20.0",
138
138
  "ajv-formats": "3.0.1",
139
- "eslint-config-custom": "5.57.0",
139
+ "eslint-config-custom": "5.58.0",
140
140
  jest: "29.6.0",
141
141
  supertest: "7.2.2",
142
- tsconfig: "5.57.0",
142
+ tsconfig: "5.58.0",
143
143
  vitest: "catalog:",
144
- "vitest-config": "5.57.0"
144
+ "vitest-config": "5.58.0"
145
145
  };
146
146
  var engines = {
147
147
  node: ">=20.0.0 <=26.x.x",
@@ -1 +1 @@
1
- {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/services/document-service/transform/fields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAElD,KAAK,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC;AAE5E,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAgChD,CAAC"}
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/services/document-service/transform/fields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAElD,KAAK,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC;AAE5E,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAmChD,CAAC"}
@@ -23,8 +23,14 @@ const transformFields = (fields)=>{
23
23
  }
24
24
  // Ensure we are always selecting the documentId
25
25
  // ['name', 'description'] => ['name', 'description', 'documentId']
26
+ //
27
+ // Returns a new array rather than pushing onto the given one: it belongs to the caller's
28
+ // query params, which they may reuse.
26
29
  if (!fields.includes('documentId')) {
27
- fields.push('documentId');
30
+ return [
31
+ ...fields,
32
+ 'documentId'
33
+ ];
28
34
  }
29
35
  return fields;
30
36
  };
@@ -1 +1 @@
1
- {"version":3,"file":"fields.js","sources":["../../../../src/services/document-service/transform/fields.ts"],"sourcesContent":["import type { Modules, UID } from '@strapi/types';\n\ntype Fields = Modules.Documents.Params.Pick<UID.Schema, 'fields'>['fields'];\n\nexport const transformFields = (fields: Fields): Fields => {\n // If it's a string, and it doesn't contain documentId, should be an array\n if (typeof fields === 'string') {\n // '*' => '*'\n if (fields === '*') {\n return fields;\n }\n\n // '' => 'documentId'\n if (fields === '') {\n return 'documentId';\n }\n\n // 'name,description' => 'name,description,documentId'\n if (!fields.split(',').includes('documentId')) {\n return `${fields},documentId`;\n }\n }\n\n // It's not an array, ignore it\n if (!fields || !Array.isArray(fields)) {\n // If fields is empty, return it as is\n return fields;\n }\n\n // Ensure we are always selecting the documentId\n // ['name', 'description'] => ['name', 'description', 'documentId']\n if (!fields.includes('documentId')) {\n fields.push('documentId');\n }\n\n return fields;\n};\n"],"names":["transformFields","fields","split","includes","Array","isArray","push"],"mappings":";;AAIO,MAAMA,kBAAkB,CAACC,MAAAA,GAAAA;;IAE9B,IAAI,OAAOA,WAAW,QAAA,EAAU;;AAE9B,QAAA,IAAIA,WAAW,GAAA,EAAK;YAClB,OAAOA,MAAAA;AACT,QAAA;;AAGA,QAAA,IAAIA,WAAW,EAAA,EAAI;YACjB,OAAO,YAAA;AACT,QAAA;;AAGA,QAAA,IAAI,CAACA,MAAAA,CAAOC,KAAK,CAAC,GAAA,CAAA,CAAKC,QAAQ,CAAC,YAAA,CAAA,EAAe;YAC7C,OAAO,CAAA,EAAGF,MAAAA,CAAO,WAAW,CAAC;AAC/B,QAAA;AACF,IAAA;;AAGA,IAAA,IAAI,CAACA,MAAAA,IAAU,CAACG,KAAAA,CAAMC,OAAO,CAACJ,MAAAA,CAAAA,EAAS;;QAErC,OAAOA,MAAAA;AACT,IAAA;;;AAIA,IAAA,IAAI,CAACA,MAAAA,CAAOE,QAAQ,CAAC,YAAA,CAAA,EAAe;AAClCF,QAAAA,MAAAA,CAAOK,IAAI,CAAC,YAAA,CAAA;AACd,IAAA;IAEA,OAAOL,MAAAA;AACT;;;;"}
1
+ {"version":3,"file":"fields.js","sources":["../../../../src/services/document-service/transform/fields.ts"],"sourcesContent":["import type { Modules, UID } from '@strapi/types';\n\ntype Fields = Modules.Documents.Params.Pick<UID.Schema, 'fields'>['fields'];\n\nexport const transformFields = (fields: Fields): Fields => {\n // If it's a string, and it doesn't contain documentId, should be an array\n if (typeof fields === 'string') {\n // '*' => '*'\n if (fields === '*') {\n return fields;\n }\n\n // '' => 'documentId'\n if (fields === '') {\n return 'documentId';\n }\n\n // 'name,description' => 'name,description,documentId'\n if (!fields.split(',').includes('documentId')) {\n return `${fields},documentId`;\n }\n }\n\n // It's not an array, ignore it\n if (!fields || !Array.isArray(fields)) {\n // If fields is empty, return it as is\n return fields;\n }\n\n // Ensure we are always selecting the documentId\n // ['name', 'description'] => ['name', 'description', 'documentId']\n //\n // Returns a new array rather than pushing onto the given one: it belongs to the caller's\n // query params, which they may reuse.\n if (!fields.includes('documentId')) {\n return [...fields, 'documentId'];\n }\n\n return fields;\n};\n"],"names":["transformFields","fields","split","includes","Array","isArray"],"mappings":";;AAIO,MAAMA,kBAAkB,CAACC,MAAAA,GAAAA;;IAE9B,IAAI,OAAOA,WAAW,QAAA,EAAU;;AAE9B,QAAA,IAAIA,WAAW,GAAA,EAAK;YAClB,OAAOA,MAAAA;AACT,QAAA;;AAGA,QAAA,IAAIA,WAAW,EAAA,EAAI;YACjB,OAAO,YAAA;AACT,QAAA;;AAGA,QAAA,IAAI,CAACA,MAAAA,CAAOC,KAAK,CAAC,GAAA,CAAA,CAAKC,QAAQ,CAAC,YAAA,CAAA,EAAe;YAC7C,OAAO,CAAA,EAAGF,MAAAA,CAAO,WAAW,CAAC;AAC/B,QAAA;AACF,IAAA;;AAGA,IAAA,IAAI,CAACA,MAAAA,IAAU,CAACG,KAAAA,CAAMC,OAAO,CAACJ,MAAAA,CAAAA,EAAS;;QAErC,OAAOA,MAAAA;AACT,IAAA;;;;;;AAOA,IAAA,IAAI,CAACA,MAAAA,CAAOE,QAAQ,CAAC,YAAA,CAAA,EAAe;QAClC,OAAO;AAAIF,YAAAA,GAAAA,MAAAA;AAAQ,YAAA;AAAa,SAAA;AAClC,IAAA;IAEA,OAAOA,MAAAA;AACT;;;;"}
@@ -21,8 +21,14 @@ const transformFields = (fields)=>{
21
21
  }
22
22
  // Ensure we are always selecting the documentId
23
23
  // ['name', 'description'] => ['name', 'description', 'documentId']
24
+ //
25
+ // Returns a new array rather than pushing onto the given one: it belongs to the caller's
26
+ // query params, which they may reuse.
24
27
  if (!fields.includes('documentId')) {
25
- fields.push('documentId');
28
+ return [
29
+ ...fields,
30
+ 'documentId'
31
+ ];
26
32
  }
27
33
  return fields;
28
34
  };
@@ -1 +1 @@
1
- {"version":3,"file":"fields.mjs","sources":["../../../../src/services/document-service/transform/fields.ts"],"sourcesContent":["import type { Modules, UID } from '@strapi/types';\n\ntype Fields = Modules.Documents.Params.Pick<UID.Schema, 'fields'>['fields'];\n\nexport const transformFields = (fields: Fields): Fields => {\n // If it's a string, and it doesn't contain documentId, should be an array\n if (typeof fields === 'string') {\n // '*' => '*'\n if (fields === '*') {\n return fields;\n }\n\n // '' => 'documentId'\n if (fields === '') {\n return 'documentId';\n }\n\n // 'name,description' => 'name,description,documentId'\n if (!fields.split(',').includes('documentId')) {\n return `${fields},documentId`;\n }\n }\n\n // It's not an array, ignore it\n if (!fields || !Array.isArray(fields)) {\n // If fields is empty, return it as is\n return fields;\n }\n\n // Ensure we are always selecting the documentId\n // ['name', 'description'] => ['name', 'description', 'documentId']\n if (!fields.includes('documentId')) {\n fields.push('documentId');\n }\n\n return fields;\n};\n"],"names":["transformFields","fields","split","includes","Array","isArray","push"],"mappings":"AAIO,MAAMA,kBAAkB,CAACC,MAAAA,GAAAA;;IAE9B,IAAI,OAAOA,WAAW,QAAA,EAAU;;AAE9B,QAAA,IAAIA,WAAW,GAAA,EAAK;YAClB,OAAOA,MAAAA;AACT,QAAA;;AAGA,QAAA,IAAIA,WAAW,EAAA,EAAI;YACjB,OAAO,YAAA;AACT,QAAA;;AAGA,QAAA,IAAI,CAACA,MAAAA,CAAOC,KAAK,CAAC,GAAA,CAAA,CAAKC,QAAQ,CAAC,YAAA,CAAA,EAAe;YAC7C,OAAO,CAAA,EAAGF,MAAAA,CAAO,WAAW,CAAC;AAC/B,QAAA;AACF,IAAA;;AAGA,IAAA,IAAI,CAACA,MAAAA,IAAU,CAACG,KAAAA,CAAMC,OAAO,CAACJ,MAAAA,CAAAA,EAAS;;QAErC,OAAOA,MAAAA;AACT,IAAA;;;AAIA,IAAA,IAAI,CAACA,MAAAA,CAAOE,QAAQ,CAAC,YAAA,CAAA,EAAe;AAClCF,QAAAA,MAAAA,CAAOK,IAAI,CAAC,YAAA,CAAA;AACd,IAAA;IAEA,OAAOL,MAAAA;AACT;;;;"}
1
+ {"version":3,"file":"fields.mjs","sources":["../../../../src/services/document-service/transform/fields.ts"],"sourcesContent":["import type { Modules, UID } from '@strapi/types';\n\ntype Fields = Modules.Documents.Params.Pick<UID.Schema, 'fields'>['fields'];\n\nexport const transformFields = (fields: Fields): Fields => {\n // If it's a string, and it doesn't contain documentId, should be an array\n if (typeof fields === 'string') {\n // '*' => '*'\n if (fields === '*') {\n return fields;\n }\n\n // '' => 'documentId'\n if (fields === '') {\n return 'documentId';\n }\n\n // 'name,description' => 'name,description,documentId'\n if (!fields.split(',').includes('documentId')) {\n return `${fields},documentId`;\n }\n }\n\n // It's not an array, ignore it\n if (!fields || !Array.isArray(fields)) {\n // If fields is empty, return it as is\n return fields;\n }\n\n // Ensure we are always selecting the documentId\n // ['name', 'description'] => ['name', 'description', 'documentId']\n //\n // Returns a new array rather than pushing onto the given one: it belongs to the caller's\n // query params, which they may reuse.\n if (!fields.includes('documentId')) {\n return [...fields, 'documentId'];\n }\n\n return fields;\n};\n"],"names":["transformFields","fields","split","includes","Array","isArray"],"mappings":"AAIO,MAAMA,kBAAkB,CAACC,MAAAA,GAAAA;;IAE9B,IAAI,OAAOA,WAAW,QAAA,EAAU;;AAE9B,QAAA,IAAIA,WAAW,GAAA,EAAK;YAClB,OAAOA,MAAAA;AACT,QAAA;;AAGA,QAAA,IAAIA,WAAW,EAAA,EAAI;YACjB,OAAO,YAAA;AACT,QAAA;;AAGA,QAAA,IAAI,CAACA,MAAAA,CAAOC,KAAK,CAAC,GAAA,CAAA,CAAKC,QAAQ,CAAC,YAAA,CAAA,EAAe;YAC7C,OAAO,CAAA,EAAGF,MAAAA,CAAO,WAAW,CAAC;AAC/B,QAAA;AACF,IAAA;;AAGA,IAAA,IAAI,CAACA,MAAAA,IAAU,CAACG,KAAAA,CAAMC,OAAO,CAACJ,MAAAA,CAAAA,EAAS;;QAErC,OAAOA,MAAAA;AACT,IAAA;;;;;;AAOA,IAAA,IAAI,CAACA,MAAAA,CAAOE,QAAQ,CAAC,YAAA,CAAA,EAAe;QAClC,OAAO;AAAIF,YAAAA,GAAAA,MAAAA;AAAQ,YAAA;AAAa,SAAA;AAClC,IAAA;IAEA,OAAOA,MAAAA;AACT;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"populate.d.ts","sourceRoot":"","sources":["../../../../src/services/document-service/transform/populate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AAGpC,eAAO,MAAM,iBAAiB,GAAU,MAAM,IAAI,EAAE,MAAM;IAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAA;CAAE,qBAqB5E,CAAC"}
1
+ {"version":3,"file":"populate.d.ts","sourceRoot":"","sources":["../../../../src/services/document-service/transform/populate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AAGpC,eAAO,MAAM,iBAAiB,GAAU,MAAM,IAAI,EAAE,MAAM;IAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAA;CAAE,qBAyB5E,CAAC"}
@@ -12,8 +12,15 @@ const transformPopulate = async (data, opts)=>{
12
12
  If the attribute is a relation
13
13
  Look for fields in the value
14
14
  and apply the relevant transformation to these objects
15
+
16
+ The transformed value is set as a new object: a visitor must not write into the
17
+ `value` it is handed, which may be shared with the data the traversal was given.
15
18
  */ if ('fields' in value && Array.isArray(value.fields)) {
16
- value.fields = fields.transformFields(value.fields);
19
+ set(key, {
20
+ ...value,
21
+ fields: fields.transformFields(value.fields)
22
+ });
23
+ return;
17
24
  }
18
25
  set(key, value);
19
26
  }, {
@@ -1 +1 @@
1
- {"version":3,"file":"populate.js","sources":["../../../../src/services/document-service/transform/populate.ts"],"sourcesContent":["import { traverse } from '@strapi/utils';\nimport type { UID } from '@strapi/types';\n\nimport { type Data } from './types';\nimport { transformFields } from './fields';\n\nexport const transformPopulate = async (data: Data, opts: { uid: UID.Schema }) => {\n return traverse.traverseQueryPopulate(\n async ({ attribute, key, value }, { set }) => {\n if (!value || typeof value !== 'object' || attribute?.type !== 'relation') {\n return;\n }\n\n /*\n If the attribute is a relation\n Look for fields in the value\n and apply the relevant transformation to these objects\n */\n if ('fields' in value && Array.isArray(value.fields)) {\n value.fields = transformFields(value.fields);\n }\n\n set(key, value);\n },\n { schema: strapi.getModel(opts.uid), getModel: strapi.getModel.bind(strapi) },\n data\n );\n};\n"],"names":["transformPopulate","data","opts","traverse","traverseQueryPopulate","attribute","key","value","set","type","Array","isArray","fields","transformFields","schema","strapi","getModel","uid","bind"],"mappings":";;;;;AAMO,MAAMA,iBAAAA,GAAoB,OAAOC,IAAAA,EAAYC,IAAAA,GAAAA;AAClD,IAAA,OAAOC,oBAAAA,CAASC,qBAAqB,CACnC,OAAO,EAAEC,SAAS,EAAEC,GAAG,EAAEC,KAAK,EAAE,EAAE,EAAEC,GAAG,EAAE,GAAA;AACvC,QAAA,IAAI,CAACD,KAAAA,IAAS,OAAOA,UAAU,QAAA,IAAYF,SAAAA,EAAWI,SAAS,UAAA,EAAY;AACzE,YAAA;AACF,QAAA;AAEA;;;;SAKA,IAAI,YAAYF,KAAAA,IAASG,KAAAA,CAAMC,OAAO,CAACJ,KAAAA,CAAMK,MAAM,CAAA,EAAG;AACpDL,YAAAA,KAAAA,CAAMK,MAAM,GAAGC,sBAAAA,CAAgBN,KAAAA,CAAMK,MAAM,CAAA;AAC7C,QAAA;AAEAJ,QAAAA,GAAAA,CAAIF,GAAAA,EAAKC,KAAAA,CAAAA;IACX,CAAA,EACA;AAAEO,QAAAA,MAAAA,EAAQC,MAAAA,CAAOC,QAAQ,CAACd,IAAAA,CAAKe,GAAG,CAAA;AAAGD,QAAAA,QAAAA,EAAUD,MAAAA,CAAOC,QAAQ,CAACE,IAAI,CAACH,MAAAA;KAAQ,EAC5Ed,IAAAA,CAAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"populate.js","sources":["../../../../src/services/document-service/transform/populate.ts"],"sourcesContent":["import { traverse } from '@strapi/utils';\nimport type { UID } from '@strapi/types';\n\nimport { type Data } from './types';\nimport { transformFields } from './fields';\n\nexport const transformPopulate = async (data: Data, opts: { uid: UID.Schema }) => {\n return traverse.traverseQueryPopulate(\n async ({ attribute, key, value }, { set }) => {\n if (!value || typeof value !== 'object' || attribute?.type !== 'relation') {\n return;\n }\n\n /*\n If the attribute is a relation\n Look for fields in the value\n and apply the relevant transformation to these objects\n\n The transformed value is set as a new object: a visitor must not write into the\n `value` it is handed, which may be shared with the data the traversal was given.\n */\n if ('fields' in value && Array.isArray(value.fields)) {\n set(key, { ...value, fields: transformFields(value.fields) });\n return;\n }\n\n set(key, value);\n },\n { schema: strapi.getModel(opts.uid), getModel: strapi.getModel.bind(strapi) },\n data\n );\n};\n"],"names":["transformPopulate","data","opts","traverse","traverseQueryPopulate","attribute","key","value","set","type","Array","isArray","fields","transformFields","schema","strapi","getModel","uid","bind"],"mappings":";;;;;AAMO,MAAMA,iBAAAA,GAAoB,OAAOC,IAAAA,EAAYC,IAAAA,GAAAA;AAClD,IAAA,OAAOC,oBAAAA,CAASC,qBAAqB,CACnC,OAAO,EAAEC,SAAS,EAAEC,GAAG,EAAEC,KAAK,EAAE,EAAE,EAAEC,GAAG,EAAE,GAAA;AACvC,QAAA,IAAI,CAACD,KAAAA,IAAS,OAAOA,UAAU,QAAA,IAAYF,SAAAA,EAAWI,SAAS,UAAA,EAAY;AACzE,YAAA;AACF,QAAA;AAEA;;;;;;;SAQA,IAAI,YAAYF,KAAAA,IAASG,KAAAA,CAAMC,OAAO,CAACJ,KAAAA,CAAMK,MAAM,CAAA,EAAG;AACpDJ,YAAAA,GAAAA,CAAIF,GAAAA,EAAK;AAAE,gBAAA,GAAGC,KAAK;gBAAEK,MAAAA,EAAQC,sBAAAA,CAAgBN,MAAMK,MAAM;AAAE,aAAA,CAAA;AAC3D,YAAA;AACF,QAAA;AAEAJ,QAAAA,GAAAA,CAAIF,GAAAA,EAAKC,KAAAA,CAAAA;IACX,CAAA,EACA;AAAEO,QAAAA,MAAAA,EAAQC,MAAAA,CAAOC,QAAQ,CAACd,IAAAA,CAAKe,GAAG,CAAA;AAAGD,QAAAA,QAAAA,EAAUD,MAAAA,CAAOC,QAAQ,CAACE,IAAI,CAACH,MAAAA;KAAQ,EAC5Ed,IAAAA,CAAAA;AAEJ;;;;"}
@@ -10,8 +10,15 @@ const transformPopulate = async (data, opts)=>{
10
10
  If the attribute is a relation
11
11
  Look for fields in the value
12
12
  and apply the relevant transformation to these objects
13
+
14
+ The transformed value is set as a new object: a visitor must not write into the
15
+ `value` it is handed, which may be shared with the data the traversal was given.
13
16
  */ if ('fields' in value && Array.isArray(value.fields)) {
14
- value.fields = transformFields(value.fields);
17
+ set(key, {
18
+ ...value,
19
+ fields: transformFields(value.fields)
20
+ });
21
+ return;
15
22
  }
16
23
  set(key, value);
17
24
  }, {
@@ -1 +1 @@
1
- {"version":3,"file":"populate.mjs","sources":["../../../../src/services/document-service/transform/populate.ts"],"sourcesContent":["import { traverse } from '@strapi/utils';\nimport type { UID } from '@strapi/types';\n\nimport { type Data } from './types';\nimport { transformFields } from './fields';\n\nexport const transformPopulate = async (data: Data, opts: { uid: UID.Schema }) => {\n return traverse.traverseQueryPopulate(\n async ({ attribute, key, value }, { set }) => {\n if (!value || typeof value !== 'object' || attribute?.type !== 'relation') {\n return;\n }\n\n /*\n If the attribute is a relation\n Look for fields in the value\n and apply the relevant transformation to these objects\n */\n if ('fields' in value && Array.isArray(value.fields)) {\n value.fields = transformFields(value.fields);\n }\n\n set(key, value);\n },\n { schema: strapi.getModel(opts.uid), getModel: strapi.getModel.bind(strapi) },\n data\n );\n};\n"],"names":["transformPopulate","data","opts","traverse","traverseQueryPopulate","attribute","key","value","set","type","Array","isArray","fields","transformFields","schema","strapi","getModel","uid","bind"],"mappings":";;;AAMO,MAAMA,iBAAAA,GAAoB,OAAOC,IAAAA,EAAYC,IAAAA,GAAAA;AAClD,IAAA,OAAOC,QAAAA,CAASC,qBAAqB,CACnC,OAAO,EAAEC,SAAS,EAAEC,GAAG,EAAEC,KAAK,EAAE,EAAE,EAAEC,GAAG,EAAE,GAAA;AACvC,QAAA,IAAI,CAACD,KAAAA,IAAS,OAAOA,UAAU,QAAA,IAAYF,SAAAA,EAAWI,SAAS,UAAA,EAAY;AACzE,YAAA;AACF,QAAA;AAEA;;;;SAKA,IAAI,YAAYF,KAAAA,IAASG,KAAAA,CAAMC,OAAO,CAACJ,KAAAA,CAAMK,MAAM,CAAA,EAAG;AACpDL,YAAAA,KAAAA,CAAMK,MAAM,GAAGC,eAAAA,CAAgBN,KAAAA,CAAMK,MAAM,CAAA;AAC7C,QAAA;AAEAJ,QAAAA,GAAAA,CAAIF,GAAAA,EAAKC,KAAAA,CAAAA;IACX,CAAA,EACA;AAAEO,QAAAA,MAAAA,EAAQC,MAAAA,CAAOC,QAAQ,CAACd,IAAAA,CAAKe,GAAG,CAAA;AAAGD,QAAAA,QAAAA,EAAUD,MAAAA,CAAOC,QAAQ,CAACE,IAAI,CAACH,MAAAA;KAAQ,EAC5Ed,IAAAA,CAAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"populate.mjs","sources":["../../../../src/services/document-service/transform/populate.ts"],"sourcesContent":["import { traverse } from '@strapi/utils';\nimport type { UID } from '@strapi/types';\n\nimport { type Data } from './types';\nimport { transformFields } from './fields';\n\nexport const transformPopulate = async (data: Data, opts: { uid: UID.Schema }) => {\n return traverse.traverseQueryPopulate(\n async ({ attribute, key, value }, { set }) => {\n if (!value || typeof value !== 'object' || attribute?.type !== 'relation') {\n return;\n }\n\n /*\n If the attribute is a relation\n Look for fields in the value\n and apply the relevant transformation to these objects\n\n The transformed value is set as a new object: a visitor must not write into the\n `value` it is handed, which may be shared with the data the traversal was given.\n */\n if ('fields' in value && Array.isArray(value.fields)) {\n set(key, { ...value, fields: transformFields(value.fields) });\n return;\n }\n\n set(key, value);\n },\n { schema: strapi.getModel(opts.uid), getModel: strapi.getModel.bind(strapi) },\n data\n );\n};\n"],"names":["transformPopulate","data","opts","traverse","traverseQueryPopulate","attribute","key","value","set","type","Array","isArray","fields","transformFields","schema","strapi","getModel","uid","bind"],"mappings":";;;AAMO,MAAMA,iBAAAA,GAAoB,OAAOC,IAAAA,EAAYC,IAAAA,GAAAA;AAClD,IAAA,OAAOC,QAAAA,CAASC,qBAAqB,CACnC,OAAO,EAAEC,SAAS,EAAEC,GAAG,EAAEC,KAAK,EAAE,EAAE,EAAEC,GAAG,EAAE,GAAA;AACvC,QAAA,IAAI,CAACD,KAAAA,IAAS,OAAOA,UAAU,QAAA,IAAYF,SAAAA,EAAWI,SAAS,UAAA,EAAY;AACzE,YAAA;AACF,QAAA;AAEA;;;;;;;SAQA,IAAI,YAAYF,KAAAA,IAASG,KAAAA,CAAMC,OAAO,CAACJ,KAAAA,CAAMK,MAAM,CAAA,EAAG;AACpDJ,YAAAA,GAAAA,CAAIF,GAAAA,EAAK;AAAE,gBAAA,GAAGC,KAAK;gBAAEK,MAAAA,EAAQC,eAAAA,CAAgBN,MAAMK,MAAM;AAAE,aAAA,CAAA;AAC3D,YAAA;AACF,QAAA;AAEAJ,QAAAA,GAAAA,CAAIF,GAAAA,EAAKC,KAAAA,CAAAA;IACX,CAAA,EACA;AAAEO,QAAAA,MAAAA,EAAQC,MAAAA,CAAOC,QAAQ,CAACd,IAAAA,CAAKe,GAAG,CAAA;AAAGD,QAAAA,QAAAA,EAAUD,MAAAA,CAAOC,QAAQ,CAACE,IAAI,CAACH,MAAAA;KAAQ,EAC5Ed,IAAAA,CAAAA;AAEJ;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"map-relation.d.ts","sourceRoot":"","sources":["../../../../../../src/services/document-service/transform/relations/utils/map-relation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAS,MAAM,eAAe,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA8GnC,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,KAAK,8BAA8B,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA2CrF,QAAA,MAAM,kBAAkB,+CAzHD,GAAG,KAAK,GAAG,8BAyHW,CAAC;AAC9C,QAAA,MAAM,8BAA8B,oNAAiC,CAAC;AAEtE,OAAO,EACL,kBAAkB,IAAI,WAAW,EACjC,8BAA8B,IAAI,uBAAuB,GAC1D,CAAC"}
1
+ {"version":3,"file":"map-relation.d.ts","sourceRoot":"","sources":["../../../../../../src/services/document-service/transform/relations/utils/map-relation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAS,MAAM,eAAe,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAqHnC,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,KAAK,8BAA8B,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA2CrF,QAAA,MAAM,kBAAkB,+CAzHD,GAAG,KAAK,GAAG,8BAyHW,CAAC;AAC9C,QAAA,MAAM,8BAA8B,oNAAiC,CAAC;AAEtE,OAAO,EACL,kBAAkB,IAAI,WAAW,EACjC,8BAA8B,IAAI,uBAAuB,GAC1D,CAAC"}
@@ -3,10 +3,16 @@
3
3
  var _ = require('lodash');
4
4
  var strapiUtils = require('@strapi/utils');
5
5
 
6
- const isNumeric = (value)=>{
7
- if (Array.isArray(value)) return false; // Handle [1, 'docId'] case
8
- const parsed = parseInt(value, 10);
9
- return !Number.isNaN(parsed);
6
+ /**
7
+ * Whether a shorthand relation value is a numeric entry id (as opposed to a documentId).
8
+ *
9
+ * A string only counts as numeric when it is made of digits *entirely*. `parseInt` must not be
10
+ * used here: it stops at the first non-digit character, so a documentId that happens to start
11
+ * with a digit (e.g. `'3d52c864df72fcc68ab62ebe'` -> `3`) would be misclassified as an id.
12
+ */ const isNumeric = (value)=>{
13
+ if (typeof value === 'number') return Number.isFinite(value);
14
+ if (typeof value === 'string') return /^\d+$/.test(value);
15
+ return false; // Arrays (e.g. [1, 'docId']), objects, etc.
10
16
  };
11
17
  const toArray = (value)=>{
12
18
  // Keep value as it is if it's a nullish value
@@ -1 +1 @@
1
- {"version":3,"file":"map-relation.js","sources":["../../../../../../src/services/document-service/transform/relations/utils/map-relation.ts"],"sourcesContent":["/* eslint-disable node/no-callback-literal */\nimport { curry, isObject } from 'lodash';\n\nimport { traverseEntity, async } from '@strapi/utils';\n\nimport { Relation } from './types';\n\nconst isNumeric = (value: any): value is number => {\n if (Array.isArray(value)) return false; // Handle [1, 'docId'] case\n const parsed = parseInt(value, 10);\n return !Number.isNaN(parsed);\n};\n\nconst toArray = (value: any) => {\n // Keep value as it is if it's a nullish value\n if (value == null) return value;\n if (Array.isArray(value)) return value;\n\n return [value];\n};\n\n/**\n * There are multiple ways to create Strapi relations.\n * This is a utility to traverse and transform relation data\n *\n *\n * For consistency and ease of use, the response will always be an object with the following shape:\n * { set: [{...}], connect: [{...}], disconnect: [{...}] }\n *\n * @example\n * transformRelationData({\n * onLongHand: (relation) => {\n * // Change the id of the relation\n * return { id: 'other' };\n * },\n * }, relation)\n */\nconst mapRelation = async (\n callback: (relation: any) => any,\n rel: Relation,\n isRecursive = false\n): Promise<Relation> => {\n let relation: Relation = rel;\n\n const wrapInSet = (value: any) => {\n // Ignore wrapping if it's a recursive call\n if (isRecursive) {\n return value;\n }\n return { set: toArray(value) };\n };\n\n // undefined | null\n if (relation == null) {\n return callback(relation);\n }\n\n // LongHand[] | ShortHand[]\n if (Array.isArray(relation)) {\n return async\n .map(relation, (r: Relation) => mapRelation(callback, r, true))\n .then((result: any) => result.flat().filter(Boolean))\n .then(wrapInSet);\n }\n\n // LongHand\n if (isObject(relation)) {\n // { id: 1 } || { documentId: 1 }\n if ('id' in relation || 'documentId' in relation) {\n const result = await callback(relation);\n return wrapInSet(result);\n }\n\n // If not connecting anything, return default visitor\n if (!relation.set && !relation.disconnect && !relation.connect) {\n return callback(relation);\n }\n\n // { set }\n if (relation.set) {\n const set: any = await mapRelation(callback, relation.set, true);\n relation = { ...relation, set: toArray(set) };\n }\n\n // { disconnect}\n if (relation.disconnect) {\n const disconnect: any = await mapRelation(callback, relation.disconnect, true);\n relation = { ...relation, disconnect: toArray(disconnect) };\n }\n\n // { connect }\n if (relation.connect) {\n // Transform the relation to connect\n const connect: any = await mapRelation(callback, relation.connect, true);\n relation = { ...relation, connect: toArray(connect) };\n }\n\n return relation;\n }\n\n // ShortHand\n if (isNumeric(relation)) {\n const result = await callback({ id: relation });\n return wrapInSet(result);\n }\n\n if (typeof relation === 'string') {\n const result = await callback({ documentId: relation });\n return wrapInSet(result);\n }\n\n // Anything else\n return callback(relation);\n};\n\ntype TraverseEntity = Parameters<typeof traverseEntity>;\ntype TraverseEntityRelationsOptions = TraverseEntity[1] & { includeMedia?: boolean };\n\n/**\n * Utility function, same as `traverseEntity` but only for relations and, when requested, media.\n */\nconst traverseEntityRelations = async (\n visitor: TraverseEntity[0],\n options: TraverseEntityRelationsOptions,\n data: TraverseEntity[2]\n) => {\n const { includeMedia = false, ...traverseOptions } = options;\n\n return traverseEntity(\n async (options, utils) => {\n const { attribute } = options;\n\n if (!attribute) {\n return;\n }\n\n if (attribute.type !== 'relation' && !(includeMedia && attribute.type === 'media')) {\n return;\n }\n\n if (attribute.type === 'relation') {\n // TODO: Handle join columns\n if (attribute.useJoinTable === false) {\n return;\n }\n\n // morphToOne uses morphColumn (inline columns on the entity), handled directly in processData\n if (attribute.relation === 'morphToOne') {\n return;\n }\n }\n\n return visitor(options, utils);\n },\n traverseOptions,\n data\n );\n};\n\nconst mapRelationCurried = curry(mapRelation);\nconst traverseEntityRelationsCurried = curry(traverseEntityRelations);\n\nexport {\n mapRelationCurried as mapRelation,\n traverseEntityRelationsCurried as traverseEntityRelations,\n};\n"],"names":["isNumeric","value","Array","isArray","parsed","parseInt","Number","isNaN","toArray","mapRelation","callback","rel","isRecursive","relation","wrapInSet","set","async","map","r","then","result","flat","filter","Boolean","isObject","disconnect","connect","id","documentId","traverseEntityRelations","visitor","options","data","includeMedia","traverseOptions","traverseEntity","utils","attribute","type","useJoinTable","mapRelationCurried","curry","traverseEntityRelationsCurried"],"mappings":"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"}
1
+ {"version":3,"file":"map-relation.js","sources":["../../../../../../src/services/document-service/transform/relations/utils/map-relation.ts"],"sourcesContent":["/* eslint-disable node/no-callback-literal */\nimport { curry, isObject } from 'lodash';\n\nimport { traverseEntity, async } from '@strapi/utils';\n\nimport { Relation } from './types';\n\n/**\n * Whether a shorthand relation value is a numeric entry id (as opposed to a documentId).\n *\n * A string only counts as numeric when it is made of digits *entirely*. `parseInt` must not be\n * used here: it stops at the first non-digit character, so a documentId that happens to start\n * with a digit (e.g. `'3d52c864df72fcc68ab62ebe'` -> `3`) would be misclassified as an id.\n */\nconst isNumeric = (value: any): value is number => {\n if (typeof value === 'number') return Number.isFinite(value);\n if (typeof value === 'string') return /^\\d+$/.test(value);\n return false; // Arrays (e.g. [1, 'docId']), objects, etc.\n};\n\nconst toArray = (value: any) => {\n // Keep value as it is if it's a nullish value\n if (value == null) return value;\n if (Array.isArray(value)) return value;\n\n return [value];\n};\n\n/**\n * There are multiple ways to create Strapi relations.\n * This is a utility to traverse and transform relation data\n *\n *\n * For consistency and ease of use, the response will always be an object with the following shape:\n * { set: [{...}], connect: [{...}], disconnect: [{...}] }\n *\n * @example\n * transformRelationData({\n * onLongHand: (relation) => {\n * // Change the id of the relation\n * return { id: 'other' };\n * },\n * }, relation)\n */\nconst mapRelation = async (\n callback: (relation: any) => any,\n rel: Relation,\n isRecursive = false\n): Promise<Relation> => {\n let relation: Relation = rel;\n\n const wrapInSet = (value: any) => {\n // Ignore wrapping if it's a recursive call\n if (isRecursive) {\n return value;\n }\n return { set: toArray(value) };\n };\n\n // undefined | null\n if (relation == null) {\n return callback(relation);\n }\n\n // LongHand[] | ShortHand[]\n if (Array.isArray(relation)) {\n return async\n .map(relation, (r: Relation) => mapRelation(callback, r, true))\n .then((result: any) => result.flat().filter(Boolean))\n .then(wrapInSet);\n }\n\n // LongHand\n if (isObject(relation)) {\n // { id: 1 } || { documentId: 1 }\n if ('id' in relation || 'documentId' in relation) {\n const result = await callback(relation);\n return wrapInSet(result);\n }\n\n // If not connecting anything, return default visitor\n if (!relation.set && !relation.disconnect && !relation.connect) {\n return callback(relation);\n }\n\n // { set }\n if (relation.set) {\n const set: any = await mapRelation(callback, relation.set, true);\n relation = { ...relation, set: toArray(set) };\n }\n\n // { disconnect}\n if (relation.disconnect) {\n const disconnect: any = await mapRelation(callback, relation.disconnect, true);\n relation = { ...relation, disconnect: toArray(disconnect) };\n }\n\n // { connect }\n if (relation.connect) {\n // Transform the relation to connect\n const connect: any = await mapRelation(callback, relation.connect, true);\n relation = { ...relation, connect: toArray(connect) };\n }\n\n return relation;\n }\n\n // ShortHand\n if (isNumeric(relation)) {\n const result = await callback({ id: relation });\n return wrapInSet(result);\n }\n\n if (typeof relation === 'string') {\n const result = await callback({ documentId: relation });\n return wrapInSet(result);\n }\n\n // Anything else\n return callback(relation);\n};\n\ntype TraverseEntity = Parameters<typeof traverseEntity>;\ntype TraverseEntityRelationsOptions = TraverseEntity[1] & { includeMedia?: boolean };\n\n/**\n * Utility function, same as `traverseEntity` but only for relations and, when requested, media.\n */\nconst traverseEntityRelations = async (\n visitor: TraverseEntity[0],\n options: TraverseEntityRelationsOptions,\n data: TraverseEntity[2]\n) => {\n const { includeMedia = false, ...traverseOptions } = options;\n\n return traverseEntity(\n async (options, utils) => {\n const { attribute } = options;\n\n if (!attribute) {\n return;\n }\n\n if (attribute.type !== 'relation' && !(includeMedia && attribute.type === 'media')) {\n return;\n }\n\n if (attribute.type === 'relation') {\n // TODO: Handle join columns\n if (attribute.useJoinTable === false) {\n return;\n }\n\n // morphToOne uses morphColumn (inline columns on the entity), handled directly in processData\n if (attribute.relation === 'morphToOne') {\n return;\n }\n }\n\n return visitor(options, utils);\n },\n traverseOptions,\n data\n );\n};\n\nconst mapRelationCurried = curry(mapRelation);\nconst traverseEntityRelationsCurried = curry(traverseEntityRelations);\n\nexport {\n mapRelationCurried as mapRelation,\n traverseEntityRelationsCurried as traverseEntityRelations,\n};\n"],"names":["isNumeric","value","Number","isFinite","test","toArray","Array","isArray","mapRelation","callback","rel","isRecursive","relation","wrapInSet","set","async","map","r","then","result","flat","filter","Boolean","isObject","disconnect","connect","id","documentId","traverseEntityRelations","visitor","options","data","includeMedia","traverseOptions","traverseEntity","utils","attribute","type","useJoinTable","mapRelationCurried","curry","traverseEntityRelationsCurried"],"mappings":"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"}
@@ -1,10 +1,16 @@
1
1
  import { curry, isObject } from 'lodash';
2
2
  import { async, traverseEntity } from '@strapi/utils';
3
3
 
4
- const isNumeric = (value)=>{
5
- if (Array.isArray(value)) return false; // Handle [1, 'docId'] case
6
- const parsed = parseInt(value, 10);
7
- return !Number.isNaN(parsed);
4
+ /**
5
+ * Whether a shorthand relation value is a numeric entry id (as opposed to a documentId).
6
+ *
7
+ * A string only counts as numeric when it is made of digits *entirely*. `parseInt` must not be
8
+ * used here: it stops at the first non-digit character, so a documentId that happens to start
9
+ * with a digit (e.g. `'3d52c864df72fcc68ab62ebe'` -> `3`) would be misclassified as an id.
10
+ */ const isNumeric = (value)=>{
11
+ if (typeof value === 'number') return Number.isFinite(value);
12
+ if (typeof value === 'string') return /^\d+$/.test(value);
13
+ return false; // Arrays (e.g. [1, 'docId']), objects, etc.
8
14
  };
9
15
  const toArray = (value)=>{
10
16
  // Keep value as it is if it's a nullish value