payload-mcp-toolkit 0.3.4 → 0.7.4

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 (116) hide show
  1. package/README.md +253 -151
  2. package/dist/api-keys.d.ts +46 -0
  3. package/dist/api-keys.js +308 -0
  4. package/dist/api-keys.js.map +1 -0
  5. package/dist/auth-strategy.d.ts +96 -0
  6. package/dist/auth-strategy.js +261 -0
  7. package/dist/auth-strategy.js.map +1 -0
  8. package/dist/components/CollectionScopesMatrix.d.ts +8 -0
  9. package/dist/components/CollectionScopesMatrix.js +32 -0
  10. package/dist/components/CollectionScopesMatrix.js.map +1 -0
  11. package/dist/components/GlobalScopesMatrix.d.ts +8 -0
  12. package/dist/components/GlobalScopesMatrix.js +28 -0
  13. package/dist/components/GlobalScopesMatrix.js.map +1 -0
  14. package/dist/components/ScopesTable.d.ts +19 -0
  15. package/dist/components/ScopesTable.js +285 -0
  16. package/dist/components/ScopesTable.js.map +1 -0
  17. package/dist/components/index.d.ts +2 -0
  18. package/dist/components/index.js +4 -0
  19. package/dist/components/index.js.map +1 -0
  20. package/dist/conflict-detection.d.ts +13 -0
  21. package/dist/conflict-detection.js +41 -0
  22. package/dist/conflict-detection.js.map +1 -0
  23. package/dist/draft-workflow.d.ts +46 -48
  24. package/dist/draft-workflow.js +53 -135
  25. package/dist/draft-workflow.js.map +1 -1
  26. package/dist/endpoint.d.ts +35 -0
  27. package/dist/endpoint.js +105 -0
  28. package/dist/endpoint.js.map +1 -0
  29. package/dist/hash.d.ts +21 -0
  30. package/dist/hash.js +36 -0
  31. package/dist/hash.js.map +1 -0
  32. package/dist/index.d.ts +9 -9
  33. package/dist/index.js +167 -69
  34. package/dist/index.js.map +1 -1
  35. package/dist/introspection.d.ts +17 -3
  36. package/dist/introspection.js +95 -36
  37. package/dist/introspection.js.map +1 -1
  38. package/dist/prompts.js +5 -5
  39. package/dist/prompts.js.map +1 -1
  40. package/dist/registry.d.ts +50 -0
  41. package/dist/registry.js +169 -0
  42. package/dist/registry.js.map +1 -0
  43. package/dist/resources.d.ts +5 -3
  44. package/dist/resources.js +23 -11
  45. package/dist/resources.js.map +1 -1
  46. package/dist/scope/audit-log.d.ts +18 -0
  47. package/dist/scope/audit-log.js +50 -0
  48. package/dist/scope/audit-log.js.map +1 -0
  49. package/dist/scope/policy.d.ts +73 -0
  50. package/dist/scope/policy.js +218 -0
  51. package/dist/scope/policy.js.map +1 -0
  52. package/dist/tools/_helpers.d.ts +62 -1
  53. package/dist/tools/_helpers.js +181 -0
  54. package/dist/tools/_helpers.js.map +1 -1
  55. package/dist/tools/_layout-helpers.d.ts +43 -0
  56. package/dist/tools/_layout-helpers.js +159 -0
  57. package/dist/tools/_layout-helpers.js.map +1 -0
  58. package/dist/tools/create-document.d.ts +5 -5
  59. package/dist/tools/create-document.js +25 -21
  60. package/dist/tools/create-document.js.map +1 -1
  61. package/dist/tools/delete-document.d.ts +25 -0
  62. package/dist/tools/delete-document.js +49 -0
  63. package/dist/tools/delete-document.js.map +1 -0
  64. package/dist/tools/find-document.d.ts +33 -0
  65. package/dist/tools/find-document.js +97 -0
  66. package/dist/tools/find-document.js.map +1 -0
  67. package/dist/tools/find-global.d.ts +26 -0
  68. package/dist/tools/find-global.js +122 -0
  69. package/dist/tools/find-global.js.map +1 -0
  70. package/dist/tools/global-versions.d.ts +39 -0
  71. package/dist/tools/global-versions.js +132 -0
  72. package/dist/tools/global-versions.js.map +1 -0
  73. package/dist/tools/patch-global-layout.d.ts +31 -0
  74. package/dist/tools/patch-global-layout.js +127 -0
  75. package/dist/tools/patch-global-layout.js.map +1 -0
  76. package/dist/tools/patch-layout.d.ts +5 -8
  77. package/dist/tools/patch-layout.js +18 -100
  78. package/dist/tools/patch-layout.js.map +1 -1
  79. package/dist/tools/publish-draft.d.ts +5 -4
  80. package/dist/tools/publish-draft.js +39 -2
  81. package/dist/tools/publish-draft.js.map +1 -1
  82. package/dist/tools/publish-global-draft.d.ts +20 -0
  83. package/dist/tools/publish-global-draft.js +79 -0
  84. package/dist/tools/publish-global-draft.js.map +1 -0
  85. package/dist/tools/resolve-reference.d.ts +5 -4
  86. package/dist/tools/resolve-reference.js +4 -0
  87. package/dist/tools/resolve-reference.js.map +1 -1
  88. package/dist/tools/safe-delete.d.ts +5 -5
  89. package/dist/tools/safe-delete.js +20 -15
  90. package/dist/tools/safe-delete.js.map +1 -1
  91. package/dist/tools/schedule-publish.d.ts +5 -5
  92. package/dist/tools/schedule-publish.js +23 -19
  93. package/dist/tools/schedule-publish.js.map +1 -1
  94. package/dist/tools/search-content.d.ts +5 -9
  95. package/dist/tools/search-content.js +16 -12
  96. package/dist/tools/search-content.js.map +1 -1
  97. package/dist/tools/update-document.d.ts +5 -5
  98. package/dist/tools/update-document.js +10 -5
  99. package/dist/tools/update-document.js.map +1 -1
  100. package/dist/tools/update-global.d.ts +27 -0
  101. package/dist/tools/update-global.js +72 -0
  102. package/dist/tools/update-global.js.map +1 -0
  103. package/dist/tools/upload-media.d.ts +5 -4
  104. package/dist/tools/upload-media.js +6 -1
  105. package/dist/tools/upload-media.js.map +1 -1
  106. package/dist/tools/versions.d.ts +10 -9
  107. package/dist/tools/versions.js +15 -7
  108. package/dist/tools/versions.js.map +1 -1
  109. package/dist/types.d.ts +56 -3
  110. package/dist/types.js +13 -6
  111. package/dist/types.js.map +1 -1
  112. package/package.json +39 -18
  113. package/dist/__tests__/introspection.test.js +0 -459
  114. package/dist/__tests__/introspection.test.js.map +0 -1
  115. package/dist/__tests__/url-validator.test.js +0 -326
  116. package/dist/__tests__/url-validator.test.js.map +0 -1
@@ -1,459 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { introspectCollection, introspectCollections, introspectBlocks, buildBlockNestingMap, buildRelationshipGraph } from '../introspection';
3
- // ─── Sample schema (kept inline so the test is self-contained) ─────
4
- const Media = {
5
- slug: 'media',
6
- upload: true,
7
- fields: [
8
- {
9
- name: 'alt',
10
- type: 'text',
11
- required: true
12
- }
13
- ]
14
- };
15
- const Categories = {
16
- slug: 'categories',
17
- fields: [
18
- {
19
- name: 'name',
20
- type: 'text',
21
- required: true
22
- },
23
- {
24
- name: 'slug',
25
- type: 'text',
26
- required: true
27
- }
28
- ]
29
- };
30
- const Authors = {
31
- slug: 'authors',
32
- fields: [
33
- {
34
- name: 'name',
35
- type: 'text',
36
- required: true
37
- },
38
- {
39
- name: 'slug',
40
- type: 'text',
41
- required: true
42
- },
43
- {
44
- name: 'avatar',
45
- type: 'upload',
46
- relationTo: 'media'
47
- }
48
- ]
49
- };
50
- // Leaf-style blocks
51
- const Heading = {
52
- slug: 'heading',
53
- fields: [
54
- {
55
- name: 'text',
56
- type: 'text',
57
- required: true
58
- },
59
- {
60
- name: 'level',
61
- type: 'select',
62
- options: [
63
- 'h1',
64
- 'h2',
65
- 'h3'
66
- ],
67
- defaultValue: 'h2'
68
- },
69
- {
70
- name: 'align',
71
- type: 'select',
72
- options: [
73
- 'left',
74
- 'center',
75
- 'right'
76
- ],
77
- defaultValue: 'left'
78
- }
79
- ]
80
- };
81
- const RichText = {
82
- slug: 'richText',
83
- fields: [
84
- {
85
- name: 'content',
86
- type: 'richText'
87
- }
88
- ]
89
- };
90
- const ImageBlock = {
91
- slug: 'image',
92
- fields: [
93
- {
94
- name: 'image',
95
- type: 'upload',
96
- relationTo: 'media',
97
- required: true
98
- },
99
- {
100
- name: 'caption',
101
- type: 'text'
102
- }
103
- ]
104
- };
105
- // Container-style blocks (have nested blocks fields)
106
- const FullWidth = {
107
- slug: 'fullWidth',
108
- fields: [
109
- {
110
- name: 'content',
111
- type: 'blocks',
112
- blocks: [
113
- Heading,
114
- RichText,
115
- ImageBlock
116
- ]
117
- }
118
- ]
119
- };
120
- const HeadingOnly = {
121
- slug: 'headingOnly',
122
- fields: [
123
- {
124
- name: 'content',
125
- type: 'blocks',
126
- maxRows: 1,
127
- blocks: [
128
- Heading
129
- ]
130
- }
131
- ]
132
- };
133
- const CtaBanner = {
134
- slug: 'ctaBanner',
135
- fields: [
136
- {
137
- name: 'headline',
138
- type: 'text',
139
- required: true
140
- },
141
- {
142
- name: 'buttonLabel',
143
- type: 'text'
144
- },
145
- {
146
- name: 'buttonHref',
147
- type: 'text'
148
- }
149
- ]
150
- };
151
- // Deeply-nestable container — exercises the recursive path
152
- const Accordion = {
153
- slug: 'accordion',
154
- fields: [
155
- {
156
- name: 'panels',
157
- type: 'array',
158
- fields: [
159
- {
160
- name: 'title',
161
- type: 'text'
162
- },
163
- {
164
- name: 'body',
165
- type: 'blocks',
166
- blocks: [
167
- Heading,
168
- RichText,
169
- FullWidth
170
- ]
171
- }
172
- ]
173
- }
174
- ]
175
- };
176
- const allBlocks = [
177
- Heading,
178
- RichText,
179
- ImageBlock,
180
- FullWidth,
181
- HeadingOnly,
182
- CtaBanner,
183
- Accordion
184
- ];
185
- const Posts = {
186
- slug: 'posts',
187
- versions: {
188
- drafts: true
189
- },
190
- fields: [
191
- {
192
- name: 'title',
193
- type: 'text',
194
- required: true
195
- },
196
- {
197
- name: 'slug',
198
- type: 'text',
199
- required: true
200
- },
201
- {
202
- name: 'featured',
203
- type: 'checkbox'
204
- },
205
- {
206
- name: 'category',
207
- type: 'relationship',
208
- relationTo: 'categories'
209
- },
210
- {
211
- name: 'authors',
212
- type: 'relationship',
213
- relationTo: 'authors',
214
- hasMany: true
215
- },
216
- {
217
- name: 'coverImage',
218
- type: 'upload',
219
- relationTo: 'media'
220
- },
221
- {
222
- name: 'tags',
223
- type: 'array',
224
- fields: [
225
- {
226
- name: 'tag',
227
- type: 'text'
228
- }
229
- ]
230
- }
231
- ]
232
- };
233
- const Pages = {
234
- slug: 'pages',
235
- versions: {
236
- drafts: true
237
- },
238
- fields: [
239
- {
240
- type: 'tabs',
241
- tabs: [
242
- {
243
- name: 'hero',
244
- label: 'Hero',
245
- fields: [
246
- {
247
- name: 'heroTitle',
248
- type: 'text'
249
- },
250
- {
251
- name: 'heroSize',
252
- type: 'select',
253
- options: [
254
- 'small',
255
- 'medium',
256
- 'large'
257
- ],
258
- defaultValue: 'medium'
259
- }
260
- ]
261
- },
262
- {
263
- label: 'Content',
264
- fields: [
265
- {
266
- name: 'slug',
267
- type: 'text',
268
- required: true
269
- },
270
- {
271
- name: 'layout',
272
- type: 'blocks',
273
- blocks: [
274
- FullWidth,
275
- HeadingOnly,
276
- CtaBanner,
277
- Accordion
278
- ]
279
- }
280
- ]
281
- }
282
- ]
283
- }
284
- ]
285
- };
286
- // ─── introspectCollection ──────────────────────────────────────────
287
- describe('introspectCollection', ()=>{
288
- it('extracts Posts collection fields, relationships, and draft status', ()=>{
289
- const schema = introspectCollection(Posts);
290
- expect(schema.slug).toBe('posts');
291
- expect(schema.hasDrafts).toBe(true);
292
- const fieldNames = schema.fields.map((f)=>f.name);
293
- expect(fieldNames).toContain('title');
294
- expect(fieldNames).toContain('slug');
295
- expect(fieldNames).toContain('featured');
296
- expect(fieldNames).toContain('tags');
297
- const relFieldNames = schema.relationships.map((r)=>r.fieldName);
298
- expect(relFieldNames).toContain('category');
299
- expect(relFieldNames).toContain('authors');
300
- const cover = schema.relationships.find((r)=>r.fieldName === 'coverImage');
301
- expect(cover).toBeDefined();
302
- expect(cover.relationTo).toBe('media');
303
- expect(schema.searchableFields).toContain('title');
304
- expect(schema.searchableFields).toContain('slug');
305
- });
306
- it('extracts Pages collection with tab-nested fields', ()=>{
307
- const schema = introspectCollection(Pages);
308
- expect(schema.slug).toBe('pages');
309
- expect(schema.hasDrafts).toBe(true);
310
- const fieldNames = schema.fields.map((f)=>f.name);
311
- expect(fieldNames).toContain('heroTitle');
312
- expect(fieldNames).toContain('slug');
313
- expect(fieldNames).toContain('layout');
314
- });
315
- it('detects collections without draft support', ()=>{
316
- const schema = introspectCollection(Categories);
317
- expect(schema.hasDrafts).toBe(false);
318
- });
319
- it('extracts select field options from Pages heroSize', ()=>{
320
- const schema = introspectCollection(Pages);
321
- const heroSize = schema.fields.find((f)=>f.name === 'heroSize');
322
- expect(heroSize).toBeDefined();
323
- expect(heroSize.type).toBe('select');
324
- expect(heroSize.options).toBeDefined();
325
- expect(heroSize.options.length).toBe(3);
326
- });
327
- });
328
- // ─── introspectBlocks (flat catalog) ───────────────────────────────
329
- describe('introspectBlocks', ()=>{
330
- it('returns a flat catalog of every block with no section/leaf split', ()=>{
331
- const catalog = introspectBlocks(allBlocks);
332
- const slugs = catalog.blocks.map((b)=>b.slug);
333
- expect(slugs).toEqual([
334
- 'heading',
335
- 'richText',
336
- 'image',
337
- 'fullWidth',
338
- 'headingOnly',
339
- 'ctaBanner',
340
- 'accordion'
341
- ]);
342
- });
343
- it('extracts each block\'s fields including select options', ()=>{
344
- const catalog = introspectBlocks(allBlocks);
345
- const heading = catalog.blocks.find((b)=>b.slug === 'heading');
346
- expect(heading).toBeDefined();
347
- const headingFieldNames = heading.fields.map((f)=>f.name);
348
- expect(headingFieldNames).toEqual([
349
- 'text',
350
- 'level',
351
- 'align'
352
- ]);
353
- const level = heading.fields.find((f)=>f.name === 'level');
354
- expect(level.options).toBeDefined();
355
- });
356
- });
357
- // ─── buildBlockNestingMap ──────────────────────────────────────────
358
- describe('buildBlockNestingMap', ()=>{
359
- it('records the layout field on Pages with the slugs it accepts', ()=>{
360
- const map = buildBlockNestingMap([
361
- Pages,
362
- Posts
363
- ], allBlocks);
364
- const pageLayout = map.find((e)=>e.ownerType === 'collection' && e.owner === 'pages' && e.fieldPath === 'layout');
365
- expect(pageLayout).toBeDefined();
366
- expect(pageLayout.acceptedBlockSlugs).toEqual([
367
- 'fullWidth',
368
- 'headingOnly',
369
- 'ctaBanner',
370
- 'accordion'
371
- ]);
372
- });
373
- it('records nested blocks fields inside container blocks', ()=>{
374
- const map = buildBlockNestingMap([
375
- Pages
376
- ], allBlocks);
377
- const fullWidthContent = map.find((e)=>e.ownerType === 'block' && e.owner === 'fullWidth' && e.fieldPath === 'content');
378
- expect(fullWidthContent).toBeDefined();
379
- expect(fullWidthContent.acceptedBlockSlugs).toEqual([
380
- 'heading',
381
- 'richText',
382
- 'image'
383
- ]);
384
- const headingOnly = map.find((e)=>e.ownerType === 'block' && e.owner === 'headingOnly' && e.fieldPath === 'content');
385
- expect(headingOnly.acceptedBlockSlugs).toEqual([
386
- 'heading'
387
- ]);
388
- expect(headingOnly.maxRows).toBe(1);
389
- });
390
- it('handles arbitrarily-deep nesting via array fields inside blocks', ()=>{
391
- const map = buildBlockNestingMap([
392
- Pages
393
- ], allBlocks);
394
- const accordionPanelBody = map.find((e)=>e.ownerType === 'block' && e.owner === 'accordion' && e.fieldPath === 'panels[].body');
395
- expect(accordionPanelBody).toBeDefined();
396
- expect(accordionPanelBody.acceptedBlockSlugs).toEqual([
397
- 'heading',
398
- 'richText',
399
- 'fullWidth'
400
- ]);
401
- });
402
- it('omits unknown slugs not present in the block list', ()=>{
403
- const Stray = {
404
- slug: 'stray',
405
- fields: [
406
- {
407
- name: 'layout',
408
- type: 'blocks',
409
- blocks: [
410
- Heading,
411
- {
412
- slug: 'mystery',
413
- fields: []
414
- }
415
- ]
416
- }
417
- ]
418
- };
419
- const map = buildBlockNestingMap([
420
- Stray
421
- ], [
422
- Heading
423
- ]) // mystery not in catalog
424
- ;
425
- const stray = map.find((e)=>e.owner === 'stray' && e.fieldPath === 'layout');
426
- expect(stray.acceptedBlockSlugs).toEqual([
427
- 'heading'
428
- ]);
429
- });
430
- it('omits fixed blocks (no nested blocks fields) from the map', ()=>{
431
- const map = buildBlockNestingMap([
432
- Pages
433
- ], allBlocks);
434
- const ctaEntries = map.filter((e)=>e.owner === 'ctaBanner');
435
- expect(ctaEntries).toHaveLength(0);
436
- });
437
- });
438
- // ─── buildRelationshipGraph ────────────────────────────────────────
439
- describe('buildRelationshipGraph', ()=>{
440
- it('builds correct graph from sample collections', ()=>{
441
- const schemas = introspectCollections([
442
- Posts,
443
- Pages,
444
- Categories,
445
- Authors,
446
- Media
447
- ]);
448
- const edges = buildRelationshipGraph(schemas);
449
- const postEdges = edges.filter((e)=>e.fromCollection === 'posts');
450
- const postTargets = postEdges.map((e)=>e.toCollection);
451
- expect(postTargets).toContain('categories');
452
- expect(postTargets).toContain('authors');
453
- expect(postTargets).toContain('media');
454
- const authorEdges = edges.filter((e)=>e.fromCollection === 'authors');
455
- expect(authorEdges.map((e)=>e.toCollection)).toContain('media');
456
- });
457
- });
458
-
459
- //# sourceMappingURL=introspection.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/__tests__/introspection.test.ts"],"sourcesContent":["import { describe, it, expect } from 'vitest'\nimport type { Block, CollectionConfig } from 'payload'\nimport {\n introspectCollection,\n introspectCollections,\n introspectBlocks,\n buildBlockNestingMap,\n buildRelationshipGraph,\n} from '../introspection'\n\n// ─── Sample schema (kept inline so the test is self-contained) ─────\n\nconst Media: CollectionConfig = {\n slug: 'media',\n upload: true,\n fields: [{ name: 'alt', type: 'text', required: true }],\n}\n\nconst Categories: CollectionConfig = {\n slug: 'categories',\n fields: [\n { name: 'name', type: 'text', required: true },\n { name: 'slug', type: 'text', required: true },\n ],\n}\n\nconst Authors: CollectionConfig = {\n slug: 'authors',\n fields: [\n { name: 'name', type: 'text', required: true },\n { name: 'slug', type: 'text', required: true },\n { name: 'avatar', type: 'upload', relationTo: 'media' },\n ],\n}\n\n// Leaf-style blocks\nconst Heading: Block = {\n slug: 'heading',\n fields: [\n { name: 'text', type: 'text', required: true },\n {\n name: 'level',\n type: 'select',\n options: ['h1', 'h2', 'h3'],\n defaultValue: 'h2',\n },\n {\n name: 'align',\n type: 'select',\n options: ['left', 'center', 'right'],\n defaultValue: 'left',\n },\n ],\n}\n\nconst RichText: Block = {\n slug: 'richText',\n fields: [{ name: 'content', type: 'richText' }],\n}\n\nconst ImageBlock: Block = {\n slug: 'image',\n fields: [\n { name: 'image', type: 'upload', relationTo: 'media', required: true },\n { name: 'caption', type: 'text' },\n ],\n}\n\n// Container-style blocks (have nested blocks fields)\nconst FullWidth: Block = {\n slug: 'fullWidth',\n fields: [\n {\n name: 'content',\n type: 'blocks',\n blocks: [Heading, RichText, ImageBlock],\n },\n ],\n}\n\nconst HeadingOnly: Block = {\n slug: 'headingOnly',\n fields: [\n {\n name: 'content',\n type: 'blocks',\n maxRows: 1,\n blocks: [Heading],\n },\n ],\n}\n\nconst CtaBanner: Block = {\n slug: 'ctaBanner',\n fields: [\n { name: 'headline', type: 'text', required: true },\n { name: 'buttonLabel', type: 'text' },\n { name: 'buttonHref', type: 'text' },\n ],\n}\n\n// Deeply-nestable container — exercises the recursive path\nconst Accordion: Block = {\n slug: 'accordion',\n fields: [\n {\n name: 'panels',\n type: 'array',\n fields: [\n { name: 'title', type: 'text' },\n {\n name: 'body',\n type: 'blocks',\n blocks: [Heading, RichText, FullWidth],\n },\n ],\n },\n ],\n}\n\nconst allBlocks: Block[] = [Heading, RichText, ImageBlock, FullWidth, HeadingOnly, CtaBanner, Accordion]\n\nconst Posts: CollectionConfig = {\n slug: 'posts',\n versions: { drafts: true },\n fields: [\n { name: 'title', type: 'text', required: true },\n { name: 'slug', type: 'text', required: true },\n { name: 'featured', type: 'checkbox' },\n { name: 'category', type: 'relationship', relationTo: 'categories' },\n {\n name: 'authors',\n type: 'relationship',\n relationTo: 'authors',\n hasMany: true,\n },\n { name: 'coverImage', type: 'upload', relationTo: 'media' },\n {\n name: 'tags',\n type: 'array',\n fields: [{ name: 'tag', type: 'text' }],\n },\n ],\n}\n\nconst Pages: CollectionConfig = {\n slug: 'pages',\n versions: { drafts: true },\n fields: [\n {\n type: 'tabs',\n tabs: [\n {\n name: 'hero',\n label: 'Hero',\n fields: [\n { name: 'heroTitle', type: 'text' },\n {\n name: 'heroSize',\n type: 'select',\n options: ['small', 'medium', 'large'],\n defaultValue: 'medium',\n },\n ],\n },\n {\n label: 'Content',\n fields: [\n { name: 'slug', type: 'text', required: true },\n { name: 'layout', type: 'blocks', blocks: [FullWidth, HeadingOnly, CtaBanner, Accordion] },\n ],\n },\n ],\n },\n ],\n}\n\n// ─── introspectCollection ──────────────────────────────────────────\n\ndescribe('introspectCollection', () => {\n it('extracts Posts collection fields, relationships, and draft status', () => {\n const schema = introspectCollection(Posts)\n\n expect(schema.slug).toBe('posts')\n expect(schema.hasDrafts).toBe(true)\n\n const fieldNames = schema.fields.map((f) => f.name)\n expect(fieldNames).toContain('title')\n expect(fieldNames).toContain('slug')\n expect(fieldNames).toContain('featured')\n expect(fieldNames).toContain('tags')\n\n const relFieldNames = schema.relationships.map((r) => r.fieldName)\n expect(relFieldNames).toContain('category')\n expect(relFieldNames).toContain('authors')\n\n const cover = schema.relationships.find((r) => r.fieldName === 'coverImage')\n expect(cover).toBeDefined()\n expect(cover!.relationTo).toBe('media')\n\n expect(schema.searchableFields).toContain('title')\n expect(schema.searchableFields).toContain('slug')\n })\n\n it('extracts Pages collection with tab-nested fields', () => {\n const schema = introspectCollection(Pages)\n\n expect(schema.slug).toBe('pages')\n expect(schema.hasDrafts).toBe(true)\n\n const fieldNames = schema.fields.map((f) => f.name)\n expect(fieldNames).toContain('heroTitle')\n expect(fieldNames).toContain('slug')\n expect(fieldNames).toContain('layout')\n })\n\n it('detects collections without draft support', () => {\n const schema = introspectCollection(Categories)\n expect(schema.hasDrafts).toBe(false)\n })\n\n it('extracts select field options from Pages heroSize', () => {\n const schema = introspectCollection(Pages)\n const heroSize = schema.fields.find((f) => f.name === 'heroSize')\n expect(heroSize).toBeDefined()\n expect(heroSize!.type).toBe('select')\n expect(heroSize!.options).toBeDefined()\n expect(heroSize!.options!.length).toBe(3)\n })\n})\n\n// ─── introspectBlocks (flat catalog) ───────────────────────────────\n\ndescribe('introspectBlocks', () => {\n it('returns a flat catalog of every block with no section/leaf split', () => {\n const catalog = introspectBlocks(allBlocks)\n const slugs = catalog.blocks.map((b) => b.slug)\n expect(slugs).toEqual([\n 'heading',\n 'richText',\n 'image',\n 'fullWidth',\n 'headingOnly',\n 'ctaBanner',\n 'accordion',\n ])\n })\n\n it('extracts each block\\'s fields including select options', () => {\n const catalog = introspectBlocks(allBlocks)\n const heading = catalog.blocks.find((b) => b.slug === 'heading')\n expect(heading).toBeDefined()\n const headingFieldNames = heading!.fields.map((f) => f.name)\n expect(headingFieldNames).toEqual(['text', 'level', 'align'])\n const level = heading!.fields.find((f) => f.name === 'level')\n expect(level!.options).toBeDefined()\n })\n})\n\n// ─── buildBlockNestingMap ──────────────────────────────────────────\n\ndescribe('buildBlockNestingMap', () => {\n it('records the layout field on Pages with the slugs it accepts', () => {\n const map = buildBlockNestingMap([Pages, Posts], allBlocks)\n const pageLayout = map.find(\n (e) => e.ownerType === 'collection' && e.owner === 'pages' && e.fieldPath === 'layout',\n )\n expect(pageLayout).toBeDefined()\n expect(pageLayout!.acceptedBlockSlugs).toEqual(['fullWidth', 'headingOnly', 'ctaBanner', 'accordion'])\n })\n\n it('records nested blocks fields inside container blocks', () => {\n const map = buildBlockNestingMap([Pages], allBlocks)\n\n const fullWidthContent = map.find(\n (e) => e.ownerType === 'block' && e.owner === 'fullWidth' && e.fieldPath === 'content',\n )\n expect(fullWidthContent).toBeDefined()\n expect(fullWidthContent!.acceptedBlockSlugs).toEqual(['heading', 'richText', 'image'])\n\n const headingOnly = map.find(\n (e) => e.ownerType === 'block' && e.owner === 'headingOnly' && e.fieldPath === 'content',\n )\n expect(headingOnly!.acceptedBlockSlugs).toEqual(['heading'])\n expect(headingOnly!.maxRows).toBe(1)\n })\n\n it('handles arbitrarily-deep nesting via array fields inside blocks', () => {\n const map = buildBlockNestingMap([Pages], allBlocks)\n\n const accordionPanelBody = map.find(\n (e) =>\n e.ownerType === 'block' && e.owner === 'accordion' && e.fieldPath === 'panels[].body',\n )\n expect(accordionPanelBody).toBeDefined()\n expect(accordionPanelBody!.acceptedBlockSlugs).toEqual(['heading', 'richText', 'fullWidth'])\n })\n\n it('omits unknown slugs not present in the block list', () => {\n const Stray: CollectionConfig = {\n slug: 'stray',\n fields: [\n {\n name: 'layout',\n type: 'blocks',\n blocks: [Heading, { slug: 'mystery', fields: [] } as Block],\n },\n ],\n }\n const map = buildBlockNestingMap([Stray], [Heading]) // mystery not in catalog\n const stray = map.find((e) => e.owner === 'stray' && e.fieldPath === 'layout')\n expect(stray!.acceptedBlockSlugs).toEqual(['heading'])\n })\n\n it('omits fixed blocks (no nested blocks fields) from the map', () => {\n const map = buildBlockNestingMap([Pages], allBlocks)\n const ctaEntries = map.filter((e) => e.owner === 'ctaBanner')\n expect(ctaEntries).toHaveLength(0)\n })\n})\n\n// ─── buildRelationshipGraph ────────────────────────────────────────\n\ndescribe('buildRelationshipGraph', () => {\n it('builds correct graph from sample collections', () => {\n const schemas = introspectCollections([Posts, Pages, Categories, Authors, Media])\n const edges = buildRelationshipGraph(schemas)\n\n const postEdges = edges.filter((e) => e.fromCollection === 'posts')\n const postTargets = postEdges.map((e) => e.toCollection)\n expect(postTargets).toContain('categories')\n expect(postTargets).toContain('authors')\n expect(postTargets).toContain('media')\n\n const authorEdges = edges.filter((e) => e.fromCollection === 'authors')\n expect(authorEdges.map((e) => e.toCollection)).toContain('media')\n })\n})\n"],"names":["describe","it","expect","introspectCollection","introspectCollections","introspectBlocks","buildBlockNestingMap","buildRelationshipGraph","Media","slug","upload","fields","name","type","required","Categories","Authors","relationTo","Heading","options","defaultValue","RichText","ImageBlock","FullWidth","blocks","HeadingOnly","maxRows","CtaBanner","Accordion","allBlocks","Posts","versions","drafts","hasMany","Pages","tabs","label","schema","toBe","hasDrafts","fieldNames","map","f","toContain","relFieldNames","relationships","r","fieldName","cover","find","toBeDefined","searchableFields","heroSize","length","catalog","slugs","b","toEqual","heading","headingFieldNames","level","pageLayout","e","ownerType","owner","fieldPath","acceptedBlockSlugs","fullWidthContent","headingOnly","accordionPanelBody","Stray","stray","ctaEntries","filter","toHaveLength","schemas","edges","postEdges","fromCollection","postTargets","toCollection","authorEdges"],"mappings":"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"}