payload-mcp-toolkit 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -0
  3. package/dist/__tests__/introspection.test.js +364 -0
  4. package/dist/__tests__/introspection.test.js.map +1 -0
  5. package/dist/__tests__/url-validator.test.js +326 -0
  6. package/dist/__tests__/url-validator.test.js.map +1 -0
  7. package/dist/draft-workflow.d.ts +60 -0
  8. package/dist/draft-workflow.js +93 -0
  9. package/dist/draft-workflow.js.map +1 -0
  10. package/dist/index.d.ts +24 -0
  11. package/dist/index.js +142 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/introspection.d.ts +23 -0
  14. package/dist/introspection.js +238 -0
  15. package/dist/introspection.js.map +1 -0
  16. package/dist/prompts.d.ts +21 -0
  17. package/dist/prompts.js +215 -0
  18. package/dist/prompts.js.map +1 -0
  19. package/dist/rate-limiter.d.ts +25 -0
  20. package/dist/rate-limiter.js +51 -0
  21. package/dist/rate-limiter.js.map +1 -0
  22. package/dist/resources.d.ts +18 -0
  23. package/dist/resources.js +77 -0
  24. package/dist/resources.js.map +1 -0
  25. package/dist/tools/compose-helpers.d.ts +117 -0
  26. package/dist/tools/compose-helpers.js +236 -0
  27. package/dist/tools/compose-helpers.js.map +1 -0
  28. package/dist/tools/compose-layout.d.ts +139 -0
  29. package/dist/tools/compose-layout.js +61 -0
  30. package/dist/tools/compose-layout.js.map +1 -0
  31. package/dist/tools/patch-layout.d.ts +107 -0
  32. package/dist/tools/patch-layout.js +123 -0
  33. package/dist/tools/patch-layout.js.map +1 -0
  34. package/dist/tools/publish-draft.d.ts +24 -0
  35. package/dist/tools/publish-draft.js +69 -0
  36. package/dist/tools/publish-draft.js.map +1 -0
  37. package/dist/tools/resolve-reference.d.ts +31 -0
  38. package/dist/tools/resolve-reference.js +169 -0
  39. package/dist/tools/resolve-reference.js.map +1 -0
  40. package/dist/tools/safe-delete.d.ts +37 -0
  41. package/dist/tools/safe-delete.js +161 -0
  42. package/dist/tools/safe-delete.js.map +1 -0
  43. package/dist/tools/schedule-publish.d.ts +49 -0
  44. package/dist/tools/schedule-publish.js +120 -0
  45. package/dist/tools/schedule-publish.js.map +1 -0
  46. package/dist/tools/search-content.d.ts +43 -0
  47. package/dist/tools/search-content.js +210 -0
  48. package/dist/tools/search-content.js.map +1 -0
  49. package/dist/tools/update-document.d.ts +32 -0
  50. package/dist/tools/update-document.js +114 -0
  51. package/dist/tools/update-document.js.map +1 -0
  52. package/dist/tools/upload-media.d.ts +26 -0
  53. package/dist/tools/upload-media.js +115 -0
  54. package/dist/tools/upload-media.js.map +1 -0
  55. package/dist/tools/versions.d.ts +50 -0
  56. package/dist/tools/versions.js +159 -0
  57. package/dist/tools/versions.js.map +1 -0
  58. package/dist/types.d.ts +118 -0
  59. package/dist/types.js +3 -0
  60. package/dist/types.js.map +1 -0
  61. package/dist/url-validator.d.ts +36 -0
  62. package/dist/url-validator.js +222 -0
  63. package/dist/url-validator.js.map +1 -0
  64. package/package.json +85 -0
package/dist/index.js ADDED
@@ -0,0 +1,142 @@
1
+ import { mcpPlugin } from '@payloadcms/plugin-mcp';
2
+ import { introspectCollections, introspectBlocks, buildRelationshipGraph } from './introspection';
3
+ import { generatePrompts } from './prompts';
4
+ import { generateResources } from './resources';
5
+ import { generateMcpCollectionConfigs } from './draft-workflow';
6
+ import { createComposeLayoutTool } from './tools/compose-layout';
7
+ import { createPatchLayoutTool } from './tools/patch-layout';
8
+ import { createPublishDraftTool } from './tools/publish-draft';
9
+ import { createResolveReferenceTool } from './tools/resolve-reference';
10
+ import { createSafeDeleteTool } from './tools/safe-delete';
11
+ import { createSchedulePublishTool } from './tools/schedule-publish';
12
+ import { createSearchContentTool } from './tools/search-content';
13
+ import { createUpdateDocumentTool } from './tools/update-document';
14
+ import { createUploadMediaTool } from './tools/upload-media';
15
+ import { createListVersionsTool, createRestoreVersionTool } from './tools/versions';
16
+ /**
17
+ * Payload MCP Toolkit
18
+ *
19
+ * A wrapper plugin that introspects the Payload config and generates
20
+ * domain-aware MCP tools, prompts, and resources for AI content management.
21
+ *
22
+ * Usage in payload.config.ts:
23
+ * ```ts
24
+ * import { contentToolkitPlugin } from 'payload-mcp-toolkit'
25
+ *
26
+ * plugins: [
27
+ * contentToolkitPlugin({
28
+ * siteUrl: process.env.SITE_URL!,
29
+ * previewSecret: process.env.PREVIEW_SECRET!,
30
+ * previewPaths: { posts: '/blog', pages: '' },
31
+ * draftBehavior: { pages: 'always-draft' },
32
+ * }),
33
+ * ]
34
+ * ```
35
+ */ export function contentToolkitPlugin(options) {
36
+ return (incomingConfig)=>{
37
+ const collections = incomingConfig.collections ?? [];
38
+ const allBlocks = incomingConfig.blocks ?? [];
39
+ // Separate section blocks from leaf blocks.
40
+ //
41
+ // Preferred: `options.sectionBlockSlugs` — explicit, unambiguous.
42
+ // Fallback heuristic: blocks containing a nested `blocks`-type field are
43
+ // sections, all others are leaves. The heuristic mis-classifies "fixed"
44
+ // sections (no nested blocks but their own standalone fields), so prefer
45
+ // the explicit option whenever the schema has any fixed sections.
46
+ const sectionBlocks = [];
47
+ const leafBlocks = [];
48
+ if (options.sectionBlockSlugs && options.sectionBlockSlugs.length > 0) {
49
+ const sectionSlugs = new Set(options.sectionBlockSlugs);
50
+ for (const block of allBlocks){
51
+ if (sectionSlugs.has(block.slug)) {
52
+ sectionBlocks.push(block);
53
+ } else {
54
+ leafBlocks.push(block);
55
+ }
56
+ }
57
+ } else {
58
+ for (const block of allBlocks){
59
+ const hasBlocksField = block.fields.some((f)=>f.type === 'blocks' || f.type === 'tabs' && 'tabs' in f && f.tabs.some((tab)=>'fields' in tab && tab.fields.some((tf)=>tf.type === 'blocks')));
60
+ if (hasBlocksField) {
61
+ sectionBlocks.push(block);
62
+ } else {
63
+ leafBlocks.push(block);
64
+ }
65
+ }
66
+ }
67
+ // 1. Schema Introspection
68
+ const collectionSchemas = introspectCollections(collections);
69
+ const blockCatalog = introspectBlocks(sectionBlocks, leafBlocks);
70
+ const relationships = buildRelationshipGraph(collectionSchemas);
71
+ // 2. Generate Prompts
72
+ const prompts = generatePrompts(collectionSchemas, blockCatalog, relationships, options.domainPrompts);
73
+ // 3. Generate Resources
74
+ const resources = generateResources(collectionSchemas, blockCatalog, relationships);
75
+ // 4. Generate Tools
76
+ const searchableCollections = new Map();
77
+ for (const [slug, schema] of collectionSchemas){
78
+ if (schema.searchableFields.length > 0) {
79
+ searchableCollections.set(slug, schema.searchableFields);
80
+ }
81
+ }
82
+ // 5. Generate MCP Collection Configs with draft workflow
83
+ const { mcpCollections, draftCollections } = generateMcpCollectionConfigs(collections, {
84
+ siteUrl: options.siteUrl,
85
+ previewSecret: options.previewSecret,
86
+ previewPaths: options.previewPaths,
87
+ draftBehavior: options.draftBehavior,
88
+ excludeCollections: options.excludeCollections
89
+ });
90
+ const tools = [
91
+ createComposeLayoutTool(blockCatalog),
92
+ createPatchLayoutTool(blockCatalog, draftCollections),
93
+ createPublishDraftTool(draftCollections),
94
+ createResolveReferenceTool(searchableCollections),
95
+ createSafeDeleteTool(relationships),
96
+ createSearchContentTool(collectionSchemas),
97
+ createUpdateDocumentTool(collectionSchemas, draftCollections),
98
+ createUploadMediaTool({
99
+ maxFileSize: options.mediaUpload?.maxFileSize,
100
+ collectionSlug: options.mediaUpload?.collectionSlug
101
+ }),
102
+ createListVersionsTool(draftCollections),
103
+ createRestoreVersionTool(draftCollections)
104
+ ];
105
+ // schedulePublish only registers when at least one draft collection has a `publishedAt` date field
106
+ const schedulePublish = createSchedulePublishTool(collectionSchemas, draftCollections);
107
+ if (schedulePublish) tools.push(schedulePublish);
108
+ // Build MCP global configs
109
+ const mcpGlobals = {};
110
+ const excludeGlobalSlugs = new Set(options.excludeGlobals ?? []);
111
+ for (const global of incomingConfig.globals ?? []){
112
+ if (excludeGlobalSlugs.has(global.slug)) continue;
113
+ mcpGlobals[global.slug] = {
114
+ enabled: true,
115
+ description: `Manage ${global.slug} global settings`
116
+ };
117
+ }
118
+ // Apply the official MCP plugin with our generated config
119
+ const withMcp = mcpPlugin({
120
+ collections: mcpCollections,
121
+ globals: mcpGlobals,
122
+ mcp: {
123
+ tools: tools,
124
+ prompts: prompts,
125
+ resources: resources
126
+ },
127
+ // Set req.user from the API key's linked user so custom tools
128
+ // can use overrideAccess: false and relationship field validation
129
+ // has a valid user context for access control checks.
130
+ // Safe: getDefault() throws inside the official plugin if the API key
131
+ // has no linked user, so settings.user is guaranteed to exist here.
132
+ overrideAuth: async (req, getDefault)=>{
133
+ const settings = await getDefault();
134
+ req.user = settings.user;
135
+ return settings;
136
+ }
137
+ });
138
+ return withMcp(incomingConfig);
139
+ };
140
+ }
141
+
142
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Block, CollectionConfig, Config, Plugin } from 'payload'\nimport { mcpPlugin } from '@payloadcms/plugin-mcp'\nimport type { ContentToolkitOptions } from './types'\nimport { introspectCollections, introspectBlocks, buildRelationshipGraph } from './introspection'\nimport { generatePrompts } from './prompts'\nimport { generateResources } from './resources'\nimport { generateMcpCollectionConfigs } from './draft-workflow'\nimport { createComposeLayoutTool } from './tools/compose-layout'\nimport { createPatchLayoutTool } from './tools/patch-layout'\nimport { createPublishDraftTool } from './tools/publish-draft'\nimport { createResolveReferenceTool } from './tools/resolve-reference'\nimport { createSafeDeleteTool } from './tools/safe-delete'\nimport { createSchedulePublishTool } from './tools/schedule-publish'\nimport { createSearchContentTool } from './tools/search-content'\nimport { createUpdateDocumentTool } from './tools/update-document'\nimport { createUploadMediaTool } from './tools/upload-media'\nimport { createListVersionsTool, createRestoreVersionTool } from './tools/versions'\n\n/**\n * Payload MCP Toolkit\n *\n * A wrapper plugin that introspects the Payload config and generates\n * domain-aware MCP tools, prompts, and resources for AI content management.\n *\n * Usage in payload.config.ts:\n * ```ts\n * import { contentToolkitPlugin } from 'payload-mcp-toolkit'\n *\n * plugins: [\n * contentToolkitPlugin({\n * siteUrl: process.env.SITE_URL!,\n * previewSecret: process.env.PREVIEW_SECRET!,\n * previewPaths: { posts: '/blog', pages: '' },\n * draftBehavior: { pages: 'always-draft' },\n * }),\n * ]\n * ```\n */\nexport function contentToolkitPlugin(options: ContentToolkitOptions): Plugin {\n return (incomingConfig: Config): Config => {\n const collections = (incomingConfig.collections ?? []) as CollectionConfig[]\n const allBlocks = (incomingConfig.blocks ?? []) as Block[]\n\n // Separate section blocks from leaf blocks.\n //\n // Preferred: `options.sectionBlockSlugs` — explicit, unambiguous.\n // Fallback heuristic: blocks containing a nested `blocks`-type field are\n // sections, all others are leaves. The heuristic mis-classifies \"fixed\"\n // sections (no nested blocks but their own standalone fields), so prefer\n // the explicit option whenever the schema has any fixed sections.\n const sectionBlocks: Block[] = []\n const leafBlocks: Block[] = []\n\n if (options.sectionBlockSlugs && options.sectionBlockSlugs.length > 0) {\n const sectionSlugs = new Set(options.sectionBlockSlugs)\n for (const block of allBlocks) {\n if (sectionSlugs.has(block.slug)) {\n sectionBlocks.push(block)\n } else {\n leafBlocks.push(block)\n }\n }\n } else {\n for (const block of allBlocks) {\n const hasBlocksField = block.fields.some(\n (f) => f.type === 'blocks' ||\n (f.type === 'tabs' && 'tabs' in f && f.tabs.some(\n (tab: any) => 'fields' in tab && tab.fields.some((tf: any) => tf.type === 'blocks')\n ))\n )\n if (hasBlocksField) {\n sectionBlocks.push(block)\n } else {\n leafBlocks.push(block)\n }\n }\n }\n\n // 1. Schema Introspection\n const collectionSchemas = introspectCollections(collections)\n const blockCatalog = introspectBlocks(sectionBlocks, leafBlocks)\n const relationships = buildRelationshipGraph(collectionSchemas)\n\n // 2. Generate Prompts\n const prompts = generatePrompts(\n collectionSchemas,\n blockCatalog,\n relationships,\n options.domainPrompts,\n )\n\n // 3. Generate Resources\n const resources = generateResources(collectionSchemas, blockCatalog, relationships)\n\n // 4. Generate Tools\n const searchableCollections = new Map<string, string[]>()\n for (const [slug, schema] of collectionSchemas) {\n if (schema.searchableFields.length > 0) {\n searchableCollections.set(slug, schema.searchableFields)\n }\n }\n\n // 5. Generate MCP Collection Configs with draft workflow\n const { mcpCollections, draftCollections } = generateMcpCollectionConfigs(collections, {\n siteUrl: options.siteUrl,\n previewSecret: options.previewSecret,\n previewPaths: options.previewPaths,\n draftBehavior: options.draftBehavior,\n excludeCollections: options.excludeCollections,\n })\n\n const tools: any[] = [\n createComposeLayoutTool(blockCatalog),\n createPatchLayoutTool(blockCatalog, draftCollections),\n createPublishDraftTool(draftCollections),\n createResolveReferenceTool(searchableCollections),\n createSafeDeleteTool(relationships),\n createSearchContentTool(collectionSchemas),\n createUpdateDocumentTool(collectionSchemas, draftCollections),\n createUploadMediaTool({\n maxFileSize: options.mediaUpload?.maxFileSize,\n collectionSlug: options.mediaUpload?.collectionSlug,\n }),\n createListVersionsTool(draftCollections),\n createRestoreVersionTool(draftCollections),\n ]\n\n // schedulePublish only registers when at least one draft collection has a `publishedAt` date field\n const schedulePublish = createSchedulePublishTool(collectionSchemas, draftCollections)\n if (schedulePublish) tools.push(schedulePublish)\n\n // Build MCP global configs\n const mcpGlobals: Record<string, { enabled: boolean; description?: string }> = {}\n const excludeGlobalSlugs = new Set(options.excludeGlobals ?? [])\n\n for (const global of (incomingConfig.globals ?? []) as Array<{ slug: string }>) {\n if (excludeGlobalSlugs.has(global.slug)) continue\n mcpGlobals[global.slug] = {\n enabled: true,\n description: `Manage ${global.slug} global settings`,\n }\n }\n\n // Apply the official MCP plugin with our generated config\n const withMcp = mcpPlugin({\n collections: mcpCollections as any,\n globals: mcpGlobals as any,\n mcp: {\n tools: tools as any[],\n prompts: prompts as any[],\n resources: resources as any[],\n },\n // Set req.user from the API key's linked user so custom tools\n // can use overrideAccess: false and relationship field validation\n // has a valid user context for access control checks.\n // Safe: getDefault() throws inside the official plugin if the API key\n // has no linked user, so settings.user is guaranteed to exist here.\n overrideAuth: async (req, getDefault) => {\n const settings = await getDefault()\n req.user = (settings as any).user\n return settings\n },\n })\n\n return withMcp(incomingConfig)\n }\n}\n\nexport type {\n ContentToolkitOptions,\n DomainPrompt,\n DraftBehavior,\n CollectionSchema,\n BlockCatalog,\n SectionBlockSchema,\n LeafBlockSchema,\n RelationshipEdge,\n FieldSchema,\n} from './types'\n"],"names":["mcpPlugin","introspectCollections","introspectBlocks","buildRelationshipGraph","generatePrompts","generateResources","generateMcpCollectionConfigs","createComposeLayoutTool","createPatchLayoutTool","createPublishDraftTool","createResolveReferenceTool","createSafeDeleteTool","createSchedulePublishTool","createSearchContentTool","createUpdateDocumentTool","createUploadMediaTool","createListVersionsTool","createRestoreVersionTool","contentToolkitPlugin","options","incomingConfig","collections","allBlocks","blocks","sectionBlocks","leafBlocks","sectionBlockSlugs","length","sectionSlugs","Set","block","has","slug","push","hasBlocksField","fields","some","f","type","tabs","tab","tf","collectionSchemas","blockCatalog","relationships","prompts","domainPrompts","resources","searchableCollections","Map","schema","searchableFields","set","mcpCollections","draftCollections","siteUrl","previewSecret","previewPaths","draftBehavior","excludeCollections","tools","maxFileSize","mediaUpload","collectionSlug","schedulePublish","mcpGlobals","excludeGlobalSlugs","excludeGlobals","global","globals","enabled","description","withMcp","mcp","overrideAuth","req","getDefault","settings","user"],"mappings":"AACA,SAASA,SAAS,QAAQ,yBAAwB;AAElD,SAASC,qBAAqB,EAAEC,gBAAgB,EAAEC,sBAAsB,QAAQ,kBAAiB;AACjG,SAASC,eAAe,QAAQ,YAAW;AAC3C,SAASC,iBAAiB,QAAQ,cAAa;AAC/C,SAASC,4BAA4B,QAAQ,mBAAkB;AAC/D,SAASC,uBAAuB,QAAQ,yBAAwB;AAChE,SAASC,qBAAqB,QAAQ,uBAAsB;AAC5D,SAASC,sBAAsB,QAAQ,wBAAuB;AAC9D,SAASC,0BAA0B,QAAQ,4BAA2B;AACtE,SAASC,oBAAoB,QAAQ,sBAAqB;AAC1D,SAASC,yBAAyB,QAAQ,2BAA0B;AACpE,SAASC,uBAAuB,QAAQ,yBAAwB;AAChE,SAASC,wBAAwB,QAAQ,0BAAyB;AAClE,SAASC,qBAAqB,QAAQ,uBAAsB;AAC5D,SAASC,sBAAsB,EAAEC,wBAAwB,QAAQ,mBAAkB;AAEnF;;;;;;;;;;;;;;;;;;;CAmBC,GACD,OAAO,SAASC,qBAAqBC,OAA8B;IACjE,OAAO,CAACC;QACN,MAAMC,cAAeD,eAAeC,WAAW,IAAI,EAAE;QACrD,MAAMC,YAAaF,eAAeG,MAAM,IAAI,EAAE;QAE9C,4CAA4C;QAC5C,EAAE;QACF,kEAAkE;QAClE,yEAAyE;QACzE,wEAAwE;QACxE,yEAAyE;QACzE,kEAAkE;QAClE,MAAMC,gBAAyB,EAAE;QACjC,MAAMC,aAAsB,EAAE;QAE9B,IAAIN,QAAQO,iBAAiB,IAAIP,QAAQO,iBAAiB,CAACC,MAAM,GAAG,GAAG;YACrE,MAAMC,eAAe,IAAIC,IAAIV,QAAQO,iBAAiB;YACtD,KAAK,MAAMI,SAASR,UAAW;gBAC7B,IAAIM,aAAaG,GAAG,CAACD,MAAME,IAAI,GAAG;oBAChCR,cAAcS,IAAI,CAACH;gBACrB,OAAO;oBACLL,WAAWQ,IAAI,CAACH;gBAClB;YACF;QACF,OAAO;YACL,KAAK,MAAMA,SAASR,UAAW;gBAC7B,MAAMY,iBAAiBJ,MAAMK,MAAM,CAACC,IAAI,CACtC,CAACC,IAAMA,EAAEC,IAAI,KAAK,YACjBD,EAAEC,IAAI,KAAK,UAAU,UAAUD,KAAKA,EAAEE,IAAI,CAACH,IAAI,CAC9C,CAACI,MAAa,YAAYA,OAAOA,IAAIL,MAAM,CAACC,IAAI,CAAC,CAACK,KAAYA,GAAGH,IAAI,KAAK;gBAG9E,IAAIJ,gBAAgB;oBAClBV,cAAcS,IAAI,CAACH;gBACrB,OAAO;oBACLL,WAAWQ,IAAI,CAACH;gBAClB;YACF;QACF;QAEA,0BAA0B;QAC1B,MAAMY,oBAAoBzC,sBAAsBoB;QAChD,MAAMsB,eAAezC,iBAAiBsB,eAAeC;QACrD,MAAMmB,gBAAgBzC,uBAAuBuC;QAE7C,sBAAsB;QACtB,MAAMG,UAAUzC,gBACdsC,mBACAC,cACAC,eACAzB,QAAQ2B,aAAa;QAGvB,wBAAwB;QACxB,MAAMC,YAAY1C,kBAAkBqC,mBAAmBC,cAAcC;QAErE,oBAAoB;QACpB,MAAMI,wBAAwB,IAAIC;QAClC,KAAK,MAAM,CAACjB,MAAMkB,OAAO,IAAIR,kBAAmB;YAC9C,IAAIQ,OAAOC,gBAAgB,CAACxB,MAAM,GAAG,GAAG;gBACtCqB,sBAAsBI,GAAG,CAACpB,MAAMkB,OAAOC,gBAAgB;YACzD;QACF;QAEA,yDAAyD;QACzD,MAAM,EAAEE,cAAc,EAAEC,gBAAgB,EAAE,GAAGhD,6BAA6Be,aAAa;YACrFkC,SAASpC,QAAQoC,OAAO;YACxBC,eAAerC,QAAQqC,aAAa;YACpCC,cAActC,QAAQsC,YAAY;YAClCC,eAAevC,QAAQuC,aAAa;YACpCC,oBAAoBxC,QAAQwC,kBAAkB;QAChD;QAEA,MAAMC,QAAe;YACnBrD,wBAAwBoC;YACxBnC,sBAAsBmC,cAAcW;YACpC7C,uBAAuB6C;YACvB5C,2BAA2BsC;YAC3BrC,qBAAqBiC;YACrB/B,wBAAwB6B;YACxB5B,yBAAyB4B,mBAAmBY;YAC5CvC,sBAAsB;gBACpB8C,aAAa1C,QAAQ2C,WAAW,EAAED;gBAClCE,gBAAgB5C,QAAQ2C,WAAW,EAAEC;YACvC;YACA/C,uBAAuBsC;YACvBrC,yBAAyBqC;SAC1B;QAED,mGAAmG;QACnG,MAAMU,kBAAkBpD,0BAA0B8B,mBAAmBY;QACrE,IAAIU,iBAAiBJ,MAAM3B,IAAI,CAAC+B;QAEhC,2BAA2B;QAC3B,MAAMC,aAAyE,CAAC;QAChF,MAAMC,qBAAqB,IAAIrC,IAAIV,QAAQgD,cAAc,IAAI,EAAE;QAE/D,KAAK,MAAMC,UAAWhD,eAAeiD,OAAO,IAAI,EAAE,CAA8B;YAC9E,IAAIH,mBAAmBnC,GAAG,CAACqC,OAAOpC,IAAI,GAAG;YACzCiC,UAAU,CAACG,OAAOpC,IAAI,CAAC,GAAG;gBACxBsC,SAAS;gBACTC,aAAa,CAAC,OAAO,EAAEH,OAAOpC,IAAI,CAAC,gBAAgB,CAAC;YACtD;QACF;QAEA,0DAA0D;QAC1D,MAAMwC,UAAUxE,UAAU;YACxBqB,aAAagC;YACbgB,SAASJ;YACTQ,KAAK;gBACHb,OAAOA;gBACPf,SAASA;gBACTE,WAAWA;YACb;YACA,8DAA8D;YAC9D,kEAAkE;YAClE,sDAAsD;YACtD,sEAAsE;YACtE,oEAAoE;YACpE2B,cAAc,OAAOC,KAAKC;gBACxB,MAAMC,WAAW,MAAMD;gBACvBD,IAAIG,IAAI,GAAG,AAACD,SAAiBC,IAAI;gBACjC,OAAOD;YACT;QACF;QAEA,OAAOL,QAAQpD;IACjB;AACF"}
@@ -0,0 +1,23 @@
1
+ import type { Block, CollectionConfig } from 'payload';
2
+ import type { BlockCatalog, CollectionSchema, RelationshipEdge } from './types';
3
+ /**
4
+ * Introspect a Payload collection config into structured metadata.
5
+ */
6
+ export declare function introspectCollection(collection: CollectionConfig): CollectionSchema;
7
+ /**
8
+ * Introspect all collections into a map keyed by slug.
9
+ */
10
+ export declare function introspectCollections(collections: CollectionConfig[]): Map<string, CollectionSchema>;
11
+ /**
12
+ * Introspect block configs into a block catalog with section/leaf hierarchy and nesting rules.
13
+ *
14
+ * NOTE: In the Payload plugin context, blockReferences may or may not be resolved
15
+ * depending on when introspection runs. This function handles both cases:
16
+ * - If field.blocks contains resolved block objects, reads slugs from them
17
+ * - If field.blocks is empty and field.blockReferences exists, uses those slugs directly
18
+ */
19
+ export declare function introspectBlocks(sectionBlocks: Block[], leafBlocks: Block[]): BlockCatalog;
20
+ /**
21
+ * Build a relationship graph from introspected collection schemas.
22
+ */
23
+ export declare function buildRelationshipGraph(schemas: Map<string, CollectionSchema>): RelationshipEdge[];
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Introspect a Payload collection config into structured metadata.
3
+ */ export function introspectCollection(collection) {
4
+ const fields = extractFields(collection.fields);
5
+ const relationships = extractRelationships(collection.fields);
6
+ const searchableFields = fields.filter((f)=>[
7
+ 'text',
8
+ 'email'
9
+ ].includes(f.type) && [
10
+ 'name',
11
+ 'title',
12
+ 'slug'
13
+ ].includes(f.name)).map((f)=>f.name);
14
+ const hasDrafts = !!(collection.versions && typeof collection.versions === 'object' && 'drafts' in collection.versions && collection.versions.drafts);
15
+ const hasLivePreview = !!(collection.admin && typeof collection.admin === 'object' && 'livePreview' in collection.admin && collection.admin.livePreview);
16
+ return {
17
+ slug: collection.slug,
18
+ fields,
19
+ hasDrafts,
20
+ hasLivePreview,
21
+ relationships,
22
+ searchableFields
23
+ };
24
+ }
25
+ /**
26
+ * Introspect all collections into a map keyed by slug.
27
+ */ export function introspectCollections(collections) {
28
+ const map = new Map();
29
+ for (const collection of collections){
30
+ map.set(collection.slug, introspectCollection(collection));
31
+ }
32
+ return map;
33
+ }
34
+ /**
35
+ * Introspect block configs into a block catalog with section/leaf hierarchy and nesting rules.
36
+ *
37
+ * NOTE: In the Payload plugin context, blockReferences may or may not be resolved
38
+ * depending on when introspection runs. This function handles both cases:
39
+ * - If field.blocks contains resolved block objects, reads slugs from them
40
+ * - If field.blocks is empty and field.blockReferences exists, uses those slugs directly
41
+ */ export function introspectBlocks(sectionBlocks, leafBlocks) {
42
+ const leafSlugs = new Set(leafBlocks.map((b)=>b.slug));
43
+ const sections = sectionBlocks.map((section)=>{
44
+ const blockFields = findBlockFields(section.fields);
45
+ if (blockFields.length === 0) {
46
+ // Fixed section — no nested blocks
47
+ return {
48
+ slug: section.slug,
49
+ nestingType: 'fixed',
50
+ acceptedLeafSlugs: [],
51
+ fields: extractFields(section.fields)
52
+ };
53
+ }
54
+ // Determine accepted leaf slugs from all blocks-type fields
55
+ const acceptedSlugs = new Set();
56
+ let maxRows;
57
+ for (const bf of blockFields){
58
+ const slugs = getBlockSlugsFromField(bf);
59
+ for (const s of slugs){
60
+ if (leafSlugs.has(s)) acceptedSlugs.add(s);
61
+ }
62
+ if (bf.maxRows) maxRows = bf.maxRows;
63
+ }
64
+ const nestingType = acceptedSlugs.size < leafSlugs.size || maxRows ? 'constrained' : 'composable';
65
+ return {
66
+ slug: section.slug,
67
+ nestingType,
68
+ acceptedLeafSlugs: [
69
+ ...acceptedSlugs
70
+ ],
71
+ maxRows,
72
+ fields: extractFields(section.fields)
73
+ };
74
+ });
75
+ const leaves = leafBlocks.map((leaf)=>({
76
+ slug: leaf.slug,
77
+ fields: extractFields(leaf.fields)
78
+ }));
79
+ return {
80
+ sections,
81
+ leaves
82
+ };
83
+ }
84
+ /**
85
+ * Build a relationship graph from introspected collection schemas.
86
+ */ export function buildRelationshipGraph(schemas) {
87
+ const edges = [];
88
+ for (const [slug, schema] of schemas){
89
+ for (const rel of schema.relationships){
90
+ edges.push({
91
+ fromCollection: slug,
92
+ fieldName: rel.fieldName,
93
+ toCollection: rel.relationTo,
94
+ hasMany: rel.hasMany
95
+ });
96
+ }
97
+ }
98
+ return edges;
99
+ }
100
+ // ─── Internal helpers ──────────────────────────────────────────────
101
+ /**
102
+ * Recursively extract field metadata from a Payload fields array.
103
+ * Handles tabs, groups, arrays, rows, and collapsible containers.
104
+ */ function extractFields(fields) {
105
+ const result = [];
106
+ for (const field of fields){
107
+ if ('name' in field && field.name) {
108
+ const schema = {
109
+ name: field.name,
110
+ type: field.type
111
+ };
112
+ if ('required' in field && field.required) schema.required = true;
113
+ if ('hasMany' in field && field.hasMany) schema.hasMany = true;
114
+ if ('relationTo' in field && field.relationTo) schema.relationTo = field.relationTo;
115
+ if ('maxRows' in field && field.maxRows) schema.maxRows = field.maxRows;
116
+ // Extract select options
117
+ if (field.type === 'select' && 'options' in field && Array.isArray(field.options)) {
118
+ schema.options = field.options.map((opt)=>typeof opt === 'string' ? {
119
+ label: opt,
120
+ value: opt
121
+ } : {
122
+ label: String(opt.label),
123
+ value: String(opt.value)
124
+ });
125
+ }
126
+ // Recurse into nested fields (arrays, groups)
127
+ if (field.type === 'array' && 'fields' in field) {
128
+ schema.fields = extractFields(field.fields);
129
+ }
130
+ if (field.type === 'group' && 'fields' in field) {
131
+ schema.fields = extractFields(field.fields);
132
+ }
133
+ result.push(schema);
134
+ }
135
+ // Transparent containers — recurse without creating a named field
136
+ if (field.type === 'tabs' && 'tabs' in field) {
137
+ for (const tab of field.tabs){
138
+ if ('fields' in tab) {
139
+ result.push(...extractFields(tab.fields));
140
+ }
141
+ }
142
+ }
143
+ if (field.type === 'row' && 'fields' in field) {
144
+ result.push(...extractFields(field.fields));
145
+ }
146
+ if (field.type === 'collapsible' && 'fields' in field) {
147
+ result.push(...extractFields(field.fields));
148
+ }
149
+ }
150
+ return result;
151
+ }
152
+ /**
153
+ * Extract relationship metadata from fields (for the relationship graph).
154
+ */ function extractRelationships(fields, prefix = '') {
155
+ const rels = [];
156
+ for (const field of fields){
157
+ const fieldName = 'name' in field && field.name ? `${prefix}${field.name}` : prefix;
158
+ if (field.type === 'relationship' && 'relationTo' in field) {
159
+ rels.push({
160
+ fieldName,
161
+ relationTo: field.relationTo,
162
+ hasMany: !!('hasMany' in field && field.hasMany)
163
+ });
164
+ }
165
+ if (field.type === 'upload' && 'relationTo' in field) {
166
+ rels.push({
167
+ fieldName,
168
+ relationTo: field.relationTo,
169
+ hasMany: !!('hasMany' in field && field.hasMany)
170
+ });
171
+ }
172
+ // Recurse into containers
173
+ if (field.type === 'tabs' && 'tabs' in field) {
174
+ for (const tab of field.tabs){
175
+ if ('fields' in tab) {
176
+ rels.push(...extractRelationships(tab.fields, prefix));
177
+ }
178
+ }
179
+ }
180
+ if (field.type === 'group' && 'fields' in field) {
181
+ rels.push(...extractRelationships(field.fields, `${fieldName}.`));
182
+ }
183
+ if (field.type === 'array' && 'fields' in field) {
184
+ rels.push(...extractRelationships(field.fields, `${fieldName}[].`));
185
+ }
186
+ if (field.type === 'row' && 'fields' in field) {
187
+ rels.push(...extractRelationships(field.fields, prefix));
188
+ }
189
+ if (field.type === 'collapsible' && 'fields' in field) {
190
+ rels.push(...extractRelationships(field.fields, prefix));
191
+ }
192
+ }
193
+ return rels;
194
+ }
195
+ /**
196
+ * Find all blocks-type fields within a field array (recursing into tabs/rows/etc).
197
+ */ function findBlockFields(fields) {
198
+ const result = [];
199
+ for (const field of fields){
200
+ if (field.type === 'blocks') {
201
+ result.push(field);
202
+ }
203
+ if (field.type === 'tabs' && 'tabs' in field) {
204
+ for (const tab of field.tabs){
205
+ if ('fields' in tab) {
206
+ result.push(...findBlockFields(tab.fields));
207
+ }
208
+ }
209
+ }
210
+ if (field.type === 'row' && 'fields' in field) {
211
+ result.push(...findBlockFields(field.fields));
212
+ }
213
+ if (field.type === 'collapsible' && 'fields' in field) {
214
+ result.push(...findBlockFields(field.fields));
215
+ }
216
+ if (field.type === 'group' && 'fields' in field) {
217
+ result.push(...findBlockFields(field.fields));
218
+ }
219
+ }
220
+ return result;
221
+ }
222
+ /**
223
+ * Get block slugs from a blocks-type field.
224
+ * Handles both resolved blocks (field.blocks has objects) and unresolved blockReferences.
225
+ */ function getBlockSlugsFromField(field) {
226
+ const f = field;
227
+ // Resolved blocks — field.blocks contains full block objects
228
+ if (Array.isArray(f.blocks) && f.blocks.length > 0 && typeof f.blocks[0] === 'object' && f.blocks[0].slug) {
229
+ return f.blocks.map((b)=>b.slug);
230
+ }
231
+ // Unresolved — blockReferences contains slug strings
232
+ if (Array.isArray(f.blockReferences) && f.blockReferences.length > 0) {
233
+ return f.blockReferences.filter((ref)=>typeof ref === 'string');
234
+ }
235
+ return [];
236
+ }
237
+
238
+ //# sourceMappingURL=introspection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/introspection.ts"],"sourcesContent":["import type { Block, CollectionConfig, Field, GlobalConfig } from 'payload'\nimport type {\n BlockCatalog,\n BlockNestingType,\n CollectionSchema,\n FieldSchema,\n LeafBlockSchema,\n RelationshipEdge,\n SectionBlockSchema,\n} from './types'\n\n/**\n * Introspect a Payload collection config into structured metadata.\n */\nexport function introspectCollection(collection: CollectionConfig): CollectionSchema {\n const fields = extractFields(collection.fields)\n const relationships = extractRelationships(collection.fields)\n const searchableFields = fields\n .filter((f) => ['text', 'email'].includes(f.type) && ['name', 'title', 'slug'].includes(f.name))\n .map((f) => f.name)\n\n const hasDrafts = !!(collection.versions && typeof collection.versions === 'object' && 'drafts' in collection.versions && collection.versions.drafts)\n const hasLivePreview = !!(collection.admin && typeof collection.admin === 'object' && 'livePreview' in collection.admin && collection.admin.livePreview)\n\n return {\n slug: collection.slug,\n fields,\n hasDrafts,\n hasLivePreview,\n relationships,\n searchableFields,\n }\n}\n\n/**\n * Introspect all collections into a map keyed by slug.\n */\nexport function introspectCollections(\n collections: CollectionConfig[],\n): Map<string, CollectionSchema> {\n const map = new Map<string, CollectionSchema>()\n for (const collection of collections) {\n map.set(collection.slug, introspectCollection(collection))\n }\n return map\n}\n\n/**\n * Introspect block configs into a block catalog with section/leaf hierarchy and nesting rules.\n *\n * NOTE: In the Payload plugin context, blockReferences may or may not be resolved\n * depending on when introspection runs. This function handles both cases:\n * - If field.blocks contains resolved block objects, reads slugs from them\n * - If field.blocks is empty and field.blockReferences exists, uses those slugs directly\n */\nexport function introspectBlocks(\n sectionBlocks: Block[],\n leafBlocks: Block[],\n): BlockCatalog {\n const leafSlugs = new Set(leafBlocks.map((b) => b.slug))\n\n const sections: SectionBlockSchema[] = sectionBlocks.map((section) => {\n const blockFields = findBlockFields(section.fields)\n\n if (blockFields.length === 0) {\n // Fixed section — no nested blocks\n return {\n slug: section.slug,\n nestingType: 'fixed' as BlockNestingType,\n acceptedLeafSlugs: [],\n fields: extractFields(section.fields),\n }\n }\n\n // Determine accepted leaf slugs from all blocks-type fields\n const acceptedSlugs = new Set<string>()\n let maxRows: number | undefined\n\n for (const bf of blockFields) {\n const slugs = getBlockSlugsFromField(bf)\n for (const s of slugs) {\n if (leafSlugs.has(s)) acceptedSlugs.add(s)\n }\n if (bf.maxRows) maxRows = bf.maxRows\n }\n\n const nestingType: BlockNestingType =\n acceptedSlugs.size < leafSlugs.size || maxRows ? 'constrained' : 'composable'\n\n return {\n slug: section.slug,\n nestingType,\n acceptedLeafSlugs: [...acceptedSlugs],\n maxRows,\n fields: extractFields(section.fields),\n }\n })\n\n const leaves: LeafBlockSchema[] = leafBlocks.map((leaf) => ({\n slug: leaf.slug,\n fields: extractFields(leaf.fields),\n }))\n\n return { sections, leaves }\n}\n\n/**\n * Build a relationship graph from introspected collection schemas.\n */\nexport function buildRelationshipGraph(\n schemas: Map<string, CollectionSchema>,\n): RelationshipEdge[] {\n const edges: RelationshipEdge[] = []\n for (const [slug, schema] of schemas) {\n for (const rel of schema.relationships) {\n edges.push({\n fromCollection: slug,\n fieldName: rel.fieldName,\n toCollection: rel.relationTo,\n hasMany: rel.hasMany,\n })\n }\n }\n return edges\n}\n\n// ─── Internal helpers ──────────────────────────────────────────────\n\n/**\n * Recursively extract field metadata from a Payload fields array.\n * Handles tabs, groups, arrays, rows, and collapsible containers.\n */\nfunction extractFields(fields: Field[]): FieldSchema[] {\n const result: FieldSchema[] = []\n\n for (const field of fields) {\n if ('name' in field && field.name) {\n const schema: FieldSchema = {\n name: field.name,\n type: field.type,\n }\n\n if ('required' in field && field.required) schema.required = true\n if ('hasMany' in field && field.hasMany) schema.hasMany = true\n if ('relationTo' in field && field.relationTo) schema.relationTo = field.relationTo as string | string[]\n if ('maxRows' in field && field.maxRows) schema.maxRows = field.maxRows\n\n // Extract select options\n if (field.type === 'select' && 'options' in field && Array.isArray(field.options)) {\n schema.options = field.options.map((opt) =>\n typeof opt === 'string' ? { label: opt, value: opt } : { label: String(opt.label), value: String(opt.value) },\n )\n }\n\n // Recurse into nested fields (arrays, groups)\n if (field.type === 'array' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n if (field.type === 'group' && 'fields' in field) {\n schema.fields = extractFields(field.fields)\n }\n\n result.push(schema)\n }\n\n // Transparent containers — recurse without creating a named field\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n result.push(...extractFields(tab.fields))\n }\n }\n }\n if (field.type === 'row' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n result.push(...extractFields(field.fields))\n }\n }\n\n return result\n}\n\n/**\n * Extract relationship metadata from fields (for the relationship graph).\n */\nfunction extractRelationships(\n fields: Field[],\n prefix = '',\n): Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> {\n const rels: Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }> = []\n\n for (const field of fields) {\n const fieldName = 'name' in field && field.name ? `${prefix}${field.name}` : prefix\n\n if (field.type === 'relationship' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string | string[],\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n if (field.type === 'upload' && 'relationTo' in field) {\n rels.push({\n fieldName,\n relationTo: field.relationTo as string,\n hasMany: !!('hasMany' in field && field.hasMany),\n })\n }\n\n // Recurse into containers\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n rels.push(...extractRelationships(tab.fields, prefix))\n }\n }\n }\n if (field.type === 'group' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}.`))\n }\n if (field.type === 'array' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, `${fieldName}[].`))\n }\n if (field.type === 'row' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n rels.push(...extractRelationships(field.fields, prefix))\n }\n }\n\n return rels\n}\n\n/**\n * Find all blocks-type fields within a field array (recursing into tabs/rows/etc).\n */\nfunction findBlockFields(fields: Field[]): Array<Field & { type: 'blocks' }> {\n const result: Array<Field & { type: 'blocks' }> = []\n\n for (const field of fields) {\n if (field.type === 'blocks') {\n result.push(field as Field & { type: 'blocks' })\n }\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n if ('fields' in tab) {\n result.push(...findBlockFields(tab.fields))\n }\n }\n }\n if (field.type === 'row' && 'fields' in field) {\n result.push(...findBlockFields(field.fields))\n }\n if (field.type === 'collapsible' && 'fields' in field) {\n result.push(...findBlockFields(field.fields))\n }\n if (field.type === 'group' && 'fields' in field) {\n result.push(...findBlockFields(field.fields))\n }\n }\n\n return result\n}\n\n/**\n * Get block slugs from a blocks-type field.\n * Handles both resolved blocks (field.blocks has objects) and unresolved blockReferences.\n */\nfunction getBlockSlugsFromField(field: Field & { type: 'blocks' }): string[] {\n const f = field as any\n\n // Resolved blocks — field.blocks contains full block objects\n if (Array.isArray(f.blocks) && f.blocks.length > 0 && typeof f.blocks[0] === 'object' && f.blocks[0].slug) {\n return f.blocks.map((b: { slug: string }) => b.slug)\n }\n\n // Unresolved — blockReferences contains slug strings\n if (Array.isArray(f.blockReferences) && f.blockReferences.length > 0) {\n return f.blockReferences.filter((ref: unknown) => typeof ref === 'string') as string[]\n }\n\n return []\n}\n"],"names":["introspectCollection","collection","fields","extractFields","relationships","extractRelationships","searchableFields","filter","f","includes","type","name","map","hasDrafts","versions","drafts","hasLivePreview","admin","livePreview","slug","introspectCollections","collections","Map","set","introspectBlocks","sectionBlocks","leafBlocks","leafSlugs","Set","b","sections","section","blockFields","findBlockFields","length","nestingType","acceptedLeafSlugs","acceptedSlugs","maxRows","bf","slugs","getBlockSlugsFromField","s","has","add","size","leaves","leaf","buildRelationshipGraph","schemas","edges","schema","rel","push","fromCollection","fieldName","toCollection","relationTo","hasMany","result","field","required","Array","isArray","options","opt","label","value","String","tab","tabs","prefix","rels","blocks","blockReferences","ref"],"mappings":"AAWA;;CAEC,GACD,OAAO,SAASA,qBAAqBC,UAA4B;IAC/D,MAAMC,SAASC,cAAcF,WAAWC,MAAM;IAC9C,MAAME,gBAAgBC,qBAAqBJ,WAAWC,MAAM;IAC5D,MAAMI,mBAAmBJ,OACtBK,MAAM,CAAC,CAACC,IAAM;YAAC;YAAQ;SAAQ,CAACC,QAAQ,CAACD,EAAEE,IAAI,KAAK;YAAC;YAAQ;YAAS;SAAO,CAACD,QAAQ,CAACD,EAAEG,IAAI,GAC7FC,GAAG,CAAC,CAACJ,IAAMA,EAAEG,IAAI;IAEpB,MAAME,YAAY,CAAC,CAAEZ,CAAAA,WAAWa,QAAQ,IAAI,OAAOb,WAAWa,QAAQ,KAAK,YAAY,YAAYb,WAAWa,QAAQ,IAAIb,WAAWa,QAAQ,CAACC,MAAM,AAAD;IACnJ,MAAMC,iBAAiB,CAAC,CAAEf,CAAAA,WAAWgB,KAAK,IAAI,OAAOhB,WAAWgB,KAAK,KAAK,YAAY,iBAAiBhB,WAAWgB,KAAK,IAAIhB,WAAWgB,KAAK,CAACC,WAAW,AAAD;IAEtJ,OAAO;QACLC,MAAMlB,WAAWkB,IAAI;QACrBjB;QACAW;QACAG;QACAZ;QACAE;IACF;AACF;AAEA;;CAEC,GACD,OAAO,SAASc,sBACdC,WAA+B;IAE/B,MAAMT,MAAM,IAAIU;IAChB,KAAK,MAAMrB,cAAcoB,YAAa;QACpCT,IAAIW,GAAG,CAACtB,WAAWkB,IAAI,EAAEnB,qBAAqBC;IAChD;IACA,OAAOW;AACT;AAEA;;;;;;;CAOC,GACD,OAAO,SAASY,iBACdC,aAAsB,EACtBC,UAAmB;IAEnB,MAAMC,YAAY,IAAIC,IAAIF,WAAWd,GAAG,CAAC,CAACiB,IAAMA,EAAEV,IAAI;IAEtD,MAAMW,WAAiCL,cAAcb,GAAG,CAAC,CAACmB;QACxD,MAAMC,cAAcC,gBAAgBF,QAAQ7B,MAAM;QAElD,IAAI8B,YAAYE,MAAM,KAAK,GAAG;YAC5B,mCAAmC;YACnC,OAAO;gBACLf,MAAMY,QAAQZ,IAAI;gBAClBgB,aAAa;gBACbC,mBAAmB,EAAE;gBACrBlC,QAAQC,cAAc4B,QAAQ7B,MAAM;YACtC;QACF;QAEA,4DAA4D;QAC5D,MAAMmC,gBAAgB,IAAIT;QAC1B,IAAIU;QAEJ,KAAK,MAAMC,MAAMP,YAAa;YAC5B,MAAMQ,QAAQC,uBAAuBF;YACrC,KAAK,MAAMG,KAAKF,MAAO;gBACrB,IAAIb,UAAUgB,GAAG,CAACD,IAAIL,cAAcO,GAAG,CAACF;YAC1C;YACA,IAAIH,GAAGD,OAAO,EAAEA,UAAUC,GAAGD,OAAO;QACtC;QAEA,MAAMH,cACJE,cAAcQ,IAAI,GAAGlB,UAAUkB,IAAI,IAAIP,UAAU,gBAAgB;QAEnE,OAAO;YACLnB,MAAMY,QAAQZ,IAAI;YAClBgB;YACAC,mBAAmB;mBAAIC;aAAc;YACrCC;YACApC,QAAQC,cAAc4B,QAAQ7B,MAAM;QACtC;IACF;IAEA,MAAM4C,SAA4BpB,WAAWd,GAAG,CAAC,CAACmC,OAAU,CAAA;YAC1D5B,MAAM4B,KAAK5B,IAAI;YACfjB,QAAQC,cAAc4C,KAAK7C,MAAM;QACnC,CAAA;IAEA,OAAO;QAAE4B;QAAUgB;IAAO;AAC5B;AAEA;;CAEC,GACD,OAAO,SAASE,uBACdC,OAAsC;IAEtC,MAAMC,QAA4B,EAAE;IACpC,KAAK,MAAM,CAAC/B,MAAMgC,OAAO,IAAIF,QAAS;QACpC,KAAK,MAAMG,OAAOD,OAAO/C,aAAa,CAAE;YACtC8C,MAAMG,IAAI,CAAC;gBACTC,gBAAgBnC;gBAChBoC,WAAWH,IAAIG,SAAS;gBACxBC,cAAcJ,IAAIK,UAAU;gBAC5BC,SAASN,IAAIM,OAAO;YACtB;QACF;IACF;IACA,OAAOR;AACT;AAEA,sEAAsE;AAEtE;;;CAGC,GACD,SAAS/C,cAAcD,MAAe;IACpC,MAAMyD,SAAwB,EAAE;IAEhC,KAAK,MAAMC,SAAS1D,OAAQ;QAC1B,IAAI,UAAU0D,SAASA,MAAMjD,IAAI,EAAE;YACjC,MAAMwC,SAAsB;gBAC1BxC,MAAMiD,MAAMjD,IAAI;gBAChBD,MAAMkD,MAAMlD,IAAI;YAClB;YAEA,IAAI,cAAckD,SAASA,MAAMC,QAAQ,EAAEV,OAAOU,QAAQ,GAAG;YAC7D,IAAI,aAAaD,SAASA,MAAMF,OAAO,EAAEP,OAAOO,OAAO,GAAG;YAC1D,IAAI,gBAAgBE,SAASA,MAAMH,UAAU,EAAEN,OAAOM,UAAU,GAAGG,MAAMH,UAAU;YACnF,IAAI,aAAaG,SAASA,MAAMtB,OAAO,EAAEa,OAAOb,OAAO,GAAGsB,MAAMtB,OAAO;YAEvE,yBAAyB;YACzB,IAAIsB,MAAMlD,IAAI,KAAK,YAAY,aAAakD,SAASE,MAAMC,OAAO,CAACH,MAAMI,OAAO,GAAG;gBACjFb,OAAOa,OAAO,GAAGJ,MAAMI,OAAO,CAACpD,GAAG,CAAC,CAACqD,MAClC,OAAOA,QAAQ,WAAW;wBAAEC,OAAOD;wBAAKE,OAAOF;oBAAI,IAAI;wBAAEC,OAAOE,OAAOH,IAAIC,KAAK;wBAAGC,OAAOC,OAAOH,IAAIE,KAAK;oBAAE;YAEhH;YAEA,8CAA8C;YAC9C,IAAIP,MAAMlD,IAAI,KAAK,WAAW,YAAYkD,OAAO;gBAC/CT,OAAOjD,MAAM,GAAGC,cAAcyD,MAAM1D,MAAM;YAC5C;YACA,IAAI0D,MAAMlD,IAAI,KAAK,WAAW,YAAYkD,OAAO;gBAC/CT,OAAOjD,MAAM,GAAGC,cAAcyD,MAAM1D,MAAM;YAC5C;YAEAyD,OAAON,IAAI,CAACF;QACd;QAEA,kEAAkE;QAClE,IAAIS,MAAMlD,IAAI,KAAK,UAAU,UAAUkD,OAAO;YAC5C,KAAK,MAAMS,OAAOT,MAAMU,IAAI,CAAE;gBAC5B,IAAI,YAAYD,KAAK;oBACnBV,OAAON,IAAI,IAAIlD,cAAckE,IAAInE,MAAM;gBACzC;YACF;QACF;QACA,IAAI0D,MAAMlD,IAAI,KAAK,SAAS,YAAYkD,OAAO;YAC7CD,OAAON,IAAI,IAAIlD,cAAcyD,MAAM1D,MAAM;QAC3C;QACA,IAAI0D,MAAMlD,IAAI,KAAK,iBAAiB,YAAYkD,OAAO;YACrDD,OAAON,IAAI,IAAIlD,cAAcyD,MAAM1D,MAAM;QAC3C;IACF;IAEA,OAAOyD;AACT;AAEA;;CAEC,GACD,SAAStD,qBACPH,MAAe,EACfqE,SAAS,EAAE;IAEX,MAAMC,OAAsF,EAAE;IAE9F,KAAK,MAAMZ,SAAS1D,OAAQ;QAC1B,MAAMqD,YAAY,UAAUK,SAASA,MAAMjD,IAAI,GAAG,GAAG4D,SAASX,MAAMjD,IAAI,EAAE,GAAG4D;QAE7E,IAAIX,MAAMlD,IAAI,KAAK,kBAAkB,gBAAgBkD,OAAO;YAC1DY,KAAKnB,IAAI,CAAC;gBACRE;gBACAE,YAAYG,MAAMH,UAAU;gBAC5BC,SAAS,CAAC,CAAE,CAAA,aAAaE,SAASA,MAAMF,OAAO,AAAD;YAChD;QACF;QAEA,IAAIE,MAAMlD,IAAI,KAAK,YAAY,gBAAgBkD,OAAO;YACpDY,KAAKnB,IAAI,CAAC;gBACRE;gBACAE,YAAYG,MAAMH,UAAU;gBAC5BC,SAAS,CAAC,CAAE,CAAA,aAAaE,SAASA,MAAMF,OAAO,AAAD;YAChD;QACF;QAEA,0BAA0B;QAC1B,IAAIE,MAAMlD,IAAI,KAAK,UAAU,UAAUkD,OAAO;YAC5C,KAAK,MAAMS,OAAOT,MAAMU,IAAI,CAAE;gBAC5B,IAAI,YAAYD,KAAK;oBACnBG,KAAKnB,IAAI,IAAIhD,qBAAqBgE,IAAInE,MAAM,EAAEqE;gBAChD;YACF;QACF;QACA,IAAIX,MAAMlD,IAAI,KAAK,WAAW,YAAYkD,OAAO;YAC/CY,KAAKnB,IAAI,IAAIhD,qBAAqBuD,MAAM1D,MAAM,EAAE,GAAGqD,UAAU,CAAC,CAAC;QACjE;QACA,IAAIK,MAAMlD,IAAI,KAAK,WAAW,YAAYkD,OAAO;YAC/CY,KAAKnB,IAAI,IAAIhD,qBAAqBuD,MAAM1D,MAAM,EAAE,GAAGqD,UAAU,GAAG,CAAC;QACnE;QACA,IAAIK,MAAMlD,IAAI,KAAK,SAAS,YAAYkD,OAAO;YAC7CY,KAAKnB,IAAI,IAAIhD,qBAAqBuD,MAAM1D,MAAM,EAAEqE;QAClD;QACA,IAAIX,MAAMlD,IAAI,KAAK,iBAAiB,YAAYkD,OAAO;YACrDY,KAAKnB,IAAI,IAAIhD,qBAAqBuD,MAAM1D,MAAM,EAAEqE;QAClD;IACF;IAEA,OAAOC;AACT;AAEA;;CAEC,GACD,SAASvC,gBAAgB/B,MAAe;IACtC,MAAMyD,SAA4C,EAAE;IAEpD,KAAK,MAAMC,SAAS1D,OAAQ;QAC1B,IAAI0D,MAAMlD,IAAI,KAAK,UAAU;YAC3BiD,OAAON,IAAI,CAACO;QACd;QACA,IAAIA,MAAMlD,IAAI,KAAK,UAAU,UAAUkD,OAAO;YAC5C,KAAK,MAAMS,OAAOT,MAAMU,IAAI,CAAE;gBAC5B,IAAI,YAAYD,KAAK;oBACnBV,OAAON,IAAI,IAAIpB,gBAAgBoC,IAAInE,MAAM;gBAC3C;YACF;QACF;QACA,IAAI0D,MAAMlD,IAAI,KAAK,SAAS,YAAYkD,OAAO;YAC7CD,OAAON,IAAI,IAAIpB,gBAAgB2B,MAAM1D,MAAM;QAC7C;QACA,IAAI0D,MAAMlD,IAAI,KAAK,iBAAiB,YAAYkD,OAAO;YACrDD,OAAON,IAAI,IAAIpB,gBAAgB2B,MAAM1D,MAAM;QAC7C;QACA,IAAI0D,MAAMlD,IAAI,KAAK,WAAW,YAAYkD,OAAO;YAC/CD,OAAON,IAAI,IAAIpB,gBAAgB2B,MAAM1D,MAAM;QAC7C;IACF;IAEA,OAAOyD;AACT;AAEA;;;CAGC,GACD,SAASlB,uBAAuBmB,KAAiC;IAC/D,MAAMpD,IAAIoD;IAEV,6DAA6D;IAC7D,IAAIE,MAAMC,OAAO,CAACvD,EAAEiE,MAAM,KAAKjE,EAAEiE,MAAM,CAACvC,MAAM,GAAG,KAAK,OAAO1B,EAAEiE,MAAM,CAAC,EAAE,KAAK,YAAYjE,EAAEiE,MAAM,CAAC,EAAE,CAACtD,IAAI,EAAE;QACzG,OAAOX,EAAEiE,MAAM,CAAC7D,GAAG,CAAC,CAACiB,IAAwBA,EAAEV,IAAI;IACrD;IAEA,qDAAqD;IACrD,IAAI2C,MAAMC,OAAO,CAACvD,EAAEkE,eAAe,KAAKlE,EAAEkE,eAAe,CAACxC,MAAM,GAAG,GAAG;QACpE,OAAO1B,EAAEkE,eAAe,CAACnE,MAAM,CAAC,CAACoE,MAAiB,OAAOA,QAAQ;IACnE;IAEA,OAAO,EAAE;AACX"}
@@ -0,0 +1,21 @@
1
+ import type { BlockCatalog, CollectionSchema, DomainPrompt, RelationshipEdge } from './types';
2
+ /**
3
+ * Generate MCP prompts that teach the AI about the content model.
4
+ *
5
+ * Auto-generates three prompts (content model overview, block composition guide,
6
+ * draft workflow guide) and merges with any user-provided domain prompts.
7
+ */
8
+ export declare function generatePrompts(schemas: Map<string, CollectionSchema>, catalog: BlockCatalog, relationships: RelationshipEdge[], domainPrompts?: DomainPrompt[]): {
9
+ name: string;
10
+ title: string;
11
+ description: string;
12
+ handler(): {
13
+ messages: {
14
+ content: {
15
+ type: "text";
16
+ text: string;
17
+ };
18
+ role: "user";
19
+ }[];
20
+ };
21
+ }[];