@forumone/throughline-publishing 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +127 -1
- package/dist/admin/PublishButton.d.ts +24 -0
- package/dist/admin/PublishButton.d.ts.map +1 -0
- package/dist/admin/PublishButton.js +116 -0
- package/dist/admin/PublishButton.js.map +1 -0
- package/dist/admin/UnpublishButton.d.ts +15 -0
- package/dist/admin/UnpublishButton.d.ts.map +1 -0
- package/dist/admin/UnpublishButton.js +81 -0
- package/dist/admin/UnpublishButton.js.map +1 -0
- package/dist/admin/publishing-client.d.ts +53 -0
- package/dist/admin/publishing-client.d.ts.map +1 -0
- package/dist/admin/publishing-client.js +64 -0
- package/dist/admin/publishing-client.js.map +1 -0
- package/dist/endpoints/admin.d.ts +19 -0
- package/dist/endpoints/admin.d.ts.map +1 -0
- package/dist/endpoints/admin.js +93 -0
- package/dist/endpoints/admin.js.map +1 -0
- package/dist/exports/client.d.ts +14 -0
- package/dist/exports/client.d.ts.map +1 -0
- package/dist/exports/client.js +11 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/hooks/block-status-writes.d.ts +4 -0
- package/dist/hooks/block-status-writes.d.ts.map +1 -1
- package/dist/hooks/block-status-writes.js +6 -1
- package/dist/hooks/block-status-writes.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +10 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js.map +1 -1
- package/dist/pipeline/steps/execute.d.ts +5 -0
- package/dist/pipeline/steps/execute.d.ts.map +1 -1
- package/dist/pipeline/steps/execute.js +8 -0
- package/dist/pipeline/steps/execute.js.map +1 -1
- package/dist/pipeline/types.d.ts +8 -1
- package/dist/pipeline/types.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +62 -5
- package/dist/plugin.js.map +1 -1
- package/dist/service.d.ts +109 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +225 -0
- package/dist/service.js.map +1 -0
- package/dist/tools/get-publish-status.d.ts +4 -1
- package/dist/tools/get-publish-status.d.ts.map +1 -1
- package/dist/tools/get-publish-status.js +16 -30
- package/dist/tools/get-publish-status.js.map +1 -1
- package/dist/tools/publish.d.ts +4 -1
- package/dist/tools/publish.d.ts.map +1 -1
- package/dist/tools/publish.js +8 -57
- package/dist/tools/publish.js.map +1 -1
- package/dist/tools/unpublish.d.ts +4 -1
- package/dist/tools/unpublish.d.ts.map +1 -1
- package/dist/tools/unpublish.js +8 -47
- package/dist/tools/unpublish.js.map +1 -1
- package/package.json +19 -2
package/dist/service.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { resolveCollection } from './options.js';
|
|
2
|
+
import { runPreflightPipeline, runPublishPipeline } from './pipeline/index.js';
|
|
3
|
+
const SERVICE_SYMBOL = Symbol.for('@forumone/throughline/publishing-service');
|
|
4
|
+
/**
|
|
5
|
+
* The single implementation of publish / unpublish / status. Both the MCP
|
|
6
|
+
* tools and the admin endpoints call through here, so the two channels
|
|
7
|
+
* cannot drift: same pipeline, same audit shape, same trust boundary.
|
|
8
|
+
*/
|
|
9
|
+
export function createPublishingService(deps) {
|
|
10
|
+
const { payload, options, auditWriter } = deps;
|
|
11
|
+
async function loadDocument(slug, id, actor) {
|
|
12
|
+
return (await payload.findByID({
|
|
13
|
+
collection: slug,
|
|
14
|
+
id,
|
|
15
|
+
draft: true,
|
|
16
|
+
...(actor.enforceAccessAs
|
|
17
|
+
? { user: actor.enforceAccessAs, overrideAccess: false }
|
|
18
|
+
: {}),
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
async publish(request) {
|
|
23
|
+
const collection = resolveCollection(options, request.collection);
|
|
24
|
+
const document = await loadDocument(collection.slug, request.id, request.actor);
|
|
25
|
+
const result = await runPublishPipeline({
|
|
26
|
+
payload,
|
|
27
|
+
inngest: options.inngest,
|
|
28
|
+
options,
|
|
29
|
+
collection,
|
|
30
|
+
document,
|
|
31
|
+
documentId: request.id,
|
|
32
|
+
actor: toPipelineActor(request.actor),
|
|
33
|
+
...(request.meta ? { meta: request.meta } : {}),
|
|
34
|
+
});
|
|
35
|
+
await auditWriter({
|
|
36
|
+
actor: toAuditActor(request.actor),
|
|
37
|
+
action: 'publishing.publish',
|
|
38
|
+
mcpServer: 'publishing',
|
|
39
|
+
mcpTool: toolName('publish', request.actor),
|
|
40
|
+
targetCollection: request.collection,
|
|
41
|
+
targetId: request.id,
|
|
42
|
+
targetTitle: stringField(document, 'title') ?? request.id,
|
|
43
|
+
prompt: request.meta?.userPrompt,
|
|
44
|
+
reasoning: request.meta?.reasoning,
|
|
45
|
+
changesSummary: request.meta?.changesSummary,
|
|
46
|
+
success: result.success,
|
|
47
|
+
errorMessage: result.success ? undefined : result.reason,
|
|
48
|
+
});
|
|
49
|
+
if (result.success) {
|
|
50
|
+
return {
|
|
51
|
+
published: true,
|
|
52
|
+
...(result.publishedAt ? { publishedAt: result.publishedAt } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
published: false,
|
|
57
|
+
...(result.failedAt ? { failedAt: result.failedAt } : {}),
|
|
58
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
59
|
+
...(result.code ? { code: result.code } : {}),
|
|
60
|
+
...(result.issues ? { issues: result.issues } : {}),
|
|
61
|
+
...(result.suggestion ? { suggestion: result.suggestion } : {}),
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
async unpublish(request) {
|
|
65
|
+
const collection = resolveCollection(options, request.collection);
|
|
66
|
+
const document = await loadDocument(collection.slug, request.id, request.actor);
|
|
67
|
+
if (document['_status'] !== 'published') {
|
|
68
|
+
return { unpublished: false, reason: 'Document is not currently published' };
|
|
69
|
+
}
|
|
70
|
+
await payload.update({
|
|
71
|
+
collection: collection.slug,
|
|
72
|
+
id: request.id,
|
|
73
|
+
data: { _status: 'draft' },
|
|
74
|
+
...(request.actor.enforceAccessAs
|
|
75
|
+
? { user: request.actor.enforceAccessAs, overrideAccess: false }
|
|
76
|
+
: {}),
|
|
77
|
+
context: { bypassPublishingServer: true },
|
|
78
|
+
});
|
|
79
|
+
const slug = document[collection.slugField];
|
|
80
|
+
await options.inngest.send({
|
|
81
|
+
name: 'content/page.unpublished',
|
|
82
|
+
data: {
|
|
83
|
+
collection: collection.slug,
|
|
84
|
+
id: request.id,
|
|
85
|
+
slug: typeof slug === 'string' ? slug : request.id,
|
|
86
|
+
unpublishedBy: request.actor.user?.id ?? 'system',
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
await auditWriter({
|
|
90
|
+
actor: toAuditActor(request.actor),
|
|
91
|
+
action: 'publishing.unpublish',
|
|
92
|
+
mcpServer: 'publishing',
|
|
93
|
+
mcpTool: toolName('unpublish', request.actor),
|
|
94
|
+
targetCollection: request.collection,
|
|
95
|
+
targetId: request.id,
|
|
96
|
+
targetTitle: stringField(document, 'title') ?? request.id,
|
|
97
|
+
prompt: request.meta?.userPrompt,
|
|
98
|
+
reasoning: request.meta?.reasoning,
|
|
99
|
+
changesSummary: request.meta?.changesSummary,
|
|
100
|
+
success: true,
|
|
101
|
+
});
|
|
102
|
+
return { unpublished: true };
|
|
103
|
+
},
|
|
104
|
+
async getStatus(request) {
|
|
105
|
+
const collection = resolveCollection(options, request.collection);
|
|
106
|
+
const document = await loadDocument(collection.slug, request.id, request.actor);
|
|
107
|
+
const result = await runPreflightPipeline({
|
|
108
|
+
payload,
|
|
109
|
+
inngest: options.inngest,
|
|
110
|
+
options,
|
|
111
|
+
collection,
|
|
112
|
+
document,
|
|
113
|
+
documentId: request.id,
|
|
114
|
+
actor: toPipelineActor(request.actor),
|
|
115
|
+
});
|
|
116
|
+
const updatedAt = stringField(document, 'updatedAt');
|
|
117
|
+
const publishedAt = stringField(document, collection.publishedAtField);
|
|
118
|
+
return {
|
|
119
|
+
status: stringField(document, '_status') ?? 'draft',
|
|
120
|
+
publishable: result.success,
|
|
121
|
+
...(result.failedAt ? { failedAt: result.failedAt } : {}),
|
|
122
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
123
|
+
...(result.code ? { code: result.code } : {}),
|
|
124
|
+
...(result.issues ? { issues: result.issues } : {}),
|
|
125
|
+
...(result.suggestion ? { suggestion: result.suggestion } : {}),
|
|
126
|
+
publishedAt: publishedAt ?? null,
|
|
127
|
+
hasUnpublishedChanges: Boolean(updatedAt && publishedAt && Date.parse(updatedAt) > Date.parse(publishedAt)),
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function toPipelineActor(actor) {
|
|
133
|
+
return {
|
|
134
|
+
user: actor.user ?? null,
|
|
135
|
+
apiKeyName: actor.apiKeyName ?? '',
|
|
136
|
+
...(actor.enforceAccessAs ? { enforceAccessAs: actor.enforceAccessAs } : {}),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Attribution: a call carrying a real user is a user action, whoever
|
|
141
|
+
* transported it. An admin publish records the editor, not a service key.
|
|
142
|
+
*/
|
|
143
|
+
function toAuditActor(actor) {
|
|
144
|
+
return {
|
|
145
|
+
type: actor.user ? 'user' : 'system',
|
|
146
|
+
userId: actor.user?.id,
|
|
147
|
+
userName: actor.user?.name,
|
|
148
|
+
apiKeyName: actor.apiKeyName,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function toolName(tool, actor) {
|
|
152
|
+
return actor.channel === 'admin' ? `admin:${tool}` : tool;
|
|
153
|
+
}
|
|
154
|
+
function stringField(doc, name) {
|
|
155
|
+
const value = doc[name];
|
|
156
|
+
return typeof value === 'string' ? value : undefined;
|
|
157
|
+
}
|
|
158
|
+
/** Attaches the service to the Payload instance so host code can find it. */
|
|
159
|
+
export function attachPublishingService(payload, service) {
|
|
160
|
+
Object.defineProperty(payload, SERVICE_SYMBOL, {
|
|
161
|
+
value: service,
|
|
162
|
+
enumerable: false,
|
|
163
|
+
writable: false,
|
|
164
|
+
configurable: true,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Returns the publishing service attached to this Payload instance.
|
|
169
|
+
*
|
|
170
|
+
* Use this (or the `publishDocument` / `unpublishDocument` /
|
|
171
|
+
* `getPublishStatus` helpers below) from host code — a custom endpoint, a
|
|
172
|
+
* job, a Server Action — to publish through the full policy pipeline as a
|
|
173
|
+
* given user. No API key, and the audit event records that user.
|
|
174
|
+
*/
|
|
175
|
+
export function getPublishingService(payload) {
|
|
176
|
+
const service = payload[SERVICE_SYMBOL];
|
|
177
|
+
if (!service) {
|
|
178
|
+
throw new Error('Publishing service not found on this Payload instance. Add publishingPlugin() to your Payload config — the service is attached during onInit.');
|
|
179
|
+
}
|
|
180
|
+
return service;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Publishes a document through the full pipeline as `user`. Rejects if that
|
|
184
|
+
* user lacks update access on the collection.
|
|
185
|
+
*/
|
|
186
|
+
export function publishDocument(args) {
|
|
187
|
+
return getPublishingService(args.payload).publish(toRequest(args));
|
|
188
|
+
}
|
|
189
|
+
/** Reverts a published document to draft as `user`. */
|
|
190
|
+
export function unpublishDocument(args) {
|
|
191
|
+
return getPublishingService(args.payload).unpublish(toRequest(args));
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Runs every check except the write and reports whether the document would
|
|
195
|
+
* publish. Mutates nothing.
|
|
196
|
+
*/
|
|
197
|
+
export function getPublishStatus(args) {
|
|
198
|
+
return getPublishingService(args.payload).getStatus(toRequest(args));
|
|
199
|
+
}
|
|
200
|
+
function toRequest(args) {
|
|
201
|
+
return {
|
|
202
|
+
collection: args.collection,
|
|
203
|
+
id: args.id,
|
|
204
|
+
actor: {
|
|
205
|
+
user: toAuthenticatedUser(args.user),
|
|
206
|
+
enforceAccessAs: args.user,
|
|
207
|
+
channel: 'admin',
|
|
208
|
+
},
|
|
209
|
+
...(args.meta ? { meta: args.meta } : {}),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/** Narrows a Payload user document to the audit-log actor shape. */
|
|
213
|
+
export function toAuthenticatedUser(user) {
|
|
214
|
+
if (!user)
|
|
215
|
+
return null;
|
|
216
|
+
const raw = user;
|
|
217
|
+
return {
|
|
218
|
+
id: String(raw['id']),
|
|
219
|
+
email: String(raw['email'] ?? ''),
|
|
220
|
+
name: String(raw['name'] ?? raw['email'] ?? raw['id']),
|
|
221
|
+
roles: Array.isArray(raw['roles']) ? raw['roles'] : [],
|
|
222
|
+
groups: Array.isArray(raw['groups']) ? raw['groups'] : [],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { Payload } from 'payload';
|
|
2
2
|
import type { McpToolDefinition } from '@forumone/throughline-plugin-contract';
|
|
3
|
-
import {
|
|
3
|
+
import type { PublishingPluginOptions } from '../options.js';
|
|
4
|
+
import { type PublishingService } from '../service.js';
|
|
4
5
|
export interface GetPublishStatusToolDeps {
|
|
5
6
|
payload: Payload;
|
|
6
7
|
options: PublishingPluginOptions;
|
|
8
|
+
/** Injected by the plugin so every channel shares one service instance. */
|
|
9
|
+
service?: PublishingService;
|
|
7
10
|
}
|
|
8
11
|
export declare function createGetPublishStatusTool(deps: GetPublishStatusToolDeps): McpToolDefinition;
|
|
9
12
|
//# sourceMappingURL=get-publish-status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-publish-status.d.ts","sourceRoot":"","sources":["../../src/tools/get-publish-status.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAC9E,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"get-publish-status.d.ts","sourceRoot":"","sources":["../../src/tools/get-publish-status.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAC9E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAE/E,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,uBAAuB,CAAA;IAChC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAC5B;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,wBAAwB,GAAG,iBAAiB,CAyC5F"}
|
|
@@ -1,51 +1,37 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { withMeta } from '@forumone/throughline-core';
|
|
3
|
-
import {
|
|
4
|
-
import { runPreflightPipeline } from '../pipeline/index.js';
|
|
3
|
+
import { createPublishingService } from '../service.js';
|
|
5
4
|
export function createGetPublishStatusTool(deps) {
|
|
6
5
|
const inputSchema = withMeta({
|
|
7
6
|
collection: z.string(),
|
|
8
7
|
id: z.string(),
|
|
9
8
|
});
|
|
9
|
+
const service = deps.service ??
|
|
10
|
+
createPublishingService({ ...deps, auditWriter: async () => { } });
|
|
10
11
|
return {
|
|
11
12
|
name: 'get_publish_status',
|
|
12
13
|
description: "Returns the current publishability of a document without actually publishing. Reports current status, whether unpublished changes exist, the last publish timestamp, and a preflight result indicating whether `publish` would currently succeed. Read-only; no audit record is written.",
|
|
13
14
|
inputSchema,
|
|
14
15
|
handler: async (input, ctx) => {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
collection: collection.slug,
|
|
16
|
+
const status = await service.getStatus({
|
|
17
|
+
collection: input.collection,
|
|
18
18
|
id: input.id,
|
|
19
|
-
|
|
20
|
-
}));
|
|
21
|
-
const preflight = await runPreflightPipeline({
|
|
22
|
-
payload: deps.payload,
|
|
23
|
-
inngest: deps.options.inngest,
|
|
24
|
-
options: deps.options,
|
|
25
|
-
collection,
|
|
26
|
-
document,
|
|
27
|
-
documentId: input.id,
|
|
28
|
-
actor: { user: ctx.user, apiKeyName: ctx.apiKeyName },
|
|
19
|
+
actor: { user: ctx.user, apiKeyName: ctx.apiKeyName, channel: 'mcp' },
|
|
29
20
|
});
|
|
30
|
-
const updatedAt = document['updatedAt'];
|
|
31
|
-
const publishedAt = document[collection.publishedAtField];
|
|
32
|
-
const hasUnpublishedChanges = typeof updatedAt === 'string' &&
|
|
33
|
-
typeof publishedAt === 'string' &&
|
|
34
|
-
Date.parse(updatedAt) > Date.parse(publishedAt);
|
|
35
21
|
return {
|
|
36
|
-
currentStatus:
|
|
37
|
-
hasUnpublishedChanges,
|
|
38
|
-
lastPublished:
|
|
22
|
+
currentStatus: status.status,
|
|
23
|
+
hasUnpublishedChanges: status.hasUnpublishedChanges,
|
|
24
|
+
lastPublished: status.publishedAt,
|
|
39
25
|
wouldPublish: {
|
|
40
|
-
canPublish:
|
|
41
|
-
...(
|
|
26
|
+
canPublish: status.publishable,
|
|
27
|
+
...(status.publishable
|
|
42
28
|
? {}
|
|
43
29
|
: {
|
|
44
|
-
blockedAt:
|
|
45
|
-
code:
|
|
46
|
-
reason:
|
|
47
|
-
suggestion:
|
|
48
|
-
issues:
|
|
30
|
+
blockedAt: status.failedAt,
|
|
31
|
+
code: status.code,
|
|
32
|
+
reason: status.reason,
|
|
33
|
+
suggestion: status.suggestion,
|
|
34
|
+
issues: status.issues ?? [],
|
|
49
35
|
}),
|
|
50
36
|
},
|
|
51
37
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-publish-status.js","sourceRoot":"","sources":["../../src/tools/get-publish-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"get-publish-status.js","sourceRoot":"","sources":["../../src/tools/get-publish-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAGrD,OAAO,EAAE,uBAAuB,EAA0B,MAAM,eAAe,CAAA;AAS/E,MAAM,UAAU,0BAA0B,CAAC,IAA8B;IACvE,MAAM,WAAW,GAAG,QAAQ,CAAC;QAC3B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;KACf,CAAC,CAAA;IAEF,MAAM,OAAO,GACX,IAAI,CAAC,OAAO;QACZ,uBAAuB,CAAC,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,CAAC,CAAA;IAEnE,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EACT,0RAA0R;QAC5R,WAAW;QACX,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC5B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC;gBACrC,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE;aACtE,CAAC,CAAA;YAEF,OAAO;gBACL,aAAa,EAAE,MAAM,CAAC,MAAM;gBAC5B,qBAAqB,EAAE,MAAM,CAAC,qBAAqB;gBACnD,aAAa,EAAE,MAAM,CAAC,WAAW;gBACjC,YAAY,EAAE;oBACZ,UAAU,EAAE,MAAM,CAAC,WAAW;oBAC9B,GAAG,CAAC,MAAM,CAAC,WAAW;wBACpB,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC;4BACE,SAAS,EAAE,MAAM,CAAC,QAAQ;4BAC1B,IAAI,EAAE,MAAM,CAAC,IAAI;4BACjB,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,UAAU,EAAE,MAAM,CAAC,UAAU;4BAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;yBAC5B,CAAC;iBACP;aACF,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/tools/publish.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { Payload } from 'payload';
|
|
2
2
|
import { type AuditWriter } from '@forumone/throughline-core';
|
|
3
3
|
import type { McpToolDefinition } from '@forumone/throughline-plugin-contract';
|
|
4
|
-
import {
|
|
4
|
+
import type { PublishingPluginOptions } from '../options.js';
|
|
5
|
+
import { type PublishingService } from '../service.js';
|
|
5
6
|
export interface PublishToolDeps {
|
|
6
7
|
payload: Payload;
|
|
7
8
|
options: PublishingPluginOptions;
|
|
8
9
|
auditWriter: AuditWriter;
|
|
10
|
+
/** Injected by the plugin so every channel shares one service instance. */
|
|
11
|
+
service?: PublishingService;
|
|
9
12
|
}
|
|
10
13
|
export declare function createPublishTool(deps: PublishToolDeps): McpToolDefinition;
|
|
11
14
|
//# sourceMappingURL=publish.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/tools/publish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,KAAK,WAAW,EAAY,MAAM,4BAA4B,CAAA;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAC9E,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/tools/publish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,KAAK,WAAW,EAAY,MAAM,4BAA4B,CAAA;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAC9E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAE/E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,uBAAuB,CAAA;IAChC,WAAW,EAAE,WAAW,CAAA;IACxB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAC5B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAqB1E"}
|
package/dist/tools/publish.js
CHANGED
|
@@ -1,71 +1,22 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { withMeta } from '@forumone/throughline-core';
|
|
3
|
-
import {
|
|
4
|
-
import { runPublishPipeline } from '../pipeline/index.js';
|
|
3
|
+
import { createPublishingService } from '../service.js';
|
|
5
4
|
export function createPublishTool(deps) {
|
|
6
5
|
const inputSchema = withMeta({
|
|
7
6
|
collection: z.string().describe('The collection slug (e.g. "pages")'),
|
|
8
7
|
id: z.string().describe('The document ID'),
|
|
9
8
|
});
|
|
9
|
+
const service = deps.service ?? createPublishingService(deps);
|
|
10
10
|
return {
|
|
11
11
|
name: 'publish',
|
|
12
12
|
description: "Publishes a draft document. Runs the full publish pipeline: composition, accessibility, required-field, embargo, and approval checks. Returns success with the publish timestamp, or a specific failedAt step with reason / suggestion when something blocks the publish.",
|
|
13
13
|
inputSchema,
|
|
14
|
-
handler: async (input, ctx) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}));
|
|
21
|
-
const result = await runPublishPipeline({
|
|
22
|
-
payload: deps.payload,
|
|
23
|
-
inngest: deps.options.inngest,
|
|
24
|
-
options: deps.options,
|
|
25
|
-
collection,
|
|
26
|
-
document,
|
|
27
|
-
documentId: input.id,
|
|
28
|
-
actor: { user: ctx.user, apiKeyName: ctx.apiKeyName },
|
|
29
|
-
...(input._meta ? { meta: input._meta } : {}),
|
|
30
|
-
});
|
|
31
|
-
await deps.auditWriter({
|
|
32
|
-
actor: {
|
|
33
|
-
type: ctx.user ? 'user' : 'system',
|
|
34
|
-
userId: ctx.user?.id,
|
|
35
|
-
userName: ctx.user?.name,
|
|
36
|
-
apiKeyName: ctx.apiKeyName,
|
|
37
|
-
},
|
|
38
|
-
action: 'publishing.publish',
|
|
39
|
-
mcpServer: 'publishing',
|
|
40
|
-
mcpTool: 'publish',
|
|
41
|
-
targetCollection: input.collection,
|
|
42
|
-
targetId: input.id,
|
|
43
|
-
targetTitle: stringField(document, 'title') ?? input.id,
|
|
44
|
-
prompt: input._meta?.userPrompt,
|
|
45
|
-
reasoning: input._meta?.reasoning,
|
|
46
|
-
changesSummary: input._meta?.changesSummary,
|
|
47
|
-
success: result.success,
|
|
48
|
-
errorMessage: result.success ? undefined : result.reason,
|
|
49
|
-
});
|
|
50
|
-
if (result.success) {
|
|
51
|
-
return {
|
|
52
|
-
published: true,
|
|
53
|
-
publishedAt: result.publishedAt,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
published: false,
|
|
58
|
-
failedAt: result.failedAt,
|
|
59
|
-
reason: result.reason,
|
|
60
|
-
code: result.code,
|
|
61
|
-
issues: result.issues,
|
|
62
|
-
suggestion: result.suggestion,
|
|
63
|
-
};
|
|
64
|
-
},
|
|
14
|
+
handler: async (input, ctx) => service.publish({
|
|
15
|
+
collection: input.collection,
|
|
16
|
+
id: input.id,
|
|
17
|
+
actor: { user: ctx.user, apiKeyName: ctx.apiKeyName, channel: 'mcp' },
|
|
18
|
+
...(input._meta ? { meta: input._meta } : {}),
|
|
19
|
+
}),
|
|
65
20
|
};
|
|
66
21
|
}
|
|
67
|
-
function stringField(doc, name) {
|
|
68
|
-
const value = doc[name];
|
|
69
|
-
return typeof value === 'string' ? value : undefined;
|
|
70
|
-
}
|
|
71
22
|
//# sourceMappingURL=publish.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/tools/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAoB,QAAQ,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/tools/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAoB,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAGvE,OAAO,EAAE,uBAAuB,EAA0B,MAAM,eAAe,CAAA;AAU/E,MAAM,UAAU,iBAAiB,CAAC,IAAqB;IACrD,MAAM,WAAW,GAAG,QAAQ,CAAC;QAC3B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;QACrE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;KAC3C,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAA;IAE7D,OAAO;QACL,IAAI,EAAE,SAAS;QACf,WAAW,EACT,2QAA2Q;QAC7Q,WAAW;QACX,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAC5B,OAAO,CAAC,OAAO,CAAC;YACd,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE;YACrE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9C,CAAC;KACL,CAAA;AACH,CAAC"}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { Payload } from 'payload';
|
|
2
2
|
import { type AuditWriter } from '@forumone/throughline-core';
|
|
3
3
|
import type { McpToolDefinition } from '@forumone/throughline-plugin-contract';
|
|
4
|
-
import {
|
|
4
|
+
import type { PublishingPluginOptions } from '../options.js';
|
|
5
|
+
import { type PublishingService } from '../service.js';
|
|
5
6
|
export interface UnpublishToolDeps {
|
|
6
7
|
payload: Payload;
|
|
7
8
|
options: PublishingPluginOptions;
|
|
8
9
|
auditWriter: AuditWriter;
|
|
10
|
+
/** Injected by the plugin so every channel shares one service instance. */
|
|
11
|
+
service?: PublishingService;
|
|
9
12
|
}
|
|
10
13
|
export declare function createUnpublishTool(deps: UnpublishToolDeps): McpToolDefinition;
|
|
11
14
|
//# sourceMappingURL=unpublish.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unpublish.d.ts","sourceRoot":"","sources":["../../src/tools/unpublish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,KAAK,WAAW,EAAY,MAAM,4BAA4B,CAAA;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAC9E,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"unpublish.d.ts","sourceRoot":"","sources":["../../src/tools/unpublish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,KAAK,WAAW,EAAY,MAAM,4BAA4B,CAAA;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAC9E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAE/E,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,uBAAuB,CAAA;IAChC,WAAW,EAAE,WAAW,CAAA;IACxB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAC5B;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,CAqB9E"}
|
package/dist/tools/unpublish.js
CHANGED
|
@@ -1,61 +1,22 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { withMeta } from '@forumone/throughline-core';
|
|
3
|
-
import {
|
|
3
|
+
import { createPublishingService } from '../service.js';
|
|
4
4
|
export function createUnpublishTool(deps) {
|
|
5
5
|
const inputSchema = withMeta({
|
|
6
6
|
collection: z.string(),
|
|
7
7
|
id: z.string(),
|
|
8
8
|
});
|
|
9
|
+
const service = deps.service ?? createPublishingService(deps);
|
|
9
10
|
return {
|
|
10
11
|
name: 'unpublish',
|
|
11
12
|
description: 'Unpublishes a published document by reverting it to draft. Use when content needs to be removed from the public site without deleting it. Fires content/page.unpublished so revalidation and integrations can react.',
|
|
12
13
|
inputSchema,
|
|
13
|
-
handler: async (input, ctx) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}));
|
|
20
|
-
if (document['_status'] !== 'published') {
|
|
21
|
-
return { unpublished: false, reason: 'Document is not currently published' };
|
|
22
|
-
}
|
|
23
|
-
await deps.payload.update({
|
|
24
|
-
collection: collection.slug,
|
|
25
|
-
id: input.id,
|
|
26
|
-
data: { _status: 'draft' },
|
|
27
|
-
context: { bypassPublishingServer: true },
|
|
28
|
-
});
|
|
29
|
-
const slug = document[collection.slugField];
|
|
30
|
-
await deps.options.inngest.send({
|
|
31
|
-
name: 'content/page.unpublished',
|
|
32
|
-
data: {
|
|
33
|
-
collection: collection.slug,
|
|
34
|
-
id: input.id,
|
|
35
|
-
slug: typeof slug === 'string' ? slug : input.id,
|
|
36
|
-
unpublishedBy: ctx.user?.id ?? 'system',
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
await deps.auditWriter({
|
|
40
|
-
actor: {
|
|
41
|
-
type: ctx.user ? 'user' : 'system',
|
|
42
|
-
userId: ctx.user?.id,
|
|
43
|
-
userName: ctx.user?.name,
|
|
44
|
-
apiKeyName: ctx.apiKeyName,
|
|
45
|
-
},
|
|
46
|
-
action: 'publishing.unpublish',
|
|
47
|
-
mcpServer: 'publishing',
|
|
48
|
-
mcpTool: 'unpublish',
|
|
49
|
-
targetCollection: input.collection,
|
|
50
|
-
targetId: input.id,
|
|
51
|
-
targetTitle: typeof document['title'] === 'string' ? document['title'] : input.id,
|
|
52
|
-
prompt: input._meta?.userPrompt,
|
|
53
|
-
reasoning: input._meta?.reasoning,
|
|
54
|
-
changesSummary: input._meta?.changesSummary,
|
|
55
|
-
success: true,
|
|
56
|
-
});
|
|
57
|
-
return { unpublished: true };
|
|
58
|
-
},
|
|
14
|
+
handler: async (input, ctx) => service.unpublish({
|
|
15
|
+
collection: input.collection,
|
|
16
|
+
id: input.id,
|
|
17
|
+
actor: { user: ctx.user, apiKeyName: ctx.apiKeyName, channel: 'mcp' },
|
|
18
|
+
...(input._meta ? { meta: input._meta } : {}),
|
|
19
|
+
}),
|
|
59
20
|
};
|
|
60
21
|
}
|
|
61
22
|
//# sourceMappingURL=unpublish.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unpublish.js","sourceRoot":"","sources":["../../src/tools/unpublish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAoB,QAAQ,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"unpublish.js","sourceRoot":"","sources":["../../src/tools/unpublish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAoB,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAGvE,OAAO,EAAE,uBAAuB,EAA0B,MAAM,eAAe,CAAA;AAU/E,MAAM,UAAU,mBAAmB,CAAC,IAAuB;IACzD,MAAM,WAAW,GAAG,QAAQ,CAAC;QAC3B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;KACf,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAA;IAE7D,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,WAAW,EACT,sNAAsN;QACxN,WAAW;QACX,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAC5B,OAAO,CAAC,SAAS,CAAC;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE;YACrE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9C,CAAC;KACL,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forumone/throughline-publishing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Policy-gated publishing server for Throughline. Wraps Payload's update operation with composition, accessibility, required-field, embargo, and approval gating.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"./checks": {
|
|
14
14
|
"types": "./dist/checks/index.d.ts",
|
|
15
15
|
"default": "./dist/checks/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./client": {
|
|
18
|
+
"types": "./dist/exports/client.d.ts",
|
|
19
|
+
"default": "./dist/exports/client.js"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"files": [
|
|
@@ -40,8 +44,18 @@
|
|
|
40
44
|
"access": "public"
|
|
41
45
|
},
|
|
42
46
|
"peerDependencies": {
|
|
47
|
+
"@payloadcms/ui": "^3.0.0",
|
|
48
|
+
"inngest": "^4.0.0",
|
|
43
49
|
"payload": "^3.0.0",
|
|
44
|
-
"
|
|
50
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"@payloadcms/ui": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"react": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
45
59
|
},
|
|
46
60
|
"dependencies": {
|
|
47
61
|
"zod": "^3.23.0",
|
|
@@ -49,10 +63,13 @@
|
|
|
49
63
|
"@forumone/throughline-plugin-contract": "0.2.1"
|
|
50
64
|
},
|
|
51
65
|
"devDependencies": {
|
|
66
|
+
"@payloadcms/ui": "^3.83.0",
|
|
52
67
|
"@types/node": "^20.17.0",
|
|
68
|
+
"@types/react": "^19.2.0",
|
|
53
69
|
"eslint": "^9.15.0",
|
|
54
70
|
"inngest": "^4.2.0",
|
|
55
71
|
"payload": "^3.83.0",
|
|
72
|
+
"react": "^19.2.0",
|
|
56
73
|
"typescript": "^5.6.0",
|
|
57
74
|
"vitest": "^2.1.0",
|
|
58
75
|
"@forumone/throughline-eslint-config": "0.0.0",
|