payload-mcp-toolkit 0.7.0 → 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 (48) hide show
  1. package/README.md +29 -8
  2. package/dist/api-keys.js +57 -21
  3. package/dist/api-keys.js.map +1 -1
  4. package/dist/auth-strategy.d.ts +18 -7
  5. package/dist/auth-strategy.js +54 -12
  6. package/dist/auth-strategy.js.map +1 -1
  7. package/dist/tools/_helpers.d.ts +34 -0
  8. package/dist/tools/_helpers.js +98 -0
  9. package/dist/tools/_helpers.js.map +1 -1
  10. package/dist/tools/publish-draft.js +33 -1
  11. package/dist/tools/publish-draft.js.map +1 -1
  12. package/dist/tools/publish-global-draft.js +30 -1
  13. package/dist/tools/publish-global-draft.js.map +1 -1
  14. package/package.json +29 -15
  15. package/dist/__tests__/api-keys.test.js +0 -292
  16. package/dist/__tests__/api-keys.test.js.map +0 -1
  17. package/dist/__tests__/auth-strategy.test.js +0 -681
  18. package/dist/__tests__/auth-strategy.test.js.map +0 -1
  19. package/dist/__tests__/conflict-detection.test.js +0 -69
  20. package/dist/__tests__/conflict-detection.test.js.map +0 -1
  21. package/dist/__tests__/delete-document.test.js +0 -70
  22. package/dist/__tests__/delete-document.test.js.map +0 -1
  23. package/dist/__tests__/endpoint.test.js +0 -143
  24. package/dist/__tests__/endpoint.test.js.map +0 -1
  25. package/dist/__tests__/find-document.test.js +0 -178
  26. package/dist/__tests__/find-document.test.js.map +0 -1
  27. package/dist/__tests__/find-global.test.js +0 -173
  28. package/dist/__tests__/find-global.test.js.map +0 -1
  29. package/dist/__tests__/global-versions.test.js +0 -183
  30. package/dist/__tests__/global-versions.test.js.map +0 -1
  31. package/dist/__tests__/hash.test.js +0 -58
  32. package/dist/__tests__/hash.test.js.map +0 -1
  33. package/dist/__tests__/index-integration.test.js +0 -191
  34. package/dist/__tests__/index-integration.test.js.map +0 -1
  35. package/dist/__tests__/introspection.test.js +0 -659
  36. package/dist/__tests__/introspection.test.js.map +0 -1
  37. package/dist/__tests__/patch-global-layout.test.js +0 -474
  38. package/dist/__tests__/patch-global-layout.test.js.map +0 -1
  39. package/dist/__tests__/patch-layout.test.js +0 -171
  40. package/dist/__tests__/patch-layout.test.js.map +0 -1
  41. package/dist/__tests__/registry.test.js +0 -795
  42. package/dist/__tests__/registry.test.js.map +0 -1
  43. package/dist/__tests__/resources.test.js +0 -139
  44. package/dist/__tests__/resources.test.js.map +0 -1
  45. package/dist/__tests__/update-global.test.js +0 -157
  46. package/dist/__tests__/update-global.test.js.map +0 -1
  47. package/dist/__tests__/url-validator.test.js +0 -326
  48. package/dist/__tests__/url-validator.test.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/__tests__/registry.test.ts"],"sourcesContent":["import { describe, it, expect, vi, beforeEach } from 'vitest'\r\nimport { z } from 'zod'\r\nimport {\r\n buildScopeChecker,\r\n createInitializeServer,\r\n type ToolFactoryOutput,\r\n type ToolRouting,\r\n} from '../registry'\r\n\r\n// Test fixture: mirrors the production tool catalogue's routing so the\r\n// scope-checker tests stay stable across factory-internal changes. Each\r\n// entry is a minimal `ToolFactoryOutput` — only `name` and `routing` are\r\n// read by `buildScopeChecker`.\r\nfunction fixtureTool(name: string, routing: ToolRouting): ToolFactoryOutput {\r\n return {\r\n name,\r\n routing,\r\n description: '',\r\n parameters: {},\r\n handler: async () => ({ content: [{ type: 'text', text: '' }] }),\r\n }\r\n}\r\n\r\nconst PRODUCTION_TOOLS: ToolFactoryOutput[] = [\r\n // collection\r\n fixtureTool('findDocument', { kind: 'collection', action: 'read' }),\r\n fixtureTool('listVersions', { kind: 'collection', action: 'read' }),\r\n fixtureTool('createDocument', { kind: 'collection', action: 'create' }),\r\n fixtureTool('updateDocument', { kind: 'collection', action: 'update' }),\r\n fixtureTool('patchLayout', { kind: 'collection', action: 'update' }),\r\n fixtureTool('publishDraft', { kind: 'collection', action: 'update' }),\r\n fixtureTool('schedulePublish', { kind: 'collection', action: 'update' }),\r\n fixtureTool('restoreVersion', { kind: 'collection', action: 'update' }),\r\n fixtureTool('deleteDocument', { kind: 'collection', action: 'delete' }),\r\n fixtureTool('safeDelete', { kind: 'collection', action: 'delete' }),\r\n // global\r\n fixtureTool('findGlobal', { kind: 'global', action: 'read' }),\r\n fixtureTool('updateGlobal', { kind: 'global', action: 'update' }),\r\n fixtureTool('patchGlobalLayout', { kind: 'global', action: 'update' }),\r\n fixtureTool('publishGlobalDraft', { kind: 'global', action: 'update' }),\r\n fixtureTool('listGlobalVersions', { kind: 'global', action: 'read' }),\r\n fixtureTool('restoreGlobalVersion', { kind: 'global', action: 'update' }),\r\n // account\r\n fixtureTool('searchContent', { kind: 'account', action: 'read' }),\r\n fixtureTool('resolveReference', { kind: 'account', action: 'read' }),\r\n fixtureTool('uploadMedia', { kind: 'account', action: 'create' }),\r\n]\r\n\r\nconst assertScopeAllows = buildScopeChecker(PRODUCTION_TOOLS)\r\n\r\ndescribe('assertScopeAllows', () => {\r\n it('grants full access when scopes is null/undefined or empty', () => {\r\n expect(assertScopeAllows(null, 'createDocument', 'posts').allowed).toBe(true)\r\n expect(assertScopeAllows(undefined, 'deleteDocument', 'posts').allowed).toBe(true)\r\n expect(assertScopeAllows({}, 'createDocument', 'posts').allowed).toBe(true)\r\n })\r\n\r\n it('denies every dispatch path under the deny-all sentinel from composeScopes', () => {\r\n // composeScopes returns this shape for \"preset = custom, no overrides\".\r\n // The registry must reject all three dispatch shapes:\r\n // - collection-keyed tools (createDocument, etc.)\r\n // - account-wide tools (searchContent, uploadMedia)\r\n // - delete tools\r\n const denyAll = { collections: {}, tools: { allow: [] } }\r\n expect(assertScopeAllows(denyAll, 'createDocument', 'posts').allowed).toBe(false)\r\n expect(assertScopeAllows(denyAll, 'findDocument', 'posts').allowed).toBe(false)\r\n expect(assertScopeAllows(denyAll, 'deleteDocument', 'posts').allowed).toBe(false)\r\n expect(assertScopeAllows(denyAll, 'searchContent', undefined).allowed).toBe(false)\r\n expect(assertScopeAllows(denyAll, 'uploadMedia', undefined).allowed).toBe(false)\r\n })\r\n\r\n it('respects the read-only preset for write tools', () => {\r\n const decision = assertScopeAllows({ preset: 'read-only' }, 'createDocument', 'posts')\r\n expect(decision.allowed).toBe(false)\r\n expect(decision.reason).toMatch(/create/)\r\n })\r\n\r\n it('respects the editor preset (no deletes)', () => {\r\n expect(assertScopeAllows({ preset: 'editor' }, 'createDocument', 'posts').allowed).toBe(true)\r\n expect(assertScopeAllows({ preset: 'editor' }, 'deleteDocument', 'posts').allowed).toBe(false)\r\n expect(assertScopeAllows({ preset: 'editor' }, 'safeDelete', 'posts').allowed).toBe(false)\r\n })\r\n\r\n it('admin preset allows everything', () => {\r\n expect(assertScopeAllows({ preset: 'admin' }, 'deleteDocument', 'posts').allowed).toBe(true)\r\n })\r\n\r\n it('per-collection override replaces preset for that slug', () => {\r\n const decision = assertScopeAllows(\r\n {\r\n preset: 'admin',\r\n collections: { posts: ['read'] },\r\n },\r\n 'updateDocument',\r\n 'posts',\r\n )\r\n expect(decision.allowed).toBe(false)\r\n })\r\n\r\n it('treats scopes.collections as a whitelist — unlisted collections are denied', () => {\r\n const scopes = { collections: { posts: ['read', 'update'] as never } }\r\n expect(assertScopeAllows(scopes, 'updateDocument', 'pages').allowed).toBe(false)\r\n expect(assertScopeAllows(scopes, 'deleteDocument', 'categories').allowed).toBe(false)\r\n // Listed collection still works for allowed actions\r\n expect(assertScopeAllows(scopes, 'updateDocument', 'posts').allowed).toBe(true)\r\n })\r\n\r\n it('blocks no-collection tools at the preset action level (read-only cannot uploadMedia)', () => {\r\n const decision = assertScopeAllows({ preset: 'read-only' }, 'uploadMedia', undefined)\r\n expect(decision.allowed).toBe(false)\r\n expect(decision.reason).toMatch(/create/)\r\n })\r\n\r\n it('allows no-collection read tools under read-only preset', () => {\r\n expect(assertScopeAllows({ preset: 'read-only' }, 'searchContent', undefined).allowed).toBe(\r\n true,\r\n )\r\n expect(assertScopeAllows({ preset: 'read-only' }, 'resolveReference', undefined).allowed).toBe(\r\n true,\r\n )\r\n })\r\n\r\n it('denies no-collection tools when key is collection-scoped only (no preset)', () => {\r\n const scopes = { collections: { posts: ['read'] as never } }\r\n expect(assertScopeAllows(scopes, 'searchContent', undefined).allowed).toBe(false)\r\n expect(assertScopeAllows(scopes, 'uploadMedia', undefined).allowed).toBe(false)\r\n })\r\n\r\n it('tools.deny blocks an explicitly listed tool', () => {\r\n const decision = assertScopeAllows(\r\n { preset: 'admin', tools: { deny: ['safeDelete'] } },\r\n 'safeDelete',\r\n 'posts',\r\n )\r\n expect(decision.allowed).toBe(false)\r\n expect(decision.reason).toMatch(/denied/)\r\n })\r\n\r\n it('tools.allow restricts to listed tools only', () => {\r\n const decision = assertScopeAllows(\r\n { tools: { allow: ['findDocument'] } },\r\n 'searchContent',\r\n undefined,\r\n )\r\n expect(decision.allowed).toBe(false)\r\n })\r\n\r\n it('skips collection check when the tool has no collection arg', () => {\r\n expect(\r\n assertScopeAllows({ preset: 'read-only' }, 'searchContent', undefined).allowed,\r\n ).toBe(true)\r\n })\r\n})\r\n\r\n// ─── Globals scope routing (U6) ──────────────────────────────────────\r\n\r\ndescribe('assertScopeAllows — globals', () => {\r\n it('read-only preset allows findGlobal', () => {\r\n expect(\r\n assertScopeAllows({ preset: 'read-only' }, 'findGlobal', 'siteSettings').allowed,\r\n ).toBe(true)\r\n })\r\n\r\n it('editor preset allows findGlobal (read still permitted on globals)', () => {\r\n expect(\r\n assertScopeAllows({ preset: 'editor' }, 'findGlobal', 'siteSettings').allowed,\r\n ).toBe(true)\r\n })\r\n\r\n it('editor preset DENIES updateGlobal (asymmetric — global writes need admin)', () => {\r\n const decision = assertScopeAllows({ preset: 'editor' }, 'updateGlobal', 'siteSettings')\r\n expect(decision.allowed).toBe(false)\r\n expect(decision.reason).toMatch(/global \"siteSettings\"/)\r\n expect(decision.reason).toMatch(/preset/)\r\n })\r\n\r\n it('admin preset allows updateGlobal', () => {\r\n expect(\r\n assertScopeAllows({ preset: 'admin' }, 'updateGlobal', 'siteSettings').allowed,\r\n ).toBe(true)\r\n })\r\n\r\n it('Custom override grants update via globals scope', () => {\r\n expect(\r\n assertScopeAllows(\r\n { globals: { siteSettings: ['read', 'update'] } },\r\n 'updateGlobal',\r\n 'siteSettings',\r\n ).allowed,\r\n ).toBe(true)\r\n })\r\n\r\n it('Custom narrow denies update when only read is granted', () => {\r\n const decision = assertScopeAllows(\r\n { globals: { siteSettings: ['read'] } },\r\n 'updateGlobal',\r\n 'siteSettings',\r\n )\r\n expect(decision.allowed).toBe(false)\r\n expect(decision.reason).toMatch(/Action \"update\" on global \"siteSettings\"/)\r\n })\r\n\r\n it('globals-only key denies a collection tool', () => {\r\n const decision = assertScopeAllows(\r\n { globals: { siteSettings: ['read', 'update'] } },\r\n 'findDocument',\r\n 'pages',\r\n )\r\n expect(decision.allowed).toBe(false)\r\n })\r\n\r\n it('read-only preset denies updateGlobal', () => {\r\n const decision = assertScopeAllows({ preset: 'read-only' }, 'updateGlobal', 'siteSettings')\r\n expect(decision.allowed).toBe(false)\r\n })\r\n\r\n it('globals whitelist: unlisted slug is denied', () => {\r\n const decision = assertScopeAllows(\r\n { globals: { siteSettings: ['read', 'update'] } },\r\n 'updateGlobal',\r\n 'footer',\r\n )\r\n expect(decision.allowed).toBe(false)\r\n expect(decision.reason).toMatch(/Global \"footer\" is not in this API key's allowed globals/)\r\n })\r\n})\r\n\r\n// ─── Fail-closed extensions for tools.allow without resource scope ───\r\n\r\ndescribe('assertScopeAllows — tools.allow-only fail-closed', () => {\r\n it('denies updateGlobal when only tools.allow is set (no globals map, no preset)', () => {\r\n const decision = assertScopeAllows(\r\n { tools: { allow: ['updateGlobal'] } },\r\n 'updateGlobal',\r\n 'siteSettings',\r\n )\r\n expect(decision.allowed).toBe(false)\r\n expect(decision.reason).toMatch(/explicit global scope or preset/)\r\n })\r\n\r\n it('denies updateDocument when only tools.allow is set (no collections map, no preset)', () => {\r\n const decision = assertScopeAllows(\r\n { tools: { allow: ['updateDocument'] } },\r\n 'updateDocument',\r\n 'pages',\r\n )\r\n expect(decision.allowed).toBe(false)\r\n expect(decision.reason).toMatch(/explicit collection scope or preset/)\r\n })\r\n\r\n it('post-empty-Custom sentinel denies every dispatch path including globals', () => {\r\n const denyAll = { collections: {}, globals: {}, tools: { allow: [] } }\r\n expect(assertScopeAllows(denyAll, 'findGlobal', 'siteSettings').allowed).toBe(false)\r\n expect(assertScopeAllows(denyAll, 'updateGlobal', 'siteSettings').allowed).toBe(false)\r\n expect(assertScopeAllows(denyAll, 'findDocument', 'pages').allowed).toBe(false)\r\n expect(assertScopeAllows(denyAll, 'searchContent', undefined).allowed).toBe(false)\r\n })\r\n})\r\n\r\n// ─── Account-level routing ──────────────────────────────────────────\r\n\r\ndescribe('assertScopeAllows — account-level tools', () => {\r\n it('searchContent allowed under read-only preset', () => {\r\n expect(\r\n assertScopeAllows({ preset: 'read-only' }, 'searchContent', undefined).allowed,\r\n ).toBe(true)\r\n })\r\n\r\n it('uploadMedia denied under read-only preset (requires create)', () => {\r\n expect(\r\n assertScopeAllows({ preset: 'read-only' }, 'uploadMedia', undefined).allowed,\r\n ).toBe(false)\r\n })\r\n\r\n it('explicit collections override denies account tools even under admin preset', () => {\r\n // Regression: account-routed tools (searchContent, resolveReference,\r\n // uploadMedia) cross every collection, so an explicit resource\r\n // whitelist must trump the preset's wider grant.\r\n const scopes = { preset: 'admin' as const, collections: { posts: ['read' as const] } }\r\n expect(assertScopeAllows(scopes, 'searchContent', undefined).allowed).toBe(false)\r\n expect(assertScopeAllows(scopes, 'resolveReference', undefined).allowed).toBe(false)\r\n expect(assertScopeAllows(scopes, 'uploadMedia', undefined).allowed).toBe(false)\r\n })\r\n\r\n it('explicit globals override denies account tools even under admin preset', () => {\r\n const scopes = { preset: 'admin' as const, globals: { siteSettings: ['read' as const] } }\r\n expect(assertScopeAllows(scopes, 'searchContent', undefined).allowed).toBe(false)\r\n })\r\n\r\n it('unregistered tool name is denied with the registry-mapping reason', () => {\r\n const decision = assertScopeAllows({ preset: 'admin' }, 'whatIsThisTool', undefined)\r\n expect(decision.allowed).toBe(false)\r\n expect(decision.reason).toMatch(/no registered scope mapping/)\r\n })\r\n})\r\n\r\n// ─── Routing collocation (TS-enforced; spot-check via the checker) ───\r\n//\r\n// The boot-time `assertScopeRegistryInvariant` is gone: every factory\r\n// must declare its `routing` field, so \"missing routing\" is a TS error\r\n// at the factory return site. Duplicate routing is impossible because\r\n// each tool name appears exactly once in the production tool list (the\r\n// plugin entry assembles the array). What remains is a behavioural\r\n// spot-check that the production fixture routes each canonical tool to\r\n// the correct policy branch.\r\n\r\ndescribe('routing is collocated on every tool factory', () => {\r\n it('collection-keyed tool dispatches through the collection policy', () => {\r\n expect(assertScopeAllows({ preset: 'editor' }, 'createDocument', 'posts').allowed).toBe(true)\r\n })\r\n it('global-keyed tool dispatches through the global policy', () => {\r\n expect(\r\n assertScopeAllows({ preset: 'admin' }, 'updateGlobal', 'siteSettings').allowed,\r\n ).toBe(true)\r\n })\r\n it('account-keyed tool dispatches through the account policy', () => {\r\n expect(\r\n assertScopeAllows({ preset: 'read-only' }, 'searchContent', undefined).allowed,\r\n ).toBe(true)\r\n })\r\n})\r\n\r\n// ─── Initializer integration ─────────────────────────────────────────\r\n\r\ninterface MockServer {\r\n registerTool: ReturnType<typeof vi.fn>\r\n registerPrompt: ReturnType<typeof vi.fn>\r\n registerResource: ReturnType<typeof vi.fn>\r\n}\r\n\r\nfunction buildMockServer(): MockServer {\r\n return {\r\n registerTool: vi.fn(),\r\n registerPrompt: vi.fn(),\r\n registerResource: vi.fn(),\r\n }\r\n}\r\n\r\nfunction buildReq(opts: {\r\n scopes?: Parameters<typeof assertScopeAllows>[0]\r\n keyId?: string\r\n keyPrefix?: string\r\n requestId?: string\r\n} = {}) {\r\n const headers = new Headers()\r\n if (opts.requestId) headers.set('x-request-id', opts.requestId)\r\n return {\r\n headers,\r\n context: {},\r\n payload: {\r\n logger: {\r\n info: vi.fn(),\r\n warn: vi.fn(),\r\n error: vi.fn(),\r\n },\r\n },\r\n user: {\r\n _mcpKey: {\r\n keyId: opts.keyId ?? 'k1',\r\n keyPrefix: opts.keyPrefix ?? 'abc12345',\r\n scopes: opts.scopes ?? null,\r\n },\r\n },\r\n }\r\n}\r\n\r\nfunction makeTool(handler: ToolFactoryOutput['handler']): ToolFactoryOutput {\r\n return {\r\n name: 'createDocument',\r\n routing: { kind: 'collection', action: 'create' },\r\n description: 'create a document',\r\n parameters: { collection: z.string(), data: z.string() },\r\n handler,\r\n }\r\n}\r\n\r\ndescribe('createInitializeServer', () => {\r\n let server: MockServer\r\n\r\n beforeEach(() => {\r\n server = buildMockServer()\r\n })\r\n\r\n it('registers each tool exactly once on the McpServer', () => {\r\n const toolHandler = vi.fn(async () => ({ content: [{ type: 'text', text: 'ok' }] }))\r\n const init = createInitializeServer({ tools: [makeTool(toolHandler)] })\r\n init(buildReq() as never)(server as never)\r\n expect(server.registerTool).toHaveBeenCalledTimes(1)\r\n expect(server.registerTool.mock.calls[0]![0]).toBe('createDocument')\r\n })\r\n\r\n it('wraps the handler so a happy call invokes the tool and logs success', async () => {\r\n const toolHandler = vi.fn(async () => ({ content: [{ type: 'text', text: 'ok' }] }))\r\n const req = buildReq()\r\n const init = createInitializeServer({ tools: [makeTool(toolHandler)] })\r\n init(req as never)(server as never)\r\n const wrapped = server.registerTool.mock.calls[0]![2] as (\r\n args: Record<string, unknown>,\r\n extra: unknown,\r\n ) => Promise<unknown>\r\n\r\n const result = await wrapped({ collection: 'posts', data: '{\"title\":\"hi\"}' }, { ok: 1 })\r\n expect(toolHandler).toHaveBeenCalledTimes(1)\r\n expect(result).toEqual({ content: [{ type: 'text', text: 'ok' }] })\r\n expect(req.payload.logger.info).toHaveBeenCalled()\r\n })\r\n\r\n it('rejects with isError result when scopes deny the call (no JSON-RPC error)', async () => {\r\n const toolHandler = vi.fn(async () => ({ content: [{ type: 'text', text: 'ok' }] }))\r\n const req = buildReq({ scopes: { preset: 'read-only' } })\r\n const init = createInitializeServer({ tools: [makeTool(toolHandler)] })\r\n init(req as never)(server as never)\r\n const wrapped = server.registerTool.mock.calls[0]![2] as (\r\n args: Record<string, unknown>,\r\n extra: unknown,\r\n ) => Promise<unknown>\r\n\r\n const result = (await wrapped({ collection: 'posts', data: '{}' }, {})) as {\r\n isError: boolean\r\n content: Array<{ text: string }>\r\n }\r\n expect(result.isError).toBe(true)\r\n expect(result.content[0]!.text).toMatch(/scope/i)\r\n expect(toolHandler).not.toHaveBeenCalled()\r\n expect(req.payload.logger.warn).toHaveBeenCalledWith(\r\n expect.objectContaining({ errorClass: 'ScopeRejection', success: false }),\r\n expect.any(String),\r\n )\r\n })\r\n\r\n it('catches handler errors and returns isError result without throwing', async () => {\r\n const toolHandler = vi.fn(async () => {\r\n throw new Error('payload exploded')\r\n })\r\n const req = buildReq()\r\n const init = createInitializeServer({ tools: [makeTool(toolHandler)] })\r\n init(req as never)(server as never)\r\n const wrapped = server.registerTool.mock.calls[0]![2] as (\r\n args: Record<string, unknown>,\r\n extra: unknown,\r\n ) => Promise<unknown>\r\n\r\n const result = (await wrapped({ collection: 'posts', data: '{}' }, {})) as {\r\n isError: boolean\r\n content: Array<{ text: string }>\r\n }\r\n expect(result.isError).toBe(true)\r\n expect(result.content[0]!.text).toMatch(/payload exploded/)\r\n expect(req.payload.logger.error).toHaveBeenCalledWith(\r\n expect.objectContaining({ errorClass: 'Error' }),\r\n expect.any(String),\r\n )\r\n })\r\n\r\n it('logs the parsed top-level data keys (not values)', async () => {\r\n const toolHandler = vi.fn(async () => ({ content: [{ type: 'text', text: 'ok' }] }))\r\n const req = buildReq()\r\n const init = createInitializeServer({ tools: [makeTool(toolHandler)] })\r\n init(req as never)(server as never)\r\n const wrapped = server.registerTool.mock.calls[0]![2] as (\r\n args: Record<string, unknown>,\r\n extra: unknown,\r\n ) => Promise<unknown>\r\n\r\n await wrapped(\r\n { collection: 'posts', data: '{\"title\":\"hi\",\"slug\":\"hi\",\"secretField\":\"sensitive\"}' },\r\n {},\r\n )\r\n\r\n const [logFields] = req.payload.logger.info.mock.calls[0]!\r\n expect(logFields.dataKeys).toEqual(['title', 'slug', 'secretField'])\r\n // Values must NOT be in the log, only key names\r\n expect(JSON.stringify(logFields)).not.toContain('sensitive')\r\n })\r\n\r\n it('truncates long string args in the error log summary', async () => {\r\n const toolHandler = vi.fn(async () => {\r\n throw new Error('boom')\r\n })\r\n const req = buildReq()\r\n const init = createInitializeServer({ tools: [makeTool(toolHandler)] })\r\n init(req as never)(server as never)\r\n const wrapped = server.registerTool.mock.calls[0]![2] as (\r\n args: Record<string, unknown>,\r\n extra: unknown,\r\n ) => Promise<unknown>\r\n\r\n const big = 'x'.repeat(5000)\r\n await wrapped({ collection: 'posts', data: big }, {})\r\n const [logFields] = req.payload.logger.error.mock.calls[0]!\r\n expect(logFields.argsSummary.data).toBe('<truncated:5000>')\r\n })\r\n\r\n it('normalizes z.object() params to a raw shape before registering with the SDK', () => {\r\n const tool: ToolFactoryOutput = {\r\n name: 'resolveReference',\r\n routing: { kind: 'account', action: 'read' },\r\n description: 'x',\r\n parameters: z.object({ query: z.string(), collection: z.string().optional() }),\r\n handler: async () => ({ content: [{ type: 'text' as const, text: 'ok' }] }),\r\n }\r\n const init = createInitializeServer({ tools: [tool] })\r\n init(buildReq() as never)(server as never)\r\n const inputSchema = server.registerTool.mock.calls[0]![1]!.inputSchema as Record<\r\n string,\r\n unknown\r\n >\r\n expect(Object.keys(inputSchema).sort()).toEqual(['collection', 'query'])\r\n })\r\n\r\n it('audit log carries targetSlug + targetKind for a collection tool', async () => {\r\n const toolHandler = vi.fn(async () => ({ content: [{ type: 'text', text: 'ok' }] }))\r\n const req = buildReq()\r\n const tool: ToolFactoryOutput = {\r\n name: 'findDocument',\r\n routing: { kind: 'collection', action: 'read' },\r\n description: 'x',\r\n parameters: { collection: z.string() },\r\n handler: toolHandler,\r\n }\r\n const init = createInitializeServer({ tools: [tool] })\r\n init(req as never)(server as never)\r\n const wrapped = server.registerTool.mock.calls[0]![2] as (\r\n args: Record<string, unknown>,\r\n extra: unknown,\r\n ) => Promise<unknown>\r\n\r\n await wrapped({ collection: 'pages' }, {})\r\n const [logFields] = req.payload.logger.info.mock.calls[0]!\r\n expect(logFields.targetSlug).toBe('pages')\r\n expect(logFields.targetKind).toBe('collection')\r\n expect((logFields as Record<string, unknown>).collectionArg).toBeUndefined()\r\n })\r\n\r\n it('audit log carries targetSlug + targetKind for a global tool', async () => {\r\n const toolHandler = vi.fn(async () => ({ content: [{ type: 'text', text: 'ok' }] }))\r\n const req = buildReq()\r\n const tool: ToolFactoryOutput = {\r\n name: 'findGlobal',\r\n routing: { kind: 'global', action: 'read' },\r\n description: 'x',\r\n parameters: { slug: z.string() },\r\n handler: toolHandler,\r\n }\r\n const init = createInitializeServer({ tools: [tool] })\r\n init(req as never)(server as never)\r\n const wrapped = server.registerTool.mock.calls[0]![2] as (\r\n args: Record<string, unknown>,\r\n extra: unknown,\r\n ) => Promise<unknown>\r\n\r\n await wrapped({ slug: 'siteSettings' }, {})\r\n const [logFields] = req.payload.logger.info.mock.calls[0]!\r\n expect(logFields.targetSlug).toBe('siteSettings')\r\n expect(logFields.targetKind).toBe('global')\r\n })\r\n\r\n it('audit log marks account-level tools with targetKind=\"account\" and no targetSlug', async () => {\r\n const toolHandler = vi.fn(async () => ({ content: [{ type: 'text', text: 'ok' }] }))\r\n const req = buildReq()\r\n const tool: ToolFactoryOutput = {\r\n name: 'searchContent',\r\n routing: { kind: 'account', action: 'read' },\r\n description: 'x',\r\n parameters: { q: z.string() },\r\n handler: toolHandler,\r\n }\r\n const init = createInitializeServer({ tools: [tool] })\r\n init(req as never)(server as never)\r\n const wrapped = server.registerTool.mock.calls[0]![2] as (\r\n args: Record<string, unknown>,\r\n extra: unknown,\r\n ) => Promise<unknown>\r\n\r\n await wrapped({ q: 'hello' }, {})\r\n const [logFields] = req.payload.logger.info.mock.calls[0]!\r\n expect(logFields.targetSlug).toBeUndefined()\r\n expect(logFields.targetKind).toBe('account')\r\n })\r\n\r\n it('stamps mcp context on req before invoking the handler', async () => {\r\n const toolHandler = vi.fn(async (_args, req: Record<string, { source?: string }>) => {\r\n expect(req.context!.source).toBe('mcp')\r\n return { content: [{ type: 'text', text: 'ok' }] }\r\n })\r\n const req = buildReq()\r\n const init = createInitializeServer({ tools: [makeTool(toolHandler)] })\r\n init(req as never)(server as never)\r\n const wrapped = server.registerTool.mock.calls[0]![2] as (\r\n args: Record<string, unknown>,\r\n extra: unknown,\r\n ) => Promise<unknown>\r\n await wrapped({ collection: 'posts', data: '{}' }, {})\r\n expect(toolHandler).toHaveBeenCalled()\r\n })\r\n})\r\n"],"names":["describe","it","expect","vi","beforeEach","z","buildScopeChecker","createInitializeServer","fixtureTool","name","routing","description","parameters","handler","content","type","text","PRODUCTION_TOOLS","kind","action","assertScopeAllows","allowed","toBe","undefined","denyAll","collections","tools","allow","decision","preset","reason","toMatch","posts","scopes","deny","globals","siteSettings","buildMockServer","registerTool","fn","registerPrompt","registerResource","buildReq","opts","headers","Headers","requestId","set","context","payload","logger","info","warn","error","user","_mcpKey","keyId","keyPrefix","makeTool","collection","string","data","server","toolHandler","init","toHaveBeenCalledTimes","mock","calls","req","wrapped","result","ok","toEqual","toHaveBeenCalled","isError","not","toHaveBeenCalledWith","objectContaining","errorClass","success","any","String","Error","logFields","dataKeys","JSON","stringify","toContain","big","repeat","argsSummary","tool","object","query","optional","inputSchema","Object","keys","sort","targetSlug","targetKind","collectionArg","toBeUndefined","slug","q","_args","source"],"mappings":"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"}
@@ -1,139 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { introspectCollections, introspectGlobals, introspectBlocks, buildBlockNestingMap, buildRelationshipGraph } from '../introspection';
3
- import { generateResources } from '../resources';
4
- const Heading = {
5
- slug: 'heading',
6
- fields: [
7
- {
8
- name: 'text',
9
- type: 'text'
10
- }
11
- ]
12
- };
13
- const Pages = {
14
- slug: 'pages',
15
- fields: [
16
- {
17
- name: 'slug',
18
- type: 'text'
19
- },
20
- {
21
- name: 'layout',
22
- type: 'blocks',
23
- blocks: [
24
- Heading
25
- ]
26
- }
27
- ]
28
- };
29
- const SiteSettings = {
30
- slug: 'site-settings',
31
- fields: [
32
- {
33
- name: 'siteName',
34
- type: 'text',
35
- required: true
36
- },
37
- {
38
- name: 'tagline',
39
- type: 'text'
40
- }
41
- ]
42
- };
43
- const FooterGlobal = {
44
- slug: 'footer',
45
- fields: [
46
- {
47
- name: 'links',
48
- type: 'blocks',
49
- blocks: [
50
- Heading
51
- ]
52
- }
53
- ]
54
- };
55
- function build(globals = []) {
56
- const collectionSchemas = introspectCollections([
57
- Pages
58
- ]);
59
- const globalSchemas = introspectGlobals(globals);
60
- const catalog = introspectBlocks([
61
- Heading
62
- ]);
63
- const nesting = buildBlockNestingMap([
64
- Pages
65
- ], globals, [
66
- Heading
67
- ]);
68
- const relationships = buildRelationshipGraph(collectionSchemas);
69
- return generateResources(collectionSchemas, catalog, nesting, relationships, globalSchemas);
70
- }
71
- function bodyOf(resource) {
72
- const result = resource.handler(new URL(resource.uri));
73
- const text = result.contents[0].text;
74
- return JSON.parse(text);
75
- }
76
- describe('generateResources — globals', ()=>{
77
- it('omits globals://schema when no globals are registered', ()=>{
78
- const resources = build([]);
79
- expect(resources.find((r)=>r.uri === 'globals://schema')).toBeUndefined();
80
- });
81
- it('emits globals://schema when at least one global is registered', ()=>{
82
- const resources = build([
83
- SiteSettings
84
- ]);
85
- const res = resources.find((r)=>r.uri === 'globals://schema');
86
- expect(res).toBeDefined();
87
- const body = bodyOf(res);
88
- expect(Object.keys(body)).toEqual([
89
- 'site-settings'
90
- ]);
91
- expect(body['site-settings'].hasDrafts).toBe(false);
92
- });
93
- it('globals://schema body lists every supplied global', ()=>{
94
- const resources = build([
95
- SiteSettings,
96
- FooterGlobal
97
- ]);
98
- const body = bodyOf(resources.find((r)=>r.uri === 'globals://schema'));
99
- expect(new Set(Object.keys(body))).toEqual(new Set([
100
- 'site-settings',
101
- 'footer'
102
- ]));
103
- });
104
- it('blocks://nesting includes global-owned edges when a global has a blocks field', ()=>{
105
- const resources = build([
106
- FooterGlobal
107
- ]);
108
- const nesting = bodyOf(resources.find((r)=>r.uri === 'blocks://nesting'));
109
- const edge = nesting.find((e)=>e.ownerType === 'global' && e.owner === 'footer' && e.fieldPath === 'links');
110
- expect(edge).toBeDefined();
111
- // Existing collection-owned edges still present
112
- const pageLayout = nesting.find((e)=>e.ownerType === 'collection' && e.owner === 'pages' && e.fieldPath === 'layout');
113
- expect(pageLayout).toBeDefined();
114
- });
115
- it('back-compat: omitting the globalSchemas arg still produces the four legacy resources', ()=>{
116
- const collectionSchemas = introspectCollections([
117
- Pages
118
- ]);
119
- const catalog = introspectBlocks([
120
- Heading
121
- ]);
122
- const nesting = buildBlockNestingMap([
123
- Pages
124
- ], [], [
125
- Heading
126
- ]);
127
- const relationships = buildRelationshipGraph(collectionSchemas);
128
- const resources = generateResources(collectionSchemas, catalog, nesting, relationships);
129
- const uris = resources.map((r)=>r.uri);
130
- expect(uris).toEqual([
131
- 'blocks://catalog',
132
- 'blocks://nesting',
133
- 'collections://schema',
134
- 'collections://relationships'
135
- ]);
136
- });
137
- });
138
-
139
- //# sourceMappingURL=resources.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/__tests__/resources.test.ts"],"sourcesContent":["import { describe, it, expect } from 'vitest'\r\nimport type { Block, CollectionConfig, GlobalConfig } from 'payload'\r\nimport {\r\n introspectCollections,\r\n introspectGlobals,\r\n introspectBlocks,\r\n buildBlockNestingMap,\r\n buildRelationshipGraph,\r\n} from '../introspection'\r\nimport { generateResources } from '../resources'\r\n\r\nconst Heading: Block = {\r\n slug: 'heading',\r\n fields: [{ name: 'text', type: 'text' }],\r\n}\r\n\r\nconst Pages: CollectionConfig = {\r\n slug: 'pages',\r\n fields: [\r\n { name: 'slug', type: 'text' },\r\n { name: 'layout', type: 'blocks', blocks: [Heading] },\r\n ],\r\n}\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}\r\n\r\nconst FooterGlobal: GlobalConfig = {\r\n slug: 'footer',\r\n fields: [{ name: 'links', type: 'blocks', blocks: [Heading] }],\r\n}\r\n\r\nfunction build(globals: GlobalConfig[] = []) {\r\n const collectionSchemas = introspectCollections([Pages])\r\n const globalSchemas = introspectGlobals(globals)\r\n const catalog = introspectBlocks([Heading])\r\n const nesting = buildBlockNestingMap([Pages], globals, [Heading])\r\n const relationships = buildRelationshipGraph(collectionSchemas)\r\n return generateResources(collectionSchemas, catalog, nesting, relationships, globalSchemas)\r\n}\r\n\r\nfunction bodyOf(resource: ReturnType<typeof generateResources>[number]): unknown {\r\n const result = resource.handler(new URL(resource.uri))\r\n const text = (result as { contents: Array<{ text: string }> }).contents[0].text\r\n return JSON.parse(text)\r\n}\r\n\r\ndescribe('generateResources — globals', () => {\r\n it('omits globals://schema when no globals are registered', () => {\r\n const resources = build([])\r\n expect(resources.find((r) => r.uri === 'globals://schema')).toBeUndefined()\r\n })\r\n\r\n it('emits globals://schema when at least one global is registered', () => {\r\n const resources = build([SiteSettings])\r\n const res = resources.find((r) => r.uri === 'globals://schema')\r\n expect(res).toBeDefined()\r\n const body = bodyOf(res!) as Record<string, { slug: string; hasDrafts: boolean }>\r\n expect(Object.keys(body)).toEqual(['site-settings'])\r\n expect(body['site-settings'].hasDrafts).toBe(false)\r\n })\r\n\r\n it('globals://schema body lists every supplied global', () => {\r\n const resources = build([SiteSettings, FooterGlobal])\r\n const body = bodyOf(resources.find((r) => r.uri === 'globals://schema')!) as Record<\r\n string,\r\n unknown\r\n >\r\n expect(new Set(Object.keys(body))).toEqual(new Set(['site-settings', 'footer']))\r\n })\r\n\r\n it('blocks://nesting includes global-owned edges when a global has a blocks field', () => {\r\n const resources = build([FooterGlobal])\r\n const nesting = bodyOf(resources.find((r) => r.uri === 'blocks://nesting')!) as Array<{\r\n owner: string\r\n ownerType: string\r\n fieldPath: string\r\n }>\r\n const edge = nesting.find(\r\n (e) => e.ownerType === 'global' && e.owner === 'footer' && e.fieldPath === 'links',\r\n )\r\n expect(edge).toBeDefined()\r\n\r\n // Existing collection-owned edges still present\r\n const pageLayout = nesting.find(\r\n (e) => e.ownerType === 'collection' && e.owner === 'pages' && e.fieldPath === 'layout',\r\n )\r\n expect(pageLayout).toBeDefined()\r\n })\r\n\r\n it('back-compat: omitting the globalSchemas arg still produces the four legacy resources', () => {\r\n const collectionSchemas = introspectCollections([Pages])\r\n const catalog = introspectBlocks([Heading])\r\n const nesting = buildBlockNestingMap([Pages], [], [Heading])\r\n const relationships = buildRelationshipGraph(collectionSchemas)\r\n const resources = generateResources(collectionSchemas, catalog, nesting, relationships)\r\n const uris = resources.map((r) => r.uri)\r\n expect(uris).toEqual([\r\n 'blocks://catalog',\r\n 'blocks://nesting',\r\n 'collections://schema',\r\n 'collections://relationships',\r\n ])\r\n })\r\n})\r\n"],"names":["describe","it","expect","introspectCollections","introspectGlobals","introspectBlocks","buildBlockNestingMap","buildRelationshipGraph","generateResources","Heading","slug","fields","name","type","Pages","blocks","SiteSettings","required","FooterGlobal","build","globals","collectionSchemas","globalSchemas","catalog","nesting","relationships","bodyOf","resource","result","handler","URL","uri","text","contents","JSON","parse","resources","find","r","toBeUndefined","res","toBeDefined","body","Object","keys","toEqual","hasDrafts","toBe","Set","edge","e","ownerType","owner","fieldPath","pageLayout","uris","map"],"mappings":"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"}
@@ -1,157 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { createUpdateGlobalTool } from '../tools/update-global';
3
- const siteSettings = {
4
- slug: 'site-settings',
5
- fields: [
6
- {
7
- name: 'siteName',
8
- type: 'text'
9
- },
10
- {
11
- name: 'tagline',
12
- type: 'text'
13
- }
14
- ],
15
- hasDrafts: false,
16
- hasLivePreview: false
17
- };
18
- const footer = {
19
- slug: 'footer',
20
- fields: [
21
- {
22
- name: 'copyright',
23
- type: 'text'
24
- }
25
- ],
26
- hasDrafts: true,
27
- hasLivePreview: false
28
- };
29
- function buildReq() {
30
- return {
31
- payload: {
32
- updateGlobal: vi.fn(),
33
- logger: {
34
- info: vi.fn(),
35
- warn: vi.fn(),
36
- error: vi.fn()
37
- }
38
- },
39
- context: {},
40
- user: null
41
- };
42
- }
43
- describe('updateGlobal', ()=>{
44
- const schemas = new Map([
45
- [
46
- 'site-settings',
47
- siteSettings
48
- ],
49
- [
50
- 'footer',
51
- footer
52
- ]
53
- ]);
54
- const drafts = new Set([
55
- 'footer'
56
- ]);
57
- it('partial-merges only the supplied fields', async ()=>{
58
- const tool = createUpdateGlobalTool(schemas, drafts);
59
- const req = buildReq();
60
- req.payload.updateGlobal.mockResolvedValue({
61
- siteName: 'Acme'
62
- });
63
- const result = await tool.handler({
64
- slug: 'site-settings',
65
- data: '{"siteName":"Acme"}'
66
- }, req, {});
67
- expect(req.payload.updateGlobal).toHaveBeenCalledWith(expect.objectContaining({
68
- slug: 'site-settings',
69
- data: {
70
- siteName: 'Acme'
71
- },
72
- overrideAccess: false
73
- }));
74
- expect(result.content[0].text).toMatch(/Changed fields: siteName/);
75
- });
76
- it('passes draft:true for draft-enabled globals (always-draft default)', async ()=>{
77
- const tool = createUpdateGlobalTool(schemas, drafts);
78
- const req = buildReq();
79
- req.payload.updateGlobal.mockResolvedValue({
80
- copyright: '© 2026'
81
- });
82
- await tool.handler({
83
- slug: 'footer',
84
- data: '{"copyright":"© 2026"}'
85
- }, req, {});
86
- expect(req.payload.updateGlobal).toHaveBeenCalledWith(expect.objectContaining({
87
- slug: 'footer',
88
- draft: true
89
- }));
90
- });
91
- it('passes draft:false for non-draft globals', async ()=>{
92
- const tool = createUpdateGlobalTool(schemas, drafts);
93
- const req = buildReq();
94
- req.payload.updateGlobal.mockResolvedValue({
95
- siteName: 'Acme'
96
- });
97
- await tool.handler({
98
- slug: 'site-settings',
99
- data: '{"siteName":"Acme"}'
100
- }, req, {});
101
- expect(req.payload.updateGlobal).toHaveBeenCalledWith(expect.objectContaining({
102
- slug: 'site-settings',
103
- draft: false
104
- }));
105
- });
106
- it('returns text error on malformed JSON', async ()=>{
107
- const tool = createUpdateGlobalTool(schemas, drafts);
108
- const req = buildReq();
109
- const result = await tool.handler({
110
- slug: 'site-settings',
111
- data: 'not-json'
112
- }, req, {});
113
- expect(result.content[0].text).toMatch(/must be a valid JSON/);
114
- expect(req.payload.updateGlobal).not.toHaveBeenCalled();
115
- });
116
- it('returns text error when slug is unknown', async ()=>{
117
- const tool = createUpdateGlobalTool(schemas, drafts);
118
- const req = buildReq();
119
- const result = await tool.handler({
120
- slug: 'no-such-global',
121
- data: '{"x":1}'
122
- }, req, {});
123
- expect(result.content[0].text).toMatch(/Unknown global/);
124
- });
125
- it('rejects empty data objects', async ()=>{
126
- const tool = createUpdateGlobalTool(schemas, drafts);
127
- const req = buildReq();
128
- const result = await tool.handler({
129
- slug: 'site-settings',
130
- data: '{}'
131
- }, req, {});
132
- expect(result.content[0].text).toMatch(/No fields provided/);
133
- expect(req.payload.updateGlobal).not.toHaveBeenCalled();
134
- });
135
- it('catches Payload errors and returns text error response', async ()=>{
136
- const tool = createUpdateGlobalTool(schemas, drafts);
137
- const req = buildReq();
138
- req.payload.updateGlobal.mockRejectedValue(new Error('validation failed'));
139
- const result = await tool.handler({
140
- slug: 'site-settings',
141
- data: '{"siteName":"X"}'
142
- }, req, {});
143
- expect(result.content[0].text).toMatch(/Error updating global "site-settings"/);
144
- });
145
- it('mentions the draft note in the response for draft-enabled globals', async ()=>{
146
- const tool = createUpdateGlobalTool(schemas, drafts);
147
- const req = buildReq();
148
- req.payload.updateGlobal.mockResolvedValue({});
149
- const result = await tool.handler({
150
- slug: 'footer',
151
- data: '{"copyright":"x"}'
152
- }, req, {});
153
- expect(result.content[0].text).toMatch(/draft/i);
154
- });
155
- });
156
-
157
- //# sourceMappingURL=update-global.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/__tests__/update-global.test.ts"],"sourcesContent":["import { describe, it, expect, vi } from 'vitest'\r\nimport { createUpdateGlobalTool } from '../tools/update-global'\r\nimport type { GlobalSchema } from '../types'\r\n\r\nconst siteSettings: GlobalSchema = {\r\n slug: 'site-settings',\r\n fields: [\r\n { name: 'siteName', type: 'text' },\r\n { name: 'tagline', type: 'text' },\r\n ],\r\n hasDrafts: false,\r\n hasLivePreview: false,\r\n}\r\n\r\nconst footer: GlobalSchema = {\r\n slug: 'footer',\r\n fields: [{ name: 'copyright', type: 'text' }],\r\n hasDrafts: true,\r\n hasLivePreview: false,\r\n}\r\n\r\nfunction buildReq() {\r\n return {\r\n payload: {\r\n updateGlobal: vi.fn(),\r\n logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },\r\n },\r\n context: {},\r\n user: null,\r\n }\r\n}\r\n\r\ndescribe('updateGlobal', () => {\r\n const schemas = new Map([\r\n ['site-settings', siteSettings],\r\n ['footer', footer],\r\n ])\r\n const drafts = new Set(['footer'])\r\n\r\n it('partial-merges only the supplied fields', async () => {\r\n const tool = createUpdateGlobalTool(schemas, drafts)\r\n const req = buildReq()\r\n req.payload.updateGlobal.mockResolvedValue({ siteName: 'Acme' })\r\n\r\n const result = await tool.handler(\r\n { slug: 'site-settings', data: '{\"siteName\":\"Acme\"}' },\r\n req as never,\r\n {},\r\n )\r\n expect(req.payload.updateGlobal).toHaveBeenCalledWith(\r\n expect.objectContaining({\r\n slug: 'site-settings',\r\n data: { siteName: 'Acme' },\r\n overrideAccess: false,\r\n }),\r\n )\r\n expect(result.content[0]!.text).toMatch(/Changed fields: siteName/)\r\n })\r\n\r\n it('passes draft:true for draft-enabled globals (always-draft default)', async () => {\r\n const tool = createUpdateGlobalTool(schemas, drafts)\r\n const req = buildReq()\r\n req.payload.updateGlobal.mockResolvedValue({ copyright: '© 2026' })\r\n\r\n await tool.handler({ slug: 'footer', data: '{\"copyright\":\"© 2026\"}' }, req as never, {})\r\n expect(req.payload.updateGlobal).toHaveBeenCalledWith(\r\n expect.objectContaining({ slug: 'footer', draft: true }),\r\n )\r\n })\r\n\r\n it('passes draft:false for non-draft globals', async () => {\r\n const tool = createUpdateGlobalTool(schemas, drafts)\r\n const req = buildReq()\r\n req.payload.updateGlobal.mockResolvedValue({ siteName: 'Acme' })\r\n\r\n await tool.handler({ slug: 'site-settings', data: '{\"siteName\":\"Acme\"}' }, req as never, {})\r\n expect(req.payload.updateGlobal).toHaveBeenCalledWith(\r\n expect.objectContaining({ slug: 'site-settings', draft: false }),\r\n )\r\n })\r\n\r\n it('returns text error on malformed JSON', async () => {\r\n const tool = createUpdateGlobalTool(schemas, drafts)\r\n const req = buildReq()\r\n const result = await tool.handler(\r\n { slug: 'site-settings', data: 'not-json' },\r\n req as never,\r\n {},\r\n )\r\n expect(result.content[0]!.text).toMatch(/must be a valid JSON/)\r\n expect(req.payload.updateGlobal).not.toHaveBeenCalled()\r\n })\r\n\r\n it('returns text error when slug is unknown', async () => {\r\n const tool = createUpdateGlobalTool(schemas, drafts)\r\n const req = buildReq()\r\n const result = await tool.handler(\r\n { slug: 'no-such-global' as never, data: '{\"x\":1}' },\r\n req as never,\r\n {},\r\n )\r\n expect(result.content[0]!.text).toMatch(/Unknown global/)\r\n })\r\n\r\n it('rejects empty data objects', async () => {\r\n const tool = createUpdateGlobalTool(schemas, drafts)\r\n const req = buildReq()\r\n const result = await tool.handler(\r\n { slug: 'site-settings', data: '{}' },\r\n req as never,\r\n {},\r\n )\r\n expect(result.content[0]!.text).toMatch(/No fields provided/)\r\n expect(req.payload.updateGlobal).not.toHaveBeenCalled()\r\n })\r\n\r\n it('catches Payload errors and returns text error response', async () => {\r\n const tool = createUpdateGlobalTool(schemas, drafts)\r\n const req = buildReq()\r\n req.payload.updateGlobal.mockRejectedValue(new Error('validation failed'))\r\n\r\n const result = await tool.handler(\r\n { slug: 'site-settings', data: '{\"siteName\":\"X\"}' },\r\n req as never,\r\n {},\r\n )\r\n expect(result.content[0]!.text).toMatch(/Error updating global \"site-settings\"/)\r\n })\r\n\r\n it('mentions the draft note in the response for draft-enabled globals', async () => {\r\n const tool = createUpdateGlobalTool(schemas, drafts)\r\n const req = buildReq()\r\n req.payload.updateGlobal.mockResolvedValue({})\r\n\r\n const result = await tool.handler(\r\n { slug: 'footer', data: '{\"copyright\":\"x\"}' },\r\n req as never,\r\n {},\r\n )\r\n expect(result.content[0]!.text).toMatch(/draft/i)\r\n })\r\n})\r\n"],"names":["describe","it","expect","vi","createUpdateGlobalTool","siteSettings","slug","fields","name","type","hasDrafts","hasLivePreview","footer","buildReq","payload","updateGlobal","fn","logger","info","warn","error","context","user","schemas","Map","drafts","Set","tool","req","mockResolvedValue","siteName","result","handler","data","toHaveBeenCalledWith","objectContaining","overrideAccess","content","text","toMatch","copyright","draft","not","toHaveBeenCalled","mockRejectedValue","Error"],"mappings":"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"}