payload-mcp-toolkit 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/types.d.ts CHANGED
@@ -1,44 +1,60 @@
1
- /** Draft behavior per collection */
2
- export type DraftBehavior = 'always-draft' | 'always-publish';
3
- /** Configuration for the content toolkit plugin */
1
+ /**
2
+ * payload-mcp-toolkit configuration.
3
+ *
4
+ * The plugin works with zero options — every field below is an escape hatch
5
+ * for the cases where Payload's own config doesn't carry enough signal.
6
+ */
4
7
  export interface ContentToolkitOptions {
5
- /** Base URL of the site (used for preview URLs). e.g. "https://example.com" */
6
- siteUrl: string;
7
- /** Secret used for preview URL authentication */
8
- previewSecret: string;
9
8
  /**
10
- * Per-collection URL path prefix used when constructing preview URLs.
11
- * Keys are collection slugs; values are the path segment placed before the doc slug.
12
- * Use an empty string for collections that live at the site root (e.g. pages).
13
- *
14
- * Example:
15
- * {
16
- * posts: '/blog',
17
- * products: '/shop',
18
- * pages: '',
19
- * }
20
- *
21
- * Collections without an entry default to `/{slug}`.
9
+ * Preview URL behavior. The toolkit reads `collection.admin.livePreview.url`
10
+ * (or `collection.admin.preview` as a fallback) when generating preview links
11
+ * for draft documents. Provide this object only to override what Payload
12
+ * already knows.
22
13
  */
23
- previewPaths?: Record<string, string>;
14
+ preview?: {
15
+ /**
16
+ * Absolute base URL prepended to relative preview paths. Defaults to
17
+ * `incomingConfig.serverURL`, then `process.env.NEXT_PUBLIC_SERVER_URL`,
18
+ * then `process.env.SITE_URL`. If none of those resolve and your preview
19
+ * URL function returns a relative path, no preview URL is appended.
20
+ */
21
+ siteUrl?: string;
22
+ /**
23
+ * Disable preview URL injection entirely.
24
+ */
25
+ disabled?: boolean;
26
+ };
24
27
  /**
25
- * Explicit list of block slugs that should be treated as **section** blocks
26
- * (top-level layout containers). Any block not in this list is treated as
27
- * a **leaf** block (composable inside a section's `blocks` field).
28
+ * Per-collection draft behavior overrides. The default behavior is inferred
29
+ * from each collection's `versions.drafts` setting:
30
+ * - drafts enabled → `'always-draft'` (raw `update` is locked; clients go
31
+ * through `publishDraft` / `patchLayout` / `updateDocument` which preserve
32
+ * draft semantics)
33
+ * - drafts disabled → `'always-publish'`
28
34
  *
29
- * When omitted, the toolkit falls back to a heuristic: blocks that contain
30
- * a nested `blocks`-type field are sections, all others are leaves. This
31
- * heuristic mis-classifies "fixed" sections (sections with no nested blocks
32
- * but their own standalone fields, e.g. a CTA banner). Pass this option
33
- * to disambiguate.
34
- *
35
- * Example: `['hero', 'fullWidth', 'twoColumn', 'ctaBanner']`
35
+ * Override per slug only if you need to allow raw publish on a draftable
36
+ * collection.
37
+ */
38
+ draftBehavior?: Record<string, 'always-draft' | 'always-publish'>;
39
+ /**
40
+ * Override the auth collection used for API key linkage. By default the
41
+ * toolkit scans `incomingConfig.collections` for the first collection with
42
+ * `auth: true`, preferring one named `'users'`.
43
+ */
44
+ userCollection?: string;
45
+ /**
46
+ * Hide collections or globals from the MCP surface. Useful for internal
47
+ * bookkeeping collections that should not be exposed to AI clients.
48
+ */
49
+ exclude?: {
50
+ collections?: string[];
51
+ globals?: string[];
52
+ };
53
+ /**
54
+ * Site-specific domain prompts that teach the AI business vocabulary.
55
+ * Merged with the auto-generated prompts.
36
56
  */
37
- sectionBlockSlugs?: string[];
38
- /** Site-specific domain prompts that teach the AI business vocabulary */
39
57
  domainPrompts?: DomainPrompt[];
40
- /** Per-collection draft behavior overrides (keyed by collection slug) */
41
- draftBehavior?: Record<string, DraftBehavior>;
42
58
  /** Media upload configuration */
43
59
  mediaUpload?: {
44
60
  /** Maximum file size in bytes (default: 10MB) */
@@ -46,10 +62,6 @@ export interface ContentToolkitOptions {
46
62
  /** Media collection slug (default: 'media') */
47
63
  collectionSlug?: string;
48
64
  };
49
- /** Collections to exclude from MCP exposure */
50
- excludeCollections?: string[];
51
- /** Globals to exclude from MCP exposure */
52
- excludeGlobals?: string[];
53
65
  }
54
66
  /** A domain prompt that teaches the AI site-specific vocabulary */
55
67
  export interface DomainPrompt {
@@ -89,26 +101,43 @@ export interface CollectionSchema {
89
101
  }>;
90
102
  searchableFields: string[];
91
103
  }
92
- /** Block nesting classification */
93
- export type BlockNestingType = 'composable' | 'constrained' | 'fixed';
94
- /** Introspected section block metadata */
95
- export interface SectionBlockSchema {
96
- slug: string;
97
- nestingType: BlockNestingType;
98
- acceptedLeafSlugs: string[];
99
- maxRows?: number;
100
- fields: FieldSchema[];
101
- }
102
- /** Introspected leaf block metadata */
103
- export interface LeafBlockSchema {
104
+ /**
105
+ * One block in the catalog. Flat — no section/leaf distinction. Whether a
106
+ * block can nest other blocks is encoded in the `BlockNestingMap` keyed by
107
+ * the path to its `blocks` field.
108
+ */
109
+ export interface BlockSchema {
104
110
  slug: string;
105
111
  fields: FieldSchema[];
106
112
  }
107
- /** Complete block catalog */
113
+ /**
114
+ * Flat catalog of every block referenced by the schema.
115
+ */
108
116
  export interface BlockCatalog {
109
- sections: SectionBlockSchema[];
110
- leaves: LeafBlockSchema[];
117
+ blocks: BlockSchema[];
118
+ }
119
+ /**
120
+ * One entry per `blocks`-typed field anywhere in the schema.
121
+ *
122
+ * `path` is `<owner>.<dottedFieldPath>` where owner is the collection or
123
+ * block slug that contains the field. Values list the slugs that field
124
+ * accepts. The AI uses this to compose blocks at any nesting depth without
125
+ * us pre-classifying anything as a "section" or "leaf".
126
+ */
127
+ export interface BlockNestingEdge {
128
+ /** Owner of the blocks field — either a collection slug or a block slug */
129
+ owner: string;
130
+ /** Whether the owner is a collection or a block */
131
+ ownerType: 'collection' | 'block';
132
+ /** Dotted path to the blocks field within the owner (e.g. `layout`, `hero.content`) */
133
+ fieldPath: string;
134
+ /** Block slugs that this field accepts */
135
+ acceptedBlockSlugs: string[];
136
+ /** Optional row cap from the field config */
137
+ maxRows?: number;
111
138
  }
139
+ /** Map of every blocks-field in the schema to the slugs it accepts */
140
+ export type BlockNestingMap = BlockNestingEdge[];
112
141
  /** Relationship edge in the collection graph */
113
142
  export interface RelationshipEdge {
114
143
  fromCollection: string;
package/dist/types.js CHANGED
@@ -1,3 +1,8 @@
1
- /** Relationship edge in the collection graph */ export { };
1
+ /**
2
+ * payload-mcp-toolkit configuration.
3
+ *
4
+ * The plugin works with zero options — every field below is an escape hatch
5
+ * for the cases where Payload's own config doesn't carry enough signal.
6
+ */ /** Relationship edge in the collection graph */ export { };
2
7
 
3
8
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionConfig, Block, Config, GlobalConfig } from 'payload'\n\n/** Draft behavior per collection */\nexport type DraftBehavior = 'always-draft' | 'always-publish'\n\n/** Configuration for the content toolkit plugin */\nexport interface ContentToolkitOptions {\n /** Base URL of the site (used for preview URLs). e.g. \"https://example.com\" */\n siteUrl: string\n\n /** Secret used for preview URL authentication */\n previewSecret: string\n\n /**\n * Per-collection URL path prefix used when constructing preview URLs.\n * Keys are collection slugs; values are the path segment placed before the doc slug.\n * Use an empty string for collections that live at the site root (e.g. pages).\n *\n * Example:\n * {\n * posts: '/blog',\n * products: '/shop',\n * pages: '',\n * }\n *\n * Collections without an entry default to `/{slug}`.\n */\n previewPaths?: Record<string, string>\n\n /**\n * Explicit list of block slugs that should be treated as **section** blocks\n * (top-level layout containers). Any block not in this list is treated as\n * a **leaf** block (composable inside a section's `blocks` field).\n *\n * When omitted, the toolkit falls back to a heuristic: blocks that contain\n * a nested `blocks`-type field are sections, all others are leaves. This\n * heuristic mis-classifies \"fixed\" sections (sections with no nested blocks\n * but their own standalone fields, e.g. a CTA banner). Pass this option\n * to disambiguate.\n *\n * Example: `['hero', 'fullWidth', 'twoColumn', 'ctaBanner']`\n */\n sectionBlockSlugs?: string[]\n\n /** Site-specific domain prompts that teach the AI business vocabulary */\n domainPrompts?: DomainPrompt[]\n\n /** Per-collection draft behavior overrides (keyed by collection slug) */\n draftBehavior?: Record<string, DraftBehavior>\n\n /** Media upload configuration */\n mediaUpload?: {\n /** Maximum file size in bytes (default: 10MB) */\n maxFileSize?: number\n /** Media collection slug (default: 'media') */\n collectionSlug?: string\n }\n\n /** Collections to exclude from MCP exposure */\n excludeCollections?: string[]\n\n /** Globals to exclude from MCP exposure */\n excludeGlobals?: string[]\n}\n\n/** A domain prompt that teaches the AI site-specific vocabulary */\nexport interface DomainPrompt {\n /** Unique name for the prompt */\n name: string\n /** Display title */\n title: string\n /** Description of what this prompt teaches */\n description: string\n /** The prompt content */\n content: string\n}\n\n/** Introspected field metadata */\nexport interface FieldSchema {\n name: string\n type: string\n required?: boolean\n hasMany?: boolean\n relationTo?: string | string[]\n options?: Array<{ label: string; value: string }>\n fields?: FieldSchema[]\n maxRows?: number\n}\n\n/** Introspected collection metadata */\nexport interface CollectionSchema {\n slug: string\n fields: FieldSchema[]\n hasDrafts: boolean\n hasLivePreview: boolean\n relationships: Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }>\n searchableFields: string[]\n}\n\n/** Block nesting classification */\nexport type BlockNestingType = 'composable' | 'constrained' | 'fixed'\n\n/** Introspected section block metadata */\nexport interface SectionBlockSchema {\n slug: string\n nestingType: BlockNestingType\n acceptedLeafSlugs: string[]\n maxRows?: number\n fields: FieldSchema[]\n}\n\n/** Introspected leaf block metadata */\nexport interface LeafBlockSchema {\n slug: string\n fields: FieldSchema[]\n}\n\n/** Complete block catalog */\nexport interface BlockCatalog {\n sections: SectionBlockSchema[]\n leaves: LeafBlockSchema[]\n}\n\n/** Relationship edge in the collection graph */\nexport interface RelationshipEdge {\n fromCollection: string\n fieldName: string\n toCollection: string | string[]\n hasMany: boolean\n}\n"],"names":[],"mappings":"AA2HA,8CAA8C,GAC9C,WAKC"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * payload-mcp-toolkit configuration.\n *\n * The plugin works with zero options every field below is an escape hatch\n * for the cases where Payload's own config doesn't carry enough signal.\n */\nexport interface ContentToolkitOptions {\n /**\n * Preview URL behavior. The toolkit reads `collection.admin.livePreview.url`\n * (or `collection.admin.preview` as a fallback) when generating preview links\n * for draft documents. Provide this object only to override what Payload\n * already knows.\n */\n preview?: {\n /**\n * Absolute base URL prepended to relative preview paths. Defaults to\n * `incomingConfig.serverURL`, then `process.env.NEXT_PUBLIC_SERVER_URL`,\n * then `process.env.SITE_URL`. If none of those resolve and your preview\n * URL function returns a relative path, no preview URL is appended.\n */\n siteUrl?: string\n\n /**\n * Disable preview URL injection entirely.\n */\n disabled?: boolean\n }\n\n /**\n * Per-collection draft behavior overrides. The default behavior is inferred\n * from each collection's `versions.drafts` setting:\n * - drafts enabled → `'always-draft'` (raw `update` is locked; clients go\n * through `publishDraft` / `patchLayout` / `updateDocument` which preserve\n * draft semantics)\n * - drafts disabled → `'always-publish'`\n *\n * Override per slug only if you need to allow raw publish on a draftable\n * collection.\n */\n draftBehavior?: Record<string, 'always-draft' | 'always-publish'>\n\n /**\n * Override the auth collection used for API key linkage. By default the\n * toolkit scans `incomingConfig.collections` for the first collection with\n * `auth: true`, preferring one named `'users'`.\n */\n userCollection?: string\n\n /**\n * Hide collections or globals from the MCP surface. Useful for internal\n * bookkeeping collections that should not be exposed to AI clients.\n */\n exclude?: {\n collections?: string[]\n globals?: string[]\n }\n\n /**\n * Site-specific domain prompts that teach the AI business vocabulary.\n * Merged with the auto-generated prompts.\n */\n domainPrompts?: DomainPrompt[]\n\n /** Media upload configuration */\n mediaUpload?: {\n /** Maximum file size in bytes (default: 10MB) */\n maxFileSize?: number\n /** Media collection slug (default: 'media') */\n collectionSlug?: string\n }\n}\n\n/** A domain prompt that teaches the AI site-specific vocabulary */\nexport interface DomainPrompt {\n /** Unique name for the prompt */\n name: string\n /** Display title */\n title: string\n /** Description of what this prompt teaches */\n description: string\n /** The prompt content */\n content: string\n}\n\n/** Introspected field metadata */\nexport interface FieldSchema {\n name: string\n type: string\n required?: boolean\n hasMany?: boolean\n relationTo?: string | string[]\n options?: Array<{ label: string; value: string }>\n fields?: FieldSchema[]\n maxRows?: number\n}\n\n/** Introspected collection metadata */\nexport interface CollectionSchema {\n slug: string\n fields: FieldSchema[]\n hasDrafts: boolean\n hasLivePreview: boolean\n relationships: Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }>\n searchableFields: string[]\n}\n\n/**\n * One block in the catalog. Flat no section/leaf distinction. Whether a\n * block can nest other blocks is encoded in the `BlockNestingMap` keyed by\n * the path to its `blocks` field.\n */\nexport interface BlockSchema {\n slug: string\n fields: FieldSchema[]\n}\n\n/**\n * Flat catalog of every block referenced by the schema.\n */\nexport interface BlockCatalog {\n blocks: BlockSchema[]\n}\n\n/**\n * One entry per `blocks`-typed field anywhere in the schema.\n *\n * `path` is `<owner>.<dottedFieldPath>` where owner is the collection or\n * block slug that contains the field. Values list the slugs that field\n * accepts. The AI uses this to compose blocks at any nesting depth without\n * us pre-classifying anything as a \"section\" or \"leaf\".\n */\nexport interface BlockNestingEdge {\n /** Owner of the blocks field — either a collection slug or a block slug */\n owner: string\n /** Whether the owner is a collection or a block */\n ownerType: 'collection' | 'block'\n /** Dotted path to the blocks field within the owner (e.g. `layout`, `hero.content`) */\n fieldPath: string\n /** Block slugs that this field accepts */\n acceptedBlockSlugs: string[]\n /** Optional row cap from the field config */\n maxRows?: number\n}\n\n/** Map of every blocks-field in the schema to the slugs it accepts */\nexport type BlockNestingMap = BlockNestingEdge[]\n\n/** Relationship edge in the collection graph */\nexport interface RelationshipEdge {\n fromCollection: string\n fieldName: string\n toCollection: string | string[]\n hasMany: boolean\n}\n"],"names":[],"mappings":"AAAA;;;;;CAKC,GA8ID,8CAA8C,GAC9C,WAKC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payload-mcp-toolkit",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Schema-aware MCP toolkit for Payload CMS — wraps the official @payloadcms/plugin-mcp with introspected prompts, resources, draft workflow, and AI-friendly tools so non-technical editors can manage content via AI chat.",
5
5
  "license": "MIT",
6
6
  "author": "jon8800",
@@ -1,117 +0,0 @@
1
- import { z } from 'zod';
2
- import type { BlockCatalog } from '../types';
3
- /** Schema for a single leaf block within a section */
4
- export declare const leafBlockSchema: z.ZodObject<{
5
- blockType: z.ZodString;
6
- fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
7
- }, "strip", z.ZodTypeAny, {
8
- blockType: string;
9
- fields?: Record<string, unknown> | undefined;
10
- }, {
11
- blockType: string;
12
- fields?: Record<string, unknown> | undefined;
13
- }>;
14
- /** Schema for a single section in a layout */
15
- export declare const sectionSchema: z.ZodObject<{
16
- sectionType: z.ZodString;
17
- config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
18
- /** Leaf blocks for single-content sections */
19
- content: z.ZodOptional<z.ZodArray<z.ZodObject<{
20
- blockType: z.ZodString;
21
- fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
22
- }, "strip", z.ZodTypeAny, {
23
- blockType: string;
24
- fields?: Record<string, unknown> | undefined;
25
- }, {
26
- blockType: string;
27
- fields?: Record<string, unknown> | undefined;
28
- }>, "many">>;
29
- /** Left column leaf blocks (for two-column sections) */
30
- leftColumn: z.ZodOptional<z.ZodArray<z.ZodObject<{
31
- blockType: z.ZodString;
32
- fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
33
- }, "strip", z.ZodTypeAny, {
34
- blockType: string;
35
- fields?: Record<string, unknown> | undefined;
36
- }, {
37
- blockType: string;
38
- fields?: Record<string, unknown> | undefined;
39
- }>, "many">>;
40
- /** Right column leaf blocks (for two-column sections) */
41
- rightColumn: z.ZodOptional<z.ZodArray<z.ZodObject<{
42
- blockType: z.ZodString;
43
- fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
44
- }, "strip", z.ZodTypeAny, {
45
- blockType: string;
46
- fields?: Record<string, unknown> | undefined;
47
- }, {
48
- blockType: string;
49
- fields?: Record<string, unknown> | undefined;
50
- }>, "many">>;
51
- /** Flat field values for fixed sections */
52
- fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
53
- }, "strip", z.ZodTypeAny, {
54
- sectionType: string;
55
- fields?: Record<string, unknown> | undefined;
56
- config?: Record<string, unknown> | undefined;
57
- content?: {
58
- blockType: string;
59
- fields?: Record<string, unknown> | undefined;
60
- }[] | undefined;
61
- leftColumn?: {
62
- blockType: string;
63
- fields?: Record<string, unknown> | undefined;
64
- }[] | undefined;
65
- rightColumn?: {
66
- blockType: string;
67
- fields?: Record<string, unknown> | undefined;
68
- }[] | undefined;
69
- }, {
70
- sectionType: string;
71
- fields?: Record<string, unknown> | undefined;
72
- config?: Record<string, unknown> | undefined;
73
- content?: {
74
- blockType: string;
75
- fields?: Record<string, unknown> | undefined;
76
- }[] | undefined;
77
- leftColumn?: {
78
- blockType: string;
79
- fields?: Record<string, unknown> | undefined;
80
- }[] | undefined;
81
- rightColumn?: {
82
- blockType: string;
83
- fields?: Record<string, unknown> | undefined;
84
- }[] | undefined;
85
- }>;
86
- export type LeafBlockInput = z.infer<typeof leafBlockSchema>;
87
- export type SectionInput = z.infer<typeof sectionSchema>;
88
- export interface ValidationError {
89
- sectionIndex: number;
90
- message: string;
91
- validAlternatives?: string[];
92
- }
93
- export interface ComposeResult {
94
- blocks: Record<string, unknown>[];
95
- errors: ValidationError[];
96
- }
97
- /**
98
- * Validate and compose an array of section inputs into block JSON.
99
- * Returns either composed blocks or a list of validation errors.
100
- */
101
- export declare function composeSections(sections: SectionInput[], catalog: BlockCatalog): ComposeResult;
102
- /** Build a hint payload describing the available section/leaf vocabulary. */
103
- export declare function buildHint(catalog: BlockCatalog): {
104
- availableSections: string[];
105
- availableLeaves: string[];
106
- sectionDetails: {
107
- slug: string;
108
- nestingType: import("../types").BlockNestingType;
109
- acceptedLeaves: string[];
110
- maxRows: number | undefined;
111
- }[];
112
- };
113
- /**
114
- * Apply a list operation against an existing array of blocks.
115
- * `full` always replaces; the rest preserve the existing array.
116
- */
117
- export declare function applyOperation(composedBlocks: Record<string, unknown>[], operation: 'full' | 'append' | 'prepend' | 'insertAt' | 'replaceAt', insertIndex: number | undefined, existingLayout: Record<string, unknown>[] | undefined): Record<string, unknown>[];
@@ -1,236 +0,0 @@
1
- import { z } from 'zod';
2
- /** Schema for a single leaf block within a section */ export const leafBlockSchema = z.object({
3
- blockType: z.string(),
4
- fields: z.record(z.string(), z.unknown()).optional()
5
- });
6
- /** Schema for a single section in a layout */ export const sectionSchema = z.object({
7
- sectionType: z.string(),
8
- config: z.record(z.string(), z.unknown()).optional(),
9
- /** Leaf blocks for single-content sections */ content: z.array(leafBlockSchema).optional(),
10
- /** Left column leaf blocks (for two-column sections) */ leftColumn: z.array(leafBlockSchema).optional(),
11
- /** Right column leaf blocks (for two-column sections) */ rightColumn: z.array(leafBlockSchema).optional(),
12
- /** Flat field values for fixed sections */ fields: z.record(z.string(), z.unknown()).optional()
13
- });
14
- /**
15
- * Validate and compose an array of section inputs into block JSON.
16
- * Returns either composed blocks or a list of validation errors.
17
- */ export function composeSections(sections, catalog) {
18
- const sectionMap = new Map(catalog.sections.map((s)=>[
19
- s.slug,
20
- s
21
- ]));
22
- const allLeafSlugs = catalog.leaves.map((l)=>l.slug);
23
- const blocks = [];
24
- const errors = [];
25
- for(let i = 0; i < sections.length; i++){
26
- const result = composeSingleSection(sections[i], i, sectionMap, allLeafSlugs);
27
- if (result.errors.length > 0) {
28
- errors.push(...result.errors);
29
- } else if (result.block) {
30
- blocks.push(result.block);
31
- }
32
- }
33
- return {
34
- blocks,
35
- errors
36
- };
37
- }
38
- /** Build a hint payload describing the available section/leaf vocabulary. */ export function buildHint(catalog) {
39
- return {
40
- availableSections: catalog.sections.map((s)=>s.slug),
41
- availableLeaves: catalog.leaves.map((l)=>l.slug),
42
- sectionDetails: catalog.sections.map((s)=>({
43
- slug: s.slug,
44
- nestingType: s.nestingType,
45
- acceptedLeaves: s.acceptedLeafSlugs,
46
- maxRows: s.maxRows
47
- }))
48
- };
49
- }
50
- /**
51
- * Apply a list operation against an existing array of blocks.
52
- * `full` always replaces; the rest preserve the existing array.
53
- */ export function applyOperation(composedBlocks, operation, insertIndex, existingLayout) {
54
- if (operation === 'full' || !existingLayout) {
55
- return composedBlocks;
56
- }
57
- const existing = [
58
- ...existingLayout
59
- ];
60
- if (operation === 'append') {
61
- return [
62
- ...existing,
63
- ...composedBlocks
64
- ];
65
- }
66
- if (operation === 'prepend') {
67
- return [
68
- ...composedBlocks,
69
- ...existing
70
- ];
71
- }
72
- if (operation === 'insertAt') {
73
- if (insertIndex === undefined || insertIndex < 0 || insertIndex > existing.length) {
74
- return [
75
- ...existing,
76
- ...composedBlocks
77
- ];
78
- }
79
- existing.splice(insertIndex, 0, ...composedBlocks);
80
- return existing;
81
- }
82
- if (operation === 'replaceAt') {
83
- if (insertIndex === undefined || insertIndex < 0 || insertIndex >= existing.length) {
84
- return existing;
85
- }
86
- existing.splice(insertIndex, composedBlocks.length, ...composedBlocks);
87
- return existing;
88
- }
89
- return composedBlocks;
90
- }
91
- function composeSingleSection(section, index, sectionMap, allLeafSlugs) {
92
- const sectionSchema = sectionMap.get(section.sectionType);
93
- if (!sectionSchema) {
94
- return {
95
- block: null,
96
- errors: [
97
- {
98
- sectionIndex: index,
99
- message: `Unknown section type: "${section.sectionType}"`,
100
- validAlternatives: [
101
- ...sectionMap.keys()
102
- ]
103
- }
104
- ]
105
- };
106
- }
107
- if (sectionSchema.nestingType === 'fixed') {
108
- return composeFixedSection(section, index, sectionSchema);
109
- }
110
- // Detect two-column layout by leftColumn block field
111
- const hasDualColumns = sectionSchema.fields.some((f)=>f.name === 'leftColumn' && f.type === 'blocks');
112
- if (hasDualColumns) {
113
- return composeTwoColumnSection(section, index, sectionSchema, allLeafSlugs);
114
- }
115
- return composeContentSection(section, index, sectionSchema, allLeafSlugs);
116
- }
117
- function composeFixedSection(section, index, schema) {
118
- const errors = validateRequiredFields(section.fields ?? {}, schema.fields, index);
119
- if (errors.length > 0) return {
120
- block: null,
121
- errors
122
- };
123
- return {
124
- block: {
125
- blockType: schema.slug,
126
- ...section.fields ?? {},
127
- ...section.config ?? {}
128
- },
129
- errors: []
130
- };
131
- }
132
- function composeContentSection(section, index, schema, allLeafSlugs) {
133
- const content = section.content ?? [];
134
- const errors = validateLeafBlocks(content, schema, index, 'content', allLeafSlugs);
135
- if (schema.maxRows && content.length > schema.maxRows) {
136
- errors.push({
137
- sectionIndex: index,
138
- message: `Section "${schema.slug}" allows at most ${schema.maxRows} content block(s), got ${content.length}`
139
- });
140
- }
141
- if (errors.length > 0) return {
142
- block: null,
143
- errors
144
- };
145
- const blockFieldName = findBlockFieldName(schema, 'content');
146
- const composedLeaves = content.map((leaf)=>({
147
- blockType: leaf.blockType,
148
- ...leaf.fields ?? {}
149
- }));
150
- return {
151
- block: {
152
- blockType: schema.slug,
153
- [blockFieldName]: composedLeaves,
154
- ...section.config ?? {},
155
- ...section.fields ?? {}
156
- },
157
- errors: []
158
- };
159
- }
160
- function composeTwoColumnSection(section, index, schema, allLeafSlugs) {
161
- const leftColumn = section.leftColumn ?? [];
162
- const rightColumn = section.rightColumn ?? [];
163
- const errors = [
164
- ...validateLeafBlocks(leftColumn, schema, index, 'leftColumn', allLeafSlugs),
165
- ...validateLeafBlocks(rightColumn, schema, index, 'rightColumn', allLeafSlugs)
166
- ];
167
- if (errors.length > 0) return {
168
- block: null,
169
- errors
170
- };
171
- return {
172
- block: {
173
- blockType: schema.slug,
174
- leftColumn: leftColumn.map((leaf)=>({
175
- blockType: leaf.blockType,
176
- ...leaf.fields ?? {}
177
- })),
178
- rightColumn: rightColumn.map((leaf)=>({
179
- blockType: leaf.blockType,
180
- ...leaf.fields ?? {}
181
- })),
182
- ...section.config ?? {}
183
- },
184
- errors: []
185
- };
186
- }
187
- function validateLeafBlocks(leaves, sectionSchema, sectionIndex, columnName, allLeafSlugs) {
188
- const errors = [];
189
- for(let i = 0; i < leaves.length; i++){
190
- const leaf = leaves[i];
191
- if (!allLeafSlugs.includes(leaf.blockType)) {
192
- errors.push({
193
- sectionIndex,
194
- message: `Unknown leaf block type "${leaf.blockType}" at ${columnName}[${i}]`,
195
- validAlternatives: allLeafSlugs
196
- });
197
- continue;
198
- }
199
- if (!sectionSchema.acceptedLeafSlugs.includes(leaf.blockType)) {
200
- errors.push({
201
- sectionIndex,
202
- message: `Leaf block "${leaf.blockType}" is not allowed in section "${sectionSchema.slug}" at ${columnName}[${i}]`,
203
- validAlternatives: sectionSchema.acceptedLeafSlugs
204
- });
205
- }
206
- }
207
- return errors;
208
- }
209
- function validateRequiredFields(values, fieldSchemas, sectionIndex) {
210
- const errors = [];
211
- for (const field of fieldSchemas){
212
- if (!field.required) continue;
213
- if ([
214
- 'id',
215
- 'blockName',
216
- 'blockType'
217
- ].includes(field.name)) continue;
218
- const value = values[field.name];
219
- if (value === undefined || value === null || value === '') {
220
- errors.push({
221
- sectionIndex,
222
- message: `Required field "${field.name}" is missing`
223
- });
224
- }
225
- }
226
- return errors;
227
- }
228
- function findBlockFieldName(schema, defaultName) {
229
- const hasDefault = schema.fields.some((f)=>f.name === defaultName && f.type === 'blocks');
230
- if (hasDefault) return defaultName;
231
- const blocksField = schema.fields.find((f)=>f.type === 'blocks');
232
- if (blocksField) return blocksField.name;
233
- return defaultName;
234
- }
235
-
236
- //# sourceMappingURL=compose-helpers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/tools/compose-helpers.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { BlockCatalog, SectionBlockSchema } from '../types'\n\n/** Schema for a single leaf block within a section */\nexport const leafBlockSchema = z.object({\n blockType: z.string(),\n fields: z.record(z.string(), z.unknown()).optional(),\n})\n\n/** Schema for a single section in a layout */\nexport const sectionSchema = z.object({\n sectionType: z.string(),\n config: z.record(z.string(), z.unknown()).optional(),\n /** Leaf blocks for single-content sections */\n content: z.array(leafBlockSchema).optional(),\n /** Left column leaf blocks (for two-column sections) */\n leftColumn: z.array(leafBlockSchema).optional(),\n /** Right column leaf blocks (for two-column sections) */\n rightColumn: z.array(leafBlockSchema).optional(),\n /** Flat field values for fixed sections */\n fields: z.record(z.string(), z.unknown()).optional(),\n})\n\nexport type LeafBlockInput = z.infer<typeof leafBlockSchema>\nexport type SectionInput = z.infer<typeof sectionSchema>\n\nexport interface ValidationError {\n sectionIndex: number\n message: string\n validAlternatives?: string[]\n}\n\nexport interface ComposeResult {\n blocks: Record<string, unknown>[]\n errors: ValidationError[]\n}\n\n/**\n * Validate and compose an array of section inputs into block JSON.\n * Returns either composed blocks or a list of validation errors.\n */\nexport function composeSections(\n sections: SectionInput[],\n catalog: BlockCatalog,\n): ComposeResult {\n const sectionMap = new Map(catalog.sections.map((s) => [s.slug, s]))\n const allLeafSlugs = catalog.leaves.map((l) => l.slug)\n\n const blocks: Record<string, unknown>[] = []\n const errors: ValidationError[] = []\n\n for (let i = 0; i < sections.length; i++) {\n const result = composeSingleSection(sections[i], i, sectionMap, allLeafSlugs)\n if (result.errors.length > 0) {\n errors.push(...result.errors)\n } else if (result.block) {\n blocks.push(result.block)\n }\n }\n\n return { blocks, errors }\n}\n\n/** Build a hint payload describing the available section/leaf vocabulary. */\nexport function buildHint(catalog: BlockCatalog) {\n return {\n availableSections: catalog.sections.map((s) => s.slug),\n availableLeaves: catalog.leaves.map((l) => l.slug),\n sectionDetails: catalog.sections.map((s) => ({\n slug: s.slug,\n nestingType: s.nestingType,\n acceptedLeaves: s.acceptedLeafSlugs,\n maxRows: s.maxRows,\n })),\n }\n}\n\n/**\n * Apply a list operation against an existing array of blocks.\n * `full` always replaces; the rest preserve the existing array.\n */\nexport function applyOperation(\n composedBlocks: Record<string, unknown>[],\n operation: 'full' | 'append' | 'prepend' | 'insertAt' | 'replaceAt',\n insertIndex: number | undefined,\n existingLayout: Record<string, unknown>[] | undefined,\n): Record<string, unknown>[] {\n if (operation === 'full' || !existingLayout) {\n return composedBlocks\n }\n\n const existing = [...existingLayout]\n\n if (operation === 'append') {\n return [...existing, ...composedBlocks]\n }\n\n if (operation === 'prepend') {\n return [...composedBlocks, ...existing]\n }\n\n if (operation === 'insertAt') {\n if (insertIndex === undefined || insertIndex < 0 || insertIndex > existing.length) {\n return [...existing, ...composedBlocks]\n }\n existing.splice(insertIndex, 0, ...composedBlocks)\n return existing\n }\n\n if (operation === 'replaceAt') {\n if (insertIndex === undefined || insertIndex < 0 || insertIndex >= existing.length) {\n return existing\n }\n existing.splice(insertIndex, composedBlocks.length, ...composedBlocks)\n return existing\n }\n\n return composedBlocks\n}\n\n// ─── Internal composition logic ──────────────────────────────────\n\ninterface SingleResult {\n block: Record<string, unknown> | null\n errors: ValidationError[]\n}\n\nfunction composeSingleSection(\n section: SectionInput,\n index: number,\n sectionMap: Map<string, SectionBlockSchema>,\n allLeafSlugs: string[],\n): SingleResult {\n const sectionSchema = sectionMap.get(section.sectionType)\n\n if (!sectionSchema) {\n return {\n block: null,\n errors: [\n {\n sectionIndex: index,\n message: `Unknown section type: \"${section.sectionType}\"`,\n validAlternatives: [...sectionMap.keys()],\n },\n ],\n }\n }\n\n if (sectionSchema.nestingType === 'fixed') {\n return composeFixedSection(section, index, sectionSchema)\n }\n\n // Detect two-column layout by leftColumn block field\n const hasDualColumns = sectionSchema.fields.some(\n (f) => f.name === 'leftColumn' && f.type === 'blocks',\n )\n\n if (hasDualColumns) {\n return composeTwoColumnSection(section, index, sectionSchema, allLeafSlugs)\n }\n\n return composeContentSection(section, index, sectionSchema, allLeafSlugs)\n}\n\nfunction composeFixedSection(\n section: SectionInput,\n index: number,\n schema: SectionBlockSchema,\n): SingleResult {\n const errors = validateRequiredFields(section.fields ?? {}, schema.fields, index)\n if (errors.length > 0) return { block: null, errors }\n\n return {\n block: {\n blockType: schema.slug,\n ...(section.fields ?? {}),\n ...(section.config ?? {}),\n },\n errors: [],\n }\n}\n\nfunction composeContentSection(\n section: SectionInput,\n index: number,\n schema: SectionBlockSchema,\n allLeafSlugs: string[],\n): SingleResult {\n const content = section.content ?? []\n const errors = validateLeafBlocks(content, schema, index, 'content', allLeafSlugs)\n\n if (schema.maxRows && content.length > schema.maxRows) {\n errors.push({\n sectionIndex: index,\n message: `Section \"${schema.slug}\" allows at most ${schema.maxRows} content block(s), got ${content.length}`,\n })\n }\n\n if (errors.length > 0) return { block: null, errors }\n\n const blockFieldName = findBlockFieldName(schema, 'content')\n const composedLeaves = content.map((leaf) => ({\n blockType: leaf.blockType,\n ...(leaf.fields ?? {}),\n }))\n\n return {\n block: {\n blockType: schema.slug,\n [blockFieldName]: composedLeaves,\n ...(section.config ?? {}),\n ...(section.fields ?? {}),\n },\n errors: [],\n }\n}\n\nfunction composeTwoColumnSection(\n section: SectionInput,\n index: number,\n schema: SectionBlockSchema,\n allLeafSlugs: string[],\n): SingleResult {\n const leftColumn = section.leftColumn ?? []\n const rightColumn = section.rightColumn ?? []\n const errors: ValidationError[] = [\n ...validateLeafBlocks(leftColumn, schema, index, 'leftColumn', allLeafSlugs),\n ...validateLeafBlocks(rightColumn, schema, index, 'rightColumn', allLeafSlugs),\n ]\n\n if (errors.length > 0) return { block: null, errors }\n\n return {\n block: {\n blockType: schema.slug,\n leftColumn: leftColumn.map((leaf) => ({\n blockType: leaf.blockType,\n ...(leaf.fields ?? {}),\n })),\n rightColumn: rightColumn.map((leaf) => ({\n blockType: leaf.blockType,\n ...(leaf.fields ?? {}),\n })),\n ...(section.config ?? {}),\n },\n errors: [],\n }\n}\n\nfunction validateLeafBlocks(\n leaves: LeafBlockInput[],\n sectionSchema: SectionBlockSchema,\n sectionIndex: number,\n columnName: string,\n allLeafSlugs: string[],\n): ValidationError[] {\n const errors: ValidationError[] = []\n\n for (let i = 0; i < leaves.length; i++) {\n const leaf = leaves[i]\n\n if (!allLeafSlugs.includes(leaf.blockType)) {\n errors.push({\n sectionIndex,\n message: `Unknown leaf block type \"${leaf.blockType}\" at ${columnName}[${i}]`,\n validAlternatives: allLeafSlugs,\n })\n continue\n }\n\n if (!sectionSchema.acceptedLeafSlugs.includes(leaf.blockType)) {\n errors.push({\n sectionIndex,\n message: `Leaf block \"${leaf.blockType}\" is not allowed in section \"${sectionSchema.slug}\" at ${columnName}[${i}]`,\n validAlternatives: sectionSchema.acceptedLeafSlugs,\n })\n }\n }\n\n return errors\n}\n\nfunction validateRequiredFields(\n values: Record<string, unknown>,\n fieldSchemas: Array<{ name: string; type: string; required?: boolean }>,\n sectionIndex: number,\n): ValidationError[] {\n const errors: ValidationError[] = []\n\n for (const field of fieldSchemas) {\n if (!field.required) continue\n if (['id', 'blockName', 'blockType'].includes(field.name)) continue\n\n const value = values[field.name]\n if (value === undefined || value === null || value === '') {\n errors.push({\n sectionIndex,\n message: `Required field \"${field.name}\" is missing`,\n })\n }\n }\n\n return errors\n}\n\nfunction findBlockFieldName(\n schema: SectionBlockSchema,\n defaultName: string,\n): string {\n const hasDefault = schema.fields.some((f) => f.name === defaultName && f.type === 'blocks')\n if (hasDefault) return defaultName\n\n const blocksField = schema.fields.find((f) => f.type === 'blocks')\n if (blocksField) return blocksField.name\n\n return defaultName\n}\n"],"names":["z","leafBlockSchema","object","blockType","string","fields","record","unknown","optional","sectionSchema","sectionType","config","content","array","leftColumn","rightColumn","composeSections","sections","catalog","sectionMap","Map","map","s","slug","allLeafSlugs","leaves","l","blocks","errors","i","length","result","composeSingleSection","push","block","buildHint","availableSections","availableLeaves","sectionDetails","nestingType","acceptedLeaves","acceptedLeafSlugs","maxRows","applyOperation","composedBlocks","operation","insertIndex","existingLayout","existing","undefined","splice","section","index","get","sectionIndex","message","validAlternatives","keys","composeFixedSection","hasDualColumns","some","f","name","type","composeTwoColumnSection","composeContentSection","schema","validateRequiredFields","validateLeafBlocks","blockFieldName","findBlockFieldName","composedLeaves","leaf","columnName","includes","values","fieldSchemas","field","required","value","defaultName","hasDefault","blocksField","find"],"mappings":"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"}