@abinnovision/payloadcms-mcpx 1.1.0-beta.3 → 1.1.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -170,16 +170,17 @@ every `collection` and `global` enum contains only the slugs the key may touch.
170
170
  Builtin tools reject unknown arguments by name instead of silently ignoring
171
171
  them.
172
172
 
173
- | Tool | Purpose | Key arguments |
174
- | ------------------ | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
175
- | `listCapabilities` | What this key may do, `create` apart from `write`; call first to orient. | none |
176
- | `describeSchema` | Field shape of one node; `next` lists the drill-down paths. | `collection` \| `global`, `paths?`, `expand?` |
177
- | `findDocuments` | Query documents. | `collection`, `where?`, `sort?`, `limit?`, `page?`, `depth?`, `select?`, `locale?`, `draft?` |
178
- | `getDocument` | Read one document or a subtree of it. | `collection` + `id` \| `global`, `path?`, `depth?`, `locale?`, `draft?`, `outline?` |
179
- | `patchDocument` | Apply RFC 6902 operations to the current draft. | `collection` + `id` \| `global`, `locale`, `patches`, `expectedUpdatedAt?` |
180
- | `createDocument` | Create a draft from a minimal seed. Not for upload collections. | `collection`, `locale`, `data` |
181
- | `validateDocument` | Publish blockers without saving anything. | `collection` + `id` \| `global`, `locale` |
182
- | `publishDocument` | Publish the current draft. | `collection` + `id` \| `global`, `expectedUpdatedAt?` |
173
+ | Tool | Purpose | Key arguments |
174
+ | ------------------ | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
175
+ | `listCapabilities` | What this key may do, `create` apart from `write`; call first to orient. | none |
176
+ | `describeSchema` | Field shape of one node; `next` lists the drill-down paths. | `collection` \| `global`, `paths?`, `expand?` |
177
+ | `findDocuments` | Query documents. | `collection`, `where?`, `sort?`, `limit?`, `page?`, `depth?`, `select?`, `locale?`, `draft?` |
178
+ | `getDocument` | Read one document or a subtree of it, an old version, or a diff. | `collection` + `id` \| `global`, `path?`, `depth?`, `locale?`, `draft?`, `outline?`, `versionId?`, `diffFrom?` |
179
+ | `findVersions` | Version history of one document or global, metadata only. | `collection` + `id` \| `global`, `limit?`, `page?`, `status?`, `locale?` |
180
+ | `patchDocument` | Apply RFC 6902 operations to the current draft. | `collection` + `id` \| `global`, `locale`, `patches`, `expectedUpdatedAt?` |
181
+ | `createDocument` | Create a draft from a minimal seed. Not for upload collections. | `collection`, `locale`, `data` |
182
+ | `validateDocument` | Publish blockers without saving anything. | `collection` + `id` \| `global`, `locale` |
183
+ | `publishDocument` | Publish the current draft. | `collection` + `id` \| `global`, `expectedUpdatedAt?` |
183
184
 
184
185
  ### Paths and pointers
185
186
 
@@ -416,6 +417,27 @@ privileged, so blocker paths and messages may name fields the key's user cannot
416
417
  read, though values are never included. `validateDocument` runs the same
417
418
  traversal without saving anything, which is why it carries no `readOnlyHint`.
418
419
 
420
+ ## Version history
421
+
422
+ On a collection or global with `versions` (with or without drafts),
423
+ `findVersions` lists the history newest first: `versionId`, timestamps,
424
+ `status`, `latest` and `autosave`, but no bodies. It is a read, so it follows
425
+ the `read` capability. The document's own `read` access is checked first, then
426
+ Payload's `readVersions`.
427
+
428
+ `getDocument` takes `versionId` to read one of those versions in place of the
429
+ document, with `path`, `outline`, `locale` and `depth` working as usual. With
430
+ `diffFrom`, a `versionId` or `"published"` (the newest version with published
431
+ status, in any locale), it returns `{ from, to, patch }`: the RFC 6902
432
+ operations that turn `diffFrom` into the document read (the current draft, or
433
+ `versionId` when given). The ops use the pointers `patchDocument` takes, `path`
434
+ limits them to a subtree, and `id`, timestamps and `_status` are left out. Arrays diff by position, so a reordered block shows up as replaces.
435
+
436
+ There is no restore tool. To revert, pass the old version as `versionId` and the
437
+ latest one as `diffFrom`, then apply the patch with `patchDocument`. The revert
438
+ lands as a draft like any other write, with `expectedUpdatedAt` and publish
439
+ blockers in force.
440
+
419
441
  ## Custom tools
420
442
 
421
443
  ```ts
package/dist/options.mjs CHANGED
@@ -66,6 +66,7 @@ const normalizeCollections = (config, options, apiKeysSlug) => {
66
66
  read: settings.read ?? true,
67
67
  write: normalizeWriteMode("Collection", slug, settings.write),
68
68
  hasDrafts,
69
+ hasVersions: Boolean(collection.versions),
69
70
  isUpload: Boolean(collection.upload),
70
71
  fieldName: toCamelCase(slug)
71
72
  };
@@ -90,6 +91,7 @@ const normalizeGlobals = (config, options) => {
90
91
  read: settings.read ?? true,
91
92
  write: normalizeWriteMode("Global", slug, settings.write),
92
93
  hasDrafts,
94
+ hasVersions: Boolean(global.versions),
93
95
  isUpload: false,
94
96
  fieldName: toCamelCase(slug)
95
97
  };
@@ -1,6 +1,7 @@
1
1
  import { createDocument } from "./create-document.mjs";
2
2
  import { describeSchema } from "./describe-schema.mjs";
3
3
  import { findDocuments } from "./find-documents.mjs";
4
+ import { findVersions } from "./find-versions.mjs";
4
5
  import { getDocument } from "./get-document.mjs";
5
6
  import { listCapabilities } from "./list-capabilities.mjs";
6
7
  import { patchDocument } from "./patch-document.mjs";
@@ -17,6 +18,7 @@ import { validateDocument } from "./validate-document.mjs";
17
18
  describeSchema,
18
19
  findDocuments,
19
20
  getDocument,
21
+ findVersions,
20
22
  patchDocument,
21
23
  createDocument,
22
24
  validateDocument,
@@ -0,0 +1,70 @@
1
+ import { jsonResult } from "../result.mjs";
2
+ import { idShape, localeOf, localeShape, slugsFor, targetShape } from "./shared.mjs";
3
+ import { requireIdFor, resolveTarget } from "./target.mjs";
4
+ import { defineMcpxTool } from "../types.mjs";
5
+ import { queryVersions } from "./versions.mjs";
6
+ import { z } from "zod";
7
+ /**
8
+ * Bodies are left out so a long history stays small; `getDocument` reads one
9
+ * when it is needed. The document's `read` access is checked first, then
10
+ * Payload's `readVersions`.
11
+ */ const findVersions = defineMcpxTool({
12
+ name: "findVersions",
13
+ description: `Lists the version history of one document or global, newest first. Returns metadata only; read a version's body with getDocument and "versionId", or what changed with "diffFrom".
14
+
15
+ Pass exactly one of "collection" and "global". "id" is required with "collection" and must be omitted with "global". Autosave versions are listed too, marked "autosave".`,
16
+ annotations: {
17
+ readOnlyHint: true,
18
+ openWorldHint: false
19
+ },
20
+ isEnabled: (scope) => {
21
+ const { collections, globals } = slugsFor(scope, "versions");
22
+ return collections.length + globals.length > 0;
23
+ },
24
+ inputSchema: (scope) => ({
25
+ ...targetShape(scope, "versions", {
26
+ collection: "Collection holding the document.",
27
+ global: "Global whose history to list."
28
+ }),
29
+ ...idShape(scope, "versions"),
30
+ limit: z.number().int().min(1).max(scope.limits.maxLimit).optional().describe(`Versions per page. Default 10, at most ${String(scope.limits.maxLimit)}.`),
31
+ page: z.number().int().min(1).optional().describe("Page number, from 1."),
32
+ status: z.enum(["published", "draft"]).optional().describe("Only versions with this status."),
33
+ ...localeShape(scope, {
34
+ required: false,
35
+ description: "Locale whose status to report. Defaults to the default locale."
36
+ })
37
+ }),
38
+ handler: async ({ args, scope }) => {
39
+ const target = resolveTarget(scope, args, "versions");
40
+ const id = requireIdFor(target, args.id);
41
+ const result = await queryVersions(scope, {
42
+ target,
43
+ id,
44
+ depth: 0,
45
+ locale: localeOf(scope, args.locale)
46
+ }, {
47
+ limit: args.limit ?? 10,
48
+ ...args.page === void 0 ? {} : { page: args.page },
49
+ ...args.status === void 0 ? {} : { status: args.status }
50
+ });
51
+ const versions = result.docs.map((doc) => ({
52
+ versionId: doc.id,
53
+ createdAt: doc["createdAt"],
54
+ updatedAt: doc["updatedAt"],
55
+ status: doc.version["_status"] ?? null,
56
+ latest: doc["latest"] === true,
57
+ autosave: doc["autosave"] === true,
58
+ ...doc["publishedLocale"] ? { publishedLocale: doc["publishedLocale"] } : {}
59
+ }));
60
+ return jsonResult({
61
+ versions,
62
+ totalDocs: result.totalDocs,
63
+ page: result.page,
64
+ totalPages: result.totalPages,
65
+ hasNextPage: result.hasNextPage
66
+ });
67
+ }
68
+ });
69
+ //#endregion
70
+ export { findVersions };
@@ -3,24 +3,46 @@ import { JSON_POINTER_PATTERN, findRichTextField, splitPath } from "../schema/wa
3
3
  import { lexicalOutline } from "../schema/outline.mjs";
4
4
  import { resolveDataPointer } from "../schema/pointer.mjs";
5
5
  import "../schema/index.mjs";
6
- import { depthShape, idShape, localeOf, localeShape, targetShape } from "./shared.mjs";
6
+ import { depthShape, idSchema, idShape, localeOf, localeShape, slugsFor, targetShape } from "./shared.mjs";
7
7
  import { refOf, requireIdFor, resolveTarget } from "./target.mjs";
8
8
  import { defineMcpxTool } from "../types.mjs";
9
+ import { diffDocuments, loadPublished, loadVersion, readLive } from "./versions.mjs";
10
+ import { APIError } from "payload";
9
11
  import { z } from "zod";
10
12
  import { Pointer } from "rfc6902";
11
13
  //#region src/tools/get-document.ts
12
14
  const OUTLINE_ERROR = "\"outline\" applies to a rich text field; give \"path\" for one.";
15
+ const DESCRIPTION = `Reads one document, or one subtree of it when "path" is given as a JSON pointer such as "/layout/sections/2". Returns the latest draft by default. Read before patching: the response carries "updatedAt" for expectedUpdatedAt and the indices pointers need.
16
+
17
+ Pass exactly one of "collection" and "global". "id" is required with "collection" and must be omitted with "global", because a global is a singleton.
18
+
19
+ Set "outline" on a rich text "path" to get a compact positional listing of its nodes instead of the raw editor state.
20
+
21
+ On an entity with versions, "versionId" (from findVersions) reads that version instead. "diffFrom" returns the RFC 6902 operations turning a version, or "published" (the newest version with published status, regardless of locale), into the document read, in the pointer syntax patchDocument takes and limited to "path" when given. To revert, pass the old version as "versionId" and the latest one from findVersions as "diffFrom", then apply the patch with patchDocument.`;
22
+ /** Refuses `versionId` and `diffFrom` where they cannot apply. */ const assertVersionArgs = (scope, target, args) => {
23
+ if (args.versionId === void 0 && args.diffFrom === void 0) return;
24
+ const { collections, globals } = slugsFor(scope, "versions");
25
+ if (!(target.kind === "collection" ? collections : globals).includes(target.slug)) throw new APIError(`"${target.slug}" keeps no versions.`, 400);
26
+ if (args.versionId !== void 0 && args.draft !== void 0) throw new APIError("Pass either \"versionId\" or \"draft\", not both.", 400);
27
+ if (args.diffFrom !== void 0 && args.outline) throw new APIError("\"outline\" cannot be combined with \"diffFrom\".", 400);
28
+ };
29
+ const diffResult = async (scope, read, diff, doc) => {
30
+ const from = diff.from === "published" ? await loadPublished(scope, read) : await loadVersion(scope, read, diff.from);
31
+ if (!from) return errorResult(diff.from === "published" ? `"${read.target.slug}" has no published version to diff from.` : `Version "${String(diff.from)}" not found.`);
32
+ return jsonResult({
33
+ from: from.id,
34
+ to: diff.to ?? "current",
35
+ ...diff.pointer === void 0 ? {} : { path: diff.pointer.toString() },
36
+ patch: diffDocuments(from.version, doc, diff.pointer)
37
+ });
38
+ };
13
39
  /**
14
40
  * With `path` the handler returns the subtree plus the `id`, `_status` and
15
41
  * `updatedAt` a client needs to write back, so a caller reading one branch
16
42
  * still gets the timestamp `expectedUpdatedAt` wants without a second call.
17
43
  */ const getDocument = defineMcpxTool({
18
44
  name: "getDocument",
19
- description: `Reads one document, or one subtree of it when "path" is given as a JSON pointer such as "/layout/sections/2". Returns the latest draft by default. Read before patching: the response carries "updatedAt" for expectedUpdatedAt and the indices pointers need.
20
-
21
- Pass exactly one of "collection" and "global". "id" is required with "collection" and must be omitted with "global", because a global is a singleton.
22
-
23
- Set "outline" on a rich text "path" to get a compact positional listing of its nodes instead of the raw editor state.`,
45
+ description: DESCRIPTION,
24
46
  annotations: {
25
47
  readOnlyHint: true,
26
48
  openWorldHint: false
@@ -39,42 +61,44 @@ Set "outline" on a rich text "path" to get a compact positional listing of its n
39
61
  description: "Locale to read. Defaults to the default locale."
40
62
  }),
41
63
  draft: z.boolean().optional().describe("Return the latest draft. Default true."),
42
- outline: z.boolean().optional().describe("For a rich text field, return a compact positional outline instead of the editor state. Requires \"path\".")
64
+ outline: z.boolean().optional().describe("For a rich text field, return a compact positional outline instead of the editor state. Requires \"path\"."),
65
+ versionId: idSchema.optional().describe("Version to read instead of the document, from findVersions. Only for entities with versions; not with \"draft\"."),
66
+ diffFrom: idSchema.optional().describe("Version id, or \"published\" for the newest published version in any locale, to diff from. Returns {from, to, patch} instead of the document. Only for entities with versions.")
43
67
  }),
44
68
  handler: async ({ args, scope }) => {
45
69
  const target = resolveTarget(scope, args, "read");
46
70
  const id = requireIdFor(target, args.id);
47
- const locale = localeOf(scope, args.locale);
48
- const shared = {
71
+ assertVersionArgs(scope, target, args);
72
+ let pointer;
73
+ try {
74
+ pointer = args.path ? Pointer.fromJSON(args.path) : void 0;
75
+ } catch {
76
+ return errorResult(`"${String(args.path)}" is not a valid JSON pointer.`);
77
+ }
78
+ const read = {
79
+ target,
80
+ id,
49
81
  depth: args.depth ?? 0,
50
- draft: args.draft ?? true,
51
- overrideAccess: false,
52
- req: scope.req,
53
- ...locale === void 0 ? {} : { locale }
82
+ locale: localeOf(scope, args.locale)
54
83
  };
55
- const doc = await (target.kind === "collection" ? scope.req.payload.findByID({
56
- ...shared,
57
- collection: target.slug,
58
- id
59
- }) : scope.req.payload.findGlobal({
60
- ...shared,
61
- slug: target.slug
62
- }));
63
- if (args.path === void 0 || args.path === "") {
84
+ const doc = args.versionId === void 0 ? await readLive(scope, read, { draft: args.draft ?? true }) : (await loadVersion(scope, read, args.versionId))?.version;
85
+ if (!doc) return errorResult(`Version "${String(args.versionId)}" not found.`);
86
+ if (args.diffFrom !== void 0) return await diffResult(scope, read, {
87
+ from: args.diffFrom,
88
+ to: args.versionId,
89
+ pointer
90
+ }, doc);
91
+ if (pointer === void 0) {
64
92
  if (args.outline) return errorResult(OUTLINE_ERROR);
65
93
  return jsonResult(doc);
66
94
  }
67
- let value;
68
- try {
69
- value = Pointer.fromJSON(args.path).get(doc);
70
- } catch {
71
- return errorResult(`"${args.path}" is not a valid JSON pointer.`);
72
- }
95
+ const path = pointer.toString();
96
+ const value = pointer.get(doc);
73
97
  const envelope = {
74
- ...target.kind === "collection" ? { id: doc["id"] } : { global: target.slug },
98
+ ...target.kind === "collection" ? { id } : { global: target.slug },
75
99
  status: doc["_status"],
76
100
  updatedAt: doc["updatedAt"],
77
- path: args.path
101
+ path
78
102
  };
79
103
  if (!args.outline) return jsonResult({
80
104
  ...envelope,
@@ -84,7 +108,7 @@ Set "outline" on a rich text "path" to get a compact positional listing of its n
84
108
  try {
85
109
  resolution = resolveDataPointer(scope.req.payload.config, {
86
110
  doc,
87
- pointer: args.path,
111
+ pointer: path,
88
112
  ref: refOf(target)
89
113
  });
90
114
  } catch (error) {
@@ -94,7 +118,7 @@ Set "outline" on a rich text "path" to get a compact positional listing of its n
94
118
  if (!field) return errorResult(OUTLINE_ERROR);
95
119
  return jsonResult({
96
120
  ...envelope,
97
- outline: lexicalOutline(value, args.path, field)
121
+ outline: lexicalOutline(value, path, field)
98
122
  });
99
123
  }
100
124
  });
@@ -42,6 +42,7 @@ A global is a singleton: it has no id, is not listed by findDocuments and cannot
42
42
  create: capability.write && canCreate(entry),
43
43
  publish: capability.publish,
44
44
  drafts: entry.hasDrafts,
45
+ versions: entry.hasVersions,
45
46
  draftValidation: hasDraftValidationEnabled(config),
46
47
  idType: collection.customIDType ?? payload.db.defaultIDType
47
48
  }];
@@ -59,6 +60,7 @@ A global is a singleton: it has no id, is not listed by findDocuments and cannot
59
60
  write: capability.write,
60
61
  publish: capability.publish,
61
62
  drafts: entry.hasDrafts,
63
+ versions: entry.hasVersions,
62
64
  draftValidation: hasDraftValidationEnabled(config)
63
65
  }];
64
66
  });
@@ -6,6 +6,7 @@
6
6
  "describeSchema",
7
7
  "findDocuments",
8
8
  "getDocument",
9
+ "findVersions",
9
10
  "patchDocument",
10
11
  "createDocument",
11
12
  "validateDocument",
@@ -60,6 +60,16 @@ const slugsWhere = (scope, predicate, allowed) => {
60
60
  collections: scope.readable,
61
61
  globals: scope.readableGlobals
62
62
  };
63
+ case "versions": return {
64
+ collections: slugsWhere(scope, (entity) => entity.hasVersions, {
65
+ collections: scope.readable,
66
+ globals: []
67
+ }),
68
+ globals: slugsWhere(scope, (entity) => entity.hasVersions, {
69
+ collections: [],
70
+ globals: scope.readableGlobals
71
+ })
72
+ };
63
73
  case "write": return {
64
74
  collections: scope.writable,
65
75
  globals: scope.writableGlobals
@@ -0,0 +1,119 @@
1
+ import { Pointer, createPatch } from "rfc6902";
2
+ //#region src/tools/versions.ts
3
+ /** Bookkeeping that differs between saves without saying anything about content. */ const NOISE = [
4
+ "id",
5
+ "globalType",
6
+ "createdAt",
7
+ "updatedAt",
8
+ "_status"
9
+ ];
10
+ /** Newest first, with the id breaking ties between saves in the same instant. */ const NEWEST_FIRST = ["-updatedAt", "-id"];
11
+ /** The document or global as the key's user sees it. */ const readLive = async (scope, read, options) => {
12
+ const shared = {
13
+ depth: read.depth,
14
+ draft: options.draft,
15
+ overrideAccess: false,
16
+ req: scope.req,
17
+ ...options.select === void 0 ? {} : { select: options.select },
18
+ ...read.locale === void 0 ? {} : { locale: read.locale }
19
+ };
20
+ return await (read.target.kind === "collection" ? scope.req.payload.findByID({
21
+ ...shared,
22
+ collection: read.target.slug,
23
+ id: read.id
24
+ }) : scope.req.payload.findGlobal({
25
+ ...shared,
26
+ slug: read.target.slug
27
+ }));
28
+ };
29
+ /**
30
+ * Payload checks only `readVersions` on a version read, never `read`, so the
31
+ * document itself is read first and throws when the key's user may not see it.
32
+ */ const assertReadable = async (scope, read) => {
33
+ await readLive(scope, {
34
+ ...read,
35
+ depth: 0
36
+ }, {
37
+ draft: true,
38
+ select: {}
39
+ });
40
+ };
41
+ /** `parent` comes back as a raw id or, populated, as the document itself. */ const isVersionOf = (version, id) => {
42
+ const parent = version["parent"];
43
+ const parentId = typeof parent === "object" && parent !== null ? parent["id"] : parent;
44
+ return (typeof parentId === "string" || typeof parentId === "number") && String(parentId) === String(id);
45
+ };
46
+ /**
47
+ * Reads one version, or `null` when it does not exist, is not readable, or
48
+ * belongs to another document. `findVersionByID` knows nothing of the document
49
+ * the caller asked about, so the parent check is what keeps one readable
50
+ * document from opening the history of another.
51
+ */ const loadVersion = async (scope, read, versionId) => {
52
+ await assertReadable(scope, read);
53
+ const { payload } = scope.req;
54
+ const shared = {
55
+ id: versionId,
56
+ depth: read.depth,
57
+ disableErrors: true,
58
+ overrideAccess: false,
59
+ req: scope.req,
60
+ ...read.locale === void 0 ? {} : { locale: read.locale }
61
+ };
62
+ const version = await (read.target.kind === "collection" ? payload.findVersionByID({
63
+ ...shared,
64
+ collection: read.target.slug
65
+ }) : payload.findGlobalVersionByID({
66
+ ...shared,
67
+ slug: read.target.slug
68
+ }));
69
+ if (!version) return null;
70
+ if (read.id !== void 0 && !isVersionOf(version, read.id)) return null;
71
+ return version;
72
+ };
73
+ /** One page of a document's or global's history, newest first. */ const queryVersions = async (scope, read, options) => {
74
+ await assertReadable(scope, read);
75
+ const where = {
76
+ ...read.id === void 0 ? {} : { parent: { equals: read.id } },
77
+ ...options.status === void 0 ? {} : { "version._status": { equals: options.status } }
78
+ };
79
+ const shared = {
80
+ depth: read.depth,
81
+ limit: options.limit,
82
+ sort: NEWEST_FIRST,
83
+ overrideAccess: false,
84
+ req: scope.req,
85
+ where,
86
+ ...options.page === void 0 ? {} : { page: options.page },
87
+ ...read.locale === void 0 ? {} : { locale: read.locale }
88
+ };
89
+ return await (read.target.kind === "collection" ? scope.req.payload.findVersions({
90
+ ...shared,
91
+ collection: read.target.slug
92
+ }) : scope.req.payload.findGlobalVersions({
93
+ ...shared,
94
+ slug: read.target.slug
95
+ }));
96
+ };
97
+ /** The newest version whose status is published, or `null` if none is. */ const loadPublished = async (scope, read) => {
98
+ return (await queryVersions(scope, read, {
99
+ status: "published",
100
+ limit: 1
101
+ })).docs[0] ?? null;
102
+ };
103
+ const strip = (doc) => Object.fromEntries(Object.entries(doc).filter(([key]) => !NOISE.includes(key)));
104
+ /**
105
+ * The RFC 6902 operations turning `from` into `to`, limited to the subtree at
106
+ * `pointer`. Op paths stay absolute, so they apply as `patchDocument` ops.
107
+ */ const diffDocuments = (from, to, pointer = Pointer.fromJSON("")) => {
108
+ const wrap = (doc) => {
109
+ const value = pointer.get(strip(doc));
110
+ return value === void 0 ? {} : { value };
111
+ };
112
+ const path = pointer.toString();
113
+ return createPatch(wrap(from), wrap(to)).map((op) => ({
114
+ ...op,
115
+ path: `${path}${op.path.slice(6)}`
116
+ }));
117
+ };
118
+ //#endregion
119
+ export { diffDocuments, isVersionOf, loadPublished, loadVersion, queryVersions, readLive };
package/dist/types.d.mts CHANGED
@@ -50,6 +50,8 @@ export interface McpxExposedEntity {
50
50
  read: boolean;
51
51
  write: McpxWriteMode;
52
52
  hasDrafts: boolean;
53
+ /** Payload keeps a version history, with or without drafts. */
54
+ hasVersions: boolean;
53
55
  /** An upload document is a file, and no tool here can supply one. */
54
56
  isUpload: boolean;
55
57
  /** Name of the capability group on the key document. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@abinnovision/payloadcms-mcpx",
4
- "version": "1.1.0-beta.3",
4
+ "version": "1.1.0-beta.6",
5
5
  "description": "Payload CMS plugin exposing a fixed, schema-aware MCP tool surface with draft-only writes and per-API-key capabilities.",
6
6
  "keywords": [
7
7
  "payload",
@@ -59,25 +59,25 @@
59
59
  "dependencies": {
60
60
  "@modelcontextprotocol/sdk": "^1.30.0",
61
61
  "rfc6902": "^5.3.0",
62
- "zod": "^4.5.4"
62
+ "zod": "^4.6.5"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@abinnovision/eslint-config-base": "^3.4.2",
66
66
  "@abinnovision/prettier-config": "^2.2.0",
67
67
  "@arethetypeswrong/core": "^0.18.5",
68
- "@payloadcms/db-sqlite": "3.88.0",
69
- "@payloadcms/richtext-lexical": "3.88.0",
70
- "@payloadcms/ui": "3.88.0",
68
+ "@payloadcms/db-sqlite": "3.89.0",
69
+ "@payloadcms/richtext-lexical": "3.89.0",
70
+ "@payloadcms/ui": "3.89.0",
71
71
  "@swc/core": "^1.16.2",
72
72
  "@types/node": "^26.5.1",
73
- "@types/react": "^19.2.18",
73
+ "@types/react": "^19.3.0",
74
74
  "@vitest/coverage-v8": "^5.0.0",
75
75
  "eslint": "^10.10.0",
76
- "payload": "3.88.0",
76
+ "payload": "3.89.0",
77
77
  "prettier": "^3.9.5",
78
78
  "publint": "^0.3.24",
79
- "react": "^19.2.8",
80
- "react-dom": "^19.2.8",
79
+ "react": "^19.3.0",
80
+ "react-dom": "^19.3.0",
81
81
  "tsdown": "^0.23.0",
82
82
  "typescript": "^6.0.3",
83
83
  "unplugin-swc": "^1.6.0",