@payloadcms/plugin-mcp 4.0.0-internal.a0ef1b8 → 4.0.0-internal.a1317b2

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 (240) hide show
  1. package/LICENSE.md +1 -1
  2. package/bin.js +7 -0
  3. package/dist/defaultAccess.d.ts +3 -0
  4. package/dist/defaultAccess.d.ts.map +1 -0
  5. package/dist/defaultAccess.js +3 -0
  6. package/dist/defaultAccess.js.map +1 -0
  7. package/dist/defineTool.d.ts +8 -8
  8. package/dist/defineTool.d.ts.map +1 -1
  9. package/dist/endpoint/access.d.ts +12 -5
  10. package/dist/endpoint/access.d.ts.map +1 -1
  11. package/dist/endpoint/access.js +75 -88
  12. package/dist/endpoint/access.js.map +1 -1
  13. package/dist/endpoint/index.d.ts +1 -1
  14. package/dist/endpoint/index.d.ts.map +1 -1
  15. package/dist/endpoint/index.js +68 -17
  16. package/dist/endpoint/index.js.map +1 -1
  17. package/dist/exports/client.d.ts +1 -1
  18. package/dist/exports/client.d.ts.map +1 -1
  19. package/dist/exports/client.js +1 -1
  20. package/dist/exports/client.js.map +1 -1
  21. package/dist/exports/internal.d.ts +2 -0
  22. package/dist/exports/internal.d.ts.map +1 -0
  23. package/dist/exports/internal.js +3 -0
  24. package/dist/exports/internal.js.map +1 -0
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +15 -20
  28. package/dist/index.js.map +1 -1
  29. package/dist/mcp/buildMcpServer.d.ts +5 -5
  30. package/dist/mcp/buildMcpServer.d.ts.map +1 -1
  31. package/dist/mcp/buildMcpServer.js +145 -80
  32. package/dist/mcp/buildMcpServer.js.map +1 -1
  33. package/dist/mcp/builtin/collections/authTools.d.ts.map +1 -1
  34. package/dist/mcp/builtin/collections/authTools.js +122 -61
  35. package/dist/mcp/builtin/collections/authTools.js.map +1 -1
  36. package/dist/mcp/builtin/collections/countTool.d.ts +2 -0
  37. package/dist/mcp/builtin/collections/countTool.d.ts.map +1 -0
  38. package/dist/mcp/builtin/collections/countTool.js +63 -0
  39. package/dist/mcp/builtin/collections/countTool.js.map +1 -0
  40. package/dist/mcp/builtin/collections/countVersionsTool.d.ts +2 -0
  41. package/dist/mcp/builtin/collections/countVersionsTool.d.ts.map +1 -0
  42. package/dist/mcp/builtin/collections/countVersionsTool.js +60 -0
  43. package/dist/mcp/builtin/collections/countVersionsTool.js.map +1 -0
  44. package/dist/mcp/builtin/collections/createTool.d.ts +1 -1
  45. package/dist/mcp/builtin/collections/createTool.d.ts.map +1 -1
  46. package/dist/mcp/builtin/collections/createTool.js +111 -68
  47. package/dist/mcp/builtin/collections/createTool.js.map +1 -1
  48. package/dist/mcp/builtin/collections/deleteTool.d.ts +1 -1
  49. package/dist/mcp/builtin/collections/deleteTool.d.ts.map +1 -1
  50. package/dist/mcp/builtin/collections/deleteTool.js +51 -61
  51. package/dist/mcp/builtin/collections/deleteTool.js.map +1 -1
  52. package/dist/mcp/builtin/collections/duplicateTool.d.ts +2 -0
  53. package/dist/mcp/builtin/collections/duplicateTool.d.ts.map +1 -0
  54. package/dist/mcp/builtin/collections/duplicateTool.js +86 -0
  55. package/dist/mcp/builtin/collections/duplicateTool.js.map +1 -0
  56. package/dist/mcp/builtin/collections/fileInput.d.ts +28 -0
  57. package/dist/mcp/builtin/collections/fileInput.d.ts.map +1 -0
  58. package/dist/mcp/builtin/collections/fileInput.js +123 -0
  59. package/dist/mcp/builtin/collections/fileInput.js.map +1 -0
  60. package/dist/mcp/builtin/collections/fileInput.spec.js +86 -0
  61. package/dist/mcp/builtin/collections/fileInput.spec.js.map +1 -0
  62. package/dist/mcp/builtin/collections/findDistinctTool.d.ts +2 -0
  63. package/dist/mcp/builtin/collections/findDistinctTool.d.ts.map +1 -0
  64. package/dist/mcp/builtin/collections/findDistinctTool.js +77 -0
  65. package/dist/mcp/builtin/collections/findDistinctTool.js.map +1 -0
  66. package/dist/mcp/builtin/collections/findTool.d.ts +1 -1
  67. package/dist/mcp/builtin/collections/findTool.d.ts.map +1 -1
  68. package/dist/mcp/builtin/collections/findTool.js +55 -72
  69. package/dist/mcp/builtin/collections/findTool.js.map +1 -1
  70. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts +2 -0
  71. package/dist/mcp/builtin/collections/findVersionByIDTool.d.ts.map +1 -0
  72. package/dist/mcp/builtin/collections/findVersionByIDTool.js +74 -0
  73. package/dist/mcp/builtin/collections/findVersionByIDTool.js.map +1 -0
  74. package/dist/mcp/builtin/collections/findVersionsTool.d.ts +2 -0
  75. package/dist/mcp/builtin/collections/findVersionsTool.d.ts.map +1 -0
  76. package/dist/mcp/builtin/collections/findVersionsTool.js +84 -0
  77. package/dist/mcp/builtin/collections/findVersionsTool.js.map +1 -0
  78. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts +2 -0
  79. package/dist/mcp/builtin/collections/getCollectionSchemaTool.d.ts.map +1 -0
  80. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js +87 -0
  81. package/dist/mcp/builtin/collections/getCollectionSchemaTool.js.map +1 -0
  82. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts +2 -0
  83. package/dist/mcp/builtin/collections/restoreVersionTool.d.ts.map +1 -0
  84. package/dist/mcp/builtin/collections/restoreVersionTool.js +69 -0
  85. package/dist/mcp/builtin/collections/restoreVersionTool.js.map +1 -0
  86. package/dist/mcp/builtin/collections/updateTool.d.ts +1 -1
  87. package/dist/mcp/builtin/collections/updateTool.d.ts.map +1 -1
  88. package/dist/mcp/builtin/collections/updateTool.js +104 -136
  89. package/dist/mcp/builtin/collections/updateTool.js.map +1 -1
  90. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts +2 -0
  91. package/dist/mcp/builtin/collections/uploadInstructionsTool.d.ts.map +1 -0
  92. package/dist/mcp/builtin/collections/uploadInstructionsTool.js +55 -0
  93. package/dist/mcp/builtin/collections/uploadInstructionsTool.js.map +1 -0
  94. package/dist/mcp/builtin/formatEntityError.d.ts +16 -0
  95. package/dist/mcp/builtin/formatEntityError.d.ts.map +1 -0
  96. package/dist/mcp/builtin/formatEntityError.js +55 -0
  97. package/dist/mcp/builtin/formatEntityError.js.map +1 -0
  98. package/dist/mcp/builtin/getConfigInfoTool.d.ts +2 -0
  99. package/dist/mcp/builtin/getConfigInfoTool.d.ts.map +1 -0
  100. package/dist/mcp/builtin/getConfigInfoTool.js +69 -0
  101. package/dist/mcp/builtin/getConfigInfoTool.js.map +1 -0
  102. package/dist/mcp/builtin/globals/countVersionsTool.d.ts +2 -0
  103. package/dist/mcp/builtin/globals/countVersionsTool.d.ts.map +1 -0
  104. package/dist/mcp/builtin/globals/countVersionsTool.js +60 -0
  105. package/dist/mcp/builtin/globals/countVersionsTool.js.map +1 -0
  106. package/dist/mcp/builtin/globals/findTool.d.ts.map +1 -1
  107. package/dist/mcp/builtin/globals/findTool.js +27 -36
  108. package/dist/mcp/builtin/globals/findTool.js.map +1 -1
  109. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts +2 -0
  110. package/dist/mcp/builtin/globals/findVersionByIDTool.d.ts.map +1 -0
  111. package/dist/mcp/builtin/globals/findVersionByIDTool.js +68 -0
  112. package/dist/mcp/builtin/globals/findVersionByIDTool.js.map +1 -0
  113. package/dist/mcp/builtin/globals/findVersionsTool.d.ts +2 -0
  114. package/dist/mcp/builtin/globals/findVersionsTool.d.ts.map +1 -0
  115. package/dist/mcp/builtin/globals/findVersionsTool.js +78 -0
  116. package/dist/mcp/builtin/globals/findVersionsTool.js.map +1 -0
  117. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts +2 -0
  118. package/dist/mcp/builtin/globals/getGlobalSchemaTool.d.ts.map +1 -0
  119. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js +65 -0
  120. package/dist/mcp/builtin/globals/getGlobalSchemaTool.js.map +1 -0
  121. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts +2 -0
  122. package/dist/mcp/builtin/globals/restoreVersionTool.d.ts.map +1 -0
  123. package/dist/mcp/builtin/globals/restoreVersionTool.js +70 -0
  124. package/dist/mcp/builtin/globals/restoreVersionTool.js.map +1 -0
  125. package/dist/mcp/builtin/globals/updateTool.d.ts.map +1 -1
  126. package/dist/mcp/builtin/globals/updateTool.js +44 -62
  127. package/dist/mcp/builtin/globals/updateTool.js.map +1 -1
  128. package/dist/mcp/builtinTools.d.ts +150 -19
  129. package/dist/mcp/builtinTools.d.ts.map +1 -1
  130. package/dist/mcp/builtinTools.js +125 -20
  131. package/dist/mcp/builtinTools.js.map +1 -1
  132. package/dist/mcp/sanitizeMCPConfig.d.ts +0 -1
  133. package/dist/mcp/sanitizeMCPConfig.d.ts.map +1 -1
  134. package/dist/mcp/sanitizeMCPConfig.js +124 -58
  135. package/dist/mcp/sanitizeMCPConfig.js.map +1 -1
  136. package/dist/stdio.d.ts +3 -3
  137. package/dist/stdio.d.ts.map +1 -1
  138. package/dist/stdio.js +84 -46
  139. package/dist/stdio.js.map +1 -1
  140. package/dist/types.d.ts +114 -112
  141. package/dist/types.d.ts.map +1 -1
  142. package/dist/types.js +2 -4
  143. package/dist/types.js.map +1 -1
  144. package/dist/utils/camelCase.d.ts.map +1 -1
  145. package/dist/utils/getPluginConfig.d.ts +1 -1
  146. package/dist/utils/getPluginConfig.js +1 -1
  147. package/dist/utils/getPluginConfig.js.map +1 -1
  148. package/dist/utils/resolveProjectRoot.d.ts.map +1 -1
  149. package/dist/utils/toStandardSchema.d.ts.map +1 -1
  150. package/package.json +11 -18
  151. package/src/defaultAccess.ts +3 -0
  152. package/src/endpoint/access.ts +80 -101
  153. package/src/endpoint/index.ts +58 -16
  154. package/src/exports/client.ts +2 -1
  155. package/src/exports/internal.ts +1 -0
  156. package/src/index.ts +12 -20
  157. package/src/mcp/buildMcpServer.ts +179 -114
  158. package/src/mcp/builtin/collections/authTools.ts +119 -75
  159. package/src/mcp/builtin/collections/countTool.ts +61 -0
  160. package/src/mcp/builtin/collections/countVersionsTool.ts +60 -0
  161. package/src/mcp/builtin/collections/createTool.ts +104 -88
  162. package/src/mcp/builtin/collections/deleteTool.ts +48 -74
  163. package/src/mcp/builtin/collections/duplicateTool.ts +83 -0
  164. package/src/mcp/builtin/collections/fileInput.spec.ts +93 -0
  165. package/src/mcp/builtin/collections/fileInput.ts +169 -0
  166. package/src/mcp/builtin/collections/findDistinctTool.ts +67 -0
  167. package/src/mcp/builtin/collections/findTool.ts +55 -108
  168. package/src/mcp/builtin/collections/findVersionByIDTool.ts +66 -0
  169. package/src/mcp/builtin/collections/findVersionsTool.ts +83 -0
  170. package/src/mcp/builtin/collections/getCollectionSchemaTool.ts +80 -0
  171. package/src/mcp/builtin/collections/restoreVersionTool.ts +65 -0
  172. package/src/mcp/builtin/collections/updateTool.ts +101 -142
  173. package/src/mcp/builtin/collections/uploadInstructionsTool.ts +64 -0
  174. package/src/mcp/builtin/formatEntityError.ts +77 -0
  175. package/src/mcp/builtin/getConfigInfoTool.ts +68 -0
  176. package/src/mcp/builtin/globals/countVersionsTool.ts +60 -0
  177. package/src/mcp/builtin/globals/findTool.ts +27 -57
  178. package/src/mcp/builtin/globals/findVersionByIDTool.ts +64 -0
  179. package/src/mcp/builtin/globals/findVersionsTool.ts +69 -0
  180. package/src/mcp/builtin/globals/getGlobalSchemaTool.ts +63 -0
  181. package/src/mcp/builtin/globals/restoreVersionTool.ts +68 -0
  182. package/src/mcp/builtin/globals/updateTool.ts +51 -79
  183. package/src/mcp/builtinTools.ts +118 -41
  184. package/src/mcp/sanitizeMCPConfig.ts +120 -78
  185. package/src/stdio.ts +79 -46
  186. package/src/types.ts +130 -117
  187. package/src/utils/getPluginConfig.ts +1 -1
  188. package/dist/@types/assets.d.js +0 -2
  189. package/dist/@types/assets.d.js.map +0 -1
  190. package/dist/collection/getAccessField.d.ts +0 -12
  191. package/dist/collection/getAccessField.d.ts.map +0 -1
  192. package/dist/collection/getAccessField.js +0 -57
  193. package/dist/collection/getAccessField.js.map +0 -1
  194. package/dist/collection/index.d.ts +0 -6
  195. package/dist/collection/index.d.ts.map +0 -1
  196. package/dist/collection/index.js +0 -60
  197. package/dist/collection/index.js.map +0 -1
  198. package/dist/components/AccessField/index.client.d.ts +0 -10
  199. package/dist/components/AccessField/index.client.d.ts.map +0 -1
  200. package/dist/components/AccessField/index.client.js +0 -305
  201. package/dist/components/AccessField/index.client.js.map +0 -1
  202. package/dist/components/AccessField/index.css +0 -93
  203. package/dist/utils/getVirtualFieldNames.d.ts +0 -14
  204. package/dist/utils/getVirtualFieldNames.d.ts.map +0 -1
  205. package/dist/utils/getVirtualFieldNames.js +0 -35
  206. package/dist/utils/getVirtualFieldNames.js.map +0 -1
  207. package/dist/utils/localAPIDefaults.d.ts +0 -20
  208. package/dist/utils/localAPIDefaults.d.ts.map +0 -1
  209. package/dist/utils/localAPIDefaults.js +0 -19
  210. package/dist/utils/localAPIDefaults.js.map +0 -1
  211. package/dist/utils/schemaConversion/buildToolInput.d.ts +0 -29
  212. package/dist/utils/schemaConversion/buildToolInput.d.ts.map +0 -1
  213. package/dist/utils/schemaConversion/buildToolInput.js +0 -51
  214. package/dist/utils/schemaConversion/buildToolInput.js.map +0 -1
  215. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts +0 -7
  216. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.d.ts.map +0 -1
  217. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js +0 -20
  218. package/dist/utils/schemaConversion/removeVirtualFieldsFromSchema.js.map +0 -1
  219. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts +0 -15
  220. package/dist/utils/schemaConversion/sanitizeEntitySchema.d.ts.map +0 -1
  221. package/dist/utils/schemaConversion/sanitizeEntitySchema.js +0 -464
  222. package/dist/utils/schemaConversion/sanitizeEntitySchema.js.map +0 -1
  223. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js +0 -158
  224. package/dist/utils/schemaConversion/sanitizeEntitySchema.spec.js.map +0 -1
  225. package/dist/utils/transformPointDataToPayload.d.ts +0 -7
  226. package/dist/utils/transformPointDataToPayload.d.ts.map +0 -1
  227. package/dist/utils/transformPointDataToPayload.js +0 -28
  228. package/dist/utils/transformPointDataToPayload.js.map +0 -1
  229. package/src/@types/assets.d.ts +0 -3
  230. package/src/collection/getAccessField.ts +0 -64
  231. package/src/collection/index.ts +0 -64
  232. package/src/components/AccessField/index.client.tsx +0 -344
  233. package/src/components/AccessField/index.css +0 -93
  234. package/src/utils/getVirtualFieldNames.ts +0 -53
  235. package/src/utils/localAPIDefaults.ts +0 -22
  236. package/src/utils/schemaConversion/buildToolInput.ts +0 -68
  237. package/src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts +0 -27
  238. package/src/utils/schemaConversion/sanitizeEntitySchema.spec.ts +0 -103
  239. package/src/utils/schemaConversion/sanitizeEntitySchema.ts +0 -529
  240. package/src/utils/transformPointDataToPayload.ts +0 -40
@@ -1,19 +0,0 @@
1
- /**
2
- * Returns the `user` and `overrideAccess` arguments every Payload local API call
3
- * should receive when initiated from an MCP request. Spread the result into the
4
- * call's arg object so the two pieces of caller state can't drift apart.
5
- *
6
- * @example
7
- * ```ts
8
- * await req.payload.create({
9
- * collection: 'posts',
10
- * data,
11
- * ...localAPIDefaults(authorizedMCP),
12
- * })
13
- * ```
14
- */ export const localAPIDefaults = (authorizedMCP)=>({
15
- overrideAccess: authorizedMCP.overrideAccess,
16
- user: authorizedMCP.user
17
- });
18
-
19
- //# sourceMappingURL=localAPIDefaults.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utils/localAPIDefaults.ts"],"sourcesContent":["import type { AuthorizedMCP } from '../types.js'\n\n/**\n * Returns the `user` and `overrideAccess` arguments every Payload local API call\n * should receive when initiated from an MCP request. Spread the result into the\n * call's arg object so the two pieces of caller state can't drift apart.\n *\n * @example\n * ```ts\n * await req.payload.create({\n * collection: 'posts',\n * data,\n * ...localAPIDefaults(authorizedMCP),\n * })\n * ```\n */\nexport const localAPIDefaults = (\n authorizedMCP: AuthorizedMCP,\n): { overrideAccess: boolean; user: AuthorizedMCP['user'] } => ({\n overrideAccess: authorizedMCP.overrideAccess,\n user: authorizedMCP.user,\n})\n"],"names":["localAPIDefaults","authorizedMCP","overrideAccess","user"],"mappings":"AAEA;;;;;;;;;;;;;CAaC,GACD,OAAO,MAAMA,mBAAmB,CAC9BC,gBAC8D,CAAA;QAC9DC,gBAAgBD,cAAcC,cAAc;QAC5CC,MAAMF,cAAcE,IAAI;IAC1B,CAAA,EAAE"}
@@ -1,29 +0,0 @@
1
- import type { StandardSchemaWithJSON } from '@modelcontextprotocol/server';
2
- import { z } from 'zod';
3
- import type { JsonSchemaType } from '../../types.js';
4
- /**
5
- * Builds a create/update tool's `input`: a `data` field (the document's fields) plus controls like
6
- * `depth` and `draft`. Using just `z.fromJSONSchema(ourJSONSchema)` has a few problems which are mitigated
7
- * in this function. z.fromJSONSchema is both bigger and lossier (≈40% larger on the lexical schema).
8
- * Each example below is `what we publish` => `what a plain zod round-trip would publish instead`.
9
- *
10
- * @example
11
- * Bigger - zod inlines shared defs instead of keeping our `$ref`s (and adds junk integer bounds), so a
12
- * reused def is duplicated at every use:
13
- * `{ $ref: '#/$defs/author' }` => `{ type: 'object', properties: { id, name }, additionalProperties: false }`
14
- *
15
- * @example
16
- * Lossier - zod drops the `description` on `enum`/`const` fields:
17
- * `{ enum: ['draft', 'published'], description: 'Publish state' }` → `{ enum: ['draft', 'published'] }`
18
- */
19
- export declare const buildToolInput: <TControls extends z.ZodRawShape>({ controls, dataDescription, dataSchema, }: {
20
- /** Tool options alongside `data` (depth, draft, where, …) as plain zod - inferred into `input`. */
21
- controls: TControls;
22
- dataDescription: string;
23
- dataSchema: JsonSchemaType;
24
- }) => StandardSchemaWithJSON<{
25
- data: Record<string, unknown>;
26
- } & Omit<z.output<z.ZodObject<{
27
- data: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
28
- } & TControls extends infer T ? { -readonly [P in keyof T]: T[P]; } : never, z.core.$strip>>, "data">>;
29
- //# sourceMappingURL=buildToolInput.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"buildToolInput.d.ts","sourceRoot":"","sources":["../../../src/utils/schemaConversion/buildToolInput.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;AAE1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,SAAS,CAAC,CAAC,WAAW,EAAE,4CAI7D;IACD,mGAAmG;IACnG,QAAQ,EAAE,SAAS,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,cAAc,CAAA;CAC3B,KAkCM,sBAAsB,CACzB;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM;;4FAAe,EAAE,MAAM,CAAC,CAE5E,CAAA"}
@@ -1,51 +0,0 @@
1
- import { z } from 'zod';
2
- /**
3
- * Builds a create/update tool's `input`: a `data` field (the document's fields) plus controls like
4
- * `depth` and `draft`. Using just `z.fromJSONSchema(ourJSONSchema)` has a few problems which are mitigated
5
- * in this function. z.fromJSONSchema is both bigger and lossier (≈40% larger on the lexical schema).
6
- * Each example below is `what we publish` => `what a plain zod round-trip would publish instead`.
7
- *
8
- * @example
9
- * Bigger - zod inlines shared defs instead of keeping our `$ref`s (and adds junk integer bounds), so a
10
- * reused def is duplicated at every use:
11
- * `{ $ref: '#/$defs/author' }` => `{ type: 'object', properties: { id, name }, additionalProperties: false }`
12
- *
13
- * @example
14
- * Lossier - zod drops the `description` on `enum`/`const` fields:
15
- * `{ enum: ['draft', 'published'], description: 'Publish state' }` → `{ enum: ['draft', 'published'] }`
16
- */ export const buildToolInput = ({ controls, dataDescription, dataSchema })=>{
17
- // `sanitizeEntitySchema` already emits draft 2020-12 with `$defs`, which is what zod's `fromJSONSchema`
18
- // and MCP clients both expect. Drop only the root `$schema` keyword, redundant once it's a sub-schema.
19
- const { $schema: _schema, ...entitySchema } = dataSchema;
20
- const schema = z.object({
21
- data: z.fromJSONSchema(entitySchema),
22
- ...controls
23
- });
24
- const standard = schema['~standard'];
25
- return {
26
- '~standard': {
27
- ...standard,
28
- jsonSchema: {
29
- ...standard.jsonSchema,
30
- input: (options)=>{
31
- // Start from zod's JSON Schema for the whole input, then replace its lossy `data` schema with
32
- // the sanitized entity schema. The entity's `$defs` move to the root so the `$ref`s resolve.
33
- const generated = standard.jsonSchema.input(options);
34
- const { $defs, ...entityBody } = entitySchema;
35
- generated.properties.data = {
36
- ...entityBody,
37
- description: dataDescription
38
- };
39
- if ($defs) {
40
- generated.$defs = $defs;
41
- } else {
42
- delete generated.$defs;
43
- }
44
- return generated;
45
- }
46
- }
47
- }
48
- };
49
- };
50
-
51
- //# sourceMappingURL=buildToolInput.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/utils/schemaConversion/buildToolInput.ts"],"sourcesContent":["import type { StandardSchemaWithJSON } from '@modelcontextprotocol/server'\n\nimport { z } from 'zod'\n\nimport type { JsonSchemaType } from '../../types.js'\n\n/**\n * Builds a create/update tool's `input`: a `data` field (the document's fields) plus controls like\n * `depth` and `draft`. Using just `z.fromJSONSchema(ourJSONSchema)` has a few problems which are mitigated\n * in this function. z.fromJSONSchema is both bigger and lossier (≈40% larger on the lexical schema).\n * Each example below is `what we publish` => `what a plain zod round-trip would publish instead`.\n *\n * @example\n * Bigger - zod inlines shared defs instead of keeping our `$ref`s (and adds junk integer bounds), so a\n * reused def is duplicated at every use:\n * `{ $ref: '#/$defs/author' }` => `{ type: 'object', properties: { id, name }, additionalProperties: false }`\n *\n * @example\n * Lossier - zod drops the `description` on `enum`/`const` fields:\n * `{ enum: ['draft', 'published'], description: 'Publish state' }` → `{ enum: ['draft', 'published'] }`\n */\nexport const buildToolInput = <TControls extends z.ZodRawShape>({\n controls,\n dataDescription,\n dataSchema,\n}: {\n /** Tool options alongside `data` (depth, draft, where, …) as plain zod - inferred into `input`. */\n controls: TControls\n dataDescription: string\n dataSchema: JsonSchemaType\n}) => {\n // `sanitizeEntitySchema` already emits draft 2020-12 with `$defs`, which is what zod's `fromJSONSchema`\n // and MCP clients both expect. Drop only the root `$schema` keyword, redundant once it's a sub-schema.\n const { $schema: _schema, ...entitySchema } = dataSchema as Record<string, unknown>\n\n const schema = z.object({\n data: z.fromJSONSchema(entitySchema as unknown as z.core.JSONSchema.JSONSchema),\n ...controls,\n })\n const standard = (schema as unknown as StandardSchemaWithJSON)['~standard']\n\n return {\n '~standard': {\n ...standard,\n jsonSchema: {\n ...standard.jsonSchema,\n input: (options) => {\n // Start from zod's JSON Schema for the whole input, then replace its lossy `data` schema with\n // the sanitized entity schema. The entity's `$defs` move to the root so the `$ref`s resolve.\n const generated = standard.jsonSchema.input(options)\n const { $defs, ...entityBody } = entitySchema\n ;(generated.properties as Record<string, unknown>).data = {\n ...entityBody,\n description: dataDescription,\n }\n if ($defs) {\n generated.$defs = $defs as object\n } else {\n delete generated.$defs\n }\n return generated\n },\n },\n },\n } as StandardSchemaWithJSON<\n { data: Record<string, unknown> } & Omit<z.output<typeof schema>, 'data'>\n >\n}\n"],"names":["z","buildToolInput","controls","dataDescription","dataSchema","$schema","_schema","entitySchema","schema","object","data","fromJSONSchema","standard","jsonSchema","input","options","generated","$defs","entityBody","properties","description"],"mappings":"AAEA,SAASA,CAAC,QAAQ,MAAK;AAIvB;;;;;;;;;;;;;;CAcC,GACD,OAAO,MAAMC,iBAAiB,CAAkC,EAC9DC,QAAQ,EACRC,eAAe,EACfC,UAAU,EAMX;IACC,wGAAwG;IACxG,uGAAuG;IACvG,MAAM,EAAEC,SAASC,OAAO,EAAE,GAAGC,cAAc,GAAGH;IAE9C,MAAMI,SAASR,EAAES,MAAM,CAAC;QACtBC,MAAMV,EAAEW,cAAc,CAACJ;QACvB,GAAGL,QAAQ;IACb;IACA,MAAMU,WAAW,AAACJ,MAA4C,CAAC,YAAY;IAE3E,OAAO;QACL,aAAa;YACX,GAAGI,QAAQ;YACXC,YAAY;gBACV,GAAGD,SAASC,UAAU;gBACtBC,OAAO,CAACC;oBACN,8FAA8F;oBAC9F,6FAA6F;oBAC7F,MAAMC,YAAYJ,SAASC,UAAU,CAACC,KAAK,CAACC;oBAC5C,MAAM,EAAEE,KAAK,EAAE,GAAGC,YAAY,GAAGX;oBAC/BS,UAAUG,UAAU,CAA6BT,IAAI,GAAG;wBACxD,GAAGQ,UAAU;wBACbE,aAAajB;oBACf;oBACA,IAAIc,OAAO;wBACTD,UAAUC,KAAK,GAAGA;oBACpB,OAAO;wBACL,OAAOD,UAAUC,KAAK;oBACxB;oBACA,OAAOD;gBACT;YACF;QACF;IACF;AAGF,EAAC"}
@@ -1,7 +0,0 @@
1
- import type { JsonSchemaType } from '../../types.js';
2
- /**
3
- * Removes virtual fields from a JSON Schema by name so they don't appear
4
- * in the generated MCP tool input schema.
5
- */
6
- export declare function removeVirtualFieldsFromSchema(schema: JsonSchemaType, virtualFieldNames: string[]): JsonSchemaType;
7
- //# sourceMappingURL=removeVirtualFieldsFromSchema.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"removeVirtualFieldsFromSchema.d.ts","sourceRoot":"","sources":["../../../src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,cAAc,EACtB,iBAAiB,EAAE,MAAM,EAAE,GAC1B,cAAc,CAiBhB"}
@@ -1,20 +0,0 @@
1
- /**
2
- * Removes virtual fields from a JSON Schema by name so they don't appear
3
- * in the generated MCP tool input schema.
4
- */ export function removeVirtualFieldsFromSchema(schema, virtualFieldNames) {
5
- if (virtualFieldNames.length === 0) {
6
- return schema;
7
- }
8
- for (const name of virtualFieldNames){
9
- delete schema?.properties?.[name];
10
- }
11
- if (Array.isArray(schema.required)) {
12
- schema.required = schema.required.filter((field)=>!virtualFieldNames.includes(field));
13
- if (schema.required.length === 0) {
14
- delete schema.required;
15
- }
16
- }
17
- return schema;
18
- }
19
-
20
- //# sourceMappingURL=removeVirtualFieldsFromSchema.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/utils/schemaConversion/removeVirtualFieldsFromSchema.ts"],"sourcesContent":["import type { JsonSchemaType } from '../../types.js'\n\n/**\n * Removes virtual fields from a JSON Schema by name so they don't appear\n * in the generated MCP tool input schema.\n */\nexport function removeVirtualFieldsFromSchema(\n schema: JsonSchemaType,\n virtualFieldNames: string[],\n): JsonSchemaType {\n if (virtualFieldNames.length === 0) {\n return schema\n }\n\n for (const name of virtualFieldNames) {\n delete schema?.properties?.[name]\n }\n\n if (Array.isArray(schema.required)) {\n schema.required = schema.required.filter((field) => !virtualFieldNames.includes(field))\n if (schema.required.length === 0) {\n delete schema.required\n }\n }\n\n return schema\n}\n"],"names":["removeVirtualFieldsFromSchema","schema","virtualFieldNames","length","name","properties","Array","isArray","required","filter","field","includes"],"mappings":"AAEA;;;CAGC,GACD,OAAO,SAASA,8BACdC,MAAsB,EACtBC,iBAA2B;IAE3B,IAAIA,kBAAkBC,MAAM,KAAK,GAAG;QAClC,OAAOF;IACT;IAEA,KAAK,MAAMG,QAAQF,kBAAmB;QACpC,OAAOD,QAAQI,YAAY,CAACD,KAAK;IACnC;IAEA,IAAIE,MAAMC,OAAO,CAACN,OAAOO,QAAQ,GAAG;QAClCP,OAAOO,QAAQ,GAAGP,OAAOO,QAAQ,CAACC,MAAM,CAAC,CAACC,QAAU,CAACR,kBAAkBS,QAAQ,CAACD;QAChF,IAAIT,OAAOO,QAAQ,CAACL,MAAM,KAAK,GAAG;YAChC,OAAOF,OAAOO,QAAQ;QACxB;IACF;IAEA,OAAOP;AACT"}
@@ -1,15 +0,0 @@
1
- import type { JsonSchemaType } from '../../types.js';
2
- /**
3
- * Turns the JSON Schema that Payload generates for a collection or global into the input schema for
4
- * an MCP create/update tool. In short, it:
5
- *
6
- * - drops fields a client can't set (`id`, `createdAt`, the draft `_status`, …),
7
- * - rewrites Payload-specific field shapes (points, relationships) into plain JSON the model can fill,
8
- * - and shrinks the result so it's cheaper for the model to read,
9
- *
10
- * while keeping every node valid JSON Schema draft 2020-12. Each step below is tagged with why it runs -
11
- * **Correctness** (valid input the API accepts), **Size** (equivalence-preserving shrink), or **LLM
12
- * ergonomics** (easier for the model to read/fill) - and carries a before/after example on its definition.
13
- */
14
- export declare const sanitizeEntitySchema: (schema: JsonSchemaType) => JsonSchemaType;
15
- //# sourceMappingURL=sanitizeEntitySchema.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sanitizeEntitySchema.d.ts","sourceRoot":"","sources":["../../../src/utils/schemaConversion/sanitizeEntitySchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,cAAc,KAAG,cA6B7D,CAAA"}
@@ -1,464 +0,0 @@
1
- /**
2
- * Turns the JSON Schema that Payload generates for a collection or global into the input schema for
3
- * an MCP create/update tool. In short, it:
4
- *
5
- * - drops fields a client can't set (`id`, `createdAt`, the draft `_status`, …),
6
- * - rewrites Payload-specific field shapes (points, relationships) into plain JSON the model can fill,
7
- * - and shrinks the result so it's cheaper for the model to read,
8
- *
9
- * while keeping every node valid JSON Schema draft 2020-12. Each step below is tagged with why it runs -
10
- * **Correctness** (valid input the API accepts), **Size** (equivalence-preserving shrink), or **LLM
11
- * ergonomics** (easier for the model to read/fill) - and carries a before/after example on its definition.
12
- */ export const sanitizeEntitySchema = (schema)=>{
13
- // Work on a copy — the caller reuses the original schema elsewhere (e.g. when listing tools).
14
- let result = structuredClone(schema);
15
- // Correctness — drop the fields a client can't set (id, createdAt, updatedAt, draft _status) + collapse nullable types.
16
- result = removeManagedFields(result);
17
- // LLM ergonomics — rewrite point fields from a `[number, number]` tuple into a `{ longitude, latitude }` object.
18
- result = pointFieldsToObjects(result);
19
- // Correctness — a relationship value can be an ID or a populated doc; on input only the ID is valid, so keep that.
20
- result = relationshipsToIds(result);
21
- // Size — strip inert type-gen leftovers that only bloat the schema (`tsType`, block-collision notes).
22
- result = removeTypeGenArtifacts(result);
23
- // Size — where a `const` already pins a value, the sibling `type` is redundant; remove it.
24
- result = dropRedundantConstType(result);
25
- // Size — fold per-collection relationship/upload variants (identical but for `relationTo`) into one `enum`.
26
- result = mergeConstDiscriminatedUnions(result);
27
- // Size — pull any subschema that appears more than once into a single shared `$defs` entry.
28
- result = deduplicateIntoDefinitions(result);
29
- // LLM ergonomics — give `$defs` short, readable names (`Code`, `paragraph`, `node`) so the `$ref`s read nicely.
30
- result = shortenDefinitionNames(result);
31
- return result;
32
- };
33
- /**
34
- * Rebuilds a schema bottom-up, calling `visit` on each node after its children. Shared by the
35
- * transforms that need to touch every node in the tree.
36
- */ const mapNodes = (node, visit)=>{
37
- if (Array.isArray(node)) {
38
- return node.map((child)=>mapNodes(child, visit));
39
- }
40
- if (!node || typeof node !== 'object') {
41
- return node;
42
- }
43
- const out = {};
44
- for (const [key, value] of Object.entries(node)){
45
- out[key] = mapNodes(value, visit);
46
- }
47
- return visit(out);
48
- };
49
- // Payload sets these on every document, so an MCP client never provides them when creating/updating.
50
- const PAYLOAD_MANAGED_FIELDS = new Set([
51
- '_status',
52
- 'createdAt',
53
- 'id',
54
- 'updatedAt'
55
- ]);
56
- /**
57
- * **Correctness.** Removes the fields a client can't set on create/update - `id`, `createdAt`, `updatedAt`, and the draft
58
- * `_status` - from every field object's `properties` and `required` (recursing into nested objects and
59
- * array items). Along the way it also collapses optional array/object types via {@link collapseNullableType}.
60
- *
61
- * @example
62
- * { properties: { id: { type: 'string' }, tags: { type: ['array', 'null'], items: {} } }, required: ['id', 'tags'] }
63
- * → { properties: { tags: { type: 'array', items: {} } }, required: ['tags'] }
64
- */ const removeManagedFields = (schema)=>{
65
- if (schema.properties && typeof schema.properties === 'object') {
66
- for (const field of PAYLOAD_MANAGED_FIELDS){
67
- delete schema.properties[field];
68
- }
69
- for (const key of Object.keys(schema.properties)){
70
- const prop = schema.properties[key];
71
- if (!prop || typeof prop !== 'object') {
72
- continue;
73
- }
74
- const isRequired = Array.isArray(schema.required) && schema.required.includes(key);
75
- collapseNullableType(prop, isRequired);
76
- if (prop.properties) {
77
- removeManagedFields(prop);
78
- }
79
- if (prop.items && typeof prop.items === 'object' && !Array.isArray(prop.items)) {
80
- removeManagedFields(prop.items);
81
- }
82
- }
83
- }
84
- if (Array.isArray(schema.required)) {
85
- schema.required = schema.required.filter((name)=>!PAYLOAD_MANAGED_FIELDS.has(name));
86
- if (schema.required.length === 0) {
87
- delete schema.required;
88
- }
89
- }
90
- return schema;
91
- };
92
- /**
93
- * Drops the `'null'` from an optional array/object type (`['array', 'null']` → `'array'`, likewise
94
- * `'object'`). Payload marks a field optional by unioning its type with `'null'`, but a create/update
95
- * tool already conveys "optional" through the `required` list - the client just omits the field - so the
96
- * `'null'` is redundant here. Dropping it shows the model a plain `type: 'array'` instead of implying it
97
- * should send a literal `null`.
98
- *
99
- * We never touch the `required` list, so this can't change whether a field is required. And we skip fields
100
- * that ARE required (`isRequired`): there a `'null'` is a value the field genuinely accepts, not optionality
101
- * encoding. Nullable scalars (`['string', 'null']`) are also left alone - they read fine as `string | null`.
102
- *
103
- * @example
104
- * optional { type: ['array', 'null'] } → { type: 'array' } (still optional, just no longer null-valued)
105
- * required { type: ['array', 'null'] } → unchanged
106
- * { type: ['string', 'null'] } → unchanged
107
- */ const collapseNullableType = (schema, isRequired)=>{
108
- if (isRequired || !Array.isArray(schema.type)) {
109
- return;
110
- }
111
- const nonNullTypes = schema.type.filter((t)=>t !== 'null');
112
- if (nonNullTypes.length === 1 && (nonNullTypes[0] === 'array' || nonNullTypes[0] === 'object')) {
113
- schema.type = nonNullTypes[0];
114
- }
115
- };
116
- /**
117
- * **LLM ergonomics.** Rewrites a point field (stored as a two-number tuple) into a `{ longitude, latitude }` object, which
118
- * is far easier for the model to fill in than a positional array. This is input-only and lossless: the
119
- * create/update handler converts the object back to the `[longitude, latitude]` tuple Payload stores,
120
- * via `transformPointDataToPayload`.
121
- *
122
- * @example
123
- * { type: 'array', items: [{ type: 'number' }, { type: 'number' }] }
124
- * → { type: 'object', properties: { longitude: { type: 'number' }, latitude: { type: 'number' } }, required: ['longitude', 'latitude'] }
125
- */ const pointFieldsToObjects = (schema)=>{
126
- if (!schema || typeof schema !== 'object') {
127
- return schema;
128
- }
129
- const transformed = {
130
- ...schema
131
- };
132
- if (transformed.properties && typeof transformed.properties === 'object') {
133
- transformed.properties = Object.fromEntries(Object.entries(transformed.properties).map(([key, value])=>{
134
- if (!value || typeof value !== 'object') {
135
- return [
136
- key,
137
- value
138
- ];
139
- }
140
- const isArrayType = value.type === 'array' || Array.isArray(value.type) && value.type.includes('array');
141
- const isPointField = isArrayType && Array.isArray(value.items) && value.items.length === 2 && value.items.every((item)=>item && typeof item === 'object' && item.type === 'number');
142
- if (isPointField) {
143
- const isNullable = Array.isArray(value.type) && value.type.includes('null');
144
- return [
145
- key,
146
- {
147
- type: isNullable ? [
148
- 'object',
149
- 'null'
150
- ] : 'object',
151
- description: value.description || 'Geographic coordinates (longitude, latitude)',
152
- properties: {
153
- latitude: {
154
- type: 'number',
155
- description: 'Latitude coordinate'
156
- },
157
- longitude: {
158
- type: 'number',
159
- description: 'Longitude coordinate'
160
- }
161
- },
162
- required: [
163
- 'longitude',
164
- 'latitude'
165
- ]
166
- }
167
- ];
168
- }
169
- return [
170
- key,
171
- pointFieldsToObjects(value)
172
- ];
173
- }));
174
- }
175
- if (transformed.items && typeof transformed.items === 'object' && !Array.isArray(transformed.items)) {
176
- transformed.items = pointFieldsToObjects(transformed.items);
177
- }
178
- return transformed;
179
- };
180
- /**
181
- * **Correctness.** Reduces relationship/upload fields to the IDs a client actually sends. Payload types the value as
182
- * "an ID or the full related document" - but the populated-document form only appears in read responses;
183
- * on create/update you always reference a relationship by its ID. So we drop that `$ref` option, leaving
184
- * the bare ID. A single remaining target collapses inline (with a description naming the target
185
- * collection); several targets become an `anyOf` of IDs.
186
- *
187
- * @example
188
- * { oneOf: [{ type: 'string' }, { $ref: '#/$defs/posts' }] }
189
- * → { type: 'string', description: 'The ID of the related "posts" document.' }
190
- */ const relationshipsToIds = (schema)=>{
191
- if (!schema || typeof schema !== 'object') {
192
- return schema;
193
- }
194
- const processed = {
195
- ...schema
196
- };
197
- if (Array.isArray(processed.oneOf)) {
198
- const isRelatedDocRef = (option)=>!!option && typeof option === 'object' && '$ref' in option;
199
- if (processed.oneOf.some(isRelatedDocRef)) {
200
- // A relationship value is "an ID, or the populated related document". Keep the ID option(s) and
201
- // drop the `$ref`s to the related collections, since a client only ever sends the ID.
202
- const idOptions = processed.oneOf.filter((option)=>!isRelatedDocRef(option)).map((option)=>typeof option === 'object' ? relationshipsToIds(option) : option);
203
- const targetCollections = processed.oneOf.filter(isRelatedDocRef).map((option)=>option.$ref.replace('#/$defs/', ''));
204
- if (idOptions.length === 1) {
205
- delete processed.oneOf;
206
- Object.assign(processed, idOptions[0]);
207
- if (targetCollections.length > 0 && !processed.description) {
208
- processed.description = `The ID of the related "${targetCollections.join('" or "')}" document.`;
209
- }
210
- } else if (idOptions.length > 1) {
211
- delete processed.oneOf;
212
- processed.anyOf = idOptions;
213
- }
214
- } else {
215
- processed.oneOf = processed.oneOf.map((option)=>typeof option === 'object' ? relationshipsToIds(option) : option);
216
- }
217
- }
218
- if (processed.properties && typeof processed.properties === 'object') {
219
- processed.properties = Object.fromEntries(Object.entries(processed.properties).map(([key, value])=>[
220
- key,
221
- typeof value === 'object' ? relationshipsToIds(value) : value
222
- ]));
223
- }
224
- if (processed.items && typeof processed.items === 'object' && !Array.isArray(processed.items)) {
225
- processed.items = relationshipsToIds(processed.items);
226
- }
227
- // Lexical node unions and blocks live under `$defs` and have their own relationship fields, so walk
228
- // those too — otherwise their `$ref`s would dangle once we don't bundle the related collections.
229
- if (processed.$defs && typeof processed.$defs === 'object') {
230
- processed.$defs = Object.fromEntries(Object.entries(processed.$defs).map(([key, value])=>[
231
- key,
232
- typeof value === 'object' ? relationshipsToIds(value) : value
233
- ]));
234
- }
235
- return processed;
236
- };
237
- /**
238
- * **Size.** Strips type-generation leftovers that bloat the schema without helping the model: the `tsType`
239
- * hint (a `json-schema-to-typescript` extension; JSON Schema validators just ignore it, they don't reject it)
240
- * and the block-interface-collision note Payload adds to some block descriptions (the
241
- * `block-interface-name-collisions` docs link set in `registerBlockInterface`, see configToJSONSchema.ts).
242
- * Both are inert here - removing them only shrinks the schema (and spares the model irrelevant noise).
243
- *
244
- * @example
245
- * { type: 'object', tsType: 'SerializedBlockNode', description: 'see …#block-interface-name-collisions' }
246
- * → { type: 'object' }
247
- */ const removeTypeGenArtifacts = (schema)=>mapNodes(schema, (node)=>{
248
- delete node.tsType;
249
- const { description } = node;
250
- if (typeof description === 'string' && description.includes('block-interface-name-collisions')) {
251
- delete node.description;
252
- }
253
- return node;
254
- });
255
- /**
256
- * **Size.** Removes `type` whenever a `const` sits next to it — the constant already fixes the value.
257
- *
258
- * @example
259
- * { type: 'string', const: 'paragraph' } → { const: 'paragraph' }
260
- */ const dropRedundantConstType = (schema)=>mapNodes(schema, (node)=>{
261
- if ('const' in node && 'type' in node) {
262
- delete node.type;
263
- }
264
- return node;
265
- });
266
- /**
267
- * **Size.** Merges the members of a `oneOf`/`anyOf` that are identical except for one `const`-valued property
268
- * into a single member with that property as an `enum`. Since the members differ only by that one
269
- * constant, the `enum` form accepts exactly the same values - it's just smaller. This folds the
270
- * per-collection relationship and upload variants, which differ only in their `relationTo` constant.
271
- *
272
- * @example
273
- * { oneOf: [
274
- * { properties: { relationTo: { const: 'posts' }, value: { type: 'string' } } },
275
- * { properties: { relationTo: { const: 'pages' }, value: { type: 'string' } } },
276
- * ] }
277
- * → { properties: { relationTo: { enum: ['posts', 'pages'] }, value: { type: 'string' } } }
278
- */ const mergeConstDiscriminatedUnions = (schema)=>mapNodes(schema, (node)=>{
279
- for (const keyword of [
280
- 'oneOf',
281
- 'anyOf'
282
- ]){
283
- const members = node[keyword];
284
- if (Array.isArray(members) && members.length > 1) {
285
- const merged = mergeMembersByConst(members);
286
- if (merged) {
287
- delete node[keyword];
288
- Object.assign(node, merged);
289
- }
290
- }
291
- }
292
- return node;
293
- });
294
- /** Returns the merged member for {@link mergeConstDiscriminatedUnions}, or `null` if they can't merge. */ const mergeMembersByConst = (members)=>{
295
- const objects = members.filter((member)=>typeof member === 'object' && member !== null && typeof member.properties === 'object');
296
- if (objects.length < 2 || objects.length !== members.length) {
297
- return null;
298
- }
299
- for (const discriminator of Object.keys(objects[0].properties)){
300
- // Each member must pin this property to a `const`...
301
- const constValues = [];
302
- const everyMemberPinsConst = objects.every((member)=>{
303
- const prop = member.properties[discriminator];
304
- if (prop && typeof prop === 'object' && 'const' in prop) {
305
- constValues.push(prop.const);
306
- return true;
307
- }
308
- return false;
309
- });
310
- if (!everyMemberPinsConst) {
311
- continue;
312
- }
313
- // ...and be otherwise identical (compare each member with the discriminator removed).
314
- const fingerprintWithoutDiscriminator = (member)=>{
315
- const { [discriminator]: _discriminator, ...otherProperties } = member.properties;
316
- return JSON.stringify({
317
- ...member,
318
- properties: otherProperties
319
- });
320
- };
321
- if (new Set(objects.map(fingerprintWithoutDiscriminator)).size !== 1) {
322
- continue;
323
- }
324
- const uniqueConstValues = [
325
- ...new Set(constValues)
326
- ];
327
- if (uniqueConstValues.length < 2) {
328
- continue;
329
- }
330
- // Replace the per-member `const` with a single `enum` of every value.
331
- const merged = structuredClone(objects[0]);
332
- const discriminatorProp = merged.properties[discriminator];
333
- if (discriminatorProp && typeof discriminatorProp === 'object') {
334
- delete discriminatorProp.const;
335
- discriminatorProp.enum = uniqueConstValues;
336
- }
337
- return merged;
338
- }
339
- return null;
340
- };
341
- // A `$ref` costs ~30 characters once names are shortened, so sharing a subschema only pays off when
342
- // it's larger than that and appears more than once.
343
- const MIN_SHARED_SIZE = 64;
344
- /** A subschema worth sharing: a standalone object/union/described schema, not a `$ref` or a primitive. */ const isShareable = (node)=>{
345
- if (!node || typeof node !== 'object' || Array.isArray(node) || '$ref' in node) {
346
- return false;
347
- }
348
- const schema = node;
349
- return Array.isArray(schema.oneOf) || Array.isArray(schema.anyOf) || Boolean(schema.properties) || Boolean(schema.items) || typeof schema.description === 'string';
350
- };
351
- /**
352
- * **Size.** Replaces any subschema that appears more than once with a single shared `$defs` entry referenced by
353
- * `$ref`. Lossless — only the serialized size shrinks. A collection with several rich-text fields, for
354
- * example, inlines the same large lexical node schema once per field; this collapses them into one.
355
- *
356
- * @example
357
- * { properties: { billing: address, shipping: address } }
358
- * → { properties: { billing: { $ref: '#/$defs/shared_0' }, shipping: { $ref: '#/$defs/shared_0' } }, $defs: { shared_0: address } }
359
- */ const deduplicateIntoDefinitions = (schema)=>{
360
- // Count how often each shareable subschema appears, keyed by its serialized form.
361
- const counts = new Map();
362
- const count = (node)=>{
363
- if (isShareable(node)) {
364
- const key = JSON.stringify(node);
365
- if (key.length >= MIN_SHARED_SIZE) {
366
- counts.set(key, (counts.get(key) ?? 0) + 1);
367
- }
368
- }
369
- if (Array.isArray(node)) {
370
- node.forEach(count);
371
- } else if (node && typeof node === 'object') {
372
- Object.values(node).forEach(count);
373
- }
374
- };
375
- count(schema);
376
- if (![
377
- ...counts.values()
378
- ].some((n)=>n >= 2)) {
379
- return schema;
380
- }
381
- // Replace each subschema seen 2+ times with a `$ref`. The first time we meet one it becomes a shared
382
- // entry; we never descend into a stored entry, so shared entries never reference one another.
383
- const sharedEntries = {};
384
- const nameByKey = new Map();
385
- const share = (node)=>{
386
- if (isShareable(node)) {
387
- const key = JSON.stringify(node);
388
- if ((counts.get(key) ?? 0) >= 2) {
389
- let name = nameByKey.get(key);
390
- if (!name) {
391
- name = `shared_${nameByKey.size}`;
392
- nameByKey.set(key, name);
393
- sharedEntries[name] = node;
394
- }
395
- return {
396
- $ref: `#/$defs/${name}`
397
- };
398
- }
399
- }
400
- if (Array.isArray(node)) {
401
- return node.map(share);
402
- }
403
- if (node && typeof node === 'object') {
404
- return Object.fromEntries(Object.entries(node).map(([childKey, value])=>[
405
- childKey,
406
- share(value)
407
- ]));
408
- }
409
- return node;
410
- };
411
- const result = share(schema);
412
- result.$defs = {
413
- ...result.$defs,
414
- ...sharedEntries
415
- };
416
- return result;
417
- };
418
- /**
419
- * **LLM ergonomics.** Renames `$defs` entries to short, readable names so the `$ref`s stay legible to the model: a block's
420
- * `blockType` (`Code`), a node's `type` (`paragraph`), `node` for a rich-text node union, otherwise the
421
- * generated name with its disambiguating hash dropped. A numeric suffix keeps collisions unique.
422
- *
423
- * @example
424
- * { properties: { body: { $ref: '#/$defs/LexicalNodes_9FBEC708' } }, $defs: { LexicalNodes_9FBEC708: {} } }
425
- * → { properties: { body: { $ref: '#/$defs/node' } }, $defs: { node: {} } }
426
- */ const shortenDefinitionNames = (schema)=>{
427
- const definitions = schema.$defs;
428
- if (!definitions || Object.keys(definitions).length === 0) {
429
- return schema;
430
- }
431
- const shortNameFor = (name, definition)=>{
432
- const properties = typeof definition === 'object' && definition.properties || {};
433
- const constString = (key)=>{
434
- const prop = properties[key];
435
- return prop && typeof prop === 'object' && typeof prop.const === 'string' ? prop.const : undefined;
436
- };
437
- const members = typeof definition === 'object' ? definition.anyOf ?? definition.oneOf : undefined;
438
- const isNodeUnion = Array.isArray(members) && members.some((m)=>typeof m === 'object' && Boolean(m.$ref || m.properties?.type));
439
- return constString('blockType') ?? constString('type') ?? (isNodeUnion ? 'node' : undefined) ?? name.replace(/_[0-9A-F]{6,}$/i, '');
440
- };
441
- const prefix = '#/$defs/';
442
- const usedNames = new Map();
443
- const rename = new Map();
444
- for (const [name, definition] of Object.entries(definitions)){
445
- const shortName = shortNameFor(name, definition);
446
- const used = usedNames.get(shortName) ?? 0;
447
- usedNames.set(shortName, used + 1);
448
- rename.set(name, used === 0 ? shortName : `${shortName}${used + 1}`);
449
- }
450
- const result = mapNodes(schema, (node)=>{
451
- if (typeof node.$ref === 'string' && node.$ref.startsWith(prefix)) {
452
- const name = node.$ref.slice(prefix.length);
453
- node.$ref = `${prefix}${rename.get(name) ?? name}`;
454
- }
455
- return node;
456
- });
457
- result.$defs = Object.fromEntries(Object.entries(result.$defs).map(([name, body])=>[
458
- rename.get(name) ?? name,
459
- body
460
- ]));
461
- return result;
462
- };
463
-
464
- //# sourceMappingURL=sanitizeEntitySchema.js.map