@forumone/throughline-publishing 0.2.2 → 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.
Files changed (59) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +127 -1
  3. package/dist/admin/PublishButton.d.ts +24 -0
  4. package/dist/admin/PublishButton.d.ts.map +1 -0
  5. package/dist/admin/PublishButton.js +116 -0
  6. package/dist/admin/PublishButton.js.map +1 -0
  7. package/dist/admin/UnpublishButton.d.ts +15 -0
  8. package/dist/admin/UnpublishButton.d.ts.map +1 -0
  9. package/dist/admin/UnpublishButton.js +81 -0
  10. package/dist/admin/UnpublishButton.js.map +1 -0
  11. package/dist/admin/publishing-client.d.ts +53 -0
  12. package/dist/admin/publishing-client.d.ts.map +1 -0
  13. package/dist/admin/publishing-client.js +64 -0
  14. package/dist/admin/publishing-client.js.map +1 -0
  15. package/dist/endpoints/admin.d.ts +19 -0
  16. package/dist/endpoints/admin.d.ts.map +1 -0
  17. package/dist/endpoints/admin.js +93 -0
  18. package/dist/endpoints/admin.js.map +1 -0
  19. package/dist/exports/client.d.ts +14 -0
  20. package/dist/exports/client.d.ts.map +1 -0
  21. package/dist/exports/client.js +11 -0
  22. package/dist/exports/client.js.map +1 -0
  23. package/dist/hooks/block-status-writes.d.ts +4 -0
  24. package/dist/hooks/block-status-writes.d.ts.map +1 -1
  25. package/dist/hooks/block-status-writes.js +6 -1
  26. package/dist/hooks/block-status-writes.js.map +1 -1
  27. package/dist/index.d.ts +7 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +6 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/options.d.ts +10 -0
  32. package/dist/options.d.ts.map +1 -1
  33. package/dist/options.js.map +1 -1
  34. package/dist/pipeline/steps/execute.d.ts +5 -0
  35. package/dist/pipeline/steps/execute.d.ts.map +1 -1
  36. package/dist/pipeline/steps/execute.js +8 -0
  37. package/dist/pipeline/steps/execute.js.map +1 -1
  38. package/dist/pipeline/types.d.ts +8 -1
  39. package/dist/pipeline/types.d.ts.map +1 -1
  40. package/dist/plugin.d.ts.map +1 -1
  41. package/dist/plugin.js +62 -5
  42. package/dist/plugin.js.map +1 -1
  43. package/dist/service.d.ts +109 -0
  44. package/dist/service.d.ts.map +1 -0
  45. package/dist/service.js +225 -0
  46. package/dist/service.js.map +1 -0
  47. package/dist/tools/get-publish-status.d.ts +4 -1
  48. package/dist/tools/get-publish-status.d.ts.map +1 -1
  49. package/dist/tools/get-publish-status.js +16 -30
  50. package/dist/tools/get-publish-status.js.map +1 -1
  51. package/dist/tools/publish.d.ts +4 -1
  52. package/dist/tools/publish.d.ts.map +1 -1
  53. package/dist/tools/publish.js +8 -57
  54. package/dist/tools/publish.js.map +1 -1
  55. package/dist/tools/unpublish.d.ts +4 -1
  56. package/dist/tools/unpublish.d.ts.map +1 -1
  57. package/dist/tools/unpublish.js +8 -47
  58. package/dist/tools/unpublish.js.map +1 -1
  59. package/package.json +21 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # @forumone/throughline-publishing
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 422b970: Ship the admin Publish/Unpublish controls and a server-side publishing API.
8
+
9
+ `publishingPlugin` blocked direct writes to `_status` — which is what Payload's native Publish button submits — but shipped no replacement, so no document in a publishable collection could be published from the admin. The pipeline was reachable only over the MCP endpoint, which requires an API key.
10
+ - **Admin controls.** The plugin now installs its own `PublishButton` and `UnpublishButton` on every configured collection, exported from `@forumone/throughline-publishing/client`. They run the pipeline as the logged-in editor over the session cookie — no API key, correct audit attribution — and render the failing step, its issues and its suggestion instead of a generic error. Hosts must run `payload generate:importmap`. Opt out with `adminComponents: false`; an explicit host-set slot is never overwritten.
11
+ - **Server-side API.** `publishDocument`, `unpublishDocument`, `getPublishStatus` and `getPublishingService` are now public, for host code that needs to publish outside the admin.
12
+ - **Audit attribution.** Publishes are attributed to the user who made them rather than to the API key that transported the call. Admin publishes record `mcpTool: 'admin:publish'`.
13
+ - **Access control.** Admin and host-API publishes run the underlying write with `overrideAccess: false` as that user, so bypassing the status hook does not bypass collection permissions.
14
+ - **Diagnosis.** The status-write hook now throws `APIError(…, 400)` rather than `Error`, so the message reaches the admin instead of becoming a 500 and a generic "Something went wrong" toast.
15
+
16
+ The MCP tools now route through the same service as the admin path, so the two channels cannot drift. Their input and output shapes are unchanged.
17
+
18
+ ## 0.2.3
19
+
20
+ ### Patch Changes
21
+
22
+ - 7ee992d: Fix broken external installs of the core plugins.
23
+
24
+ Every core plugin emits a runtime `import { getPluginRegistry } from '@forumone/throughline-plugin-contract'`, but `plugin-contract` was marked `private` and never published — so the published plugins pinned `@forumone/throughline-plugin-contract: 0.0.0`, a version that does not exist on npm, and any external `pnpm install` failed with a 404.
25
+
26
+ `plugin-contract` is now published, so the dependent plugins re-pin a real version. The cross-plugin registry is keyed on a global `Symbol.for(...)` and stored on the Payload instance, so behavior is unchanged.
27
+
28
+ Also fixes the scaffolder, which pinned `@forumone/throughline-reference-ds@^0.1.0` (latest is `0.2.0`) in the generated `apps/web` and `design-system` packages.
29
+
30
+ - Updated dependencies [7ee992d]
31
+ - @forumone/throughline-plugin-contract@0.2.1
32
+ - @forumone/throughline-core@0.2.2
33
+
3
34
  ## 0.2.2
4
35
 
5
36
  ### Patch Changes
package/README.md CHANGED
@@ -2,4 +2,130 @@
2
2
 
3
3
  Policy-gated publishing server for Throughline. The trust boundary that decides what's allowed to ship.
4
4
 
5
- _Authoring guide and full API reference land in a later commit._
5
+ ## What this package provides
6
+
7
+ - **A publish pipeline** — `exist` → `composition` → `accessibility` → `required-fields` → `embargo` → `approval` → `execute`. The first step to object stops the publish and reports which one, why, and what to do about it.
8
+ - **A trust boundary** — a `beforeChange` hook on every configured collection that rejects direct writes to `_status`. The pipeline is the only sanctioned way to publish.
9
+ - **Admin controls** — Publish and Unpublish buttons that run the pipeline as the logged-in editor. Installed automatically; no host-side code.
10
+ - **A server-side API** — `publishDocument` / `unpublishDocument` / `getPublishStatus` for host code that needs to publish outside the admin.
11
+ - **Five MCP tools** served at `/api/publishing/mcp`: `publish`, `unpublish`, `schedule_publish`, `get_publish_status`, `rollback`.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pnpm add @forumone/throughline-publishing
17
+ ```
18
+
19
+ Peers: `payload@^3.0.0`, `inngest@^4.0.0`. Required runtime peer: `@forumone/throughline-core` (audit log). The admin controls additionally use `@payloadcms/ui` and `react`, both already present in any Payload 3 admin.
20
+
21
+ ## Usage
22
+
23
+ ```ts
24
+ import { buildConfig } from 'payload'
25
+ import { auditPlugin, createInngestClient } from '@forumone/throughline-core'
26
+ import { publishingPlugin } from '@forumone/throughline-publishing'
27
+
28
+ const inngest = createInngestClient({ id: 'my-site' })
29
+
30
+ export default buildConfig({
31
+ // collections, db, secret...
32
+ plugins: [
33
+ auditPlugin({ inngest }),
34
+ publishingPlugin({
35
+ inngest,
36
+ collections: [{ slug: 'pages' }],
37
+ }),
38
+ ],
39
+ })
40
+ ```
41
+
42
+ Publishable collections must have drafts enabled (`versions: { drafts: true }`).
43
+
44
+ ## Publishing from the admin
45
+
46
+ The plugin replaces Payload's native Publish and Unpublish buttons on every configured collection. It has to: the native buttons submit `_status: 'published'` straight to the update endpoint, which is exactly the write the trust boundary exists to reject.
47
+
48
+ The replacements save pending edits as a draft, then call the plugin's own endpoint, which runs the full pipeline. **Run `payload generate:importmap` after adding the plugin** so Payload can resolve the components — the dev server does this for you; CI builds need it explicitly.
49
+
50
+ What you get:
51
+
52
+ - **No API key in the editorial publish path.** The endpoint authenticates off the Payload session cookie.
53
+ - **The person is the actor.** The audit event records the logged-in editor, with `mcpTool` set to `admin:publish` so admin publishes are distinguishable from MCP ones.
54
+ - **Access control still applies.** The write runs with `overrideAccess: false` as that user. Bypassing the status hook is not bypassing permissions.
55
+ - **Real feedback.** A blocked publish renders the failing step, its issues, and its suggestion — not a generic error.
56
+
57
+ The Publish button is hidden on the create view: the pipeline's first step is `exist`, so there is nothing to evaluate until the draft is saved. Use Payload's Save Draft button, then publish from the edit view.
58
+
59
+ To supply your own controls instead, either set `admin.components.edit.PublishButton` on the collection yourself (an explicit host setting always wins), or turn the feature off entirely:
60
+
61
+ ```ts
62
+ publishingPlugin({ inngest, collections: [{ slug: 'pages' }], adminComponents: false })
63
+ ```
64
+
65
+ With `adminComponents: false` the admin has **no** working publish path until you supply one — the native buttons will still be rejected by the hook.
66
+
67
+ ### Endpoints
68
+
69
+ | Route | Auth | Body |
70
+ |---|---|---|
71
+ | `POST /api/publishing/publish` | Payload session | `{ collection, id }` |
72
+ | `POST /api/publishing/unpublish` | Payload session | `{ collection, id }` |
73
+ | `POST /api/publishing/mcp` | Bearer API key | JSON-RPC |
74
+
75
+ A publish blocked by the pipeline returns **200** with `{ published: false, failedAt, reason, code, issues, suggestion }`. The pipeline ran correctly and the answer was no; that is not a transport error. Non-2xx is reserved for auth (401/403), bad input (400), and genuine failures (500).
76
+
77
+ ## Publishing from host code
78
+
79
+ `runPublishPipeline` needs plugin options and an audit writer that only exist inside the plugin. Rather than export the raw pipeline, the plugin attaches a service to the Payload instance at `onInit` and exposes these helpers:
80
+
81
+ ```ts
82
+ import { publishDocument, unpublishDocument, getPublishStatus } from '@forumone/throughline-publishing'
83
+
84
+ // In a custom endpoint, job, or Server Action:
85
+ const result = await publishDocument({
86
+ payload: req.payload,
87
+ collection: 'pages',
88
+ id: '42',
89
+ user: req.user, // attributed in the audit log; their permissions apply
90
+ })
91
+
92
+ if (!result.published) {
93
+ console.log(result.failedAt, result.reason, result.suggestion)
94
+ }
95
+ ```
96
+
97
+ All three run the same pipeline the admin and MCP paths use, so host code cannot drift from the plugin's policy. `getPublishStatus` runs every check except the write and mutates nothing.
98
+
99
+ ## Bypassing the pipeline
100
+
101
+ `bypassPublishingServer: true` in the Payload request context is the only way to write `_status` without the pipeline. It is intended for seed scripts and migrations:
102
+
103
+ ```ts
104
+ await payload.update({
105
+ collection: 'pages',
106
+ id,
107
+ data: { _status: 'published' },
108
+ context: { bypassPublishingServer: true },
109
+ })
110
+ ```
111
+
112
+ It skips composition, accessibility, required-field, embargo, and approval checks. Nothing in the admin path uses it.
113
+
114
+ > **Note:** the hook guards `update` only. `payload.create({ data: { _status: 'published' } })` is not intercepted, so a create can publish without the pipeline. Save as a draft and publish in a second step if that matters to you.
115
+
116
+ ## Custom accessibility checks
117
+
118
+ The built-in checks (alt text, heading hierarchy, link labels) are exported from `@forumone/throughline-publishing/checks`. Add your own via `accessibilityChecks`:
119
+
120
+ ```ts
121
+ publishingPlugin({
122
+ inngest,
123
+ collections: [{ slug: 'pages' }],
124
+ accessibilityChecks: [
125
+ {
126
+ name: 'no-empty-tables',
127
+ run: (doc) => (hasEmptyTable(doc) ? [{ message: 'Table has no rows', severity: 'error' }] : []),
128
+ },
129
+ ],
130
+ })
131
+ ```
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ export interface ThroughlinePublishButtonProps {
3
+ /** Route prefix the plugin is mounted under. Injected via `clientProps`. */
4
+ routePrefix?: string;
5
+ /** Publish-timestamp field for this collection. Injected via `clientProps`. */
6
+ publishedAtField?: string;
7
+ }
8
+ /**
9
+ * Replaces Payload's Publish button on collections the publishing plugin
10
+ * governs.
11
+ *
12
+ * The native button submits `_status: 'published'` straight to the update
13
+ * endpoint, which the plugin's trust boundary rejects by design. This one
14
+ * saves pending edits as a draft, then asks the publishing server to run the
15
+ * pipeline — as the logged-in editor, over the session cookie, with no API
16
+ * key. A block renders the failing step, its issues and its suggestion
17
+ * instead of a generic error.
18
+ *
19
+ * On the create view this renders nothing: the pipeline's first step is
20
+ * `exist`, so there is nothing to evaluate until the draft is saved.
21
+ * Payload's own Save Draft button covers that step.
22
+ */
23
+ export declare function PublishButton(props?: ThroughlinePublishButtonProps): React.ReactNode;
24
+ //# sourceMappingURL=PublishButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublishButton.d.ts","sourceRoot":"","sources":["../../src/admin/PublishButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAgC,MAAM,OAAO,CAAA;AAYpD,MAAM,WAAW,6BAA6B;IAC5C,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAE,6BAAkC,GAAG,KAAK,CAAC,SAAS,CA2HxF"}
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useCallback, useState } from 'react';
4
+ import { FormSubmit, toast, useConfig, useDocumentInfo, useForm, useFormModified, useTranslation, } from '@payloadcms/ui';
5
+ import { callPublishingEndpoint, describeBlock } from './publishing-client.js';
6
+ /**
7
+ * Replaces Payload's Publish button on collections the publishing plugin
8
+ * governs.
9
+ *
10
+ * The native button submits `_status: 'published'` straight to the update
11
+ * endpoint, which the plugin's trust boundary rejects by design. This one
12
+ * saves pending edits as a draft, then asks the publishing server to run the
13
+ * pipeline — as the logged-in editor, over the session cookie, with no API
14
+ * key. A block renders the failing step, its issues and its suggestion
15
+ * instead of a generic error.
16
+ *
17
+ * On the create view this renders nothing: the pipeline's first step is
18
+ * `exist`, so there is nothing to evaluate until the draft is saved.
19
+ * Payload's own Save Draft button covers that step.
20
+ */
21
+ export function PublishButton(props = {}) {
22
+ const routePrefix = props.routePrefix ?? '/publishing';
23
+ const { collectionSlug, data, hasPublishedDoc, hasPublishPermission, id, setHasPublishedDoc, setMostRecentVersionIsAutosaved, setUnpublishedVersionCount, unpublishedVersionCount, uploadStatus, } = useDocumentInfo();
24
+ const { config } = useConfig();
25
+ const { reset, submit } = useForm();
26
+ const modified = useFormModified();
27
+ const { t } = useTranslation();
28
+ const [publishing, setPublishing] = useState(false);
29
+ const { api } = config.routes;
30
+ const serverURL = config.serverURL ?? '';
31
+ const publish = useCallback(async () => {
32
+ if (id === undefined || id === null || !collectionSlug || publishing)
33
+ return;
34
+ setPublishing(true);
35
+ try {
36
+ // Persist pending edits first. Draft writes never touch `_status`, so
37
+ // the trust boundary lets them through; the pipeline then evaluates
38
+ // what was actually saved rather than what is on screen.
39
+ if (modified) {
40
+ const saved = await submit({
41
+ action: `${serverURL}${api}/${collectionSlug}/${id}?draft=true&depth=0`,
42
+ method: 'PATCH',
43
+ overrides: { _status: 'draft' },
44
+ skipValidation: true,
45
+ });
46
+ // `submit` returns void and toasts its own error on most failures,
47
+ // but falls through with a non-ok response when the body carries no
48
+ // message. Check both so a failed save never reaches the pipeline.
49
+ if (!saved || !saved.res.ok)
50
+ return;
51
+ }
52
+ const result = await callPublishingEndpoint({
53
+ serverURL,
54
+ apiRoute: api,
55
+ routePrefix,
56
+ action: 'publish',
57
+ collection: collectionSlug,
58
+ id,
59
+ });
60
+ if (!result.ok) {
61
+ toast.error(result.message);
62
+ return;
63
+ }
64
+ if (!result.body.published) {
65
+ const { title, description } = describeBlock(result.body);
66
+ toast.error(title, {
67
+ ...(description ? { description } : {}),
68
+ duration: 10_000,
69
+ });
70
+ return;
71
+ }
72
+ await reset({
73
+ ...(data ?? {}),
74
+ _status: 'published',
75
+ ...(props.publishedAtField && result.body.publishedAt
76
+ ? { [props.publishedAtField]: result.body.publishedAt }
77
+ : {}),
78
+ });
79
+ setHasPublishedDoc(true);
80
+ setUnpublishedVersionCount(0);
81
+ setMostRecentVersionIsAutosaved(false);
82
+ toast.success(t('version:published'));
83
+ }
84
+ finally {
85
+ setPublishing(false);
86
+ }
87
+ }, [
88
+ api,
89
+ collectionSlug,
90
+ data,
91
+ id,
92
+ modified,
93
+ props.publishedAtField,
94
+ publishing,
95
+ reset,
96
+ routePrefix,
97
+ serverURL,
98
+ setHasPublishedDoc,
99
+ setMostRecentVersionIsAutosaved,
100
+ setUnpublishedVersionCount,
101
+ submit,
102
+ t,
103
+ ]);
104
+ if (!hasPublishPermission)
105
+ return null;
106
+ // Create view — no document for the pipeline to evaluate yet.
107
+ if (id === undefined || id === null || !collectionSlug)
108
+ return null;
109
+ const canPublish = (modified || unpublishedVersionCount > 0 || !hasPublishedDoc) &&
110
+ uploadStatus !== 'uploading' &&
111
+ !publishing;
112
+ return (_jsx(FormSubmit, { buttonId: "action-save", disabled: !canPublish, onClick: () => {
113
+ void publish();
114
+ }, size: "medium", type: "button", children: publishing ? t('version:publishing') : t('version:publishChanges') }));
115
+ }
116
+ //# sourceMappingURL=PublishButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublishButton.js","sourceRoot":"","sources":["../../src/admin/PublishButton.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACpD,OAAO,EACL,UAAU,EACV,KAAK,EACL,SAAS,EACT,eAAe,EACf,OAAO,EACP,eAAe,EACf,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAS9E;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAAC,QAAuC,EAAE;IACrE,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,aAAa,CAAA;IAEtD,MAAM,EACJ,cAAc,EACd,IAAI,EACJ,eAAe,EACf,oBAAoB,EACpB,EAAE,EACF,kBAAkB,EAClB,+BAA+B,EAC/B,0BAA0B,EAC1B,uBAAuB,EACvB,YAAY,GACb,GAAG,eAAe,EAAE,CAAA;IAErB,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAA;IAC9B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAA;IACnC,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAA;IAClC,MAAM,EAAE,CAAC,EAAE,GAAG,cAAc,EAAE,CAAA;IAC9B,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEnD,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,CAAA;IAC7B,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,EAAE,CAAA;IAExC,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,cAAc,IAAI,UAAU;YAAE,OAAM;QAC5E,aAAa,CAAC,IAAI,CAAC,CAAA;QAEnB,IAAI,CAAC;YACH,sEAAsE;YACtE,oEAAoE;YACpE,yDAAyD;YACzD,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC;oBACzB,MAAM,EAAE,GAAG,SAAS,GAAG,GAAG,IAAI,cAAc,IAAI,EAAE,qBAAqB;oBACvE,MAAM,EAAE,OAAO;oBACf,SAAS,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE;oBAC/B,cAAc,EAAE,IAAI;iBACrB,CAAC,CAAA;gBACF,mEAAmE;gBACnE,oEAAoE;gBACpE,mEAAmE;gBACnE,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;oBAAE,OAAM;YACrC,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC;gBAC1C,SAAS;gBACT,QAAQ,EAAE,GAAG;gBACb,WAAW;gBACX,MAAM,EAAE,SAAS;gBACjB,UAAU,EAAE,cAAc;gBAC1B,EAAE;aACH,CAAC,CAAA;YAEF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;gBAC3B,OAAM;YACR,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC3B,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACzD,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE;oBACjB,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACvC,QAAQ,EAAE,MAAM;iBACjB,CAAC,CAAA;gBACF,OAAM;YACR,CAAC;YAED,MAAM,KAAK,CAAC;gBACV,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;gBACf,OAAO,EAAE,WAAW;gBACpB,GAAG,CAAC,KAAK,CAAC,gBAAgB,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW;oBACnD,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;oBACvD,CAAC,CAAC,EAAE,CAAC;aACR,CAAC,CAAA;YACF,kBAAkB,CAAC,IAAI,CAAC,CAAA;YACxB,0BAA0B,CAAC,CAAC,CAAC,CAAA;YAC7B,+BAA+B,CAAC,KAAK,CAAC,CAAA;YACtC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAA;QACvC,CAAC;gBAAS,CAAC;YACT,aAAa,CAAC,KAAK,CAAC,CAAA;QACtB,CAAC;IACH,CAAC,EAAE;QACD,GAAG;QACH,cAAc;QACd,IAAI;QACJ,EAAE;QACF,QAAQ;QACR,KAAK,CAAC,gBAAgB;QACtB,UAAU;QACV,KAAK;QACL,WAAW;QACX,SAAS;QACT,kBAAkB;QAClB,+BAA+B;QAC/B,0BAA0B;QAC1B,MAAM;QACN,CAAC;KACF,CAAC,CAAA;IAEF,IAAI,CAAC,oBAAoB;QAAE,OAAO,IAAI,CAAA;IACtC,8DAA8D;IAC9D,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,cAAc;QAAE,OAAO,IAAI,CAAA;IAEnE,MAAM,UAAU,GACd,CAAC,QAAQ,IAAI,uBAAuB,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC;QAC7D,YAAY,KAAK,WAAW;QAC5B,CAAC,UAAU,CAAA;IAEb,OAAO,CACL,KAAC,UAAU,IACT,QAAQ,EAAC,aAAa,EACtB,QAAQ,EAAE,CAAC,UAAU,EACrB,OAAO,EAAE,GAAG,EAAE;YACZ,KAAK,OAAO,EAAE,CAAA;QAChB,CAAC,EACD,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,QAAQ,YAEZ,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB,CAAC,GACxD,CACd,CAAA;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ export interface ThroughlineUnpublishButtonProps {
3
+ /** Route prefix the plugin is mounted under. Injected via `clientProps`. */
4
+ routePrefix?: string;
5
+ }
6
+ /**
7
+ * Replaces Payload's Unpublish control on collections the publishing plugin
8
+ * governs. Same reasoning as the Publish button: the native control writes
9
+ * `_status: 'draft'` directly, which the trust boundary rejects. This one
10
+ * routes through the publishing server so the unpublish is audited against
11
+ * the logged-in editor and `content/page.unpublished` fires for
12
+ * revalidation.
13
+ */
14
+ export declare function UnpublishButton(props?: ThroughlineUnpublishButtonProps): React.ReactNode;
15
+ //# sourceMappingURL=UnpublishButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnpublishButton.d.ts","sourceRoot":"","sources":["../../src/admin/UnpublishButton.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAgC,MAAM,OAAO,CAAA;AAapD,MAAM,WAAW,+BAA+B;IAC9C,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,KAAK,GAAE,+BAAoC,GAC1C,KAAK,CAAC,SAAS,CAmGjB"}
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React, { useCallback, useState } from 'react';
4
+ import { ConfirmationModal, PopupList, toast, useConfig, useDocumentInfo, useForm, useModal, useTranslation, } from '@payloadcms/ui';
5
+ import { callPublishingEndpoint } from './publishing-client.js';
6
+ /**
7
+ * Replaces Payload's Unpublish control on collections the publishing plugin
8
+ * governs. Same reasoning as the Publish button: the native control writes
9
+ * `_status: 'draft'` directly, which the trust boundary rejects. This one
10
+ * routes through the publishing server so the unpublish is audited against
11
+ * the logged-in editor and `content/page.unpublished` fires for
12
+ * revalidation.
13
+ */
14
+ export function UnpublishButton(props = {}) {
15
+ const routePrefix = props.routePrefix ?? '/publishing';
16
+ const { collectionSlug, data, hasPublishedDoc, hasPublishPermission, id, incrementVersionCount, isTrashed, setHasPublishedDoc, setMostRecentVersionIsAutosaved, setUnpublishedVersionCount, } = useDocumentInfo();
17
+ const { config } = useConfig();
18
+ const { reset } = useForm();
19
+ const { t } = useTranslation();
20
+ const { toggleModal } = useModal();
21
+ const [unpublishing, setUnpublishing] = useState(false);
22
+ const modalSlug = `throughline-confirm-unpublish-${id}`;
23
+ const { api } = config.routes;
24
+ const serverURL = config.serverURL ?? '';
25
+ const unpublish = useCallback(async () => {
26
+ if (id === undefined || id === null || !collectionSlug || unpublishing)
27
+ return;
28
+ setUnpublishing(true);
29
+ try {
30
+ const result = await callPublishingEndpoint({
31
+ serverURL,
32
+ apiRoute: api,
33
+ routePrefix,
34
+ action: 'unpublish',
35
+ collection: collectionSlug,
36
+ id,
37
+ });
38
+ if (!result.ok) {
39
+ toast.error(result.message);
40
+ return;
41
+ }
42
+ if (!result.body.unpublished) {
43
+ toast.error(result.body.reason ?? t('error:unPublishingDocument'));
44
+ return;
45
+ }
46
+ await reset({ ...(data ?? {}), _status: 'draft' });
47
+ incrementVersionCount();
48
+ setUnpublishedVersionCount(1);
49
+ setMostRecentVersionIsAutosaved(false);
50
+ setHasPublishedDoc(false);
51
+ toast.success(t('version:unpublishedSuccessfully'));
52
+ }
53
+ finally {
54
+ setUnpublishing(false);
55
+ }
56
+ }, [
57
+ api,
58
+ collectionSlug,
59
+ data,
60
+ id,
61
+ incrementVersionCount,
62
+ reset,
63
+ routePrefix,
64
+ serverURL,
65
+ setHasPublishedDoc,
66
+ setMostRecentVersionIsAutosaved,
67
+ setUnpublishedVersionCount,
68
+ t,
69
+ unpublishing,
70
+ ]);
71
+ if (!hasPublishPermission || !hasPublishedDoc || isTrashed)
72
+ return null;
73
+ if (id === undefined || id === null || !collectionSlug)
74
+ return null;
75
+ return (_jsxs(React.Fragment, { children: [_jsx(PopupList.Button, { id: "action-unpublish", onClick: () => {
76
+ toggleModal(modalSlug);
77
+ }, children: t('version:unpublish') }), _jsx(ConfirmationModal, { body: t('version:aboutToUnpublish'), confirmingLabel: t('version:unpublishing'), heading: t('version:confirmUnpublish'), modalSlug: modalSlug, onConfirm: () => {
78
+ void unpublish();
79
+ } })] }));
80
+ }
81
+ //# sourceMappingURL=UnpublishButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnpublishButton.js","sourceRoot":"","sources":["../../src/admin/UnpublishButton.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Client-side helpers shared by the admin Publish and Unpublish controls.
3
+ * No JSX here so the message formatting can be unit tested directly.
4
+ */
5
+ export interface PublishingIssue {
6
+ field?: string;
7
+ message: string;
8
+ severity?: 'error' | 'warning';
9
+ rule?: string;
10
+ }
11
+ export interface PublishingResponse {
12
+ published?: boolean;
13
+ unpublished?: boolean;
14
+ publishedAt?: string;
15
+ failedAt?: string;
16
+ reason?: string;
17
+ code?: string;
18
+ issues?: PublishingIssue[];
19
+ suggestion?: string;
20
+ error?: string;
21
+ }
22
+ export interface CallPublishingEndpointArgs {
23
+ serverURL: string;
24
+ apiRoute: string;
25
+ routePrefix: string;
26
+ action: 'publish' | 'unpublish';
27
+ collection: string;
28
+ id: number | string;
29
+ }
30
+ export type PublishingCallResult = {
31
+ ok: true;
32
+ body: PublishingResponse;
33
+ } | {
34
+ ok: false;
35
+ message: string;
36
+ };
37
+ /**
38
+ * POSTs to the plugin's admin endpoint using the browser's session cookie.
39
+ * Returns a transport-level failure as `ok: false`; a pipeline block comes
40
+ * back as `ok: true` with a `published: false` body.
41
+ */
42
+ export declare function callPublishingEndpoint(args: CallPublishingEndpointArgs): Promise<PublishingCallResult>;
43
+ /**
44
+ * Turns a pipeline block into something an editor can act on: which step
45
+ * said no, what it objected to, and what to do about it. This is the
46
+ * information the pipeline already returns — it just never had a way to
47
+ * reach the screen.
48
+ */
49
+ export declare function describeBlock(body: PublishingResponse): {
50
+ title: string;
51
+ description: string;
52
+ };
53
+ //# sourceMappingURL=publishing-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publishing-client.d.ts","sourceRoot":"","sources":["../../src/admin/publishing-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,eAAe,EAAE,CAAA;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,SAAS,GAAG,WAAW,CAAA;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CACpB;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,kBAAkB,CAAA;CAAE,GACtC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAElC;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CA8B/B;AAID;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,kBAAkB,GAAG;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB,CAqBA"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Client-side helpers shared by the admin Publish and Unpublish controls.
3
+ * No JSX here so the message formatting can be unit tested directly.
4
+ */
5
+ /**
6
+ * POSTs to the plugin's admin endpoint using the browser's session cookie.
7
+ * Returns a transport-level failure as `ok: false`; a pipeline block comes
8
+ * back as `ok: true` with a `published: false` body.
9
+ */
10
+ export async function callPublishingEndpoint(args) {
11
+ const url = `${args.serverURL}${args.apiRoute}${args.routePrefix}/${args.action}`;
12
+ let response;
13
+ try {
14
+ response = await fetch(url, {
15
+ method: 'POST',
16
+ credentials: 'include',
17
+ headers: { 'content-type': 'application/json' },
18
+ body: JSON.stringify({ collection: args.collection, id: args.id }),
19
+ });
20
+ }
21
+ catch {
22
+ return { ok: false, message: 'Could not reach the publishing server.' };
23
+ }
24
+ let body;
25
+ try {
26
+ body = (await response.json());
27
+ }
28
+ catch {
29
+ return { ok: false, message: `Publishing server returned ${response.status}.` };
30
+ }
31
+ if (!response.ok) {
32
+ return {
33
+ ok: false,
34
+ message: body.error ?? `Publishing server returned ${response.status}.`,
35
+ };
36
+ }
37
+ return { ok: true, body };
38
+ }
39
+ const MAX_LISTED_ISSUES = 5;
40
+ /**
41
+ * Turns a pipeline block into something an editor can act on: which step
42
+ * said no, what it objected to, and what to do about it. This is the
43
+ * information the pipeline already returns — it just never had a way to
44
+ * reach the screen.
45
+ */
46
+ export function describeBlock(body) {
47
+ const title = body.reason ?? `Publish blocked at the ${body.failedAt ?? 'policy'} check.`;
48
+ const lines = [];
49
+ if (body.failedAt && body.reason) {
50
+ lines.push(`Blocked at: ${body.failedAt}`);
51
+ }
52
+ const issues = body.issues ?? [];
53
+ for (const issue of issues.slice(0, MAX_LISTED_ISSUES)) {
54
+ lines.push(`• ${issue.field ? `${issue.field}: ` : ''}${issue.message}`);
55
+ }
56
+ if (issues.length > MAX_LISTED_ISSUES) {
57
+ lines.push(`• …and ${issues.length - MAX_LISTED_ISSUES} more`);
58
+ }
59
+ if (body.suggestion) {
60
+ lines.push(`Suggestion: ${body.suggestion}`);
61
+ }
62
+ return { title, description: lines.join('\n') };
63
+ }
64
+ //# sourceMappingURL=publishing-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publishing-client.js","sourceRoot":"","sources":["../../src/admin/publishing-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkCH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAgC;IAEhC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,EAAE,CAAA;IAEjF,IAAI,QAAkB,CAAA;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC1B,MAAM,EAAE,MAAM;YACd,WAAW,EAAE,SAAS;YACtB,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;SACnE,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,wCAAwC,EAAE,CAAA;IACzE,CAAC;IAED,IAAI,IAAwB,CAAA;IAC5B,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAA;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,8BAA8B,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAA;IACjF,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,IAAI,CAAC,KAAK,IAAI,8BAA8B,QAAQ,CAAC,MAAM,GAAG;SACxE,CAAA;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;AAC3B,CAAC;AAED,MAAM,iBAAiB,GAAG,CAAC,CAAA;AAE3B;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAwB;IAIpD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,0BAA0B,IAAI,CAAC,QAAQ,IAAI,QAAQ,SAAS,CAAA;IAEzF,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAA;IAChC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,EAAE,CAAC;QACvD,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;IAC1E,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,MAAM,GAAG,iBAAiB,OAAO,CAAC,CAAA;IAChE,CAAC;IAED,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;IAC9C,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;AACjD,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type Endpoint } from 'payload';
2
+ export interface CreateAdminEndpointsDeps {
3
+ /** Route prefix the plugin is mounted under, e.g. `/publishing`. */
4
+ routePrefix: string;
5
+ /** Collections registered as publishable; anything else is rejected. */
6
+ publishableSlugs: Set<string>;
7
+ }
8
+ /**
9
+ * The admin's path into the pipeline.
10
+ *
11
+ * These endpoints authenticate off the Payload session cookie, so `req.user`
12
+ * is the logged-in editor — no API key in the editorial publish path, and
13
+ * the audit event records the person rather than a service principal. The
14
+ * write still goes through the pipeline, and still runs with
15
+ * `overrideAccess: false`, so neither the policy checks nor the collection's
16
+ * access control are skipped.
17
+ */
18
+ export declare function createAdminEndpoints(deps: CreateAdminEndpointsDeps): Endpoint[];
19
+ //# sourceMappingURL=admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/endpoints/admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAuB,MAAM,SAAS,CAAA;AAStE,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAA;IACnB,wEAAwE;IACxE,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,wBAAwB,GAAG,QAAQ,EAAE,CAa/E"}