payload-mcp-toolkit 0.3.0 → 0.3.1

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 (49) hide show
  1. package/README.md +150 -150
  2. package/dist/__tests__/introspection.test.js.map +1 -1
  3. package/dist/draft-workflow.js +29 -29
  4. package/dist/draft-workflow.js.map +1 -1
  5. package/dist/index.js +20 -33
  6. package/dist/index.js.map +1 -1
  7. package/dist/introspection.d.ts +4 -0
  8. package/dist/introspection.js +38 -33
  9. package/dist/introspection.js.map +1 -1
  10. package/dist/prompts.js +5 -5
  11. package/dist/prompts.js.map +1 -1
  12. package/dist/resources.js +9 -16
  13. package/dist/resources.js.map +1 -1
  14. package/dist/tools/_helpers.d.ts +14 -0
  15. package/dist/tools/_helpers.js +35 -0
  16. package/dist/tools/_helpers.js.map +1 -0
  17. package/dist/tools/patch-layout.d.ts +3 -9
  18. package/dist/tools/patch-layout.js +29 -48
  19. package/dist/tools/patch-layout.js.map +1 -1
  20. package/dist/tools/publish-draft.d.ts +1 -11
  21. package/dist/tools/publish-draft.js +8 -39
  22. package/dist/tools/publish-draft.js.map +1 -1
  23. package/dist/tools/resolve-reference.d.ts +1 -12
  24. package/dist/tools/resolve-reference.js +45 -85
  25. package/dist/tools/resolve-reference.js.map +1 -1
  26. package/dist/tools/safe-delete.d.ts +8 -13
  27. package/dist/tools/safe-delete.js +68 -100
  28. package/dist/tools/safe-delete.js.map +1 -1
  29. package/dist/tools/schedule-publish.d.ts +11 -21
  30. package/dist/tools/schedule-publish.js +18 -61
  31. package/dist/tools/schedule-publish.js.map +1 -1
  32. package/dist/tools/search-content.d.ts +1 -6
  33. package/dist/tools/search-content.js +52 -64
  34. package/dist/tools/search-content.js.map +1 -1
  35. package/dist/tools/update-document.d.ts +4 -14
  36. package/dist/tools/update-document.js +23 -72
  37. package/dist/tools/update-document.js.map +1 -1
  38. package/dist/tools/upload-media.d.ts +1 -10
  39. package/dist/tools/upload-media.js +11 -54
  40. package/dist/tools/upload-media.js.map +1 -1
  41. package/dist/tools/versions.d.ts +7 -20
  42. package/dist/tools/versions.js +25 -82
  43. package/dist/tools/versions.js.map +1 -1
  44. package/dist/types.js +5 -5
  45. package/dist/types.js.map +1 -1
  46. package/package.json +1 -1
  47. package/dist/rate-limiter.d.ts +0 -25
  48. package/dist/rate-limiter.js +0 -51
  49. package/dist/rate-limiter.js.map +0 -1
@@ -1,10 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  import type { PayloadRequest } from 'payload';
3
3
  /**
4
- * Creates the listVersions MCP tool that returns recent saved versions of a draft document.
5
- *
6
- * Only works on collections in `draftCollections`. Returns id, _status, updatedAt, and a
7
- * compact display name per version so an LLM can pick one to restore.
4
+ * Lists recent saved versions of a draft document. Returns id, _status,
5
+ * updatedAt, and a compact display name per version so an LLM can pick one
6
+ * to restore.
8
7
  */
9
8
  export declare function createListVersionsTool(draftCollections: Set<string>): {
10
9
  name: string;
@@ -18,18 +17,11 @@ export declare function createListVersionsTool(draftCollections: Set<string>): {
18
17
  collection: string;
19
18
  documentId: string;
20
19
  limit?: number;
21
- }, req: PayloadRequest, _extra: unknown) => Promise<{
22
- content: {
23
- type: "text";
24
- text: string;
25
- }[];
26
- }>;
20
+ }, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
27
21
  };
28
22
  /**
29
- * Creates the restoreVersion MCP tool that rolls a document back to a saved version.
30
- *
31
- * Restoring a version creates a new version on top — the old current state is preserved
32
- * so the operation is itself reversible via another restore.
23
+ * Restoring a version creates a new version on top the previous current
24
+ * state is preserved so the operation is itself reversible.
33
25
  */
34
26
  export declare function createRestoreVersionTool(draftCollections: Set<string>): {
35
27
  name: string;
@@ -41,10 +33,5 @@ export declare function createRestoreVersionTool(draftCollections: Set<string>):
41
33
  handler: (args: {
42
34
  collection: string;
43
35
  versionId: string;
44
- }, req: PayloadRequest, _extra: unknown) => Promise<{
45
- content: {
46
- type: "text";
47
- text: string;
48
- }[];
49
- }>;
36
+ }, req: PayloadRequest, _extra: unknown) => Promise<import("./_helpers").McpTextResponse>;
50
37
  };
@@ -1,10 +1,10 @@
1
1
  import { z } from 'zod';
2
+ import { errorMessage, getDocDisplayName, jsonResponse, requireDraftCollection, stampMcpContext, textResponse } from './_helpers';
2
3
  const DEFAULT_LIST_LIMIT = 10;
3
4
  /**
4
- * Creates the listVersions MCP tool that returns recent saved versions of a draft document.
5
- *
6
- * Only works on collections in `draftCollections`. Returns id, _status, updatedAt, and a
7
- * compact display name per version so an LLM can pick one to restore.
5
+ * Lists recent saved versions of a draft document. Returns id, _status,
6
+ * updatedAt, and a compact display name per version so an LLM can pick one
7
+ * to restore.
8
8
  */ export function createListVersionsTool(draftCollections) {
9
9
  return {
10
10
  name: 'listVersions',
@@ -20,22 +20,9 @@ const DEFAULT_LIST_LIMIT = 10;
20
20
  },
21
21
  handler: async (args, req, _extra)=>{
22
22
  const { collection, documentId, limit = DEFAULT_LIST_LIMIT } = args;
23
- if (!draftCollections.has(collection)) {
24
- return {
25
- content: [
26
- {
27
- type: 'text',
28
- text: `Error: Collection "${collection}" does not support versions. ` + `Draft-enabled collections: ${[
29
- ...draftCollections
30
- ].join(', ') || 'none'}`
31
- }
32
- ]
33
- };
34
- }
35
- req.context = {
36
- ...req.context,
37
- source: 'mcp'
38
- };
23
+ const guard = requireDraftCollection(collection, draftCollections, 'versions');
24
+ if (guard) return guard;
25
+ stampMcpContext(req);
39
26
  try {
40
27
  const result = await req.payload.findVersions({
41
28
  collection: collection,
@@ -57,43 +44,26 @@ const DEFAULT_LIST_LIMIT = 10;
57
44
  updatedAt: v.updatedAt,
58
45
  createdAt: v.createdAt,
59
46
  status: snapshot._status ?? 'unknown',
60
- displayName: snapshot.name || snapshot.title || snapshot.slug || `${collection}#${documentId}`,
47
+ displayName: getDocDisplayName(snapshot, `${collection}#${documentId}`),
61
48
  autosave: v.autosave === true
62
49
  };
63
50
  });
64
- return {
65
- content: [
66
- {
67
- type: 'text',
68
- text: JSON.stringify({
69
- collection,
70
- documentId,
71
- totalDocs: result.totalDocs,
72
- returned: versions.length,
73
- versions
74
- })
75
- }
76
- ]
77
- };
51
+ return jsonResponse({
52
+ collection,
53
+ documentId,
54
+ totalDocs: result.totalDocs,
55
+ returned: versions.length,
56
+ versions
57
+ });
78
58
  } catch (error) {
79
- const message = error instanceof Error ? error.message : String(error);
80
- return {
81
- content: [
82
- {
83
- type: 'text',
84
- text: `Error listing versions for ${collection}#${documentId}: ${message}`
85
- }
86
- ]
87
- };
59
+ return textResponse(`Error listing versions for ${collection}#${documentId}: ${errorMessage(error)}`);
88
60
  }
89
61
  }
90
62
  };
91
63
  }
92
64
  /**
93
- * Creates the restoreVersion MCP tool that rolls a document back to a saved version.
94
- *
95
- * Restoring a version creates a new version on top — the old current state is preserved
96
- * so the operation is itself reversible via another restore.
65
+ * Restoring a version creates a new version on top the previous current
66
+ * state is preserved so the operation is itself reversible.
97
67
  */ export function createRestoreVersionTool(draftCollections) {
98
68
  return {
99
69
  name: 'restoreVersion',
@@ -108,22 +78,9 @@ const DEFAULT_LIST_LIMIT = 10;
108
78
  },
109
79
  handler: async (args, req, _extra)=>{
110
80
  const { collection, versionId } = args;
111
- if (!draftCollections.has(collection)) {
112
- return {
113
- content: [
114
- {
115
- type: 'text',
116
- text: `Error: Collection "${collection}" does not support versions. ` + `Draft-enabled collections: ${[
117
- ...draftCollections
118
- ].join(', ') || 'none'}`
119
- }
120
- ]
121
- };
122
- }
123
- req.context = {
124
- ...req.context,
125
- source: 'mcp'
126
- };
81
+ const guard = requireDraftCollection(collection, draftCollections, 'versions');
82
+ if (guard) return guard;
83
+ stampMcpContext(req);
127
84
  try {
128
85
  const restored = await req.payload.restoreVersion({
129
86
  collection: collection,
@@ -132,25 +89,11 @@ const DEFAULT_LIST_LIMIT = 10;
132
89
  overrideAccess: false,
133
90
  user: req.user
134
91
  });
135
- const displayName = restored.name || restored.title || restored.slug || restored.id;
136
- return {
137
- content: [
138
- {
139
- type: 'text',
140
- text: `Restored ${collection} document "${displayName}" (ID: ${restored.id}) ` + `from version ${versionId}. The document is now in draft status — ` + `use publishDraft to make the restored content live.`
141
- }
142
- ]
143
- };
92
+ const restoredId = String(restored.id);
93
+ const displayName = getDocDisplayName(restored, restoredId);
94
+ return textResponse(`Restored ${collection} document "${displayName}" (ID: ${restoredId}) ` + `from version ${versionId}. The document is now in draft status — ` + `use publishDraft to make the restored content live.`);
144
95
  } catch (error) {
145
- const message = error instanceof Error ? error.message : String(error);
146
- return {
147
- content: [
148
- {
149
- type: 'text',
150
- text: `Error restoring ${collection} from version ${versionId}: ${message}`
151
- }
152
- ]
153
- };
96
+ return textResponse(`Error restoring ${collection} from version ${versionId}: ${errorMessage(error)}`);
154
97
  }
155
98
  }
156
99
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tools/versions.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { PayloadRequest } from 'payload'\n\nconst DEFAULT_LIST_LIMIT = 10\n\n/**\n * Creates the listVersions MCP tool that returns recent saved versions of a draft document.\n *\n * Only works on collections in `draftCollections`. Returns id, _status, updatedAt, and a\n * compact display name per version so an LLM can pick one to restore.\n */\nexport function createListVersionsTool(draftCollections: Set<string>) {\n return {\n name: 'listVersions',\n description:\n 'List recent saved versions of a document on a draft-enabled collection. ' +\n 'Use before restoreVersion to pick the right point in time. ' +\n `Draft-enabled collections: ${[...draftCollections].join(', ') || 'none'}`,\n parameters: {\n collection: z\n .string()\n .describe(\n `The collection slug. Must be one of: ${[...draftCollections].join(', ') || 'none'}`,\n ),\n documentId: z.string().describe('The ID of the document whose versions you want to list'),\n limit: z\n .number()\n .optional()\n .default(DEFAULT_LIST_LIMIT)\n .describe(`Maximum number of versions to return (default ${DEFAULT_LIST_LIMIT})`),\n },\n handler: async (\n args: { collection: string; documentId: string; limit?: number },\n req: PayloadRequest,\n _extra: unknown,\n ) => {\n const { collection, documentId, limit = DEFAULT_LIST_LIMIT } = args\n\n if (!draftCollections.has(collection)) {\n return {\n content: [\n {\n type: 'text' as const,\n text:\n `Error: Collection \"${collection}\" does not support versions. ` +\n `Draft-enabled collections: ${[...draftCollections].join(', ') || 'none'}`,\n },\n ],\n }\n }\n\n req.context = { ...req.context, source: 'mcp' }\n\n try {\n const result = await req.payload.findVersions({\n collection: collection as any,\n where: { parent: { equals: documentId } },\n sort: '-updatedAt',\n limit,\n req,\n overrideAccess: false,\n user: req.user,\n })\n\n const versions = result.docs.map((v: any) => {\n const snapshot = v.version || {}\n return {\n id: v.id,\n updatedAt: v.updatedAt,\n createdAt: v.createdAt,\n status: snapshot._status ?? 'unknown',\n displayName:\n snapshot.name ||\n snapshot.title ||\n snapshot.slug ||\n `${collection}#${documentId}`,\n autosave: v.autosave === true,\n }\n })\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({\n collection,\n documentId,\n totalDocs: result.totalDocs,\n returned: versions.length,\n versions,\n }),\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error listing versions for ${collection}#${documentId}: ${message}`,\n },\n ],\n }\n }\n },\n }\n}\n\n/**\n * Creates the restoreVersion MCP tool that rolls a document back to a saved version.\n *\n * Restoring a version creates a new version on top — the old current state is preserved\n * so the operation is itself reversible via another restore.\n */\nexport function createRestoreVersionTool(draftCollections: Set<string>) {\n return {\n name: 'restoreVersion',\n description:\n 'Restore a document to a previously saved version. ' +\n 'Use listVersions first to find the version ID. ' +\n 'Restoring creates a new version on top, so the previous current state is also recoverable. ' +\n `Draft-enabled collections: ${[...draftCollections].join(', ') || 'none'}`,\n parameters: {\n collection: z\n .string()\n .describe(\n `The collection slug. Must be one of: ${[...draftCollections].join(', ') || 'none'}`,\n ),\n versionId: z\n .string()\n .describe('The version ID returned by listVersions (NOT the document ID)'),\n },\n handler: async (\n args: { collection: string; versionId: string },\n req: PayloadRequest,\n _extra: unknown,\n ) => {\n const { collection, versionId } = args\n\n if (!draftCollections.has(collection)) {\n return {\n content: [\n {\n type: 'text' as const,\n text:\n `Error: Collection \"${collection}\" does not support versions. ` +\n `Draft-enabled collections: ${[...draftCollections].join(', ') || 'none'}`,\n },\n ],\n }\n }\n\n req.context = { ...req.context, source: 'mcp' }\n\n try {\n const restored = await req.payload.restoreVersion({\n collection: collection as any,\n id: versionId,\n req,\n overrideAccess: false,\n user: req.user,\n })\n\n const displayName =\n (restored as any).name ||\n (restored as any).title ||\n (restored as any).slug ||\n (restored as any).id\n\n return {\n content: [\n {\n type: 'text' as const,\n text:\n `Restored ${collection} document \"${displayName}\" (ID: ${(restored as any).id}) ` +\n `from version ${versionId}. The document is now in draft status — ` +\n `use publishDraft to make the restored content live.`,\n },\n ],\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error restoring ${collection} from version ${versionId}: ${message}`,\n },\n ],\n }\n }\n },\n }\n}\n"],"names":["z","DEFAULT_LIST_LIMIT","createListVersionsTool","draftCollections","name","description","join","parameters","collection","string","describe","documentId","limit","number","optional","default","handler","args","req","_extra","has","content","type","text","context","source","result","payload","findVersions","where","parent","equals","sort","overrideAccess","user","versions","docs","map","v","snapshot","version","id","updatedAt","createdAt","status","_status","displayName","title","slug","autosave","JSON","stringify","totalDocs","returned","length","error","message","Error","String","createRestoreVersionTool","versionId","restored","restoreVersion"],"mappings":"AAAA,SAASA,CAAC,QAAQ,MAAK;AAGvB,MAAMC,qBAAqB;AAE3B;;;;;CAKC,GACD,OAAO,SAASC,uBAAuBC,gBAA6B;IAClE,OAAO;QACLC,MAAM;QACNC,aACE,6EACA,gEACA,CAAC,2BAA2B,EAAE;eAAIF;SAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;QAC5EC,YAAY;YACVC,YAAYR,EACTS,MAAM,GACNC,QAAQ,CACP,CAAC,qCAAqC,EAAE;mBAAIP;aAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;YAExFK,YAAYX,EAAES,MAAM,GAAGC,QAAQ,CAAC;YAChCE,OAAOZ,EACJa,MAAM,GACNC,QAAQ,GACRC,OAAO,CAACd,oBACRS,QAAQ,CAAC,CAAC,8CAA8C,EAAET,mBAAmB,CAAC,CAAC;QACpF;QACAe,SAAS,OACPC,MACAC,KACAC;YAEA,MAAM,EAAEX,UAAU,EAAEG,UAAU,EAAEC,QAAQX,kBAAkB,EAAE,GAAGgB;YAE/D,IAAI,CAACd,iBAAiBiB,GAAG,CAACZ,aAAa;gBACrC,OAAO;oBACLa,SAAS;wBACP;4BACEC,MAAM;4BACNC,MACE,CAAC,mBAAmB,EAAEf,WAAW,6BAA6B,CAAC,GAC/D,CAAC,2BAA2B,EAAE;mCAAIL;6BAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;wBAC9E;qBACD;gBACH;YACF;YAEAY,IAAIM,OAAO,GAAG;gBAAE,GAAGN,IAAIM,OAAO;gBAAEC,QAAQ;YAAM;YAE9C,IAAI;gBACF,MAAMC,SAAS,MAAMR,IAAIS,OAAO,CAACC,YAAY,CAAC;oBAC5CpB,YAAYA;oBACZqB,OAAO;wBAAEC,QAAQ;4BAAEC,QAAQpB;wBAAW;oBAAE;oBACxCqB,MAAM;oBACNpB;oBACAM;oBACAe,gBAAgB;oBAChBC,MAAMhB,IAAIgB,IAAI;gBAChB;gBAEA,MAAMC,WAAWT,OAAOU,IAAI,CAACC,GAAG,CAAC,CAACC;oBAChC,MAAMC,WAAWD,EAAEE,OAAO,IAAI,CAAC;oBAC/B,OAAO;wBACLC,IAAIH,EAAEG,EAAE;wBACRC,WAAWJ,EAAEI,SAAS;wBACtBC,WAAWL,EAAEK,SAAS;wBACtBC,QAAQL,SAASM,OAAO,IAAI;wBAC5BC,aACEP,SAASnC,IAAI,IACbmC,SAASQ,KAAK,IACdR,SAASS,IAAI,IACb,GAAGxC,WAAW,CAAC,EAAEG,YAAY;wBAC/BsC,UAAUX,EAAEW,QAAQ,KAAK;oBAC3B;gBACF;gBAEA,OAAO;oBACL5B,SAAS;wBACP;4BACEC,MAAM;4BACNC,MAAM2B,KAAKC,SAAS,CAAC;gCACnB3C;gCACAG;gCACAyC,WAAW1B,OAAO0B,SAAS;gCAC3BC,UAAUlB,SAASmB,MAAM;gCACzBnB;4BACF;wBACF;qBACD;gBACH;YACF,EAAE,OAAOoB,OAAO;gBACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;gBAChE,OAAO;oBACLlC,SAAS;wBACP;4BACEC,MAAM;4BACNC,MAAM,CAAC,2BAA2B,EAAEf,WAAW,CAAC,EAAEG,WAAW,EAAE,EAAE6C,SAAS;wBAC5E;qBACD;gBACH;YACF;QACF;IACF;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASG,yBAAyBxD,gBAA6B;IACpE,OAAO;QACLC,MAAM;QACNC,aACE,uDACA,oDACA,gGACA,CAAC,2BAA2B,EAAE;eAAIF;SAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;QAC5EC,YAAY;YACVC,YAAYR,EACTS,MAAM,GACNC,QAAQ,CACP,CAAC,qCAAqC,EAAE;mBAAIP;aAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;YAExFsD,WAAW5D,EACRS,MAAM,GACNC,QAAQ,CAAC;QACd;QACAM,SAAS,OACPC,MACAC,KACAC;YAEA,MAAM,EAAEX,UAAU,EAAEoD,SAAS,EAAE,GAAG3C;YAElC,IAAI,CAACd,iBAAiBiB,GAAG,CAACZ,aAAa;gBACrC,OAAO;oBACLa,SAAS;wBACP;4BACEC,MAAM;4BACNC,MACE,CAAC,mBAAmB,EAAEf,WAAW,6BAA6B,CAAC,GAC/D,CAAC,2BAA2B,EAAE;mCAAIL;6BAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;wBAC9E;qBACD;gBACH;YACF;YAEAY,IAAIM,OAAO,GAAG;gBAAE,GAAGN,IAAIM,OAAO;gBAAEC,QAAQ;YAAM;YAE9C,IAAI;gBACF,MAAMoC,WAAW,MAAM3C,IAAIS,OAAO,CAACmC,cAAc,CAAC;oBAChDtD,YAAYA;oBACZiC,IAAImB;oBACJ1C;oBACAe,gBAAgB;oBAChBC,MAAMhB,IAAIgB,IAAI;gBAChB;gBAEA,MAAMY,cACJ,AAACe,SAAiBzD,IAAI,IACtB,AAACyD,SAAiBd,KAAK,IACvB,AAACc,SAAiBb,IAAI,IACtB,AAACa,SAAiBpB,EAAE;gBAEtB,OAAO;oBACLpB,SAAS;wBACP;4BACEC,MAAM;4BACNC,MACE,CAAC,SAAS,EAAEf,WAAW,WAAW,EAAEsC,YAAY,OAAO,EAAE,AAACe,SAAiBpB,EAAE,CAAC,EAAE,CAAC,GACjF,CAAC,aAAa,EAAEmB,UAAU,wCAAwC,CAAC,GACnE,CAAC,mDAAmD,CAAC;wBACzD;qBACD;gBACH;YACF,EAAE,OAAOL,OAAO;gBACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;gBAChE,OAAO;oBACLlC,SAAS;wBACP;4BACEC,MAAM;4BACNC,MAAM,CAAC,gBAAgB,EAAEf,WAAW,cAAc,EAAEoD,UAAU,EAAE,EAAEJ,SAAS;wBAC7E;qBACD;gBACH;YACF;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../src/tools/versions.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { PayloadRequest } from 'payload'\nimport {\n errorMessage,\n getDocDisplayName,\n jsonResponse,\n requireDraftCollection,\n stampMcpContext,\n textResponse,\n} from './_helpers'\n\nconst DEFAULT_LIST_LIMIT = 10\n\n/**\n * Lists recent saved versions of a draft document. Returns id, _status,\n * updatedAt, and a compact display name per version so an LLM can pick one\n * to restore.\n */\nexport function createListVersionsTool(draftCollections: Set<string>) {\n return {\n name: 'listVersions',\n description:\n 'List recent saved versions of a document on a draft-enabled collection. ' +\n 'Use before restoreVersion to pick the right point in time. ' +\n `Draft-enabled collections: ${[...draftCollections].join(', ') || 'none'}`,\n parameters: {\n collection: z\n .string()\n .describe(\n `The collection slug. Must be one of: ${[...draftCollections].join(', ') || 'none'}`,\n ),\n documentId: z.string().describe('The ID of the document whose versions you want to list'),\n limit: z\n .number()\n .optional()\n .default(DEFAULT_LIST_LIMIT)\n .describe(`Maximum number of versions to return (default ${DEFAULT_LIST_LIMIT})`),\n },\n handler: async (\n args: { collection: string; documentId: string; limit?: number },\n req: PayloadRequest,\n _extra: unknown,\n ) => {\n const { collection, documentId, limit = DEFAULT_LIST_LIMIT } = args\n\n const guard = requireDraftCollection(collection, draftCollections, 'versions')\n if (guard) return guard\n\n stampMcpContext(req)\n\n try {\n const result = await req.payload.findVersions({\n collection: collection as any,\n where: { parent: { equals: documentId } },\n sort: '-updatedAt',\n limit,\n req,\n overrideAccess: false,\n user: req.user,\n })\n\n const versions = result.docs.map((v: any) => {\n const snapshot = v.version || {}\n return {\n id: v.id,\n updatedAt: v.updatedAt,\n createdAt: v.createdAt,\n status: snapshot._status ?? 'unknown',\n displayName: getDocDisplayName(snapshot, `${collection}#${documentId}`),\n autosave: v.autosave === true,\n }\n })\n\n return jsonResponse({\n collection,\n documentId,\n totalDocs: result.totalDocs,\n returned: versions.length,\n versions,\n })\n } catch (error) {\n return textResponse(\n `Error listing versions for ${collection}#${documentId}: ${errorMessage(error)}`,\n )\n }\n },\n }\n}\n\n/**\n * Restoring a version creates a new version on top — the previous current\n * state is preserved so the operation is itself reversible.\n */\nexport function createRestoreVersionTool(draftCollections: Set<string>) {\n return {\n name: 'restoreVersion',\n description:\n 'Restore a document to a previously saved version. ' +\n 'Use listVersions first to find the version ID. ' +\n 'Restoring creates a new version on top, so the previous current state is also recoverable. ' +\n `Draft-enabled collections: ${[...draftCollections].join(', ') || 'none'}`,\n parameters: {\n collection: z\n .string()\n .describe(\n `The collection slug. Must be one of: ${[...draftCollections].join(', ') || 'none'}`,\n ),\n versionId: z\n .string()\n .describe('The version ID returned by listVersions (NOT the document ID)'),\n },\n handler: async (\n args: { collection: string; versionId: string },\n req: PayloadRequest,\n _extra: unknown,\n ) => {\n const { collection, versionId } = args\n\n const guard = requireDraftCollection(collection, draftCollections, 'versions')\n if (guard) return guard\n\n stampMcpContext(req)\n\n try {\n const restored = await req.payload.restoreVersion({\n collection: collection as any,\n id: versionId,\n req,\n overrideAccess: false,\n user: req.user,\n })\n\n const restoredId = String((restored as any).id)\n const displayName = getDocDisplayName(restored, restoredId)\n\n return textResponse(\n `Restored ${collection} document \"${displayName}\" (ID: ${restoredId}) ` +\n `from version ${versionId}. The document is now in draft status — ` +\n `use publishDraft to make the restored content live.`,\n )\n } catch (error) {\n return textResponse(\n `Error restoring ${collection} from version ${versionId}: ${errorMessage(error)}`,\n )\n }\n },\n }\n}\n"],"names":["z","errorMessage","getDocDisplayName","jsonResponse","requireDraftCollection","stampMcpContext","textResponse","DEFAULT_LIST_LIMIT","createListVersionsTool","draftCollections","name","description","join","parameters","collection","string","describe","documentId","limit","number","optional","default","handler","args","req","_extra","guard","result","payload","findVersions","where","parent","equals","sort","overrideAccess","user","versions","docs","map","v","snapshot","version","id","updatedAt","createdAt","status","_status","displayName","autosave","totalDocs","returned","length","error","createRestoreVersionTool","versionId","restored","restoreVersion","restoredId","String"],"mappings":"AAAA,SAASA,CAAC,QAAQ,MAAK;AAEvB,SACEC,YAAY,EACZC,iBAAiB,EACjBC,YAAY,EACZC,sBAAsB,EACtBC,eAAe,EACfC,YAAY,QACP,aAAY;AAEnB,MAAMC,qBAAqB;AAE3B;;;;CAIC,GACD,OAAO,SAASC,uBAAuBC,gBAA6B;IAClE,OAAO;QACLC,MAAM;QACNC,aACE,6EACA,gEACA,CAAC,2BAA2B,EAAE;eAAIF;SAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;QAC5EC,YAAY;YACVC,YAAYd,EACTe,MAAM,GACNC,QAAQ,CACP,CAAC,qCAAqC,EAAE;mBAAIP;aAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;YAExFK,YAAYjB,EAAEe,MAAM,GAAGC,QAAQ,CAAC;YAChCE,OAAOlB,EACJmB,MAAM,GACNC,QAAQ,GACRC,OAAO,CAACd,oBACRS,QAAQ,CAAC,CAAC,8CAA8C,EAAET,mBAAmB,CAAC,CAAC;QACpF;QACAe,SAAS,OACPC,MACAC,KACAC;YAEA,MAAM,EAAEX,UAAU,EAAEG,UAAU,EAAEC,QAAQX,kBAAkB,EAAE,GAAGgB;YAE/D,MAAMG,QAAQtB,uBAAuBU,YAAYL,kBAAkB;YACnE,IAAIiB,OAAO,OAAOA;YAElBrB,gBAAgBmB;YAEhB,IAAI;gBACF,MAAMG,SAAS,MAAMH,IAAII,OAAO,CAACC,YAAY,CAAC;oBAC5Cf,YAAYA;oBACZgB,OAAO;wBAAEC,QAAQ;4BAAEC,QAAQf;wBAAW;oBAAE;oBACxCgB,MAAM;oBACNf;oBACAM;oBACAU,gBAAgB;oBAChBC,MAAMX,IAAIW,IAAI;gBAChB;gBAEA,MAAMC,WAAWT,OAAOU,IAAI,CAACC,GAAG,CAAC,CAACC;oBAChC,MAAMC,WAAWD,EAAEE,OAAO,IAAI,CAAC;oBAC/B,OAAO;wBACLC,IAAIH,EAAEG,EAAE;wBACRC,WAAWJ,EAAEI,SAAS;wBACtBC,WAAWL,EAAEK,SAAS;wBACtBC,QAAQL,SAASM,OAAO,IAAI;wBAC5BC,aAAa7C,kBAAkBsC,UAAU,GAAG1B,WAAW,CAAC,EAAEG,YAAY;wBACtE+B,UAAUT,EAAES,QAAQ,KAAK;oBAC3B;gBACF;gBAEA,OAAO7C,aAAa;oBAClBW;oBACAG;oBACAgC,WAAWtB,OAAOsB,SAAS;oBAC3BC,UAAUd,SAASe,MAAM;oBACzBf;gBACF;YACF,EAAE,OAAOgB,OAAO;gBACd,OAAO9C,aACL,CAAC,2BAA2B,EAAEQ,WAAW,CAAC,EAAEG,WAAW,EAAE,EAAEhB,aAAamD,QAAQ;YAEpF;QACF;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,SAASC,yBAAyB5C,gBAA6B;IACpE,OAAO;QACLC,MAAM;QACNC,aACE,uDACA,oDACA,gGACA,CAAC,2BAA2B,EAAE;eAAIF;SAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;QAC5EC,YAAY;YACVC,YAAYd,EACTe,MAAM,GACNC,QAAQ,CACP,CAAC,qCAAqC,EAAE;mBAAIP;aAAiB,CAACG,IAAI,CAAC,SAAS,QAAQ;YAExF0C,WAAWtD,EACRe,MAAM,GACNC,QAAQ,CAAC;QACd;QACAM,SAAS,OACPC,MACAC,KACAC;YAEA,MAAM,EAAEX,UAAU,EAAEwC,SAAS,EAAE,GAAG/B;YAElC,MAAMG,QAAQtB,uBAAuBU,YAAYL,kBAAkB;YACnE,IAAIiB,OAAO,OAAOA;YAElBrB,gBAAgBmB;YAEhB,IAAI;gBACF,MAAM+B,WAAW,MAAM/B,IAAII,OAAO,CAAC4B,cAAc,CAAC;oBAChD1C,YAAYA;oBACZ4B,IAAIY;oBACJ9B;oBACAU,gBAAgB;oBAChBC,MAAMX,IAAIW,IAAI;gBAChB;gBAEA,MAAMsB,aAAaC,OAAO,AAACH,SAAiBb,EAAE;gBAC9C,MAAMK,cAAc7C,kBAAkBqD,UAAUE;gBAEhD,OAAOnD,aACL,CAAC,SAAS,EAAEQ,WAAW,WAAW,EAAEiC,YAAY,OAAO,EAAEU,WAAW,EAAE,CAAC,GACrE,CAAC,aAAa,EAAEH,UAAU,wCAAwC,CAAC,GACnE,CAAC,mDAAmD,CAAC;YAE3D,EAAE,OAAOF,OAAO;gBACd,OAAO9C,aACL,CAAC,gBAAgB,EAAEQ,WAAW,cAAc,EAAEwC,UAAU,EAAE,EAAErD,aAAamD,QAAQ;YAErF;QACF;IACF;AACF"}
package/dist/types.js CHANGED
@@ -1,8 +1,8 @@
1
- /**
2
- * payload-mcp-toolkit configuration.
3
- *
4
- * The plugin works with zero options — every field below is an escape hatch
5
- * for the cases where Payload's own config doesn't carry enough signal.
1
+ /**
2
+ * payload-mcp-toolkit configuration.
3
+ *
4
+ * The plugin works with zero options — every field below is an escape hatch
5
+ * for the cases where Payload's own config doesn't carry enough signal.
6
6
  */ /** Relationship edge in the collection graph */ export { };
7
7
 
8
8
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * payload-mcp-toolkit configuration.\n *\n * The plugin works with zero options — every field below is an escape hatch\n * for the cases where Payload's own config doesn't carry enough signal.\n */\nexport interface ContentToolkitOptions {\n /**\n * Preview URL behavior. The toolkit reads `collection.admin.livePreview.url`\n * (or `collection.admin.preview` as a fallback) when generating preview links\n * for draft documents. Provide this object only to override what Payload\n * already knows.\n */\n preview?: {\n /**\n * Absolute base URL prepended to relative preview paths. Defaults to\n * `incomingConfig.serverURL`, then `process.env.NEXT_PUBLIC_SERVER_URL`,\n * then `process.env.SITE_URL`. If none of those resolve and your preview\n * URL function returns a relative path, no preview URL is appended.\n */\n siteUrl?: string\n\n /**\n * Disable preview URL injection entirely.\n */\n disabled?: boolean\n }\n\n /**\n * Per-collection draft behavior overrides. The default behavior is inferred\n * from each collection's `versions.drafts` setting:\n * - drafts enabled → `'always-draft'` (raw `update` is locked; clients go\n * through `publishDraft` / `patchLayout` / `updateDocument` which preserve\n * draft semantics)\n * - drafts disabled → `'always-publish'`\n *\n * Override per slug only if you need to allow raw publish on a draftable\n * collection.\n */\n draftBehavior?: Record<string, 'always-draft' | 'always-publish'>\n\n /**\n * Override the auth collection used for API key linkage. By default the\n * toolkit scans `incomingConfig.collections` for the first collection with\n * `auth: true`, preferring one named `'users'`.\n */\n userCollection?: string\n\n /**\n * Hide collections or globals from the MCP surface. Useful for internal\n * bookkeeping collections that should not be exposed to AI clients.\n */\n exclude?: {\n collections?: string[]\n globals?: string[]\n }\n\n /**\n * Site-specific domain prompts that teach the AI business vocabulary.\n * Merged with the auto-generated prompts.\n */\n domainPrompts?: DomainPrompt[]\n\n /** Media upload configuration */\n mediaUpload?: {\n /** Maximum file size in bytes (default: 10MB) */\n maxFileSize?: number\n /** Media collection slug (default: 'media') */\n collectionSlug?: string\n }\n}\n\n/** A domain prompt that teaches the AI site-specific vocabulary */\nexport interface DomainPrompt {\n /** Unique name for the prompt */\n name: string\n /** Display title */\n title: string\n /** Description of what this prompt teaches */\n description: string\n /** The prompt content */\n content: string\n}\n\n/** Introspected field metadata */\nexport interface FieldSchema {\n name: string\n type: string\n required?: boolean\n hasMany?: boolean\n relationTo?: string | string[]\n options?: Array<{ label: string; value: string }>\n fields?: FieldSchema[]\n maxRows?: number\n}\n\n/** Introspected collection metadata */\nexport interface CollectionSchema {\n slug: string\n fields: FieldSchema[]\n hasDrafts: boolean\n hasLivePreview: boolean\n relationships: Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }>\n searchableFields: string[]\n}\n\n/**\n * One block in the catalog. Flat — no section/leaf distinction. Whether a\n * block can nest other blocks is encoded in the `BlockNestingMap` keyed by\n * the path to its `blocks` field.\n */\nexport interface BlockSchema {\n slug: string\n fields: FieldSchema[]\n}\n\n/**\n * Flat catalog of every block referenced by the schema.\n */\nexport interface BlockCatalog {\n blocks: BlockSchema[]\n}\n\n/**\n * One entry per `blocks`-typed field anywhere in the schema.\n *\n * `path` is `<owner>.<dottedFieldPath>` where owner is the collection or\n * block slug that contains the field. Values list the slugs that field\n * accepts. The AI uses this to compose blocks at any nesting depth without\n * us pre-classifying anything as a \"section\" or \"leaf\".\n */\nexport interface BlockNestingEdge {\n /** Owner of the blocks field — either a collection slug or a block slug */\n owner: string\n /** Whether the owner is a collection or a block */\n ownerType: 'collection' | 'block'\n /** Dotted path to the blocks field within the owner (e.g. `layout`, `hero.content`) */\n fieldPath: string\n /** Block slugs that this field accepts */\n acceptedBlockSlugs: string[]\n /** Optional row cap from the field config */\n maxRows?: number\n}\n\n/** Map of every blocks-field in the schema to the slugs it accepts */\nexport type BlockNestingMap = BlockNestingEdge[]\n\n/** Relationship edge in the collection graph */\nexport interface RelationshipEdge {\n fromCollection: string\n fieldName: string\n toCollection: string | string[]\n hasMany: boolean\n}\n"],"names":[],"mappings":"AAAA;;;;;CAKC,GA8ID,8CAA8C,GAC9C,WAKC"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\r\n * payload-mcp-toolkit configuration.\r\n *\r\n * The plugin works with zero options — every field below is an escape hatch\r\n * for the cases where Payload's own config doesn't carry enough signal.\r\n */\r\nexport interface ContentToolkitOptions {\r\n /**\r\n * Preview URL behavior. The toolkit reads `collection.admin.livePreview.url`\r\n * (or `collection.admin.preview` as a fallback) when generating preview links\r\n * for draft documents. Provide this object only to override what Payload\r\n * already knows.\r\n */\r\n preview?: {\r\n /**\r\n * Absolute base URL prepended to relative preview paths. Defaults to\r\n * `incomingConfig.serverURL`, then `process.env.NEXT_PUBLIC_SERVER_URL`,\r\n * then `process.env.SITE_URL`. If none of those resolve and your preview\r\n * URL function returns a relative path, no preview URL is appended.\r\n */\r\n siteUrl?: string\r\n\r\n /**\r\n * Disable preview URL injection entirely.\r\n */\r\n disabled?: boolean\r\n }\r\n\r\n /**\r\n * Per-collection draft behavior overrides. The default behavior is inferred\r\n * from each collection's `versions.drafts` setting:\r\n * - drafts enabled → `'always-draft'` (raw `update` is locked; clients go\r\n * through `publishDraft` / `patchLayout` / `updateDocument` which preserve\r\n * draft semantics)\r\n * - drafts disabled → `'always-publish'`\r\n *\r\n * Override per slug only if you need to allow raw publish on a draftable\r\n * collection.\r\n */\r\n draftBehavior?: Record<string, 'always-draft' | 'always-publish'>\r\n\r\n /**\r\n * Override the auth collection used for API key linkage. By default the\r\n * toolkit scans `incomingConfig.collections` for the first collection with\r\n * `auth: true`, preferring one named `'users'`.\r\n */\r\n userCollection?: string\r\n\r\n /**\r\n * Hide collections or globals from the MCP surface. Useful for internal\r\n * bookkeeping collections that should not be exposed to AI clients.\r\n */\r\n exclude?: {\r\n collections?: string[]\r\n globals?: string[]\r\n }\r\n\r\n /**\r\n * Site-specific domain prompts that teach the AI business vocabulary.\r\n * Merged with the auto-generated prompts.\r\n */\r\n domainPrompts?: DomainPrompt[]\r\n\r\n /** Media upload configuration */\r\n mediaUpload?: {\r\n /** Maximum file size in bytes (default: 10MB) */\r\n maxFileSize?: number\r\n /** Media collection slug (default: 'media') */\r\n collectionSlug?: string\r\n }\r\n}\r\n\r\n/** A domain prompt that teaches the AI site-specific vocabulary */\r\nexport interface DomainPrompt {\r\n /** Unique name for the prompt */\r\n name: string\r\n /** Display title */\r\n title: string\r\n /** Description of what this prompt teaches */\r\n description: string\r\n /** The prompt content */\r\n content: string\r\n}\r\n\r\n/** Introspected field metadata */\r\nexport interface FieldSchema {\r\n name: string\r\n type: string\r\n required?: boolean\r\n hasMany?: boolean\r\n relationTo?: string | string[]\r\n options?: Array<{ label: string; value: string }>\r\n fields?: FieldSchema[]\r\n maxRows?: number\r\n}\r\n\r\n/** Introspected collection metadata */\r\nexport interface CollectionSchema {\r\n slug: string\r\n fields: FieldSchema[]\r\n hasDrafts: boolean\r\n hasLivePreview: boolean\r\n relationships: Array<{ fieldName: string; relationTo: string | string[]; hasMany: boolean }>\r\n searchableFields: string[]\r\n}\r\n\r\n/**\r\n * One block in the catalog. Flat — no section/leaf distinction. Whether a\r\n * block can nest other blocks is encoded in the `BlockNestingMap` keyed by\r\n * the path to its `blocks` field.\r\n */\r\nexport interface BlockSchema {\r\n slug: string\r\n fields: FieldSchema[]\r\n}\r\n\r\n/**\r\n * Flat catalog of every block referenced by the schema.\r\n */\r\nexport interface BlockCatalog {\r\n blocks: BlockSchema[]\r\n}\r\n\r\n/**\r\n * One entry per `blocks`-typed field anywhere in the schema.\r\n *\r\n * `path` is `<owner>.<dottedFieldPath>` where owner is the collection or\r\n * block slug that contains the field. Values list the slugs that field\r\n * accepts. The AI uses this to compose blocks at any nesting depth without\r\n * us pre-classifying anything as a \"section\" or \"leaf\".\r\n */\r\nexport interface BlockNestingEdge {\r\n /** Owner of the blocks field — either a collection slug or a block slug */\r\n owner: string\r\n /** Whether the owner is a collection or a block */\r\n ownerType: 'collection' | 'block'\r\n /** Dotted path to the blocks field within the owner (e.g. `layout`, `hero.content`) */\r\n fieldPath: string\r\n /** Block slugs that this field accepts */\r\n acceptedBlockSlugs: string[]\r\n /** Optional row cap from the field config */\r\n maxRows?: number\r\n}\r\n\r\n/** Map of every blocks-field in the schema to the slugs it accepts */\r\nexport type BlockNestingMap = BlockNestingEdge[]\r\n\r\n/** Relationship edge in the collection graph */\r\nexport interface RelationshipEdge {\r\n fromCollection: string\r\n fieldName: string\r\n toCollection: string | string[]\r\n hasMany: boolean\r\n}\r\n"],"names":[],"mappings":"AAAA;;;;;CAKC,GA8ID,8CAA8C,GAC9C,WAKC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payload-mcp-toolkit",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Schema-aware MCP toolkit for Payload CMS — wraps the official @payloadcms/plugin-mcp with introspected prompts, resources, draft workflow, and AI-friendly tools so non-technical editors can manage content via AI chat.",
5
5
  "license": "MIT",
6
6
  "author": "jon8800",
@@ -1,25 +0,0 @@
1
- /**
2
- * Simple in-memory sliding window rate limiter per API key.
3
- * State resets on server restart — not suitable for serverless.
4
- */
5
- interface RateLimitOptions {
6
- /** Window duration in milliseconds (default: 60000 = 1 minute) */
7
- windowMs?: number;
8
- /** Maximum requests per window (default: 60) */
9
- maxRequests?: number;
10
- }
11
- export declare function createRateLimiter(options?: RateLimitOptions): {
12
- /**
13
- * Check if a request is allowed for the given key.
14
- * Returns { allowed: true } or { allowed: false, retryAfterMs }.
15
- */
16
- check(key: string): {
17
- allowed: true;
18
- } | {
19
- allowed: false;
20
- retryAfterMs: number;
21
- };
22
- /** Reset the store (for testing) */
23
- reset(): void;
24
- };
25
- export {};
@@ -1,51 +0,0 @@
1
- /**
2
- * Simple in-memory sliding window rate limiter per API key.
3
- * State resets on server restart — not suitable for serverless.
4
- */ const DEFAULT_WINDOW_MS = 60_000;
5
- const DEFAULT_MAX_REQUESTS = 60;
6
- export function createRateLimiter(options) {
7
- const windowMs = options?.windowMs ?? DEFAULT_WINDOW_MS;
8
- const maxRequests = options?.maxRequests ?? DEFAULT_MAX_REQUESTS;
9
- const store = new Map();
10
- // Periodically clean up expired entries (every 5 minutes)
11
- const cleanupInterval = setInterval(()=>{
12
- const now = Date.now();
13
- for (const [key, entry] of store){
14
- entry.timestamps = entry.timestamps.filter((t)=>now - t < windowMs);
15
- if (entry.timestamps.length === 0) store.delete(key);
16
- }
17
- }, 5 * 60_000);
18
- // Allow garbage collection if the module is unloaded
19
- if (cleanupInterval.unref) cleanupInterval.unref();
20
- return {
21
- /**
22
- * Check if a request is allowed for the given key.
23
- * Returns { allowed: true } or { allowed: false, retryAfterMs }.
24
- */ check (key) {
25
- const now = Date.now();
26
- const entry = store.get(key) ?? {
27
- timestamps: []
28
- };
29
- // Remove timestamps outside the window
30
- entry.timestamps = entry.timestamps.filter((t)=>now - t < windowMs);
31
- if (entry.timestamps.length >= maxRequests) {
32
- const oldest = entry.timestamps[0];
33
- const retryAfterMs = oldest + windowMs - now;
34
- return {
35
- allowed: false,
36
- retryAfterMs: Math.max(retryAfterMs, 1000)
37
- };
38
- }
39
- entry.timestamps.push(now);
40
- store.set(key, entry);
41
- return {
42
- allowed: true
43
- };
44
- },
45
- /** Reset the store (for testing) */ reset () {
46
- store.clear();
47
- }
48
- };
49
- }
50
-
51
- //# sourceMappingURL=rate-limiter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/rate-limiter.ts"],"sourcesContent":["/**\n * Simple in-memory sliding window rate limiter per API key.\n * State resets on server restart — not suitable for serverless.\n */\n\ninterface RateLimitEntry {\n timestamps: number[]\n}\n\ninterface RateLimitOptions {\n /** Window duration in milliseconds (default: 60000 = 1 minute) */\n windowMs?: number\n /** Maximum requests per window (default: 60) */\n maxRequests?: number\n}\n\nconst DEFAULT_WINDOW_MS = 60_000\nconst DEFAULT_MAX_REQUESTS = 60\n\nexport function createRateLimiter(options?: RateLimitOptions) {\n const windowMs = options?.windowMs ?? DEFAULT_WINDOW_MS\n const maxRequests = options?.maxRequests ?? DEFAULT_MAX_REQUESTS\n const store = new Map<string, RateLimitEntry>()\n\n // Periodically clean up expired entries (every 5 minutes)\n const cleanupInterval = setInterval(() => {\n const now = Date.now()\n for (const [key, entry] of store) {\n entry.timestamps = entry.timestamps.filter((t) => now - t < windowMs)\n if (entry.timestamps.length === 0) store.delete(key)\n }\n }, 5 * 60_000)\n\n // Allow garbage collection if the module is unloaded\n if (cleanupInterval.unref) cleanupInterval.unref()\n\n return {\n /**\n * Check if a request is allowed for the given key.\n * Returns { allowed: true } or { allowed: false, retryAfterMs }.\n */\n check(key: string): { allowed: true } | { allowed: false; retryAfterMs: number } {\n const now = Date.now()\n const entry = store.get(key) ?? { timestamps: [] }\n\n // Remove timestamps outside the window\n entry.timestamps = entry.timestamps.filter((t) => now - t < windowMs)\n\n if (entry.timestamps.length >= maxRequests) {\n const oldest = entry.timestamps[0]\n const retryAfterMs = oldest + windowMs - now\n return { allowed: false, retryAfterMs: Math.max(retryAfterMs, 1000) }\n }\n\n entry.timestamps.push(now)\n store.set(key, entry)\n return { allowed: true }\n },\n\n /** Reset the store (for testing) */\n reset() {\n store.clear()\n },\n }\n}\n"],"names":["DEFAULT_WINDOW_MS","DEFAULT_MAX_REQUESTS","createRateLimiter","options","windowMs","maxRequests","store","Map","cleanupInterval","setInterval","now","Date","key","entry","timestamps","filter","t","length","delete","unref","check","get","oldest","retryAfterMs","allowed","Math","max","push","set","reset","clear"],"mappings":"AAAA;;;CAGC,GAaD,MAAMA,oBAAoB;AAC1B,MAAMC,uBAAuB;AAE7B,OAAO,SAASC,kBAAkBC,OAA0B;IAC1D,MAAMC,WAAWD,SAASC,YAAYJ;IACtC,MAAMK,cAAcF,SAASE,eAAeJ;IAC5C,MAAMK,QAAQ,IAAIC;IAElB,0DAA0D;IAC1D,MAAMC,kBAAkBC,YAAY;QAClC,MAAMC,MAAMC,KAAKD,GAAG;QACpB,KAAK,MAAM,CAACE,KAAKC,MAAM,IAAIP,MAAO;YAChCO,MAAMC,UAAU,GAAGD,MAAMC,UAAU,CAACC,MAAM,CAAC,CAACC,IAAMN,MAAMM,IAAIZ;YAC5D,IAAIS,MAAMC,UAAU,CAACG,MAAM,KAAK,GAAGX,MAAMY,MAAM,CAACN;QAClD;IACF,GAAG,IAAI;IAEP,qDAAqD;IACrD,IAAIJ,gBAAgBW,KAAK,EAAEX,gBAAgBW,KAAK;IAEhD,OAAO;QACL;;;KAGC,GACDC,OAAMR,GAAW;YACf,MAAMF,MAAMC,KAAKD,GAAG;YACpB,MAAMG,QAAQP,MAAMe,GAAG,CAACT,QAAQ;gBAAEE,YAAY,EAAE;YAAC;YAEjD,uCAAuC;YACvCD,MAAMC,UAAU,GAAGD,MAAMC,UAAU,CAACC,MAAM,CAAC,CAACC,IAAMN,MAAMM,IAAIZ;YAE5D,IAAIS,MAAMC,UAAU,CAACG,MAAM,IAAIZ,aAAa;gBAC1C,MAAMiB,SAAST,MAAMC,UAAU,CAAC,EAAE;gBAClC,MAAMS,eAAeD,SAASlB,WAAWM;gBACzC,OAAO;oBAAEc,SAAS;oBAAOD,cAAcE,KAAKC,GAAG,CAACH,cAAc;gBAAM;YACtE;YAEAV,MAAMC,UAAU,CAACa,IAAI,CAACjB;YACtBJ,MAAMsB,GAAG,CAAChB,KAAKC;YACf,OAAO;gBAAEW,SAAS;YAAK;QACzB;QAEA,kCAAkC,GAClCK;YACEvB,MAAMwB,KAAK;QACb;IACF;AACF"}