payload-mcp-toolkit 0.3.3 → 0.7.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 (144) hide show
  1. package/README.md +232 -150
  2. package/dist/__tests__/api-keys.test.js +292 -0
  3. package/dist/__tests__/api-keys.test.js.map +1 -0
  4. package/dist/__tests__/auth-strategy.test.js +681 -0
  5. package/dist/__tests__/auth-strategy.test.js.map +1 -0
  6. package/dist/__tests__/conflict-detection.test.js +69 -0
  7. package/dist/__tests__/conflict-detection.test.js.map +1 -0
  8. package/dist/__tests__/delete-document.test.js +70 -0
  9. package/dist/__tests__/delete-document.test.js.map +1 -0
  10. package/dist/__tests__/endpoint.test.js +143 -0
  11. package/dist/__tests__/endpoint.test.js.map +1 -0
  12. package/dist/__tests__/find-document.test.js +178 -0
  13. package/dist/__tests__/find-document.test.js.map +1 -0
  14. package/dist/__tests__/find-global.test.js +173 -0
  15. package/dist/__tests__/find-global.test.js.map +1 -0
  16. package/dist/__tests__/global-versions.test.js +183 -0
  17. package/dist/__tests__/global-versions.test.js.map +1 -0
  18. package/dist/__tests__/hash.test.js +58 -0
  19. package/dist/__tests__/hash.test.js.map +1 -0
  20. package/dist/__tests__/index-integration.test.js +191 -0
  21. package/dist/__tests__/index-integration.test.js.map +1 -0
  22. package/dist/__tests__/introspection.test.js +201 -1
  23. package/dist/__tests__/introspection.test.js.map +1 -1
  24. package/dist/__tests__/patch-global-layout.test.js +474 -0
  25. package/dist/__tests__/patch-global-layout.test.js.map +1 -0
  26. package/dist/__tests__/patch-layout.test.js +171 -0
  27. package/dist/__tests__/patch-layout.test.js.map +1 -0
  28. package/dist/__tests__/registry.test.js +795 -0
  29. package/dist/__tests__/registry.test.js.map +1 -0
  30. package/dist/__tests__/resources.test.js +139 -0
  31. package/dist/__tests__/resources.test.js.map +1 -0
  32. package/dist/__tests__/update-global.test.js +157 -0
  33. package/dist/__tests__/update-global.test.js.map +1 -0
  34. package/dist/api-keys.d.ts +46 -0
  35. package/dist/api-keys.js +272 -0
  36. package/dist/api-keys.js.map +1 -0
  37. package/dist/auth-strategy.d.ts +85 -0
  38. package/dist/auth-strategy.js +219 -0
  39. package/dist/auth-strategy.js.map +1 -0
  40. package/dist/components/CollectionScopesMatrix.d.ts +8 -0
  41. package/dist/components/CollectionScopesMatrix.js +32 -0
  42. package/dist/components/CollectionScopesMatrix.js.map +1 -0
  43. package/dist/components/GlobalScopesMatrix.d.ts +8 -0
  44. package/dist/components/GlobalScopesMatrix.js +28 -0
  45. package/dist/components/GlobalScopesMatrix.js.map +1 -0
  46. package/dist/components/ScopesTable.d.ts +19 -0
  47. package/dist/components/ScopesTable.js +285 -0
  48. package/dist/components/ScopesTable.js.map +1 -0
  49. package/dist/components/index.d.ts +2 -0
  50. package/dist/components/index.js +4 -0
  51. package/dist/components/index.js.map +1 -0
  52. package/dist/conflict-detection.d.ts +13 -0
  53. package/dist/conflict-detection.js +41 -0
  54. package/dist/conflict-detection.js.map +1 -0
  55. package/dist/draft-workflow.d.ts +46 -47
  56. package/dist/draft-workflow.js +53 -130
  57. package/dist/draft-workflow.js.map +1 -1
  58. package/dist/endpoint.d.ts +35 -0
  59. package/dist/endpoint.js +105 -0
  60. package/dist/endpoint.js.map +1 -0
  61. package/dist/hash.d.ts +21 -0
  62. package/dist/hash.js +36 -0
  63. package/dist/hash.js.map +1 -0
  64. package/dist/index.d.ts +9 -9
  65. package/dist/index.js +168 -68
  66. package/dist/index.js.map +1 -1
  67. package/dist/introspection.d.ts +17 -3
  68. package/dist/introspection.js +95 -36
  69. package/dist/introspection.js.map +1 -1
  70. package/dist/prompts.js +5 -5
  71. package/dist/prompts.js.map +1 -1
  72. package/dist/registry.d.ts +50 -0
  73. package/dist/registry.js +169 -0
  74. package/dist/registry.js.map +1 -0
  75. package/dist/resources.d.ts +5 -3
  76. package/dist/resources.js +23 -11
  77. package/dist/resources.js.map +1 -1
  78. package/dist/scope/audit-log.d.ts +18 -0
  79. package/dist/scope/audit-log.js +50 -0
  80. package/dist/scope/audit-log.js.map +1 -0
  81. package/dist/scope/policy.d.ts +73 -0
  82. package/dist/scope/policy.js +218 -0
  83. package/dist/scope/policy.js.map +1 -0
  84. package/dist/tools/_helpers.d.ts +28 -1
  85. package/dist/tools/_helpers.js +83 -0
  86. package/dist/tools/_helpers.js.map +1 -1
  87. package/dist/tools/_layout-helpers.d.ts +43 -0
  88. package/dist/tools/_layout-helpers.js +159 -0
  89. package/dist/tools/_layout-helpers.js.map +1 -0
  90. package/dist/tools/create-document.d.ts +36 -0
  91. package/dist/tools/create-document.js +83 -0
  92. package/dist/tools/create-document.js.map +1 -0
  93. package/dist/tools/delete-document.d.ts +25 -0
  94. package/dist/tools/delete-document.js +49 -0
  95. package/dist/tools/delete-document.js.map +1 -0
  96. package/dist/tools/find-document.d.ts +33 -0
  97. package/dist/tools/find-document.js +97 -0
  98. package/dist/tools/find-document.js.map +1 -0
  99. package/dist/tools/find-global.d.ts +26 -0
  100. package/dist/tools/find-global.js +122 -0
  101. package/dist/tools/find-global.js.map +1 -0
  102. package/dist/tools/global-versions.d.ts +39 -0
  103. package/dist/tools/global-versions.js +132 -0
  104. package/dist/tools/global-versions.js.map +1 -0
  105. package/dist/tools/patch-global-layout.d.ts +31 -0
  106. package/dist/tools/patch-global-layout.js +127 -0
  107. package/dist/tools/patch-global-layout.js.map +1 -0
  108. package/dist/tools/patch-layout.d.ts +5 -8
  109. package/dist/tools/patch-layout.js +18 -100
  110. package/dist/tools/patch-layout.js.map +1 -1
  111. package/dist/tools/publish-draft.d.ts +5 -4
  112. package/dist/tools/publish-draft.js +6 -1
  113. package/dist/tools/publish-draft.js.map +1 -1
  114. package/dist/tools/publish-global-draft.d.ts +20 -0
  115. package/dist/tools/publish-global-draft.js +50 -0
  116. package/dist/tools/publish-global-draft.js.map +1 -0
  117. package/dist/tools/resolve-reference.d.ts +5 -4
  118. package/dist/tools/resolve-reference.js +4 -0
  119. package/dist/tools/resolve-reference.js.map +1 -1
  120. package/dist/tools/safe-delete.d.ts +5 -5
  121. package/dist/tools/safe-delete.js +20 -15
  122. package/dist/tools/safe-delete.js.map +1 -1
  123. package/dist/tools/schedule-publish.d.ts +5 -5
  124. package/dist/tools/schedule-publish.js +23 -19
  125. package/dist/tools/schedule-publish.js.map +1 -1
  126. package/dist/tools/search-content.d.ts +5 -9
  127. package/dist/tools/search-content.js +16 -12
  128. package/dist/tools/search-content.js.map +1 -1
  129. package/dist/tools/update-document.d.ts +5 -5
  130. package/dist/tools/update-document.js +10 -5
  131. package/dist/tools/update-document.js.map +1 -1
  132. package/dist/tools/update-global.d.ts +27 -0
  133. package/dist/tools/update-global.js +72 -0
  134. package/dist/tools/update-global.js.map +1 -0
  135. package/dist/tools/upload-media.d.ts +5 -4
  136. package/dist/tools/upload-media.js +6 -1
  137. package/dist/tools/upload-media.js.map +1 -1
  138. package/dist/tools/versions.d.ts +10 -9
  139. package/dist/tools/versions.js +15 -7
  140. package/dist/tools/versions.js.map +1 -1
  141. package/dist/types.d.ts +56 -3
  142. package/dist/types.js +13 -6
  143. package/dist/types.js.map +1 -1
  144. package/package.json +11 -4
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/__tests__/index-integration.test.ts"],"sourcesContent":["import { describe, it, expect } from 'vitest'\r\nimport type { Config } from 'payload'\r\nimport { mcpToolkitPlugin } from '../index'\r\n\r\nfunction baseConfig(): Config {\r\n return {\r\n serverURL: 'https://app.example.com',\r\n secret: 'test-secret',\r\n admin: { user: 'users' },\r\n collections: [\r\n {\r\n slug: 'users',\r\n auth: true,\r\n fields: [{ name: 'email', type: 'email', required: true }],\r\n },\r\n {\r\n slug: 'posts',\r\n fields: [\r\n { name: 'title', type: 'text' },\r\n { name: 'slug', type: 'text' },\r\n ],\r\n },\r\n ],\r\n endpoints: [],\r\n } as never\r\n}\r\n\r\ndescribe('mcpToolkitPlugin integration', () => {\r\n it('appends the api-keys collection and the MCP endpoints additively', () => {\r\n const cfg = mcpToolkitPlugin()(baseConfig())\r\n const slugs = (cfg.collections ?? []).map((c) => c.slug)\r\n expect(slugs).toContain('payload-mcp-api-keys')\r\n expect(slugs).toContain('users')\r\n expect(slugs).toContain('posts')\r\n const endpointPaths = (cfg.endpoints ?? []).map((e) => `${e.method.toUpperCase()} ${e.path}`)\r\n expect(endpointPaths).toEqual(expect.arrayContaining(['POST /mcp', 'GET /mcp']))\r\n })\r\n\r\n it('attaches the bearer strategy to the user collection without losing existing auth config', () => {\r\n const cfg = mcpToolkitPlugin()(baseConfig())\r\n const users = (cfg.collections ?? []).find((c) => c.slug === 'users') as {\r\n auth: { strategies?: Array<{ name: string }> }\r\n }\r\n expect(users).toBeDefined()\r\n const names = users.auth.strategies?.map((s) => s.name) ?? []\r\n expect(names).toContain('mcp-toolkit-bearer')\r\n })\r\n\r\n it('preserves existing auth.strategies on the user collection', () => {\r\n const cfg = baseConfig() as Config & {\r\n collections: Array<{ slug: string; auth?: unknown; fields: unknown[] }>\r\n }\r\n const otherStrategy = { name: 'tenant-shared-strategy', authenticate: async () => ({ user: null }) }\r\n cfg.collections[0]!.auth = { strategies: [otherStrategy] } as never\r\n const out = mcpToolkitPlugin()(cfg as never)\r\n const users = (out.collections ?? []).find((c) => c.slug === 'users') as {\r\n auth: { strategies: Array<{ name: string }> }\r\n }\r\n const names = users.auth.strategies.map((s) => s.name)\r\n expect(names).toEqual(['tenant-shared-strategy', 'mcp-toolkit-bearer'])\r\n })\r\n\r\n it('respects a custom user collection slug from incomingConfig.admin.user', () => {\r\n const cfg = baseConfig() as Config & {\r\n collections: Array<{ slug: string; auth?: unknown; fields: unknown[] }>\r\n admin?: { user?: string }\r\n }\r\n cfg.admin = { user: 'admins' }\r\n cfg.collections[0]!.slug = 'admins'\r\n const out = mcpToolkitPlugin()(cfg as never)\r\n const admins = (out.collections ?? []).find((c) => c.slug === 'admins') as {\r\n auth: { strategies?: Array<{ name: string }> }\r\n }\r\n expect(admins.auth.strategies?.some((s) => s.name === 'mcp-toolkit-bearer')).toBe(true)\r\n })\r\n\r\n it('throws when @payloadcms/plugin-mcp appears to also be registered', () => {\r\n const cfg = baseConfig() as Config\r\n function mcpPlugin() {}\r\n ;(cfg as { plugins: unknown[] }).plugins = [mcpPlugin as never]\r\n expect(() => mcpToolkitPlugin()(cfg)).toThrow(/standalone successor/)\r\n })\r\n\r\n it('throws when an existing collection takes the api-keys slug', () => {\r\n const cfg = baseConfig() as Config & {\r\n collections: Array<{ slug: string; fields: unknown[] }>\r\n }\r\n cfg.collections.push({ slug: 'payload-mcp-api-keys', fields: [] })\r\n expect(() => mcpToolkitPlugin()(cfg as never)).toThrow(/payload-mcp-api-keys/)\r\n })\r\n\r\n it('honours a custom apiKeyCollection.slug', () => {\r\n const cfg = mcpToolkitPlugin({ apiKeyCollection: { slug: 'my-keys' } })(baseConfig())\r\n const slugs = (cfg.collections ?? []).map((c) => c.slug)\r\n expect(slugs).toContain('my-keys')\r\n expect(slugs).not.toContain('payload-mcp-api-keys')\r\n })\r\n\r\n it('a host config with no globals still registers the globalScopes field (matrix shows empty state)', () => {\r\n const cfg = mcpToolkitPlugin()(baseConfig())\r\n // The field always renders under Custom; the matrix component reports\r\n // the absence via its own empty-state copy when availableGlobals is [].\r\n const apiKeys = (cfg.collections ?? []).find((c) => c.slug === 'payload-mcp-api-keys') as {\r\n fields: Array<{\r\n name?: string\r\n admin?: {\r\n condition?: (data: unknown) => boolean\r\n components?: { Field?: { clientProps?: { availableGlobals?: string[] } } }\r\n }\r\n }>\r\n }\r\n const globalScopes = apiKeys.fields.find((f) => f.name === 'globalScopes')!\r\n expect(globalScopes.admin?.condition?.({ preset: 'custom' })).toBe(true)\r\n expect(globalScopes.admin?.condition?.({ preset: 'editor' })).toBe(false)\r\n expect(globalScopes.admin?.components?.Field?.clientProps?.availableGlobals).toEqual([])\r\n })\r\n\r\n it('a host config with one plain global makes globalScopes UI render under Custom', () => {\r\n const cfg = baseConfig() as Config & { globals?: unknown[] }\r\n cfg.globals = [\r\n { slug: 'site-settings', fields: [{ name: 'siteName', type: 'text' }] },\r\n ] as never\r\n const out = mcpToolkitPlugin()(cfg)\r\n const apiKeys = (out.collections ?? []).find((c) => c.slug === 'payload-mcp-api-keys') as {\r\n fields: Array<{\r\n name?: string\r\n admin?: {\r\n condition?: (data: unknown) => boolean\r\n components?: { Field?: { clientProps?: { availableGlobals?: string[] } } }\r\n }\r\n }>\r\n }\r\n const globalScopes = apiKeys.fields.find((f) => f.name === 'globalScopes')!\r\n expect(globalScopes.admin?.condition?.({ preset: 'custom' })).toBe(true)\r\n expect(globalScopes.admin?.components?.Field?.clientProps?.availableGlobals).toEqual([\r\n 'site-settings',\r\n ])\r\n })\r\n\r\n it('excluded globals are filtered out of availableGlobals at registration time', () => {\r\n const cfg = baseConfig() as Config & { globals?: unknown[] }\r\n cfg.globals = [\r\n { slug: 'site-settings', fields: [{ name: 'siteName', type: 'text' }] },\r\n { slug: 'secret-config', fields: [{ name: 'token', type: 'text' }] },\r\n ] as never\r\n const out = mcpToolkitPlugin({ exclude: { globals: ['secret-config'] } })(cfg)\r\n const apiKeys = (out.collections ?? []).find((c) => c.slug === 'payload-mcp-api-keys') as {\r\n fields: Array<{\r\n name?: string\r\n admin?: { components?: { Field?: { clientProps?: { availableGlobals?: string[] } } } }\r\n }>\r\n }\r\n const globalScopes = apiKeys.fields.find((f) => f.name === 'globalScopes')!\r\n expect(globalScopes.admin?.components?.Field?.clientProps?.availableGlobals).toEqual([\r\n 'site-settings',\r\n ])\r\n })\r\n})\r\n"],"names":["describe","it","expect","mcpToolkitPlugin","baseConfig","serverURL","secret","admin","user","collections","slug","auth","fields","name","type","required","endpoints","cfg","slugs","map","c","toContain","endpointPaths","e","method","toUpperCase","path","toEqual","arrayContaining","users","find","toBeDefined","names","strategies","s","otherStrategy","authenticate","out","admins","some","toBe","mcpPlugin","plugins","toThrow","push","apiKeyCollection","not","apiKeys","globalScopes","f","condition","preset","components","Field","clientProps","availableGlobals","globals","exclude"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { introspectCollection, introspectCollections, introspectBlocks, buildBlockNestingMap, buildRelationshipGraph } from '../introspection';
2
+ import { introspectCollection, introspectCollections, introspectBlocks, buildBlockNestingMap, buildRelationshipGraph, hasGlobalDrafts, introspectGlobal, introspectGlobals } from '../introspection';
3
3
  // ─── Sample schema (kept inline so the test is self-contained) ─────
4
4
  const Media = {
5
5
  slug: 'media',
@@ -436,6 +436,206 @@ describe('buildBlockNestingMap', ()=>{
436
436
  });
437
437
  });
438
438
  // ─── buildRelationshipGraph ────────────────────────────────────────
439
+ // ─── Global introspection ──────────────────────────────────────────
440
+ const SiteSettings = {
441
+ slug: 'site-settings',
442
+ fields: [
443
+ {
444
+ name: 'siteName',
445
+ type: 'text',
446
+ required: true
447
+ },
448
+ {
449
+ name: 'tagline',
450
+ type: 'text'
451
+ },
452
+ {
453
+ name: 'social',
454
+ type: 'group',
455
+ fields: [
456
+ {
457
+ name: 'twitter',
458
+ type: 'text'
459
+ },
460
+ {
461
+ name: 'instagram',
462
+ type: 'text'
463
+ }
464
+ ]
465
+ }
466
+ ]
467
+ };
468
+ const FooterGlobal = {
469
+ slug: 'footer',
470
+ versions: {
471
+ drafts: true
472
+ },
473
+ fields: [
474
+ {
475
+ name: 'layout',
476
+ type: 'blocks',
477
+ blocks: [
478
+ Heading,
479
+ CtaBanner
480
+ ]
481
+ }
482
+ ]
483
+ };
484
+ const HeaderGlobal = {
485
+ slug: 'header',
486
+ fields: [
487
+ {
488
+ name: 'menu',
489
+ type: 'group',
490
+ fields: [
491
+ {
492
+ name: 'label',
493
+ type: 'text'
494
+ },
495
+ {
496
+ name: 'links',
497
+ type: 'blocks',
498
+ blocks: [
499
+ Heading
500
+ ]
501
+ }
502
+ ]
503
+ }
504
+ ]
505
+ };
506
+ describe('hasGlobalDrafts', ()=>{
507
+ it('returns true for { versions: { drafts: true } }', ()=>{
508
+ expect(hasGlobalDrafts({
509
+ slug: 'g',
510
+ versions: {
511
+ drafts: true
512
+ },
513
+ fields: []
514
+ })).toBe(true);
515
+ });
516
+ it('returns false for { versions: { drafts: false } }', ()=>{
517
+ expect(hasGlobalDrafts({
518
+ slug: 'g',
519
+ versions: {
520
+ drafts: false
521
+ },
522
+ fields: []
523
+ })).toBe(false);
524
+ });
525
+ it('returns false when versions is undefined', ()=>{
526
+ expect(hasGlobalDrafts({
527
+ slug: 'g',
528
+ fields: []
529
+ })).toBe(false);
530
+ });
531
+ it('returns false for versions without drafts key', ()=>{
532
+ expect(hasGlobalDrafts({
533
+ slug: 'g',
534
+ versions: {
535
+ maxPerDoc: 10
536
+ },
537
+ fields: []
538
+ })).toBe(false);
539
+ });
540
+ });
541
+ describe('introspectGlobal', ()=>{
542
+ it('extracts SiteSettings fields and draft/live-preview flags', ()=>{
543
+ const schema = introspectGlobal(SiteSettings);
544
+ expect(schema.slug).toBe('site-settings');
545
+ expect(schema.hasDrafts).toBe(false);
546
+ expect(schema.hasLivePreview).toBe(false);
547
+ const names = schema.fields.map((f)=>f.name);
548
+ expect(names).toContain('siteName');
549
+ expect(names).toContain('tagline');
550
+ const social = schema.fields.find((f)=>f.name === 'social');
551
+ expect(social?.type).toBe('group');
552
+ expect(social?.fields?.map((f)=>f.name)).toEqual([
553
+ 'twitter',
554
+ 'instagram'
555
+ ]);
556
+ });
557
+ it('reports hasDrafts: true when versions.drafts is set', ()=>{
558
+ expect(introspectGlobal(FooterGlobal).hasDrafts).toBe(true);
559
+ });
560
+ });
561
+ describe('introspectGlobals', ()=>{
562
+ it('returns an empty Map for []', ()=>{
563
+ expect(introspectGlobals([]).size).toBe(0);
564
+ });
565
+ it('keys the map by slug', ()=>{
566
+ const map = introspectGlobals([
567
+ SiteSettings,
568
+ FooterGlobal
569
+ ]);
570
+ expect(map.has('site-settings')).toBe(true);
571
+ expect(map.has('footer')).toBe(true);
572
+ });
573
+ });
574
+ describe('buildBlockNestingMap with globals', ()=>{
575
+ it('emits an edge with ownerType "global" for a top-level blocks field', ()=>{
576
+ const map = buildBlockNestingMap([], [
577
+ FooterGlobal
578
+ ], allBlocks);
579
+ const edge = map.find((e)=>e.ownerType === 'global' && e.owner === 'footer' && e.fieldPath === 'layout');
580
+ expect(edge).toBeDefined();
581
+ expect(edge.acceptedBlockSlugs).toEqual([
582
+ 'heading',
583
+ 'ctaBanner'
584
+ ]);
585
+ });
586
+ it('walks group-nested blocks fields under a global with the dotted path', ()=>{
587
+ const map = buildBlockNestingMap([], [
588
+ HeaderGlobal
589
+ ], allBlocks);
590
+ const edge = map.find((e)=>e.ownerType === 'global' && e.owner === 'header' && e.fieldPath === 'menu.links');
591
+ expect(edge).toBeDefined();
592
+ expect(edge.acceptedBlockSlugs).toEqual([
593
+ 'heading'
594
+ ]);
595
+ });
596
+ it('two-arg call (no globals) produces the same edges as before — regression guard', ()=>{
597
+ const before = buildBlockNestingMap([
598
+ Pages
599
+ ], allBlocks);
600
+ const after = buildBlockNestingMap([
601
+ Pages
602
+ ], [], allBlocks);
603
+ expect(after).toEqual(before);
604
+ });
605
+ it('invariant: throws when (owner, fieldPath) appears with different ownerTypes', ()=>{
606
+ const ClashCollection = {
607
+ slug: 'site-settings',
608
+ fields: [
609
+ {
610
+ name: 'layout',
611
+ type: 'blocks',
612
+ blocks: [
613
+ Heading
614
+ ]
615
+ }
616
+ ]
617
+ };
618
+ const ClashGlobal = {
619
+ slug: 'site-settings',
620
+ fields: [
621
+ {
622
+ name: 'layout',
623
+ type: 'blocks',
624
+ blocks: [
625
+ Heading
626
+ ]
627
+ }
628
+ ]
629
+ };
630
+ expect(()=>buildBlockNestingMap([
631
+ ClashCollection
632
+ ], [
633
+ ClashGlobal
634
+ ], [
635
+ Heading
636
+ ])).toThrow(/invariant violated/i);
637
+ });
638
+ });
439
639
  describe('buildRelationshipGraph', ()=>{
440
640
  it('builds correct graph from sample collections', ()=>{
441
641
  const schemas = introspectCollections([
@@ -1 +1 @@
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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"}
1
+ {"version":3,"sources":["../../src/__tests__/introspection.test.ts"],"sourcesContent":["import { describe, it, expect } from 'vitest'\r\nimport type { Block, CollectionConfig, GlobalConfig } from 'payload'\r\nimport {\r\n introspectCollection,\r\n introspectCollections,\r\n introspectBlocks,\r\n buildBlockNestingMap,\r\n buildRelationshipGraph,\r\n hasGlobalDrafts,\r\n introspectGlobal,\r\n introspectGlobals,\r\n} from '../introspection'\r\n\r\n// ─── Sample schema (kept inline so the test is self-contained) ─────\r\n\r\nconst Media: CollectionConfig = {\r\n slug: 'media',\r\n upload: true,\r\n fields: [{ name: 'alt', type: 'text', required: true }],\r\n}\r\n\r\nconst Categories: CollectionConfig = {\r\n slug: 'categories',\r\n fields: [\r\n { name: 'name', type: 'text', required: true },\r\n { name: 'slug', type: 'text', required: true },\r\n ],\r\n}\r\n\r\nconst Authors: CollectionConfig = {\r\n slug: 'authors',\r\n fields: [\r\n { name: 'name', type: 'text', required: true },\r\n { name: 'slug', type: 'text', required: true },\r\n { name: 'avatar', type: 'upload', relationTo: 'media' },\r\n ],\r\n}\r\n\r\n// Leaf-style blocks\r\nconst Heading: Block = {\r\n slug: 'heading',\r\n fields: [\r\n { name: 'text', type: 'text', required: true },\r\n {\r\n name: 'level',\r\n type: 'select',\r\n options: ['h1', 'h2', 'h3'],\r\n defaultValue: 'h2',\r\n },\r\n {\r\n name: 'align',\r\n type: 'select',\r\n options: ['left', 'center', 'right'],\r\n defaultValue: 'left',\r\n },\r\n ],\r\n}\r\n\r\nconst RichText: Block = {\r\n slug: 'richText',\r\n fields: [{ name: 'content', type: 'richText' }],\r\n}\r\n\r\nconst ImageBlock: Block = {\r\n slug: 'image',\r\n fields: [\r\n { name: 'image', type: 'upload', relationTo: 'media', required: true },\r\n { name: 'caption', type: 'text' },\r\n ],\r\n}\r\n\r\n// Container-style blocks (have nested blocks fields)\r\nconst FullWidth: Block = {\r\n slug: 'fullWidth',\r\n fields: [\r\n {\r\n name: 'content',\r\n type: 'blocks',\r\n blocks: [Heading, RichText, ImageBlock],\r\n },\r\n ],\r\n}\r\n\r\nconst HeadingOnly: Block = {\r\n slug: 'headingOnly',\r\n fields: [\r\n {\r\n name: 'content',\r\n type: 'blocks',\r\n maxRows: 1,\r\n blocks: [Heading],\r\n },\r\n ],\r\n}\r\n\r\nconst CtaBanner: Block = {\r\n slug: 'ctaBanner',\r\n fields: [\r\n { name: 'headline', type: 'text', required: true },\r\n { name: 'buttonLabel', type: 'text' },\r\n { name: 'buttonHref', type: 'text' },\r\n ],\r\n}\r\n\r\n// Deeply-nestable container — exercises the recursive path\r\nconst Accordion: Block = {\r\n slug: 'accordion',\r\n fields: [\r\n {\r\n name: 'panels',\r\n type: 'array',\r\n fields: [\r\n { name: 'title', type: 'text' },\r\n {\r\n name: 'body',\r\n type: 'blocks',\r\n blocks: [Heading, RichText, FullWidth],\r\n },\r\n ],\r\n },\r\n ],\r\n}\r\n\r\nconst allBlocks: Block[] = [Heading, RichText, ImageBlock, FullWidth, HeadingOnly, CtaBanner, Accordion]\r\n\r\nconst Posts: CollectionConfig = {\r\n slug: 'posts',\r\n versions: { drafts: true },\r\n fields: [\r\n { name: 'title', type: 'text', required: true },\r\n { name: 'slug', type: 'text', required: true },\r\n { name: 'featured', type: 'checkbox' },\r\n { name: 'category', type: 'relationship', relationTo: 'categories' },\r\n {\r\n name: 'authors',\r\n type: 'relationship',\r\n relationTo: 'authors',\r\n hasMany: true,\r\n },\r\n { name: 'coverImage', type: 'upload', relationTo: 'media' },\r\n {\r\n name: 'tags',\r\n type: 'array',\r\n fields: [{ name: 'tag', type: 'text' }],\r\n },\r\n ],\r\n}\r\n\r\nconst Pages: CollectionConfig = {\r\n slug: 'pages',\r\n versions: { drafts: true },\r\n fields: [\r\n {\r\n type: 'tabs',\r\n tabs: [\r\n {\r\n name: 'hero',\r\n label: 'Hero',\r\n fields: [\r\n { name: 'heroTitle', type: 'text' },\r\n {\r\n name: 'heroSize',\r\n type: 'select',\r\n options: ['small', 'medium', 'large'],\r\n defaultValue: 'medium',\r\n },\r\n ],\r\n },\r\n {\r\n label: 'Content',\r\n fields: [\r\n { name: 'slug', type: 'text', required: true },\r\n { name: 'layout', type: 'blocks', blocks: [FullWidth, HeadingOnly, CtaBanner, Accordion] },\r\n ],\r\n },\r\n ],\r\n },\r\n ],\r\n}\r\n\r\n// ─── introspectCollection ──────────────────────────────────────────\r\n\r\ndescribe('introspectCollection', () => {\r\n it('extracts Posts collection fields, relationships, and draft status', () => {\r\n const schema = introspectCollection(Posts)\r\n\r\n expect(schema.slug).toBe('posts')\r\n expect(schema.hasDrafts).toBe(true)\r\n\r\n const fieldNames = schema.fields.map((f) => f.name)\r\n expect(fieldNames).toContain('title')\r\n expect(fieldNames).toContain('slug')\r\n expect(fieldNames).toContain('featured')\r\n expect(fieldNames).toContain('tags')\r\n\r\n const relFieldNames = schema.relationships.map((r) => r.fieldName)\r\n expect(relFieldNames).toContain('category')\r\n expect(relFieldNames).toContain('authors')\r\n\r\n const cover = schema.relationships.find((r) => r.fieldName === 'coverImage')\r\n expect(cover).toBeDefined()\r\n expect(cover!.relationTo).toBe('media')\r\n\r\n expect(schema.searchableFields).toContain('title')\r\n expect(schema.searchableFields).toContain('slug')\r\n })\r\n\r\n it('extracts Pages collection with tab-nested fields', () => {\r\n const schema = introspectCollection(Pages)\r\n\r\n expect(schema.slug).toBe('pages')\r\n expect(schema.hasDrafts).toBe(true)\r\n\r\n const fieldNames = schema.fields.map((f) => f.name)\r\n expect(fieldNames).toContain('heroTitle')\r\n expect(fieldNames).toContain('slug')\r\n expect(fieldNames).toContain('layout')\r\n })\r\n\r\n it('detects collections without draft support', () => {\r\n const schema = introspectCollection(Categories)\r\n expect(schema.hasDrafts).toBe(false)\r\n })\r\n\r\n it('extracts select field options from Pages heroSize', () => {\r\n const schema = introspectCollection(Pages)\r\n const heroSize = schema.fields.find((f) => f.name === 'heroSize')\r\n expect(heroSize).toBeDefined()\r\n expect(heroSize!.type).toBe('select')\r\n expect(heroSize!.options).toBeDefined()\r\n expect(heroSize!.options!.length).toBe(3)\r\n })\r\n})\r\n\r\n// ─── introspectBlocks (flat catalog) ───────────────────────────────\r\n\r\ndescribe('introspectBlocks', () => {\r\n it('returns a flat catalog of every block with no section/leaf split', () => {\r\n const catalog = introspectBlocks(allBlocks)\r\n const slugs = catalog.blocks.map((b) => b.slug)\r\n expect(slugs).toEqual([\r\n 'heading',\r\n 'richText',\r\n 'image',\r\n 'fullWidth',\r\n 'headingOnly',\r\n 'ctaBanner',\r\n 'accordion',\r\n ])\r\n })\r\n\r\n it('extracts each block\\'s fields including select options', () => {\r\n const catalog = introspectBlocks(allBlocks)\r\n const heading = catalog.blocks.find((b) => b.slug === 'heading')\r\n expect(heading).toBeDefined()\r\n const headingFieldNames = heading!.fields.map((f) => f.name)\r\n expect(headingFieldNames).toEqual(['text', 'level', 'align'])\r\n const level = heading!.fields.find((f) => f.name === 'level')\r\n expect(level!.options).toBeDefined()\r\n })\r\n})\r\n\r\n// ─── buildBlockNestingMap ──────────────────────────────────────────\r\n\r\ndescribe('buildBlockNestingMap', () => {\r\n it('records the layout field on Pages with the slugs it accepts', () => {\r\n const map = buildBlockNestingMap([Pages, Posts], allBlocks)\r\n const pageLayout = map.find(\r\n (e) => e.ownerType === 'collection' && e.owner === 'pages' && e.fieldPath === 'layout',\r\n )\r\n expect(pageLayout).toBeDefined()\r\n expect(pageLayout!.acceptedBlockSlugs).toEqual(['fullWidth', 'headingOnly', 'ctaBanner', 'accordion'])\r\n })\r\n\r\n it('records nested blocks fields inside container blocks', () => {\r\n const map = buildBlockNestingMap([Pages], allBlocks)\r\n\r\n const fullWidthContent = map.find(\r\n (e) => e.ownerType === 'block' && e.owner === 'fullWidth' && e.fieldPath === 'content',\r\n )\r\n expect(fullWidthContent).toBeDefined()\r\n expect(fullWidthContent!.acceptedBlockSlugs).toEqual(['heading', 'richText', 'image'])\r\n\r\n const headingOnly = map.find(\r\n (e) => e.ownerType === 'block' && e.owner === 'headingOnly' && e.fieldPath === 'content',\r\n )\r\n expect(headingOnly!.acceptedBlockSlugs).toEqual(['heading'])\r\n expect(headingOnly!.maxRows).toBe(1)\r\n })\r\n\r\n it('handles arbitrarily-deep nesting via array fields inside blocks', () => {\r\n const map = buildBlockNestingMap([Pages], allBlocks)\r\n\r\n const accordionPanelBody = map.find(\r\n (e) =>\r\n e.ownerType === 'block' && e.owner === 'accordion' && e.fieldPath === 'panels[].body',\r\n )\r\n expect(accordionPanelBody).toBeDefined()\r\n expect(accordionPanelBody!.acceptedBlockSlugs).toEqual(['heading', 'richText', 'fullWidth'])\r\n })\r\n\r\n it('omits unknown slugs not present in the block list', () => {\r\n const Stray: CollectionConfig = {\r\n slug: 'stray',\r\n fields: [\r\n {\r\n name: 'layout',\r\n type: 'blocks',\r\n blocks: [Heading, { slug: 'mystery', fields: [] } as Block],\r\n },\r\n ],\r\n }\r\n const map = buildBlockNestingMap([Stray], [Heading]) // mystery not in catalog\r\n const stray = map.find((e) => e.owner === 'stray' && e.fieldPath === 'layout')\r\n expect(stray!.acceptedBlockSlugs).toEqual(['heading'])\r\n })\r\n\r\n it('omits fixed blocks (no nested blocks fields) from the map', () => {\r\n const map = buildBlockNestingMap([Pages], allBlocks)\r\n const ctaEntries = map.filter((e) => e.owner === 'ctaBanner')\r\n expect(ctaEntries).toHaveLength(0)\r\n })\r\n})\r\n\r\n// ─── buildRelationshipGraph ────────────────────────────────────────\r\n\r\n// ─── Global introspection ──────────────────────────────────────────\r\n\r\nconst SiteSettings: GlobalConfig = {\r\n slug: 'site-settings',\r\n fields: [\r\n { name: 'siteName', type: 'text', required: true },\r\n { name: 'tagline', type: 'text' },\r\n {\r\n name: 'social',\r\n type: 'group',\r\n fields: [\r\n { name: 'twitter', type: 'text' },\r\n { name: 'instagram', type: 'text' },\r\n ],\r\n },\r\n ],\r\n}\r\n\r\nconst FooterGlobal: GlobalConfig = {\r\n slug: 'footer',\r\n versions: { drafts: true },\r\n fields: [\r\n {\r\n name: 'layout',\r\n type: 'blocks',\r\n blocks: [Heading, CtaBanner],\r\n },\r\n ],\r\n}\r\n\r\nconst HeaderGlobal: GlobalConfig = {\r\n slug: 'header',\r\n fields: [\r\n {\r\n name: 'menu',\r\n type: 'group',\r\n fields: [\r\n { name: 'label', type: 'text' },\r\n {\r\n name: 'links',\r\n type: 'blocks',\r\n blocks: [Heading],\r\n },\r\n ],\r\n },\r\n ],\r\n}\r\n\r\ndescribe('hasGlobalDrafts', () => {\r\n it('returns true for { versions: { drafts: true } }', () => {\r\n expect(hasGlobalDrafts({ slug: 'g', versions: { drafts: true }, fields: [] })).toBe(true)\r\n })\r\n\r\n it('returns false for { versions: { drafts: false } }', () => {\r\n expect(hasGlobalDrafts({ slug: 'g', versions: { drafts: false }, fields: [] })).toBe(false)\r\n })\r\n\r\n it('returns false when versions is undefined', () => {\r\n expect(hasGlobalDrafts({ slug: 'g', fields: [] })).toBe(false)\r\n })\r\n\r\n it('returns false for versions without drafts key', () => {\r\n expect(\r\n hasGlobalDrafts({ slug: 'g', versions: { maxPerDoc: 10 }, fields: [] } as GlobalConfig),\r\n ).toBe(false)\r\n })\r\n})\r\n\r\ndescribe('introspectGlobal', () => {\r\n it('extracts SiteSettings fields and draft/live-preview flags', () => {\r\n const schema = introspectGlobal(SiteSettings)\r\n expect(schema.slug).toBe('site-settings')\r\n expect(schema.hasDrafts).toBe(false)\r\n expect(schema.hasLivePreview).toBe(false)\r\n const names = schema.fields.map((f) => f.name)\r\n expect(names).toContain('siteName')\r\n expect(names).toContain('tagline')\r\n const social = schema.fields.find((f) => f.name === 'social')\r\n expect(social?.type).toBe('group')\r\n expect(social?.fields?.map((f) => f.name)).toEqual(['twitter', 'instagram'])\r\n })\r\n\r\n it('reports hasDrafts: true when versions.drafts is set', () => {\r\n expect(introspectGlobal(FooterGlobal).hasDrafts).toBe(true)\r\n })\r\n})\r\n\r\ndescribe('introspectGlobals', () => {\r\n it('returns an empty Map for []', () => {\r\n expect(introspectGlobals([]).size).toBe(0)\r\n })\r\n\r\n it('keys the map by slug', () => {\r\n const map = introspectGlobals([SiteSettings, FooterGlobal])\r\n expect(map.has('site-settings')).toBe(true)\r\n expect(map.has('footer')).toBe(true)\r\n })\r\n})\r\n\r\ndescribe('buildBlockNestingMap with globals', () => {\r\n it('emits an edge with ownerType \"global\" for a top-level blocks field', () => {\r\n const map = buildBlockNestingMap([], [FooterGlobal], allBlocks)\r\n const edge = map.find(\r\n (e) => e.ownerType === 'global' && e.owner === 'footer' && e.fieldPath === 'layout',\r\n )\r\n expect(edge).toBeDefined()\r\n expect(edge!.acceptedBlockSlugs).toEqual(['heading', 'ctaBanner'])\r\n })\r\n\r\n it('walks group-nested blocks fields under a global with the dotted path', () => {\r\n const map = buildBlockNestingMap([], [HeaderGlobal], allBlocks)\r\n const edge = map.find(\r\n (e) => e.ownerType === 'global' && e.owner === 'header' && e.fieldPath === 'menu.links',\r\n )\r\n expect(edge).toBeDefined()\r\n expect(edge!.acceptedBlockSlugs).toEqual(['heading'])\r\n })\r\n\r\n it('two-arg call (no globals) produces the same edges as before — regression guard', () => {\r\n const before = buildBlockNestingMap([Pages], allBlocks)\r\n const after = buildBlockNestingMap([Pages], [], allBlocks)\r\n expect(after).toEqual(before)\r\n })\r\n\r\n it('invariant: throws when (owner, fieldPath) appears with different ownerTypes', () => {\r\n const ClashCollection: CollectionConfig = {\r\n slug: 'site-settings',\r\n fields: [{ name: 'layout', type: 'blocks', blocks: [Heading] }],\r\n }\r\n const ClashGlobal: GlobalConfig = {\r\n slug: 'site-settings',\r\n fields: [{ name: 'layout', type: 'blocks', blocks: [Heading] }],\r\n }\r\n expect(() => buildBlockNestingMap([ClashCollection], [ClashGlobal], [Heading])).toThrow(\r\n /invariant violated/i,\r\n )\r\n })\r\n})\r\n\r\ndescribe('buildRelationshipGraph', () => {\r\n it('builds correct graph from sample collections', () => {\r\n const schemas = introspectCollections([Posts, Pages, Categories, Authors, Media])\r\n const edges = buildRelationshipGraph(schemas)\r\n\r\n const postEdges = edges.filter((e) => e.fromCollection === 'posts')\r\n const postTargets = postEdges.map((e) => e.toCollection)\r\n expect(postTargets).toContain('categories')\r\n expect(postTargets).toContain('authors')\r\n expect(postTargets).toContain('media')\r\n\r\n const authorEdges = edges.filter((e) => e.fromCollection === 'authors')\r\n expect(authorEdges.map((e) => e.toCollection)).toContain('media')\r\n })\r\n})\r\n"],"names":["describe","it","expect","introspectCollection","introspectCollections","introspectBlocks","buildBlockNestingMap","buildRelationshipGraph","hasGlobalDrafts","introspectGlobal","introspectGlobals","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","SiteSettings","FooterGlobal","HeaderGlobal","maxPerDoc","hasLivePreview","names","social","size","has","edge","before","after","ClashCollection","ClashGlobal","toThrow","schemas","edges","postEdges","fromCollection","postTargets","toCollection","authorEdges"],"mappings":"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"}