@strapi/content-manager 5.56.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.
- package/dist/admin/components/ConfigurationForm/Form.js +3 -3
- package/dist/admin/components/ConfigurationForm/Form.js.map +1 -1
- package/dist/admin/components/ConfigurationForm/Form.mjs +2 -2
- package/dist/admin/components/ConfigurationForm/Form.mjs.map +1 -1
- package/dist/admin/components/RelativeTime.js +9 -0
- package/dist/admin/components/RelativeTime.js.map +1 -1
- package/dist/admin/components/RelativeTime.mjs +10 -1
- package/dist/admin/components/RelativeTime.mjs.map +1 -1
- package/dist/admin/features/DocumentRBAC.js +0 -3
- package/dist/admin/features/DocumentRBAC.js.map +1 -1
- package/dist/admin/features/DocumentRBAC.mjs +1 -4
- package/dist/admin/features/DocumentRBAC.mjs.map +1 -1
- package/dist/admin/history/components/VersionContent.js +3 -3
- package/dist/admin/history/components/VersionContent.js.map +1 -1
- package/dist/admin/history/components/VersionContent.mjs +2 -2
- package/dist/admin/history/components/VersionContent.mjs.map +1 -1
- package/dist/admin/history/pages/History.js +2 -1
- package/dist/admin/history/pages/History.js.map +1 -1
- package/dist/admin/history/pages/History.mjs +3 -2
- package/dist/admin/history/pages/History.mjs.map +1 -1
- package/dist/admin/pages/EditView/EditViewPage.js +2 -2
- package/dist/admin/pages/EditView/EditViewPage.js.map +1 -1
- package/dist/admin/pages/EditView/EditViewPage.mjs +2 -2
- package/dist/admin/pages/EditView/EditViewPage.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/DocumentActions.js +37 -2
- package/dist/admin/pages/EditView/components/DocumentActions.js.map +1 -1
- package/dist/admin/pages/EditView/components/DocumentActions.mjs +37 -3
- package/dist/admin/pages/EditView/components/DocumentActions.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.js +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.mjs +1 -0
- package/dist/admin/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js +3 -3
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs +2 -2
- package/dist/admin/pages/EditView/components/FormInputs/DynamicZone/Field.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js +30 -2
- package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs +31 -4
- package/dist/admin/pages/EditView/components/FormInputs/Relations/RelationModal.mjs.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js +40 -8
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.js.map +1 -1
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs +41 -9
- package/dist/admin/pages/EditView/components/FormInputs/Relations/Relations.mjs.map +1 -1
- package/dist/admin/pages/EditView/utils/data.js +3 -3
- package/dist/admin/pages/EditView/utils/data.js.map +1 -1
- package/dist/admin/pages/EditView/utils/data.mjs +2 -2
- package/dist/admin/pages/EditView/utils/data.mjs.map +1 -1
- package/dist/admin/pages/ListView/ListViewPage.js +4 -3
- package/dist/admin/pages/ListView/ListViewPage.js.map +1 -1
- package/dist/admin/pages/ListView/ListViewPage.mjs +4 -3
- package/dist/admin/pages/ListView/ListViewPage.mjs.map +1 -1
- package/dist/admin/preview/pages/Preview.js +2 -1
- package/dist/admin/preview/pages/Preview.js.map +1 -1
- package/dist/admin/preview/pages/Preview.mjs +3 -2
- package/dist/admin/preview/pages/Preview.mjs.map +1 -1
- package/dist/admin/services/documents.js +3 -2
- package/dist/admin/services/documents.js.map +1 -1
- package/dist/admin/services/documents.mjs +2 -2
- package/dist/admin/services/documents.mjs.map +1 -1
- package/dist/admin/services/relations.js +3 -0
- package/dist/admin/services/relations.js.map +1 -1
- package/dist/admin/services/relations.mjs +3 -0
- package/dist/admin/services/relations.mjs.map +1 -1
- package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +2 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.d.ts +19 -2
- package/dist/admin/src/services/documents.d.ts +10 -2
- package/dist/admin/translations/en.json.js +2 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +2 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/validation.js +3 -3
- package/dist/admin/utils/validation.js.map +1 -1
- package/dist/admin/utils/validation.mjs +2 -2
- package/dist/admin/utils/validation.mjs.map +1 -1
- package/dist/server/controllers/collection-types.js +4 -4
- package/dist/server/controllers/collection-types.js.map +1 -1
- package/dist/server/controllers/collection-types.mjs +4 -4
- package/dist/server/controllers/collection-types.mjs.map +1 -1
- package/dist/server/controllers/content-types.js +5 -7
- package/dist/server/controllers/content-types.js.map +1 -1
- package/dist/server/controllers/content-types.mjs +5 -7
- package/dist/server/controllers/content-types.mjs.map +1 -1
- package/dist/server/controllers/relations.js +19 -15
- package/dist/server/controllers/relations.js.map +1 -1
- package/dist/server/controllers/relations.mjs +17 -13
- package/dist/server/controllers/relations.mjs.map +1 -1
- package/dist/server/controllers/utils/clone.js +3 -4
- package/dist/server/controllers/utils/clone.js.map +1 -1
- package/dist/server/controllers/utils/clone.mjs +4 -5
- package/dist/server/controllers/utils/clone.mjs.map +1 -1
- package/dist/server/controllers/validation/index.js +1 -1
- package/dist/server/controllers/validation/index.mjs +1 -1
- package/dist/server/history/controllers/history-version.js +3 -3
- package/dist/server/history/controllers/history-version.js.map +1 -1
- package/dist/server/history/controllers/history-version.mjs +3 -3
- package/dist/server/history/controllers/history-version.mjs.map +1 -1
- package/dist/server/history/entitlements.js +26 -0
- package/dist/server/history/entitlements.js.map +1 -0
- package/dist/server/history/entitlements.mjs +24 -0
- package/dist/server/history/entitlements.mjs.map +1 -0
- package/dist/server/history/index.js +3 -0
- package/dist/server/history/index.js.map +1 -1
- package/dist/server/history/index.mjs +3 -0
- package/dist/server/history/index.mjs.map +1 -1
- package/dist/server/history/services/history.js +4 -4
- package/dist/server/history/services/history.js.map +1 -1
- package/dist/server/history/services/history.mjs +4 -4
- package/dist/server/history/services/history.mjs.map +1 -1
- package/dist/server/history/services/lifecycles.js +8 -8
- package/dist/server/history/services/lifecycles.js.map +1 -1
- package/dist/server/history/services/lifecycles.mjs +3 -3
- package/dist/server/history/services/lifecycles.mjs.map +1 -1
- package/dist/server/history/services/utils.js +26 -18
- package/dist/server/history/services/utils.js.map +1 -1
- package/dist/server/history/services/utils.mjs +23 -17
- package/dist/server/history/services/utils.mjs.map +1 -1
- package/dist/server/mcp/permissions.js +2 -2
- package/dist/server/mcp/permissions.js.map +1 -1
- package/dist/server/mcp/permissions.mjs +2 -2
- package/dist/server/mcp/permissions.mjs.map +1 -1
- package/dist/server/mcp/register-content-manager-mcp-tools.js +7 -5
- package/dist/server/mcp/register-content-manager-mcp-tools.js.map +1 -1
- package/dist/server/mcp/register-content-manager-mcp-tools.mjs +7 -5
- package/dist/server/mcp/register-content-manager-mcp-tools.mjs.map +1 -1
- package/dist/server/preview/controllers/previewScript.js +30 -15
- package/dist/server/preview/controllers/validation/preview.js +3 -3
- package/dist/server/preview/controllers/validation/preview.js.map +1 -1
- package/dist/server/preview/controllers/validation/preview.mjs +3 -3
- package/dist/server/preview/controllers/validation/preview.mjs.map +1 -1
- package/dist/server/services/components.js +3 -3
- package/dist/server/services/components.js.map +1 -1
- package/dist/server/services/components.mjs +3 -3
- package/dist/server/services/components.mjs.map +1 -1
- package/dist/server/services/content-structure.js +2 -2
- package/dist/server/services/content-structure.js.map +1 -1
- package/dist/server/services/content-structure.mjs +2 -2
- package/dist/server/services/content-structure.mjs.map +1 -1
- package/dist/server/services/content-types.js +3 -3
- package/dist/server/services/content-types.js.map +1 -1
- package/dist/server/services/content-types.mjs +3 -3
- package/dist/server/services/content-types.mjs.map +1 -1
- package/dist/server/services/data-mapper.js +3 -3
- package/dist/server/services/data-mapper.js.map +1 -1
- package/dist/server/services/data-mapper.mjs +3 -3
- package/dist/server/services/data-mapper.mjs.map +1 -1
- package/dist/server/services/document-manager.js +6 -6
- package/dist/server/services/document-manager.js.map +1 -1
- package/dist/server/services/document-manager.mjs +6 -6
- package/dist/server/services/document-manager.mjs.map +1 -1
- package/dist/server/services/document-metadata.js +29 -31
- package/dist/server/services/document-metadata.js.map +1 -1
- package/dist/server/services/document-metadata.mjs +29 -31
- package/dist/server/services/document-metadata.mjs.map +1 -1
- package/dist/server/services/metrics.js +3 -3
- package/dist/server/services/metrics.js.map +1 -1
- package/dist/server/services/metrics.mjs +3 -3
- package/dist/server/services/metrics.mjs.map +1 -1
- package/dist/server/services/permission-checker.js +2 -2
- package/dist/server/services/permission-checker.js.map +1 -1
- package/dist/server/services/permission-checker.mjs +1 -1
- package/dist/server/services/permission-checker.mjs.map +1 -1
- package/dist/server/services/permission.js +2 -3
- package/dist/server/services/permission.js.map +1 -1
- package/dist/server/services/permission.mjs +2 -3
- package/dist/server/services/permission.mjs.map +1 -1
- package/dist/server/services/populate-builder.js +4 -4
- package/dist/server/services/populate-builder.js.map +1 -1
- package/dist/server/services/populate-builder.mjs +4 -4
- package/dist/server/services/populate-builder.mjs.map +1 -1
- package/dist/server/services/utils/configuration/attributes.js +1 -2
- package/dist/server/services/utils/configuration/attributes.js.map +1 -1
- package/dist/server/services/utils/configuration/attributes.mjs +2 -3
- package/dist/server/services/utils/configuration/attributes.mjs.map +1 -1
- package/dist/server/services/utils/configuration/settings.js +4 -7
- package/dist/server/services/utils/configuration/settings.js.map +1 -1
- package/dist/server/services/utils/configuration/settings.mjs +2 -5
- package/dist/server/services/utils/configuration/settings.mjs.map +1 -1
- package/dist/server/services/utils/draft.js +8 -7
- package/dist/server/services/utils/draft.js.map +1 -1
- package/dist/server/services/utils/draft.mjs +6 -5
- package/dist/server/services/utils/draft.mjs.map +1 -1
- package/dist/server/services/utils/populate.js +10 -11
- package/dist/server/services/utils/populate.js.map +1 -1
- package/dist/server/services/utils/populate.mjs +9 -10
- package/dist/server/services/utils/populate.mjs.map +1 -1
- package/dist/server/src/controllers/relations.d.ts.map +1 -1
- package/dist/server/src/history/controllers/history-version.d.ts.map +1 -1
- package/dist/server/src/history/entitlements.d.ts +9 -0
- package/dist/server/src/history/entitlements.d.ts.map +1 -0
- package/dist/server/src/history/index.d.ts.map +1 -1
- package/dist/server/src/history/services/utils.d.ts +8 -2
- package/dist/server/src/history/services/utils.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +1 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/mcp/permissions.d.ts +1 -1
- package/dist/server/src/mcp/permissions.d.ts.map +1 -1
- package/dist/server/src/mcp/register-content-manager-mcp-tools.d.ts +3 -1
- package/dist/server/src/mcp/register-content-manager-mcp-tools.d.ts.map +1 -1
- package/dist/server/src/mcp/types.d.ts +2 -2
- package/dist/server/src/services/content-types.d.ts.map +1 -1
- package/dist/server/src/services/data-mapper.d.ts +1 -1
- package/dist/server/src/services/data-mapper.d.ts.map +1 -1
- package/dist/server/src/services/document-manager.d.ts.map +1 -1
- package/dist/server/src/services/document-metadata.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +1 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/metrics.d.ts.map +1 -1
- package/dist/server/src/services/permission-checker.d.ts.map +1 -1
- package/dist/server/src/services/permission.d.ts.map +1 -1
- package/dist/server/src/services/populate-builder.d.ts.map +1 -1
- package/dist/server/src/services/utils/configuration/index.d.ts +10 -1
- package/dist/server/src/services/utils/configuration/index.d.ts.map +1 -1
- package/dist/server/src/services/utils/configuration/settings.d.ts +10 -1
- package/dist/server/src/services/utils/configuration/settings.d.ts.map +1 -1
- package/dist/server/src/services/utils/draft.d.ts.map +1 -1
- package/dist/server/src/services/utils/populate.d.ts.map +1 -1
- package/dist/shared/contracts/relations.d.ts +1 -0
- package/dist/shared/contracts/relations.d.ts.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sources":["../../../server/src/mcp/permissions.ts"],"sourcesContent":["import { z } from '@strapi/utils';\nimport type { Core, Modules, Struct, UID } from '@strapi/types';\n\nimport { getService } from '../utils';\nimport type { ContentManagerModelForMcp } from './types';\n\n/** Returns true if the content type identified by `uid` has the i18n `localized` plugin option enabled. */\nexport const isContentTypeLocalized = (strapi: Core.Strapi, uid: string): boolean => {\n const ct = strapi.contentTypes?.[uid as UID.ContentType];\n if (ct === undefined) return false;\n return (\n (ct as { pluginOptions?: { i18n?: { localized?: boolean } } }).pluginOptions?.i18n\n ?.localized === true\n );\n};\n\nconst localeDefaultDescription = (\n defaultLocale: string | null,\n allowedLocales: readonly string[]\n): string => {\n if (defaultLocale !== null && allowedLocales.includes(defaultLocale)) {\n return `Defaults to \"${defaultLocale}\".`;\n }\n\n return 'Defaults to the default locale.';\n};\n\n/**\n * Builds the base locale Zod schema for a derived MCP tool input.\n * When `localeCodes` is provided, constrains the field to a `z.enum` of available codes\n * with an optional default; otherwise falls back to a plain optional string.\n */\nexport const buildLocaleSchema = (\n localeCodes: [string, ...string[]] | null,\n defaultLocale: string | null\n): z.ZodTypeAny => {\n if (localeCodes !== null && localeCodes.length > 0) {\n let schema: z.ZodTypeAny = z.enum(localeCodes).optional();\n\n if (defaultLocale !== null && localeCodes.includes(defaultLocale)) {\n schema = schema.default(defaultLocale);\n }\n\n return schema.describe(\n `Locale code. Available: ${localeCodes.join(', ')}. ${localeDefaultDescription(defaultLocale, localeCodes)}`\n );\n }\n\n return z\n .string()\n .optional()\n .describe('Locale code (e.g. \"en\", \"fr\"). Defaults to the default locale.');\n};\n\n/**\n * Narrows the base locale schema to only locales the session is permitted to access\n * for the given action + uid combination.\n *\n * Returns the base schema unchanged when:\n * - localeCodes is null (i18n not installed)\n * - the content type is not localized\n * - all installed locales are permitted for this action\n *\n * Returns z.never().optional() when no locales are permitted, keeping the tool\n * registered but signalling no valid locale input.\n */\nexport const resolvePermittedLocaleSchema = (\n strapi: Core.Strapi,\n context: Modules.MCP.McpHandlerContext,\n action: string,\n uid: string,\n localeCodes: [string, ...string[]] | null,\n defaultLocale: string | null,\n baseLocaleSchema: z.ZodTypeAny\n): z.ZodTypeAny => {\n if (localeCodes === null) return baseLocaleSchema;\n\n const isLocalized = isContentTypeLocalized(strapi, uid);\n if (isLocalized === false) {\n return z.string().optional().describe('This content type is not localized. Locale is ignored.');\n }\n\n const permissionChecker = getService('permission-checker').create({\n userAbility: context.userAbility,\n model: uid,\n });\n const permitted = getPermittedLocales(permissionChecker, action, localeCodes);\n if (permitted === null) return baseLocaleSchema;\n if (permitted.length === 0) {\n return z.never().optional().describe('No locale access for this action.');\n }\n\n let schema: z.ZodTypeAny = z.enum(permitted).optional();\n\n if (defaultLocale !== null && permitted.includes(defaultLocale)) {\n schema = schema.default(defaultLocale);\n }\n\n return schema.describe(\n `Locale code. Permitted: ${permitted.join(', ')}. ${localeDefaultDescription(defaultLocale, permitted)}`\n );\n};\n\n/**\n * Recursively resolves leaf field paths for a component, matching the nested\n * path format used by CASL rules (e.g. 'SEO.title', 'SEO.og.image').\n *\n * The admin RBAC system decomposes component attrs into nested paths and removes\n * the parent key — so checking `ability.can(action, uid, 'SEO')` returns false\n * even when the user has full access to the component's sub-fields.\n */\nexport const getComponentLeafPaths = (\n strapi: Core.Strapi,\n componentUid: string,\n prefix: string,\n visited: Set<string> = new Set()\n): string[] => {\n if (visited.has(componentUid) === true) return [prefix];\n\n type ComponentEntry = { attributes: Record<string, { type: string; component?: string }> };\n const component = (strapi.components as unknown as Record<string, ComponentEntry | undefined>)[\n componentUid\n ];\n if (component === undefined) return [prefix];\n\n visited.add(componentUid);\n const paths: string[] = [];\n\n for (const [key, attr] of Object.entries(component.attributes)) {\n if (key === 'id') {\n // skip system id field — it is not a user-facing permission path\n // eslint-disable-next-line no-continue\n continue;\n }\n const fieldPath = `${prefix}.${key}`;\n\n if (attr.type === 'component' && attr.component !== undefined) {\n paths.push(...getComponentLeafPaths(strapi, attr.component, fieldPath, visited));\n } else {\n paths.push(fieldPath);\n }\n }\n\n visited.delete(componentUid);\n\n return paths.length > 0 ? paths : [prefix];\n};\n\n/**\n * Returns the subset of attribute keys the session may access for `action` on `uid`.\n * Returns `null` when all fields are permitted (caller should skip field filtering).\n * Component attributes are resolved to their nested leaf paths before checking CASL rules.\n */\nexport const getPermittedFields = (\n strapi: Core.Strapi,\n userAbility: Modules.MCP.McpHandlerContext['userAbility'],\n action: string,\n uid: string,\n attributes: Struct.SchemaAttributes\n): Set<string> | null => {\n const allKeys = Object.keys(attributes);\n const permitted = allKeys.filter((key) => {\n if (userAbility.can(action, uid, key) === true) return true;\n\n // Component attrs: CASL rules use nested paths (e.g. 'SEO.title').\n // Check if at least one sub-field path is permitted.\n const attr = attributes[key] as { type: string; component?: string };\n if (attr.type === 'component' && attr.component !== undefined) {\n const leafPaths = getComponentLeafPaths(strapi, attr.component, key);\n return leafPaths.some((path) => userAbility.can(action, uid, path) === true);\n }\n\n return false;\n });\n\n if (permitted.length === allKeys.length) {\n return null;\n }\n\n return new Set(permitted);\n};\n\n/**\n * Filters `localeCodes` to only those the session may access for `action`.\n * Returns `null` when all locales are permitted (caller should use the unfiltered base schema).\n * Returns an empty tuple-like array when no locale is permitted.\n */\nexport const getPermittedLocales = (\n permissionChecker: { cannot: (action: string, entity?: unknown) => boolean },\n action: string,\n localeCodes: [string, ...string[]]\n): [string, ...string[]] | null => {\n const permitted = localeCodes.filter(\n (code) => permissionChecker.cannot(action, { locale: code }) === false\n );\n\n if (permitted.length === localeCodes.length) {\n return null;\n }\n\n return permitted.length > 0\n ? (permitted as [string, ...string[]])\n : ([] as unknown as [string, ...string[]]);\n};\n\n// Re-export for use in handler files (avoids needing to import from permissions in each handler)\nexport type { ContentManagerModelForMcp };\n"],"names":["isContentTypeLocalized","strapi","uid","ct","contentTypes","undefined","pluginOptions","i18n","localized","localeDefaultDescription","defaultLocale","allowedLocales","includes","buildLocaleSchema","localeCodes","length","schema","z","enum","optional","default","describe","join","string","resolvePermittedLocaleSchema","context","action","baseLocaleSchema","isLocalized","permissionChecker","getService","create","userAbility","model","permitted","getPermittedLocales","never","getComponentLeafPaths","componentUid","prefix","visited","Set","has","component","components","add","paths","key","attr","Object","entries","attributes","fieldPath","type","push","delete","getPermittedFields","allKeys","keys","filter","can","leafPaths","some","path","code","cannot","locale"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"permissions.js","sources":["../../../server/src/mcp/permissions.ts"],"sourcesContent":["import { z } from '@strapi/utils';\nimport type { Core, Modules, Struct, UID } from '@strapi/types';\n\nimport { getService } from '../utils';\nimport type { ContentManagerModelForMcp } from './types';\n\n/** Returns true if the registered localization provider reports the content type identified by `uid` as localized. */\nexport const isContentTypeLocalized = (strapi: Core.Strapi, uid: string): boolean => {\n const ct = strapi.contentTypes?.[uid as UID.ContentType];\n if (ct === undefined) return false;\n return strapi.localization.isLocalizedContentType(ct);\n};\n\nconst localeDefaultDescription = (\n defaultLocale: string | null,\n allowedLocales: readonly string[]\n): string => {\n if (defaultLocale !== null && allowedLocales.includes(defaultLocale)) {\n return `Defaults to \"${defaultLocale}\".`;\n }\n\n return 'Defaults to the default locale.';\n};\n\n/**\n * Builds the base locale Zod schema for a derived MCP tool input.\n * When `localeCodes` is provided, constrains the field to a `z.enum` of available codes\n * with an optional default; otherwise falls back to a plain optional string.\n */\nexport const buildLocaleSchema = (\n localeCodes: [string, ...string[]] | null,\n defaultLocale: string | null\n): z.ZodTypeAny => {\n if (localeCodes !== null && localeCodes.length > 0) {\n let schema: z.ZodTypeAny = z.enum(localeCodes).optional();\n\n if (defaultLocale !== null && localeCodes.includes(defaultLocale)) {\n schema = schema.default(defaultLocale);\n }\n\n return schema.describe(\n `Locale code. Available: ${localeCodes.join(', ')}. ${localeDefaultDescription(defaultLocale, localeCodes)}`\n );\n }\n\n return z\n .string()\n .optional()\n .describe('Locale code (e.g. \"en\", \"fr\"). Defaults to the default locale.');\n};\n\n/**\n * Narrows the base locale schema to only locales the session is permitted to access\n * for the given action + uid combination.\n *\n * Returns the base schema unchanged when:\n * - localeCodes is null (i18n not installed)\n * - the content type is not localized\n * - all installed locales are permitted for this action\n *\n * Returns z.never().optional() when no locales are permitted, keeping the tool\n * registered but signalling no valid locale input.\n */\nexport const resolvePermittedLocaleSchema = (\n strapi: Core.Strapi,\n context: Modules.MCP.McpHandlerContext,\n action: string,\n uid: string,\n localeCodes: [string, ...string[]] | null,\n defaultLocale: string | null,\n baseLocaleSchema: z.ZodTypeAny\n): z.ZodTypeAny => {\n if (localeCodes === null) return baseLocaleSchema;\n\n const isLocalized = isContentTypeLocalized(strapi, uid);\n if (isLocalized === false) {\n return z.string().optional().describe('This content type is not localized. Locale is ignored.');\n }\n\n const permissionChecker = getService('permission-checker').create({\n userAbility: context.userAbility,\n model: uid,\n });\n const permitted = getPermittedLocales(permissionChecker, action, localeCodes);\n if (permitted === null) return baseLocaleSchema;\n if (permitted.length === 0) {\n return z.never().optional().describe('No locale access for this action.');\n }\n\n let schema: z.ZodTypeAny = z.enum(permitted).optional();\n\n if (defaultLocale !== null && permitted.includes(defaultLocale)) {\n schema = schema.default(defaultLocale);\n }\n\n return schema.describe(\n `Locale code. Permitted: ${permitted.join(', ')}. ${localeDefaultDescription(defaultLocale, permitted)}`\n );\n};\n\n/**\n * Recursively resolves leaf field paths for a component, matching the nested\n * path format used by CASL rules (e.g. 'SEO.title', 'SEO.og.image').\n *\n * The admin RBAC system decomposes component attrs into nested paths and removes\n * the parent key — so checking `ability.can(action, uid, 'SEO')` returns false\n * even when the user has full access to the component's sub-fields.\n */\nexport const getComponentLeafPaths = (\n strapi: Core.Strapi,\n componentUid: string,\n prefix: string,\n visited: Set<string> = new Set()\n): string[] => {\n if (visited.has(componentUid) === true) return [prefix];\n\n type ComponentEntry = { attributes: Record<string, { type: string; component?: string }> };\n const component = (strapi.components as unknown as Record<string, ComponentEntry | undefined>)[\n componentUid\n ];\n if (component === undefined) return [prefix];\n\n visited.add(componentUid);\n const paths: string[] = [];\n\n for (const [key, attr] of Object.entries(component.attributes)) {\n if (key === 'id') {\n // skip system id field — it is not a user-facing permission path\n // eslint-disable-next-line no-continue\n continue;\n }\n const fieldPath = `${prefix}.${key}`;\n\n if (attr.type === 'component' && attr.component !== undefined) {\n paths.push(...getComponentLeafPaths(strapi, attr.component, fieldPath, visited));\n } else {\n paths.push(fieldPath);\n }\n }\n\n visited.delete(componentUid);\n\n return paths.length > 0 ? paths : [prefix];\n};\n\n/**\n * Returns the subset of attribute keys the session may access for `action` on `uid`.\n * Returns `null` when all fields are permitted (caller should skip field filtering).\n * Component attributes are resolved to their nested leaf paths before checking CASL rules.\n */\nexport const getPermittedFields = (\n strapi: Core.Strapi,\n userAbility: Modules.MCP.McpHandlerContext['userAbility'],\n action: string,\n uid: string,\n attributes: Struct.SchemaAttributes\n): Set<string> | null => {\n const allKeys = Object.keys(attributes);\n const permitted = allKeys.filter((key) => {\n if (userAbility.can(action, uid, key) === true) return true;\n\n // Component attrs: CASL rules use nested paths (e.g. 'SEO.title').\n // Check if at least one sub-field path is permitted.\n const attr = attributes[key] as { type: string; component?: string };\n if (attr.type === 'component' && attr.component !== undefined) {\n const leafPaths = getComponentLeafPaths(strapi, attr.component, key);\n return leafPaths.some((path) => userAbility.can(action, uid, path) === true);\n }\n\n return false;\n });\n\n if (permitted.length === allKeys.length) {\n return null;\n }\n\n return new Set(permitted);\n};\n\n/**\n * Filters `localeCodes` to only those the session may access for `action`.\n * Returns `null` when all locales are permitted (caller should use the unfiltered base schema).\n * Returns an empty tuple-like array when no locale is permitted.\n */\nexport const getPermittedLocales = (\n permissionChecker: { cannot: (action: string, entity?: unknown) => boolean },\n action: string,\n localeCodes: [string, ...string[]]\n): [string, ...string[]] | null => {\n const permitted = localeCodes.filter(\n (code) => permissionChecker.cannot(action, { locale: code }) === false\n );\n\n if (permitted.length === localeCodes.length) {\n return null;\n }\n\n return permitted.length > 0\n ? (permitted as [string, ...string[]])\n : ([] as unknown as [string, ...string[]]);\n};\n\n// Re-export for use in handler files (avoids needing to import from permissions in each handler)\nexport type { ContentManagerModelForMcp };\n"],"names":["isContentTypeLocalized","strapi","uid","ct","contentTypes","undefined","localization","isLocalizedContentType","localeDefaultDescription","defaultLocale","allowedLocales","includes","buildLocaleSchema","localeCodes","length","schema","z","enum","optional","default","describe","join","string","resolvePermittedLocaleSchema","context","action","baseLocaleSchema","isLocalized","permissionChecker","getService","create","userAbility","model","permitted","getPermittedLocales","never","getComponentLeafPaths","componentUid","prefix","visited","Set","has","component","components","add","paths","key","attr","Object","entries","attributes","fieldPath","type","push","delete","getPermittedFields","allKeys","keys","filter","can","leafPaths","some","path","code","cannot","locale"],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { z } from '@strapi/utils';
|
|
2
2
|
import { getService } from '../utils/index.mjs';
|
|
3
3
|
|
|
4
|
-
/** Returns true if the content type identified by `uid`
|
|
4
|
+
/** Returns true if the registered localization provider reports the content type identified by `uid` as localized. */ const isContentTypeLocalized = (strapi, uid)=>{
|
|
5
5
|
const ct = strapi.contentTypes?.[uid];
|
|
6
6
|
if (ct === undefined) return false;
|
|
7
|
-
return ct
|
|
7
|
+
return strapi.localization.isLocalizedContentType(ct);
|
|
8
8
|
};
|
|
9
9
|
const localeDefaultDescription = (defaultLocale, allowedLocales)=>{
|
|
10
10
|
if (defaultLocale !== null && allowedLocales.includes(defaultLocale)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.mjs","sources":["../../../server/src/mcp/permissions.ts"],"sourcesContent":["import { z } from '@strapi/utils';\nimport type { Core, Modules, Struct, UID } from '@strapi/types';\n\nimport { getService } from '../utils';\nimport type { ContentManagerModelForMcp } from './types';\n\n/** Returns true if the content type identified by `uid` has the i18n `localized` plugin option enabled. */\nexport const isContentTypeLocalized = (strapi: Core.Strapi, uid: string): boolean => {\n const ct = strapi.contentTypes?.[uid as UID.ContentType];\n if (ct === undefined) return false;\n return (\n (ct as { pluginOptions?: { i18n?: { localized?: boolean } } }).pluginOptions?.i18n\n ?.localized === true\n );\n};\n\nconst localeDefaultDescription = (\n defaultLocale: string | null,\n allowedLocales: readonly string[]\n): string => {\n if (defaultLocale !== null && allowedLocales.includes(defaultLocale)) {\n return `Defaults to \"${defaultLocale}\".`;\n }\n\n return 'Defaults to the default locale.';\n};\n\n/**\n * Builds the base locale Zod schema for a derived MCP tool input.\n * When `localeCodes` is provided, constrains the field to a `z.enum` of available codes\n * with an optional default; otherwise falls back to a plain optional string.\n */\nexport const buildLocaleSchema = (\n localeCodes: [string, ...string[]] | null,\n defaultLocale: string | null\n): z.ZodTypeAny => {\n if (localeCodes !== null && localeCodes.length > 0) {\n let schema: z.ZodTypeAny = z.enum(localeCodes).optional();\n\n if (defaultLocale !== null && localeCodes.includes(defaultLocale)) {\n schema = schema.default(defaultLocale);\n }\n\n return schema.describe(\n `Locale code. Available: ${localeCodes.join(', ')}. ${localeDefaultDescription(defaultLocale, localeCodes)}`\n );\n }\n\n return z\n .string()\n .optional()\n .describe('Locale code (e.g. \"en\", \"fr\"). Defaults to the default locale.');\n};\n\n/**\n * Narrows the base locale schema to only locales the session is permitted to access\n * for the given action + uid combination.\n *\n * Returns the base schema unchanged when:\n * - localeCodes is null (i18n not installed)\n * - the content type is not localized\n * - all installed locales are permitted for this action\n *\n * Returns z.never().optional() when no locales are permitted, keeping the tool\n * registered but signalling no valid locale input.\n */\nexport const resolvePermittedLocaleSchema = (\n strapi: Core.Strapi,\n context: Modules.MCP.McpHandlerContext,\n action: string,\n uid: string,\n localeCodes: [string, ...string[]] | null,\n defaultLocale: string | null,\n baseLocaleSchema: z.ZodTypeAny\n): z.ZodTypeAny => {\n if (localeCodes === null) return baseLocaleSchema;\n\n const isLocalized = isContentTypeLocalized(strapi, uid);\n if (isLocalized === false) {\n return z.string().optional().describe('This content type is not localized. Locale is ignored.');\n }\n\n const permissionChecker = getService('permission-checker').create({\n userAbility: context.userAbility,\n model: uid,\n });\n const permitted = getPermittedLocales(permissionChecker, action, localeCodes);\n if (permitted === null) return baseLocaleSchema;\n if (permitted.length === 0) {\n return z.never().optional().describe('No locale access for this action.');\n }\n\n let schema: z.ZodTypeAny = z.enum(permitted).optional();\n\n if (defaultLocale !== null && permitted.includes(defaultLocale)) {\n schema = schema.default(defaultLocale);\n }\n\n return schema.describe(\n `Locale code. Permitted: ${permitted.join(', ')}. ${localeDefaultDescription(defaultLocale, permitted)}`\n );\n};\n\n/**\n * Recursively resolves leaf field paths for a component, matching the nested\n * path format used by CASL rules (e.g. 'SEO.title', 'SEO.og.image').\n *\n * The admin RBAC system decomposes component attrs into nested paths and removes\n * the parent key — so checking `ability.can(action, uid, 'SEO')` returns false\n * even when the user has full access to the component's sub-fields.\n */\nexport const getComponentLeafPaths = (\n strapi: Core.Strapi,\n componentUid: string,\n prefix: string,\n visited: Set<string> = new Set()\n): string[] => {\n if (visited.has(componentUid) === true) return [prefix];\n\n type ComponentEntry = { attributes: Record<string, { type: string; component?: string }> };\n const component = (strapi.components as unknown as Record<string, ComponentEntry | undefined>)[\n componentUid\n ];\n if (component === undefined) return [prefix];\n\n visited.add(componentUid);\n const paths: string[] = [];\n\n for (const [key, attr] of Object.entries(component.attributes)) {\n if (key === 'id') {\n // skip system id field — it is not a user-facing permission path\n // eslint-disable-next-line no-continue\n continue;\n }\n const fieldPath = `${prefix}.${key}`;\n\n if (attr.type === 'component' && attr.component !== undefined) {\n paths.push(...getComponentLeafPaths(strapi, attr.component, fieldPath, visited));\n } else {\n paths.push(fieldPath);\n }\n }\n\n visited.delete(componentUid);\n\n return paths.length > 0 ? paths : [prefix];\n};\n\n/**\n * Returns the subset of attribute keys the session may access for `action` on `uid`.\n * Returns `null` when all fields are permitted (caller should skip field filtering).\n * Component attributes are resolved to their nested leaf paths before checking CASL rules.\n */\nexport const getPermittedFields = (\n strapi: Core.Strapi,\n userAbility: Modules.MCP.McpHandlerContext['userAbility'],\n action: string,\n uid: string,\n attributes: Struct.SchemaAttributes\n): Set<string> | null => {\n const allKeys = Object.keys(attributes);\n const permitted = allKeys.filter((key) => {\n if (userAbility.can(action, uid, key) === true) return true;\n\n // Component attrs: CASL rules use nested paths (e.g. 'SEO.title').\n // Check if at least one sub-field path is permitted.\n const attr = attributes[key] as { type: string; component?: string };\n if (attr.type === 'component' && attr.component !== undefined) {\n const leafPaths = getComponentLeafPaths(strapi, attr.component, key);\n return leafPaths.some((path) => userAbility.can(action, uid, path) === true);\n }\n\n return false;\n });\n\n if (permitted.length === allKeys.length) {\n return null;\n }\n\n return new Set(permitted);\n};\n\n/**\n * Filters `localeCodes` to only those the session may access for `action`.\n * Returns `null` when all locales are permitted (caller should use the unfiltered base schema).\n * Returns an empty tuple-like array when no locale is permitted.\n */\nexport const getPermittedLocales = (\n permissionChecker: { cannot: (action: string, entity?: unknown) => boolean },\n action: string,\n localeCodes: [string, ...string[]]\n): [string, ...string[]] | null => {\n const permitted = localeCodes.filter(\n (code) => permissionChecker.cannot(action, { locale: code }) === false\n );\n\n if (permitted.length === localeCodes.length) {\n return null;\n }\n\n return permitted.length > 0\n ? (permitted as [string, ...string[]])\n : ([] as unknown as [string, ...string[]]);\n};\n\n// Re-export for use in handler files (avoids needing to import from permissions in each handler)\nexport type { ContentManagerModelForMcp };\n"],"names":["isContentTypeLocalized","strapi","uid","ct","contentTypes","undefined","pluginOptions","i18n","localized","localeDefaultDescription","defaultLocale","allowedLocales","includes","buildLocaleSchema","localeCodes","length","schema","z","enum","optional","default","describe","join","string","resolvePermittedLocaleSchema","context","action","baseLocaleSchema","isLocalized","permissionChecker","getService","create","userAbility","model","permitted","getPermittedLocales","never","getComponentLeafPaths","componentUid","prefix","visited","Set","has","component","components","add","paths","key","attr","Object","entries","attributes","fieldPath","type","push","delete","getPermittedFields","allKeys","keys","filter","can","leafPaths","some","path","code","cannot","locale"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"permissions.mjs","sources":["../../../server/src/mcp/permissions.ts"],"sourcesContent":["import { z } from '@strapi/utils';\nimport type { Core, Modules, Struct, UID } from '@strapi/types';\n\nimport { getService } from '../utils';\nimport type { ContentManagerModelForMcp } from './types';\n\n/** Returns true if the registered localization provider reports the content type identified by `uid` as localized. */\nexport const isContentTypeLocalized = (strapi: Core.Strapi, uid: string): boolean => {\n const ct = strapi.contentTypes?.[uid as UID.ContentType];\n if (ct === undefined) return false;\n return strapi.localization.isLocalizedContentType(ct);\n};\n\nconst localeDefaultDescription = (\n defaultLocale: string | null,\n allowedLocales: readonly string[]\n): string => {\n if (defaultLocale !== null && allowedLocales.includes(defaultLocale)) {\n return `Defaults to \"${defaultLocale}\".`;\n }\n\n return 'Defaults to the default locale.';\n};\n\n/**\n * Builds the base locale Zod schema for a derived MCP tool input.\n * When `localeCodes` is provided, constrains the field to a `z.enum` of available codes\n * with an optional default; otherwise falls back to a plain optional string.\n */\nexport const buildLocaleSchema = (\n localeCodes: [string, ...string[]] | null,\n defaultLocale: string | null\n): z.ZodTypeAny => {\n if (localeCodes !== null && localeCodes.length > 0) {\n let schema: z.ZodTypeAny = z.enum(localeCodes).optional();\n\n if (defaultLocale !== null && localeCodes.includes(defaultLocale)) {\n schema = schema.default(defaultLocale);\n }\n\n return schema.describe(\n `Locale code. Available: ${localeCodes.join(', ')}. ${localeDefaultDescription(defaultLocale, localeCodes)}`\n );\n }\n\n return z\n .string()\n .optional()\n .describe('Locale code (e.g. \"en\", \"fr\"). Defaults to the default locale.');\n};\n\n/**\n * Narrows the base locale schema to only locales the session is permitted to access\n * for the given action + uid combination.\n *\n * Returns the base schema unchanged when:\n * - localeCodes is null (i18n not installed)\n * - the content type is not localized\n * - all installed locales are permitted for this action\n *\n * Returns z.never().optional() when no locales are permitted, keeping the tool\n * registered but signalling no valid locale input.\n */\nexport const resolvePermittedLocaleSchema = (\n strapi: Core.Strapi,\n context: Modules.MCP.McpHandlerContext,\n action: string,\n uid: string,\n localeCodes: [string, ...string[]] | null,\n defaultLocale: string | null,\n baseLocaleSchema: z.ZodTypeAny\n): z.ZodTypeAny => {\n if (localeCodes === null) return baseLocaleSchema;\n\n const isLocalized = isContentTypeLocalized(strapi, uid);\n if (isLocalized === false) {\n return z.string().optional().describe('This content type is not localized. Locale is ignored.');\n }\n\n const permissionChecker = getService('permission-checker').create({\n userAbility: context.userAbility,\n model: uid,\n });\n const permitted = getPermittedLocales(permissionChecker, action, localeCodes);\n if (permitted === null) return baseLocaleSchema;\n if (permitted.length === 0) {\n return z.never().optional().describe('No locale access for this action.');\n }\n\n let schema: z.ZodTypeAny = z.enum(permitted).optional();\n\n if (defaultLocale !== null && permitted.includes(defaultLocale)) {\n schema = schema.default(defaultLocale);\n }\n\n return schema.describe(\n `Locale code. Permitted: ${permitted.join(', ')}. ${localeDefaultDescription(defaultLocale, permitted)}`\n );\n};\n\n/**\n * Recursively resolves leaf field paths for a component, matching the nested\n * path format used by CASL rules (e.g. 'SEO.title', 'SEO.og.image').\n *\n * The admin RBAC system decomposes component attrs into nested paths and removes\n * the parent key — so checking `ability.can(action, uid, 'SEO')` returns false\n * even when the user has full access to the component's sub-fields.\n */\nexport const getComponentLeafPaths = (\n strapi: Core.Strapi,\n componentUid: string,\n prefix: string,\n visited: Set<string> = new Set()\n): string[] => {\n if (visited.has(componentUid) === true) return [prefix];\n\n type ComponentEntry = { attributes: Record<string, { type: string; component?: string }> };\n const component = (strapi.components as unknown as Record<string, ComponentEntry | undefined>)[\n componentUid\n ];\n if (component === undefined) return [prefix];\n\n visited.add(componentUid);\n const paths: string[] = [];\n\n for (const [key, attr] of Object.entries(component.attributes)) {\n if (key === 'id') {\n // skip system id field — it is not a user-facing permission path\n // eslint-disable-next-line no-continue\n continue;\n }\n const fieldPath = `${prefix}.${key}`;\n\n if (attr.type === 'component' && attr.component !== undefined) {\n paths.push(...getComponentLeafPaths(strapi, attr.component, fieldPath, visited));\n } else {\n paths.push(fieldPath);\n }\n }\n\n visited.delete(componentUid);\n\n return paths.length > 0 ? paths : [prefix];\n};\n\n/**\n * Returns the subset of attribute keys the session may access for `action` on `uid`.\n * Returns `null` when all fields are permitted (caller should skip field filtering).\n * Component attributes are resolved to their nested leaf paths before checking CASL rules.\n */\nexport const getPermittedFields = (\n strapi: Core.Strapi,\n userAbility: Modules.MCP.McpHandlerContext['userAbility'],\n action: string,\n uid: string,\n attributes: Struct.SchemaAttributes\n): Set<string> | null => {\n const allKeys = Object.keys(attributes);\n const permitted = allKeys.filter((key) => {\n if (userAbility.can(action, uid, key) === true) return true;\n\n // Component attrs: CASL rules use nested paths (e.g. 'SEO.title').\n // Check if at least one sub-field path is permitted.\n const attr = attributes[key] as { type: string; component?: string };\n if (attr.type === 'component' && attr.component !== undefined) {\n const leafPaths = getComponentLeafPaths(strapi, attr.component, key);\n return leafPaths.some((path) => userAbility.can(action, uid, path) === true);\n }\n\n return false;\n });\n\n if (permitted.length === allKeys.length) {\n return null;\n }\n\n return new Set(permitted);\n};\n\n/**\n * Filters `localeCodes` to only those the session may access for `action`.\n * Returns `null` when all locales are permitted (caller should use the unfiltered base schema).\n * Returns an empty tuple-like array when no locale is permitted.\n */\nexport const getPermittedLocales = (\n permissionChecker: { cannot: (action: string, entity?: unknown) => boolean },\n action: string,\n localeCodes: [string, ...string[]]\n): [string, ...string[]] | null => {\n const permitted = localeCodes.filter(\n (code) => permissionChecker.cannot(action, { locale: code }) === false\n );\n\n if (permitted.length === localeCodes.length) {\n return null;\n }\n\n return permitted.length > 0\n ? (permitted as [string, ...string[]])\n : ([] as unknown as [string, ...string[]]);\n};\n\n// Re-export for use in handler files (avoids needing to import from permissions in each handler)\nexport type { ContentManagerModelForMcp };\n"],"names":["isContentTypeLocalized","strapi","uid","ct","contentTypes","undefined","localization","isLocalizedContentType","localeDefaultDescription","defaultLocale","allowedLocales","includes","buildLocaleSchema","localeCodes","length","schema","z","enum","optional","default","describe","join","string","resolvePermittedLocaleSchema","context","action","baseLocaleSchema","isLocalized","permissionChecker","getService","create","userAbility","model","permitted","getPermittedLocales","never","getComponentLeafPaths","componentUid","prefix","visited","Set","has","component","components","add","paths","key","attr","Object","entries","attributes","fieldPath","type","push","delete","getPermittedFields","allKeys","keys","filter","can","leafPaths","some","path","code","cannot","locale"],"mappings":"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"}
|
|
@@ -5,19 +5,21 @@ var index = require('../utils/index.js');
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Registers derived content-type MCP tools via strapi.ai.mcp.registerTool().
|
|
8
|
-
* Must be called from the plugin
|
|
8
|
+
* Must be called from the plugin bootstrap phase: after every plugin's register (so the
|
|
9
|
+
* localization provider is installed and locale rows are readable) and before the MCP HTTP
|
|
10
|
+
* server starts.
|
|
9
11
|
*/ const registerContentManagerMcpTools = async ({ strapi })=>{
|
|
10
12
|
// Performance only: registerTool() is safe when MCP is disabled (definitions are stored but
|
|
11
13
|
// never exposed). Skip the expensive derivation below when the MCP server will not start.
|
|
12
14
|
if (strapi.ai.mcp.isEnabled() !== true) {
|
|
13
15
|
return;
|
|
14
16
|
}
|
|
15
|
-
const i18nPlugin = strapi.plugin('i18n');
|
|
16
17
|
let localeCodes = null;
|
|
17
18
|
let defaultLocale = null;
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
if (strapi.localization.isEnabled() === true) {
|
|
20
|
+
// The tuple type is a lie for zero locales; consumers guard on length > 0 before z.enum.
|
|
21
|
+
localeCodes = (await strapi.localization.getLocales()).map((locale)=>locale.code);
|
|
22
|
+
defaultLocale = await strapi.localization.getDefaultLocale();
|
|
21
23
|
}
|
|
22
24
|
const models = index.getService('content-types').findDisplayedContentTypes();
|
|
23
25
|
const tools = deriveContentTypeMcpTools.deriveDisplayedContentTypeMcpToolDefinitions(strapi, models, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-content-manager-mcp-tools.js","sources":["../../../server/src/mcp/register-content-manager-mcp-tools.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { deriveDisplayedContentTypeMcpToolDefinitions } from './derive-content-type-mcp-tools';\nimport { getService } from '../utils';\n\n/**\n * Registers derived content-type MCP tools via strapi.ai.mcp.registerTool().\n * Must be called from the plugin
|
|
1
|
+
{"version":3,"file":"register-content-manager-mcp-tools.js","sources":["../../../server/src/mcp/register-content-manager-mcp-tools.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { deriveDisplayedContentTypeMcpToolDefinitions } from './derive-content-type-mcp-tools';\nimport { getService } from '../utils';\n\n/**\n * Registers derived content-type MCP tools via strapi.ai.mcp.registerTool().\n * Must be called from the plugin bootstrap phase: after every plugin's register (so the\n * localization provider is installed and locale rows are readable) and before the MCP HTTP\n * server starts.\n */\nexport const registerContentManagerMcpTools = async ({\n strapi,\n}: {\n strapi: Core.Strapi;\n}): Promise<void> => {\n // Performance only: registerTool() is safe when MCP is disabled (definitions are stored but\n // never exposed). Skip the expensive derivation below when the MCP server will not start.\n if (strapi.ai.mcp.isEnabled() !== true) {\n return;\n }\n\n let localeCodes: [string, ...string[]] | null = null;\n let defaultLocale: string | null = null;\n if (strapi.localization.isEnabled() === true) {\n // The tuple type is a lie for zero locales; consumers guard on length > 0 before z.enum.\n localeCodes = (await strapi.localization.getLocales()).map((locale) => locale.code) as [\n string,\n ...string[],\n ];\n defaultLocale = await strapi.localization.getDefaultLocale();\n }\n\n const models = getService('content-types').findDisplayedContentTypes();\n const tools = deriveDisplayedContentTypeMcpToolDefinitions(strapi, models, {\n localeCodes,\n defaultLocale,\n });\n\n for (const tool of tools) {\n strapi.ai.mcp.registerTool(tool);\n }\n};\n"],"names":["registerContentManagerMcpTools","strapi","ai","mcp","isEnabled","localeCodes","defaultLocale","localization","getLocales","map","locale","code","getDefaultLocale","models","getService","findDisplayedContentTypes","tools","deriveDisplayedContentTypeMcpToolDefinitions","tool","registerTool"],"mappings":";;;;;AAKA;;;;;AAKC,IACM,MAAMA,8BAAAA,GAAiC,OAAO,EACnDC,MAAM,EAGP,GAAA;;;AAGC,IAAA,IAAIA,OAAOC,EAAE,CAACC,GAAG,CAACC,SAAS,OAAO,IAAA,EAAM;AACtC,QAAA;AACF,IAAA;AAEA,IAAA,IAAIC,WAAAA,GAA4C,IAAA;AAChD,IAAA,IAAIC,aAAAA,GAA+B,IAAA;AACnC,IAAA,IAAIL,MAAAA,CAAOM,YAAY,CAACH,SAAS,OAAO,IAAA,EAAM;;AAE5CC,QAAAA,WAAAA,GAAc,CAAC,MAAMJ,MAAAA,CAAOM,YAAY,CAACC,UAAU,EAAC,EAAGC,GAAG,CAAC,CAACC,MAAAA,GAAWA,OAAOC,IAAI,CAAA;AAIlFL,QAAAA,aAAAA,GAAgB,MAAML,MAAAA,CAAOM,YAAY,CAACK,gBAAgB,EAAA;AAC5D,IAAA;IAEA,MAAMC,MAAAA,GAASC,gBAAAA,CAAW,eAAA,CAAA,CAAiBC,yBAAyB,EAAA;IACpE,MAAMC,KAAAA,GAAQC,sEAAAA,CAA6ChB,MAAAA,EAAQY,MAAAA,EAAQ;AACzER,QAAAA,WAAAA;AACAC,QAAAA;AACF,KAAA,CAAA;IAEA,KAAK,MAAMY,QAAQF,KAAAA,CAAO;AACxBf,QAAAA,MAAAA,CAAOC,EAAE,CAACC,GAAG,CAACgB,YAAY,CAACD,IAAAA,CAAAA;AAC7B,IAAA;AACF;;;;"}
|
|
@@ -3,19 +3,21 @@ import { getService } from '../utils/index.mjs';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Registers derived content-type MCP tools via strapi.ai.mcp.registerTool().
|
|
6
|
-
* Must be called from the plugin
|
|
6
|
+
* Must be called from the plugin bootstrap phase: after every plugin's register (so the
|
|
7
|
+
* localization provider is installed and locale rows are readable) and before the MCP HTTP
|
|
8
|
+
* server starts.
|
|
7
9
|
*/ const registerContentManagerMcpTools = async ({ strapi })=>{
|
|
8
10
|
// Performance only: registerTool() is safe when MCP is disabled (definitions are stored but
|
|
9
11
|
// never exposed). Skip the expensive derivation below when the MCP server will not start.
|
|
10
12
|
if (strapi.ai.mcp.isEnabled() !== true) {
|
|
11
13
|
return;
|
|
12
14
|
}
|
|
13
|
-
const i18nPlugin = strapi.plugin('i18n');
|
|
14
15
|
let localeCodes = null;
|
|
15
16
|
let defaultLocale = null;
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
if (strapi.localization.isEnabled() === true) {
|
|
18
|
+
// The tuple type is a lie for zero locales; consumers guard on length > 0 before z.enum.
|
|
19
|
+
localeCodes = (await strapi.localization.getLocales()).map((locale)=>locale.code);
|
|
20
|
+
defaultLocale = await strapi.localization.getDefaultLocale();
|
|
19
21
|
}
|
|
20
22
|
const models = getService('content-types').findDisplayedContentTypes();
|
|
21
23
|
const tools = deriveDisplayedContentTypeMcpToolDefinitions(strapi, models, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-content-manager-mcp-tools.mjs","sources":["../../../server/src/mcp/register-content-manager-mcp-tools.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { deriveDisplayedContentTypeMcpToolDefinitions } from './derive-content-type-mcp-tools';\nimport { getService } from '../utils';\n\n/**\n * Registers derived content-type MCP tools via strapi.ai.mcp.registerTool().\n * Must be called from the plugin
|
|
1
|
+
{"version":3,"file":"register-content-manager-mcp-tools.mjs","sources":["../../../server/src/mcp/register-content-manager-mcp-tools.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { deriveDisplayedContentTypeMcpToolDefinitions } from './derive-content-type-mcp-tools';\nimport { getService } from '../utils';\n\n/**\n * Registers derived content-type MCP tools via strapi.ai.mcp.registerTool().\n * Must be called from the plugin bootstrap phase: after every plugin's register (so the\n * localization provider is installed and locale rows are readable) and before the MCP HTTP\n * server starts.\n */\nexport const registerContentManagerMcpTools = async ({\n strapi,\n}: {\n strapi: Core.Strapi;\n}): Promise<void> => {\n // Performance only: registerTool() is safe when MCP is disabled (definitions are stored but\n // never exposed). Skip the expensive derivation below when the MCP server will not start.\n if (strapi.ai.mcp.isEnabled() !== true) {\n return;\n }\n\n let localeCodes: [string, ...string[]] | null = null;\n let defaultLocale: string | null = null;\n if (strapi.localization.isEnabled() === true) {\n // The tuple type is a lie for zero locales; consumers guard on length > 0 before z.enum.\n localeCodes = (await strapi.localization.getLocales()).map((locale) => locale.code) as [\n string,\n ...string[],\n ];\n defaultLocale = await strapi.localization.getDefaultLocale();\n }\n\n const models = getService('content-types').findDisplayedContentTypes();\n const tools = deriveDisplayedContentTypeMcpToolDefinitions(strapi, models, {\n localeCodes,\n defaultLocale,\n });\n\n for (const tool of tools) {\n strapi.ai.mcp.registerTool(tool);\n }\n};\n"],"names":["registerContentManagerMcpTools","strapi","ai","mcp","isEnabled","localeCodes","defaultLocale","localization","getLocales","map","locale","code","getDefaultLocale","models","getService","findDisplayedContentTypes","tools","deriveDisplayedContentTypeMcpToolDefinitions","tool","registerTool"],"mappings":";;;AAKA;;;;;AAKC,IACM,MAAMA,8BAAAA,GAAiC,OAAO,EACnDC,MAAM,EAGP,GAAA;;;AAGC,IAAA,IAAIA,OAAOC,EAAE,CAACC,GAAG,CAACC,SAAS,OAAO,IAAA,EAAM;AACtC,QAAA;AACF,IAAA;AAEA,IAAA,IAAIC,WAAAA,GAA4C,IAAA;AAChD,IAAA,IAAIC,aAAAA,GAA+B,IAAA;AACnC,IAAA,IAAIL,MAAAA,CAAOM,YAAY,CAACH,SAAS,OAAO,IAAA,EAAM;;AAE5CC,QAAAA,WAAAA,GAAc,CAAC,MAAMJ,MAAAA,CAAOM,YAAY,CAACC,UAAU,EAAC,EAAGC,GAAG,CAAC,CAACC,MAAAA,GAAWA,OAAOC,IAAI,CAAA;AAIlFL,QAAAA,aAAAA,GAAgB,MAAML,MAAAA,CAAOM,YAAY,CAACK,gBAAgB,EAAA;AAC5D,IAAA;IAEA,MAAMC,MAAAA,GAASC,UAAAA,CAAW,eAAA,CAAA,CAAiBC,yBAAyB,EAAA;IACpE,MAAMC,KAAAA,GAAQC,4CAAAA,CAA6ChB,MAAAA,EAAQY,MAAAA,EAAQ;AACzER,QAAAA,WAAAA;AACAC,QAAAA;AACF,KAAA,CAAA;IAEA,KAAK,MAAMY,QAAQF,KAAAA,CAAO;AACxBf,QAAAA,MAAAA,CAAOC,EAAE,CAACC,GAAG,CAACgB,YAAY,CAACD,IAAAA,CAAAA;AAC7B,IAAA;AACF;;;;"}
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
* @property {string} [STRAPI_HIGHLIGHT_ACTIVE_COLOR] Optional override for the highlight active color, injected by the admin.
|
|
40
40
|
* @property {boolean} [STRAPI_DISABLE_STEGA_DECODING] Optional flag to disable steganography decoding, injected by the admin.
|
|
41
41
|
* @property {() => void} [__strapi_previewCleanup] Optional cleanup function, injected by the admin.
|
|
42
|
+
* @property {number} [__strapi_previewRunId] Id of the latest run of this script in the page.
|
|
42
43
|
*/
|
|
43
44
|
|
|
44
45
|
/**
|
|
@@ -50,6 +51,11 @@ function previewScript(config) {
|
|
|
50
51
|
/** @type {typeof window & WindowExtensions} */
|
|
51
52
|
const win = window;
|
|
52
53
|
|
|
54
|
+
// The script can be injected more than once in the same page (e.g. React StrictMode or a
|
|
55
|
+
// remount posting previewReady twice). Only the latest run may set up; see Orchestration.
|
|
56
|
+
const runId = (win.__strapi_previewRunId ?? 0) + 1;
|
|
57
|
+
win.__strapi_previewRunId = runId;
|
|
58
|
+
|
|
53
59
|
/* -----------------------------------------------------------------------------------------------
|
|
54
60
|
* Params
|
|
55
61
|
* ---------------------------------------------------------------------------------------------*/
|
|
@@ -514,12 +520,6 @@ function previewScript(config) {
|
|
|
514
520
|
};
|
|
515
521
|
|
|
516
522
|
const createHighlightStyles = () => {
|
|
517
|
-
const existingStyles = document.getElementById(HIGHLIGHT_STYLES_ID);
|
|
518
|
-
// Remove existing styles to avoid duplicates
|
|
519
|
-
if (existingStyles) {
|
|
520
|
-
existingStyles.remove();
|
|
521
|
-
}
|
|
522
|
-
|
|
523
523
|
const styleElement = document.createElement('style');
|
|
524
524
|
styleElement.id = HIGHLIGHT_STYLES_ID;
|
|
525
525
|
styleElement.textContent = `
|
|
@@ -548,8 +548,6 @@ function previewScript(config) {
|
|
|
548
548
|
};
|
|
549
549
|
|
|
550
550
|
const createOverlaySystem = () => {
|
|
551
|
-
// Clean up before creating a new overlay so we can safely call previewScript multiple times
|
|
552
|
-
win.__strapi_previewCleanup?.();
|
|
553
551
|
document.getElementById(OVERLAY_ID)?.remove();
|
|
554
552
|
|
|
555
553
|
const overlay = document.createElement('div');
|
|
@@ -1916,6 +1914,7 @@ function previewScript(config) {
|
|
|
1916
1914
|
};
|
|
1917
1915
|
|
|
1918
1916
|
/**
|
|
1917
|
+
* @param {HTMLStyleElement} styleElement
|
|
1919
1918
|
* @param {HTMLElement} overlay
|
|
1920
1919
|
* @param {ReturnType<typeof setupObservers>} observers
|
|
1921
1920
|
* @param {ReturnType<typeof setupScrollManagement>} scrollManager
|
|
@@ -1923,6 +1922,7 @@ function previewScript(config) {
|
|
|
1923
1922
|
* @param {HighlightManager} highlightManager
|
|
1924
1923
|
*/
|
|
1925
1924
|
const createCleanupSystem = (
|
|
1925
|
+
styleElement,
|
|
1926
1926
|
overlay,
|
|
1927
1927
|
observers,
|
|
1928
1928
|
scrollManager,
|
|
@@ -1948,11 +1948,9 @@ function previewScript(config) {
|
|
|
1948
1948
|
element.removeEventListener(type, handler);
|
|
1949
1949
|
});
|
|
1950
1950
|
|
|
1951
|
-
//
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
existingStyles.remove();
|
|
1955
|
-
}
|
|
1951
|
+
// Remove this run's own element, not whatever currently holds the id: a newer run may
|
|
1952
|
+
// already have created a stylesheet with the same id.
|
|
1953
|
+
styleElement.remove();
|
|
1956
1954
|
|
|
1957
1955
|
overlay.remove();
|
|
1958
1956
|
};
|
|
@@ -1965,12 +1963,29 @@ function previewScript(config) {
|
|
|
1965
1963
|
setupStegaDOMObserver()
|
|
1966
1964
|
.catch(() => undefined)
|
|
1967
1965
|
.then((stegaObserver) => {
|
|
1968
|
-
|
|
1966
|
+
// A newer run started while this one was loading the stega decoder: let it win, and
|
|
1967
|
+
// don't leave this run's observer behind.
|
|
1968
|
+
if (runId !== win.__strapi_previewRunId) {
|
|
1969
|
+
stegaObserver?.disconnect();
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
// Last run wins: tear the previous run down completely before setting up.
|
|
1974
|
+
win.__strapi_previewCleanup?.();
|
|
1975
|
+
|
|
1976
|
+
const styleElement = createHighlightStyles();
|
|
1969
1977
|
const overlay = createOverlaySystem();
|
|
1970
1978
|
const highlightManager = createHighlightManager(overlay);
|
|
1971
1979
|
const observers = setupObservers(highlightManager, stegaObserver);
|
|
1972
1980
|
const scrollManager = setupScrollManagement(highlightManager);
|
|
1973
1981
|
const eventHandlers = setupEventHandlers(highlightManager);
|
|
1974
|
-
createCleanupSystem(
|
|
1982
|
+
createCleanupSystem(
|
|
1983
|
+
styleElement,
|
|
1984
|
+
overlay,
|
|
1985
|
+
observers,
|
|
1986
|
+
scrollManager,
|
|
1987
|
+
eventHandlers,
|
|
1988
|
+
highlightManager
|
|
1989
|
+
);
|
|
1975
1990
|
});
|
|
1976
1991
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _ = require('lodash');
|
|
4
4
|
var strapiUtils = require('@strapi/utils');
|
|
5
5
|
|
|
6
6
|
const getPreviewUrlSchema = strapiUtils.z.object({
|
|
@@ -12,11 +12,11 @@ const getPreviewUrlSchema = strapiUtils.z.object({
|
|
|
12
12
|
const validatePreviewUrl = async (strapi, uid, params)=>{
|
|
13
13
|
// Validate the request parameters format
|
|
14
14
|
strapiUtils.validateZodSchema(getPreviewUrlSchema)(params);
|
|
15
|
-
const newParams =
|
|
15
|
+
const newParams = _.pick(params, [
|
|
16
16
|
'documentId',
|
|
17
17
|
'locale',
|
|
18
18
|
'status'
|
|
19
|
-
]
|
|
19
|
+
]);
|
|
20
20
|
const model = strapi.getModel(uid);
|
|
21
21
|
// If it's not a collection type or single type
|
|
22
22
|
if (!model || model.modelType !== 'contentType') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.js","sources":["../../../../../server/src/preview/controllers/validation/preview.ts"],"sourcesContent":["import { pick } from 'lodash
|
|
1
|
+
{"version":3,"file":"preview.js","sources":["../../../../../server/src/preview/controllers/validation/preview.ts"],"sourcesContent":["import { pick } from 'lodash';\n\nimport type { Core, UID } from '@strapi/types';\nimport { z, validateZodSchema, errors } from '@strapi/utils';\n\nimport { Preview } from '../../../../../shared/contracts';\nimport type { HandlerParams } from '../../services/preview-config';\n\nconst getPreviewUrlSchema = z.object({\n // Will be undefined for single types\n documentId: z.string().optional(),\n locale: z.string().nullable().optional(),\n status: z.string().optional(),\n});\n\nexport const validatePreviewUrl = async (\n strapi: Core.Strapi,\n uid: UID.ContentType,\n params: Preview.GetPreviewUrl.Request['query']\n): Promise<HandlerParams> => {\n // Validate the request parameters format\n validateZodSchema(getPreviewUrlSchema)(params);\n\n const newParams = pick(params, ['documentId', 'locale', 'status']) as HandlerParams;\n const model = strapi.getModel(uid);\n\n // If it's not a collection type or single type\n if (!model || model.modelType !== 'contentType') {\n throw new errors.ValidationError('Invalid content type');\n }\n\n // Document id is not required for single types\n const isSingleType = model?.kind === 'singleType';\n if (!isSingleType && !params.documentId) {\n throw new errors.ValidationError('documentId is required for Collection Types');\n }\n\n // Fill the documentId if it's a single type\n if (isSingleType) {\n const doc = await strapi.documents(uid).findFirst();\n\n if (!doc) {\n throw new errors.NotFoundError('Document not found');\n }\n\n newParams.documentId = doc?.documentId;\n }\n\n /**\n * If status is not specified, follow the following rules:\n * - D&P disabled: status is considered published\n * - D&P enabled: status is considered draft\n */\n if (!newParams.status) {\n const isDPEnabled = model?.options?.draftAndPublish;\n newParams.status = isDPEnabled ? 'draft' : 'published';\n }\n\n return newParams;\n};\n"],"names":["getPreviewUrlSchema","z","object","documentId","string","optional","locale","nullable","status","validatePreviewUrl","strapi","uid","params","validateZodSchema","newParams","pick","model","getModel","modelType","errors","ValidationError","isSingleType","kind","doc","documents","findFirst","NotFoundError","isDPEnabled","options","draftAndPublish"],"mappings":";;;;;AAQA,MAAMA,mBAAAA,GAAsBC,aAAAA,CAAEC,MAAM,CAAC;;IAEnCC,UAAAA,EAAYF,aAAAA,CAAEG,MAAM,EAAA,CAAGC,QAAQ,EAAA;AAC/BC,IAAAA,MAAAA,EAAQL,aAAAA,CAAEG,MAAM,EAAA,CAAGG,QAAQ,GAAGF,QAAQ,EAAA;IACtCG,MAAAA,EAAQP,aAAAA,CAAEG,MAAM,EAAA,CAAGC,QAAQ;AAC7B,CAAA,CAAA;AAEO,MAAMI,kBAAAA,GAAqB,OAChCC,MAAAA,EACAC,GAAAA,EACAC,MAAAA,GAAAA;;AAGAC,IAAAA,6BAAAA,CAAkBb,mBAAAA,CAAAA,CAAqBY,MAAAA,CAAAA;IAEvC,MAAME,SAAAA,GAAYC,OAAKH,MAAAA,EAAQ;AAAC,QAAA,YAAA;AAAc,QAAA,QAAA;AAAU,QAAA;AAAS,KAAA,CAAA;IACjE,MAAMI,KAAAA,GAAQN,MAAAA,CAAOO,QAAQ,CAACN,GAAAA,CAAAA;;AAG9B,IAAA,IAAI,CAACK,KAAAA,IAASA,KAAAA,CAAME,SAAS,KAAK,aAAA,EAAe;QAC/C,MAAM,IAAIC,kBAAAA,CAAOC,eAAe,CAAC,sBAAA,CAAA;AACnC,IAAA;;IAGA,MAAMC,YAAAA,GAAeL,OAAOM,IAAAA,KAAS,YAAA;AACrC,IAAA,IAAI,CAACD,YAAAA,IAAgB,CAACT,MAAAA,CAAOT,UAAU,EAAE;QACvC,MAAM,IAAIgB,kBAAAA,CAAOC,eAAe,CAAC,6CAAA,CAAA;AACnC,IAAA;;AAGA,IAAA,IAAIC,YAAAA,EAAc;AAChB,QAAA,MAAME,MAAM,MAAMb,MAAAA,CAAOc,SAAS,CAACb,KAAKc,SAAS,EAAA;AAEjD,QAAA,IAAI,CAACF,GAAAA,EAAK;YACR,MAAM,IAAIJ,kBAAAA,CAAOO,aAAa,CAAC,oBAAA,CAAA;AACjC,QAAA;QAEAZ,SAAAA,CAAUX,UAAU,GAAGoB,GAAAA,EAAKpB,UAAAA;AAC9B,IAAA;AAEA;;;;AAIC,MACD,IAAI,CAACW,SAAAA,CAAUN,MAAM,EAAE;QACrB,MAAMmB,WAAAA,GAAcX,OAAOY,OAAAA,EAASC,eAAAA;QACpCf,SAAAA,CAAUN,MAAM,GAAGmB,WAAAA,GAAc,OAAA,GAAU,WAAA;AAC7C,IAAA;IAEA,OAAOb,SAAAA;AACT;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pick } from 'lodash
|
|
1
|
+
import { pick } from 'lodash';
|
|
2
2
|
import { z, validateZodSchema, errors } from '@strapi/utils';
|
|
3
3
|
|
|
4
4
|
const getPreviewUrlSchema = z.object({
|
|
@@ -10,11 +10,11 @@ const getPreviewUrlSchema = z.object({
|
|
|
10
10
|
const validatePreviewUrl = async (strapi, uid, params)=>{
|
|
11
11
|
// Validate the request parameters format
|
|
12
12
|
validateZodSchema(getPreviewUrlSchema)(params);
|
|
13
|
-
const newParams = pick([
|
|
13
|
+
const newParams = pick(params, [
|
|
14
14
|
'documentId',
|
|
15
15
|
'locale',
|
|
16
16
|
'status'
|
|
17
|
-
]
|
|
17
|
+
]);
|
|
18
18
|
const model = strapi.getModel(uid);
|
|
19
19
|
// If it's not a collection type or single type
|
|
20
20
|
if (!model || model.modelType !== 'contentType') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.mjs","sources":["../../../../../server/src/preview/controllers/validation/preview.ts"],"sourcesContent":["import { pick } from 'lodash
|
|
1
|
+
{"version":3,"file":"preview.mjs","sources":["../../../../../server/src/preview/controllers/validation/preview.ts"],"sourcesContent":["import { pick } from 'lodash';\n\nimport type { Core, UID } from '@strapi/types';\nimport { z, validateZodSchema, errors } from '@strapi/utils';\n\nimport { Preview } from '../../../../../shared/contracts';\nimport type { HandlerParams } from '../../services/preview-config';\n\nconst getPreviewUrlSchema = z.object({\n // Will be undefined for single types\n documentId: z.string().optional(),\n locale: z.string().nullable().optional(),\n status: z.string().optional(),\n});\n\nexport const validatePreviewUrl = async (\n strapi: Core.Strapi,\n uid: UID.ContentType,\n params: Preview.GetPreviewUrl.Request['query']\n): Promise<HandlerParams> => {\n // Validate the request parameters format\n validateZodSchema(getPreviewUrlSchema)(params);\n\n const newParams = pick(params, ['documentId', 'locale', 'status']) as HandlerParams;\n const model = strapi.getModel(uid);\n\n // If it's not a collection type or single type\n if (!model || model.modelType !== 'contentType') {\n throw new errors.ValidationError('Invalid content type');\n }\n\n // Document id is not required for single types\n const isSingleType = model?.kind === 'singleType';\n if (!isSingleType && !params.documentId) {\n throw new errors.ValidationError('documentId is required for Collection Types');\n }\n\n // Fill the documentId if it's a single type\n if (isSingleType) {\n const doc = await strapi.documents(uid).findFirst();\n\n if (!doc) {\n throw new errors.NotFoundError('Document not found');\n }\n\n newParams.documentId = doc?.documentId;\n }\n\n /**\n * If status is not specified, follow the following rules:\n * - D&P disabled: status is considered published\n * - D&P enabled: status is considered draft\n */\n if (!newParams.status) {\n const isDPEnabled = model?.options?.draftAndPublish;\n newParams.status = isDPEnabled ? 'draft' : 'published';\n }\n\n return newParams;\n};\n"],"names":["getPreviewUrlSchema","z","object","documentId","string","optional","locale","nullable","status","validatePreviewUrl","strapi","uid","params","validateZodSchema","newParams","pick","model","getModel","modelType","errors","ValidationError","isSingleType","kind","doc","documents","findFirst","NotFoundError","isDPEnabled","options","draftAndPublish"],"mappings":";;;AAQA,MAAMA,mBAAAA,GAAsBC,CAAAA,CAAEC,MAAM,CAAC;;IAEnCC,UAAAA,EAAYF,CAAAA,CAAEG,MAAM,EAAA,CAAGC,QAAQ,EAAA;AAC/BC,IAAAA,MAAAA,EAAQL,CAAAA,CAAEG,MAAM,EAAA,CAAGG,QAAQ,GAAGF,QAAQ,EAAA;IACtCG,MAAAA,EAAQP,CAAAA,CAAEG,MAAM,EAAA,CAAGC,QAAQ;AAC7B,CAAA,CAAA;AAEO,MAAMI,kBAAAA,GAAqB,OAChCC,MAAAA,EACAC,GAAAA,EACAC,MAAAA,GAAAA;;AAGAC,IAAAA,iBAAAA,CAAkBb,mBAAAA,CAAAA,CAAqBY,MAAAA,CAAAA;IAEvC,MAAME,SAAAA,GAAYC,KAAKH,MAAAA,EAAQ;AAAC,QAAA,YAAA;AAAc,QAAA,QAAA;AAAU,QAAA;AAAS,KAAA,CAAA;IACjE,MAAMI,KAAAA,GAAQN,MAAAA,CAAOO,QAAQ,CAACN,GAAAA,CAAAA;;AAG9B,IAAA,IAAI,CAACK,KAAAA,IAASA,KAAAA,CAAME,SAAS,KAAK,aAAA,EAAe;QAC/C,MAAM,IAAIC,MAAAA,CAAOC,eAAe,CAAC,sBAAA,CAAA;AACnC,IAAA;;IAGA,MAAMC,YAAAA,GAAeL,OAAOM,IAAAA,KAAS,YAAA;AACrC,IAAA,IAAI,CAACD,YAAAA,IAAgB,CAACT,MAAAA,CAAOT,UAAU,EAAE;QACvC,MAAM,IAAIgB,MAAAA,CAAOC,eAAe,CAAC,6CAAA,CAAA;AACnC,IAAA;;AAGA,IAAA,IAAIC,YAAAA,EAAc;AAChB,QAAA,MAAME,MAAM,MAAMb,MAAAA,CAAOc,SAAS,CAACb,KAAKc,SAAS,EAAA;AAEjD,QAAA,IAAI,CAACF,GAAAA,EAAK;YACR,MAAM,IAAIJ,MAAAA,CAAOO,aAAa,CAAC,oBAAA,CAAA;AACjC,QAAA;QAEAZ,SAAAA,CAAUX,UAAU,GAAGoB,GAAAA,EAAKpB,UAAAA;AAC9B,IAAA;AAEA;;;;AAIC,MACD,IAAI,CAACW,SAAAA,CAAUN,MAAM,EAAE;QACrB,MAAMmB,WAAAA,GAAcX,OAAOY,OAAAA,EAASC,eAAAA;QACpCf,SAAAA,CAAUN,MAAM,GAAGmB,WAAAA,GAAc,OAAA,GAAU,WAAA;AAC7C,IAAA;IAEA,OAAOb,SAAAA;AACT;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _ = require('lodash');
|
|
4
4
|
var index = require('../utils/index.js');
|
|
5
5
|
var store = require('./utils/store.js');
|
|
6
6
|
var configuration = require('./configuration.js');
|
|
@@ -12,7 +12,7 @@ const configurationService = configuration({
|
|
|
12
12
|
prefix: STORE_KEY_PREFIX,
|
|
13
13
|
getModels () {
|
|
14
14
|
const { toContentManagerModel } = index.getService('data-mapper');
|
|
15
|
-
return
|
|
15
|
+
return _.mapValues(strapi.components, (value)=>toContentManagerModel(value));
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
18
|
var components = (({ strapi: strapi1 })=>({
|
|
@@ -23,7 +23,7 @@ var components = (({ strapi: strapi1 })=>({
|
|
|
23
23
|
findComponent (uid) {
|
|
24
24
|
const { toContentManagerModel } = index.getService('data-mapper');
|
|
25
25
|
const component = strapi1.components[uid];
|
|
26
|
-
return
|
|
26
|
+
return component == null ? component : toContentManagerModel(component);
|
|
27
27
|
},
|
|
28
28
|
async findConfiguration (component) {
|
|
29
29
|
const configuration = await configurationService.getConfiguration(component.uid);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.js","sources":["../../../server/src/services/components.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"components.js","sources":["../../../server/src/services/components.ts"],"sourcesContent":["import { mapValues } from 'lodash';\n\nimport type { UID, Struct, Core } from '@strapi/types';\nimport type { Configuration } from '../../../shared/contracts/content-types';\nimport type { ConfigurationUpdate } from './configuration';\n\nimport { getService } from '../utils';\nimport storeUtils from './utils/store';\nimport createConfigurationService from './configuration';\n\nconst STORE_KEY_PREFIX = 'components';\n\nconst configurationService = createConfigurationService({\n storeUtils,\n isComponent: true,\n prefix: STORE_KEY_PREFIX,\n getModels() {\n const { toContentManagerModel } = getService('data-mapper');\n\n return mapValues(strapi.components, (value) => toContentManagerModel(value));\n },\n});\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n findAllComponents() {\n const { toContentManagerModel } = getService('data-mapper');\n\n return Object.values(strapi.components).map(toContentManagerModel);\n },\n\n findComponent(uid: UID.Component) {\n const { toContentManagerModel } = getService('data-mapper');\n\n const component = strapi.components[uid];\n\n return component == null ? component : toContentManagerModel(component);\n },\n\n async findConfiguration(component: Struct.ComponentSchema) {\n const configuration: Configuration = await configurationService.getConfiguration(component.uid);\n\n return {\n uid: component.uid,\n category: component.category,\n ...configuration,\n };\n },\n\n async updateConfiguration(\n component: Struct.ComponentSchema,\n newConfiguration: ConfigurationUpdate\n ) {\n await configurationService.setConfiguration(component.uid, newConfiguration);\n\n return this.findConfiguration(component);\n },\n\n /**\n * Batch load component configurations.\n *\n * Collects all component UIDs upfront, then loads configurations in a single\n * batch query instead of sequential queries per component.\n */\n async findComponentsConfigurations(model: Struct.ComponentSchema) {\n // Cache on request state so the same request can reuse configs\n const requestState = strapi.requestContext?.get?.()?.state as\n | { __componentsConfigurationsCache?: Map<string, Record<string, Configuration>> }\n | undefined;\n const requestCache = requestState?.__componentsConfigurationsCache;\n\n const componentUids = new Set<UID.Component>();\n\n const collectComponentUids = (schema: Struct.ComponentSchema) => {\n for (const key of Object.keys(schema.attributes)) {\n const attribute = schema.attributes[key];\n\n if (attribute.type === 'component') {\n const uid = attribute.component;\n if (!componentUids.has(uid)) {\n componentUids.add(uid);\n const nestedComponent = this.findComponent(uid);\n if (nestedComponent) {\n collectComponentUids(nestedComponent);\n }\n }\n }\n\n if (attribute.type === 'dynamiczone') {\n for (const uid of attribute.components) {\n if (!componentUids.has(uid)) {\n componentUids.add(uid);\n const nestedComponent = this.findComponent(uid);\n if (nestedComponent) {\n collectComponentUids(nestedComponent);\n }\n }\n }\n }\n }\n };\n\n collectComponentUids(model);\n\n if (componentUids.size === 0) {\n return {};\n }\n\n // Key format must match configuration.ts uidToStoreKey: `${prefix}::${uid}`\n const uidsArray = Array.from(componentUids);\n const prefixedKeys = uidsArray.map((uid) => `components::${uid}`);\n const cacheKey = prefixedKeys.slice().sort().join('|');\n if (requestCache?.has(cacheKey)) {\n return requestCache.get(cacheKey)! as Record<\n string,\n Configuration & { category: string; isComponent: boolean }\n >;\n }\n\n const configs = await storeUtils.getModelConfigurations(prefixedKeys);\n\n const componentsMap: Record<\n string,\n Configuration & { category: string; isComponent: boolean }\n > = {};\n\n for (const uid of uidsArray) {\n const component = this.findComponent(uid);\n const configKey = `components::${uid}`;\n // Fallback must include proper layouts structure for frontend compatibility\n const configuration = configs[configKey] || {\n settings: {},\n metadatas: {},\n layouts: { list: [], edit: [] },\n };\n\n componentsMap[uid] = {\n uid: component.uid,\n category: component.category,\n ...configuration,\n };\n }\n\n if (requestState) {\n if (!requestState.__componentsConfigurationsCache) {\n requestState.__componentsConfigurationsCache = new Map();\n }\n requestState.__componentsConfigurationsCache.set(cacheKey, componentsMap);\n }\n\n return componentsMap;\n },\n\n syncConfigurations() {\n return configurationService.syncConfigurations();\n },\n});\n"],"names":["STORE_KEY_PREFIX","configurationService","createConfigurationService","storeUtils","isComponent","prefix","getModels","toContentManagerModel","getService","mapValues","strapi","components","value","findAllComponents","Object","values","map","findComponent","uid","component","findConfiguration","configuration","getConfiguration","category","updateConfiguration","newConfiguration","setConfiguration","findComponentsConfigurations","model","requestState","requestContext","get","state","requestCache","__componentsConfigurationsCache","componentUids","Set","collectComponentUids","schema","key","keys","attributes","attribute","type","has","add","nestedComponent","size","uidsArray","Array","from","prefixedKeys","cacheKey","slice","sort","join","configs","getModelConfigurations","componentsMap","configKey","settings","metadatas","layouts","list","edit","Map","set","syncConfigurations"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mapValues } from 'lodash';
|
|
2
2
|
import { getService } from '../utils/index.mjs';
|
|
3
3
|
import storeUtils from './utils/store.mjs';
|
|
4
4
|
import createConfigurationService from './configuration.mjs';
|
|
@@ -10,7 +10,7 @@ const configurationService = createConfigurationService({
|
|
|
10
10
|
prefix: STORE_KEY_PREFIX,
|
|
11
11
|
getModels () {
|
|
12
12
|
const { toContentManagerModel } = getService('data-mapper');
|
|
13
|
-
return mapValues(
|
|
13
|
+
return mapValues(strapi.components, (value)=>toContentManagerModel(value));
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
16
|
var components = (({ strapi: strapi1 })=>({
|
|
@@ -21,7 +21,7 @@ var components = (({ strapi: strapi1 })=>({
|
|
|
21
21
|
findComponent (uid) {
|
|
22
22
|
const { toContentManagerModel } = getService('data-mapper');
|
|
23
23
|
const component = strapi1.components[uid];
|
|
24
|
-
return
|
|
24
|
+
return component == null ? component : toContentManagerModel(component);
|
|
25
25
|
},
|
|
26
26
|
async findConfiguration (component) {
|
|
27
27
|
const configuration = await configurationService.getConfiguration(component.uid);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.mjs","sources":["../../../server/src/services/components.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"components.mjs","sources":["../../../server/src/services/components.ts"],"sourcesContent":["import { mapValues } from 'lodash';\n\nimport type { UID, Struct, Core } from '@strapi/types';\nimport type { Configuration } from '../../../shared/contracts/content-types';\nimport type { ConfigurationUpdate } from './configuration';\n\nimport { getService } from '../utils';\nimport storeUtils from './utils/store';\nimport createConfigurationService from './configuration';\n\nconst STORE_KEY_PREFIX = 'components';\n\nconst configurationService = createConfigurationService({\n storeUtils,\n isComponent: true,\n prefix: STORE_KEY_PREFIX,\n getModels() {\n const { toContentManagerModel } = getService('data-mapper');\n\n return mapValues(strapi.components, (value) => toContentManagerModel(value));\n },\n});\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n findAllComponents() {\n const { toContentManagerModel } = getService('data-mapper');\n\n return Object.values(strapi.components).map(toContentManagerModel);\n },\n\n findComponent(uid: UID.Component) {\n const { toContentManagerModel } = getService('data-mapper');\n\n const component = strapi.components[uid];\n\n return component == null ? component : toContentManagerModel(component);\n },\n\n async findConfiguration(component: Struct.ComponentSchema) {\n const configuration: Configuration = await configurationService.getConfiguration(component.uid);\n\n return {\n uid: component.uid,\n category: component.category,\n ...configuration,\n };\n },\n\n async updateConfiguration(\n component: Struct.ComponentSchema,\n newConfiguration: ConfigurationUpdate\n ) {\n await configurationService.setConfiguration(component.uid, newConfiguration);\n\n return this.findConfiguration(component);\n },\n\n /**\n * Batch load component configurations.\n *\n * Collects all component UIDs upfront, then loads configurations in a single\n * batch query instead of sequential queries per component.\n */\n async findComponentsConfigurations(model: Struct.ComponentSchema) {\n // Cache on request state so the same request can reuse configs\n const requestState = strapi.requestContext?.get?.()?.state as\n | { __componentsConfigurationsCache?: Map<string, Record<string, Configuration>> }\n | undefined;\n const requestCache = requestState?.__componentsConfigurationsCache;\n\n const componentUids = new Set<UID.Component>();\n\n const collectComponentUids = (schema: Struct.ComponentSchema) => {\n for (const key of Object.keys(schema.attributes)) {\n const attribute = schema.attributes[key];\n\n if (attribute.type === 'component') {\n const uid = attribute.component;\n if (!componentUids.has(uid)) {\n componentUids.add(uid);\n const nestedComponent = this.findComponent(uid);\n if (nestedComponent) {\n collectComponentUids(nestedComponent);\n }\n }\n }\n\n if (attribute.type === 'dynamiczone') {\n for (const uid of attribute.components) {\n if (!componentUids.has(uid)) {\n componentUids.add(uid);\n const nestedComponent = this.findComponent(uid);\n if (nestedComponent) {\n collectComponentUids(nestedComponent);\n }\n }\n }\n }\n }\n };\n\n collectComponentUids(model);\n\n if (componentUids.size === 0) {\n return {};\n }\n\n // Key format must match configuration.ts uidToStoreKey: `${prefix}::${uid}`\n const uidsArray = Array.from(componentUids);\n const prefixedKeys = uidsArray.map((uid) => `components::${uid}`);\n const cacheKey = prefixedKeys.slice().sort().join('|');\n if (requestCache?.has(cacheKey)) {\n return requestCache.get(cacheKey)! as Record<\n string,\n Configuration & { category: string; isComponent: boolean }\n >;\n }\n\n const configs = await storeUtils.getModelConfigurations(prefixedKeys);\n\n const componentsMap: Record<\n string,\n Configuration & { category: string; isComponent: boolean }\n > = {};\n\n for (const uid of uidsArray) {\n const component = this.findComponent(uid);\n const configKey = `components::${uid}`;\n // Fallback must include proper layouts structure for frontend compatibility\n const configuration = configs[configKey] || {\n settings: {},\n metadatas: {},\n layouts: { list: [], edit: [] },\n };\n\n componentsMap[uid] = {\n uid: component.uid,\n category: component.category,\n ...configuration,\n };\n }\n\n if (requestState) {\n if (!requestState.__componentsConfigurationsCache) {\n requestState.__componentsConfigurationsCache = new Map();\n }\n requestState.__componentsConfigurationsCache.set(cacheKey, componentsMap);\n }\n\n return componentsMap;\n },\n\n syncConfigurations() {\n return configurationService.syncConfigurations();\n },\n});\n"],"names":["STORE_KEY_PREFIX","configurationService","createConfigurationService","storeUtils","isComponent","prefix","getModels","toContentManagerModel","getService","mapValues","strapi","components","value","findAllComponents","Object","values","map","findComponent","uid","component","findConfiguration","configuration","getConfiguration","category","updateConfiguration","newConfiguration","setConfiguration","findComponentsConfigurations","model","requestState","requestContext","get","state","requestCache","__componentsConfigurationsCache","componentUids","Set","collectComponentUids","schema","key","keys","attributes","attribute","type","has","add","nestedComponent","size","uidsArray","Array","from","prefixedKeys","cacheKey","slice","sort","join","configs","getModelConfigurations","componentsMap","configKey","settings","metadatas","layouts","list","edit","Map","set","syncConfigurations"],"mappings":"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"}
|