@forumone/throughline-publishing 0.3.2 → 0.3.3

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @forumone/throughline-publishing
2
2
 
3
+ ## 0.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - de1d480: Fix two defects reported against 0.3.2.
8
+
9
+ **A direct unpublish was allowed once a draft version existed.** `beforeChange` compared `data._status` against `originalDoc._status` and treated a match as a harmless no-op — but `originalDoc` is the _latest version_, not the live document. After any draft save of a published page, `originalDoc._status` is `'draft'` while the page is still live, so an unpublish matched the no-op branch and went through with no pipeline, no audit event and no revalidation. The hook now asks whether the write changes what the public sees, reading the live row when `originalDoc` cannot answer. Promoting a pending draft with a direct `_status: 'published'` write is blocked too — the live status never changes there, so no status comparison could have caught it. Ordinary edits of published documents are unaffected.
10
+
11
+ **The publish button left the form showing pre-edit content.** After publishing, the button reset the form from `useDocumentInfo().data` — the document as it was when the edit view mounted — which replaced the editor's just-saved values with the pre-edit ones and read as though the publish had discarded them. The draft save already merges the server's response into form state, so the reset is removed rather than replaced.
12
+
13
+ Also adds an integration suite that exercises the hooks inside a real Payload instance against a real database, covering the full matrix of draft saves, edits, publishes and unpublishes. Every defect in this hook so far came from unit tests encoding assumptions Payload does not hold.
14
+
3
15
  ## 0.3.2
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -103,17 +103,28 @@ The plugin installs two hooks on every configured collection, and they work as a
103
103
  - `beforeOperation` records whether the update in flight is a draft write.
104
104
  - `beforeChange` rejects writes that change the **live** document's `_status`.
105
105
 
106
+ The question they answer is not "did `_status` change" but **would this write change what the public sees**:
107
+
106
108
  | Write | Result |
107
109
  |---|---|
108
- | `draft: true` on a draft or published document | **allowed** — writes a version, live status unchanged |
109
- | `draft: false`, `_status: 'published'` | **blocked** — publish through the pipeline |
110
- | `draft: false`, `_status: 'draft'` on a published document | **blocked** — unpublish through the pipeline |
110
+ | Any `draft: true` save | **allowed** — writes a version, the live document is untouched |
111
+ | A field edit of a published document with nothing pending | **allowed** — status unchanged, no draft to promote |
112
+ | A `_status: 'draft'` write to a document that was never published, or is already down | **allowed** — nothing is live |
113
+ | `_status: 'draft'` on a live document | **blocked** — unpublish through the pipeline |
114
+ | `_status: 'published'` on a draft document | **blocked** — publish through the pipeline |
115
+ | A non-draft write while a draft is pending | **blocked** — this is the publish, or an accidental unpublish |
111
116
  | `draft: true` with `_status: 'published'` | **blocked** — Payload treats this as a publish, not a draft save |
112
117
  | Any write carrying `bypassPublishingServer` | **allowed** |
113
118
 
114
- Draft saves have to be allowed explicitly because Payload sets `data._status = 'draft'` on every `draft: true` update before `beforeChange` runs, whether or not the caller supplied it — so at that point a draft save of a published document and an unpublish look identical. The `draft` flag is read from the operation's own arguments in `beforeOperation`, which sees it the same way on the Local API, REST and GraphQL. (`req.query.draft` is only populated on the REST path, so keying on the request would cover the admin and silently miss scripts.)
119
+ Three things about Payload's update pipeline make this less obvious than it looks, and each has caused a defect here:
120
+
121
+ 1. **`data` is the stored document merged with the caller's changes**, so `_status` is present on nearly every update. An ordinary field edit of a published page arrives carrying `_status: 'published'`. The presence of `_status` means nothing by itself.
122
+ 2. **Payload injects `data._status = 'draft'` into every `draft: true` update** before `beforeChange` runs, whether or not the caller supplied it — so a draft save of a published document is shaped exactly like an unpublish. The real `draft` flag is read in `beforeOperation`, which sees it identically on the Local API, REST and GraphQL. (`req.query.draft` is only populated on the REST path, so keying on the request would cover the admin and silently miss scripts.)
123
+ 3. **`originalDoc` is the latest version, not the live document.** Once a draft is pending on a published page, `originalDoc._status` is `'draft'` while the page is still live. Comparing the two statuses therefore reads a genuine unpublish as a harmless no-op.
124
+
125
+ A consequence of (1) and (3) worth knowing: once a draft is pending, a plain `payload.update(...)` with no `draft: true` would carry the pending draft's `'draft'` status and take the page down. That is blocked. Pass `draft: true` to edit the draft, or publish through the pipeline.
115
126
 
116
- If you install `createBlockStatusWritesHook` yourself without the recorder, it fails closed: every status change is blocked.
127
+ If you install `createBlockStatusWritesHook` yourself without the recorder, it fails closed: every status change is blocked. The behaviour above is verified against a real Payload instance in `block-status-writes.integration.test.ts`.
117
128
 
118
129
  ## Bypassing the pipeline
119
130
 
@@ -2,8 +2,6 @@ import React from 'react';
2
2
  export interface ThroughlinePublishButtonProps {
3
3
  /** Route prefix the plugin is mounted under. Injected via `clientProps`. */
4
4
  routePrefix?: string;
5
- /** Publish-timestamp field for this collection. Injected via `clientProps`. */
6
- publishedAtField?: string;
7
5
  }
8
6
  /**
9
7
  * Replaces Payload's Publish button on collections the publishing plugin
@@ -1 +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,CA4IxF"}
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;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAE,6BAAkC,GAAG,KAAK,CAAC,SAAS,CAyIxF"}
@@ -20,9 +20,9 @@ import { callPublishingEndpoint, describeBlock } from './publishing-client.js';
20
20
  */
21
21
  export function PublishButton(props = {}) {
22
22
  const routePrefix = props.routePrefix ?? '/publishing';
23
- const { collectionSlug, data, hasPublishedDoc, hasPublishPermission, id, setHasPublishedDoc, setMostRecentVersionIsAutosaved, setUnpublishedVersionCount, unpublishedVersionCount, uploadStatus, } = useDocumentInfo();
23
+ const { collectionSlug, hasPublishedDoc, hasPublishPermission, id, setHasPublishedDoc, setMostRecentVersionIsAutosaved, setUnpublishedVersionCount, unpublishedVersionCount, uploadStatus, } = useDocumentInfo();
24
24
  const { config } = useConfig();
25
- const { reset, submit } = useForm();
25
+ const { submit } = useForm();
26
26
  const modified = useFormModified();
27
27
  const { t } = useTranslation();
28
28
  const [publishing, setPublishing] = useState(false);
@@ -74,13 +74,14 @@ export function PublishButton(props = {}) {
74
74
  });
75
75
  return;
76
76
  }
77
- await reset({
78
- ...(data ?? {}),
79
- _status: 'published',
80
- ...(props.publishedAtField && result.body.publishedAt
81
- ? { [props.publishedAtField]: result.body.publishedAt }
82
- : {}),
83
- });
77
+ // Deliberately not resetting the form here. The draft save above
78
+ // already merged the server's response into form state, so the fields
79
+ // on screen are what was just written. Resetting from
80
+ // `useDocumentInfo().data` the document as it was when the view
81
+ // mounted would replace the editor's saved edits with the pre-edit
82
+ // values and read as though the publish had silently discarded them.
83
+ //
84
+ // The status indicators below are what actually needs updating.
84
85
  setHasPublishedDoc(true);
85
86
  setUnpublishedVersionCount(0);
86
87
  setMostRecentVersionIsAutosaved(false);
@@ -104,12 +105,9 @@ export function PublishButton(props = {}) {
104
105
  }, [
105
106
  api,
106
107
  collectionSlug,
107
- data,
108
108
  id,
109
109
  modified,
110
- props.publishedAtField,
111
110
  publishing,
112
- reset,
113
111
  routePrefix,
114
112
  serverURL,
115
113
  setHasPublishedDoc,
@@ -1 +1 @@
1
- {"version":3,"file":"PublishButton.js","sourceRoot":"","sources":["../../src/admin/PublishButton.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PublishButton.js","sourceRoot":"","sources":["../../src/admin/PublishButton.tsx"],"names":[],"mappings":"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"}
@@ -1,25 +1,38 @@
1
1
  import type { CollectionBeforeChangeHook } from 'payload';
2
2
  /**
3
3
  * The trust boundary. This `beforeChange` hook rejects every update that
4
- * changes the live document's `_status` unless the request explicitly
5
- * carries the `bypassPublishingServer` context flag (set by the pipeline's
4
+ * changes whether a document is live, unless the request explicitly carries
5
+ * the `bypassPublishingServer` context flag (set by the pipeline's
6
6
  * `executeStep`).
7
7
  *
8
8
  * Without this hook, the Payload MCP could let Claude flip a document to
9
9
  * `published` directly, sidestepping every check the publishing server
10
10
  * exists to enforce. With it, the only sanctioned path is the pipeline.
11
11
  *
12
- * Draft writes pass through. A `draft: true` update writes a new row in the
13
- * versions table and leaves the published document alone, so it changes no
14
- * live status — but Payload injects `data._status = 'draft'` into every such
15
- * update before this hook runs, which made saving a draft of a published
16
- * document indistinguishable from unpublishing it. `createRecordDraftWritesHook`
17
- * captures the operation's real `draft` flag so the two can be told apart;
18
- * both hooks must be installed together.
12
+ * Three details of Payload's update pipeline shape this:
19
13
  *
20
- * Throws `APIError` rather than `Error` so Payload returns 400 with the
21
- * message intact instead of swallowing it into a 500 and a generic
22
- * "Something went wrong" toast.
14
+ * 1. `data` is the existing document merged with the incoming changes, so
15
+ * `_status` is present on essentially every update an ordinary field
16
+ * edit on a published document arrives carrying `_status: 'published'`.
17
+ * The presence of `_status` therefore means nothing on its own.
18
+ *
19
+ * 2. Payload injects `data._status = 'draft'` into every `draft: true`
20
+ * update before this hook runs, so a draft save of a published document
21
+ * arrives looking like an unpublish. `createRecordDraftWritesHook`
22
+ * captures the operation's real `draft` flag to tell them apart; both
23
+ * hooks must be installed together.
24
+ *
25
+ * 3. `originalDoc` is the *latest version*, not the live document. Once any
26
+ * draft version exists on a published document, `originalDoc._status` is
27
+ * `'draft'` while the document is still live — so comparing against it
28
+ * reads a genuine unpublish as a harmless no-op.
29
+ *
30
+ * So the question asked here is narrow and concrete: **would this write
31
+ * change what the public sees?** A draft write never does. A non-draft
32
+ * write does when it takes a live document down, or when it puts content
33
+ * live — including promoting a pending draft, which leaves `_status` on
34
+ * `published` throughout and so cannot be caught by comparing statuses
35
+ * alone.
23
36
  */
24
37
  export declare function createBlockStatusWritesHook(): CollectionBeforeChangeHook;
25
38
  //# sourceMappingURL=block-status-writes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"block-status-writes.d.ts","sourceRoot":"","sources":["../../src/hooks/block-status-writes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAA;AAGzD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,2BAA2B,IAAI,0BAA0B,CAoCxE"}
1
+ {"version":3,"file":"block-status-writes.d.ts","sourceRoot":"","sources":["../../src/hooks/block-status-writes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAkB,MAAM,SAAS,CAAA;AAGzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,2BAA2B,IAAI,0BAA0B,CAsCxE"}
@@ -2,57 +2,116 @@ import { APIError } from 'payload';
2
2
  import { isDraftWrite } from './draft-writes.js';
3
3
  /**
4
4
  * The trust boundary. This `beforeChange` hook rejects every update that
5
- * changes the live document's `_status` unless the request explicitly
6
- * carries the `bypassPublishingServer` context flag (set by the pipeline's
5
+ * changes whether a document is live, unless the request explicitly carries
6
+ * the `bypassPublishingServer` context flag (set by the pipeline's
7
7
  * `executeStep`).
8
8
  *
9
9
  * Without this hook, the Payload MCP could let Claude flip a document to
10
10
  * `published` directly, sidestepping every check the publishing server
11
11
  * exists to enforce. With it, the only sanctioned path is the pipeline.
12
12
  *
13
- * Draft writes pass through. A `draft: true` update writes a new row in the
14
- * versions table and leaves the published document alone, so it changes no
15
- * live status — but Payload injects `data._status = 'draft'` into every such
16
- * update before this hook runs, which made saving a draft of a published
17
- * document indistinguishable from unpublishing it. `createRecordDraftWritesHook`
18
- * captures the operation's real `draft` flag so the two can be told apart;
19
- * both hooks must be installed together.
13
+ * Three details of Payload's update pipeline shape this:
20
14
  *
21
- * Throws `APIError` rather than `Error` so Payload returns 400 with the
22
- * message intact instead of swallowing it into a 500 and a generic
23
- * "Something went wrong" toast.
15
+ * 1. `data` is the existing document merged with the incoming changes, so
16
+ * `_status` is present on essentially every update an ordinary field
17
+ * edit on a published document arrives carrying `_status: 'published'`.
18
+ * The presence of `_status` therefore means nothing on its own.
19
+ *
20
+ * 2. Payload injects `data._status = 'draft'` into every `draft: true`
21
+ * update before this hook runs, so a draft save of a published document
22
+ * arrives looking like an unpublish. `createRecordDraftWritesHook`
23
+ * captures the operation's real `draft` flag to tell them apart; both
24
+ * hooks must be installed together.
25
+ *
26
+ * 3. `originalDoc` is the *latest version*, not the live document. Once any
27
+ * draft version exists on a published document, `originalDoc._status` is
28
+ * `'draft'` while the document is still live — so comparing against it
29
+ * reads a genuine unpublish as a harmless no-op.
30
+ *
31
+ * So the question asked here is narrow and concrete: **would this write
32
+ * change what the public sees?** A draft write never does. A non-draft
33
+ * write does when it takes a live document down, or when it puts content
34
+ * live — including promoting a pending draft, which leaves `_status` on
35
+ * `published` throughout and so cannot be caught by comparing statuses
36
+ * alone.
24
37
  */
25
38
  export function createBlockStatusWritesHook() {
26
- return ({ collection, data, originalDoc, operation, context, req }) => {
39
+ return async ({ collection, data, originalDoc, operation, context, req }) => {
27
40
  if (operation !== 'update')
28
41
  return data;
29
- if (!hasStatusChange(data))
30
- return data;
31
- // No-op writes (status set to its current value) are harmless.
32
- if (originalDoc &&
33
- typeof originalDoc === 'object' &&
34
- data['_status'] ===
35
- originalDoc['_status']) {
42
+ if (!carriesStatus(data))
36
43
  return data;
37
- }
38
44
  if (isBypassed(context) || isBypassed(req?.context))
39
45
  return data;
40
- // A draft save touches a version, not the live document.
46
+ const nextStatus = data['_status'];
47
+ const previousStatus = originalDoc?.['_status'];
48
+ const id = originalDoc?.id;
49
+ // A draft save writes a version and leaves the live document alone.
41
50
  //
42
- // Mirror Payload's own `isSavingDraft`, which requires the incoming
43
- // status to be something other than `published`. A request that asks for
44
- // `draft: true` while setting `_status: 'published'` is a real publish
45
- // Payload treats it as one so it stays blocked. Without this, `draft`
46
- // would be a way to publish around the pipeline.
47
- if (data['_status'] !== 'published' &&
48
- isDraftWrite(req, collection?.slug, originalDoc?.id)) {
51
+ // Mirror Payload's own `isSavingDraft`, which excludes `published`: a
52
+ // request asking for `draft: true` while setting `_status: 'published'`
53
+ // is a real publish, so `draft` cannot become a way around the pipeline.
54
+ if (nextStatus !== 'published' && isDraftWrite(req, collection?.slug, id)) {
49
55
  return data;
50
56
  }
57
+ if (nextStatus === 'published') {
58
+ // This puts content live. Allowed only when the document is already
59
+ // live *and* nothing is pending: a latest version of `published`
60
+ // means there is no newer draft for this write to promote. When a
61
+ // draft is pending, this is the publish and it belongs in the
62
+ // pipeline — even though the live status does not change.
63
+ if (previousStatus === 'published')
64
+ return data;
65
+ }
66
+ else {
67
+ // This takes the live document down. Harmless only if nothing is
68
+ // live: a document that was never published, or is already down.
69
+ const liveStatus = await resolveLiveStatus(req, collection?.slug, id, previousStatus);
70
+ if (liveStatus !== undefined && liveStatus !== 'published')
71
+ return data;
72
+ }
51
73
  throw new APIError('Direct writes to `_status` are not allowed. Use the publishing server (publish / unpublish / rollback) so the policy pipeline runs.', 400);
52
74
  };
53
75
  }
54
- function hasStatusChange(data) {
55
- return (typeof data === 'object' && data !== null && '_status' in data);
76
+ /**
77
+ * The live document's current `_status`, or `undefined` when it cannot be
78
+ * established — in which case the caller blocks the write.
79
+ *
80
+ * `originalDoc` is the latest version. A latest version of `published`
81
+ * means the live document is published, so that answer is taken directly.
82
+ * A latest version of `draft` is ambiguous — the document may still be live
83
+ * with a newer draft sitting on top of it — and only then is the published
84
+ * row read. Draft writes return before reaching this, so the admin's
85
+ * publish flow never pays for the query.
86
+ */
87
+ async function resolveLiveStatus(req, collectionSlug, id, previousStatus) {
88
+ if (previousStatus === 'published')
89
+ return 'published';
90
+ if (!req?.payload || !collectionSlug || id === undefined || id === null)
91
+ return undefined;
92
+ try {
93
+ const live = await req.payload.findByID({
94
+ collection: collectionSlug,
95
+ id: id,
96
+ depth: 0,
97
+ overrideAccess: true,
98
+ // Share the request so this reads inside the open transaction.
99
+ req,
100
+ });
101
+ const status = live?.['_status'];
102
+ return typeof status === 'string' ? status : undefined;
103
+ }
104
+ catch {
105
+ return undefined;
106
+ }
107
+ }
108
+ /**
109
+ * Whether `_status` is present at all. Payload merges the stored document
110
+ * into `data`, so this is true for nearly every update — it only filters
111
+ * out collections without drafts.
112
+ */
113
+ function carriesStatus(data) {
114
+ return typeof data === 'object' && data !== null && '_status' in data;
56
115
  }
57
116
  function isBypassed(context) {
58
117
  if (!context || typeof context !== 'object')
@@ -1 +1 @@
1
- {"version":3,"file":"block-status-writes.js","sourceRoot":"","sources":["../../src/hooks/block-status-writes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAElC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE;QACpE,IAAI,SAAS,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QACvC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QAEvC,+DAA+D;QAC/D,IACE,WAAW;YACX,OAAO,WAAW,KAAK,QAAQ;YAC9B,IAAgC,CAAC,SAAS,CAAC;gBACzC,WAAuC,CAAC,SAAS,CAAC,EACrD,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;YAAE,OAAO,IAAI,CAAA;QAEhE,yDAAyD;QACzD,EAAE;QACF,oEAAoE;QACpE,yEAAyE;QACzE,yEAAyE;QACzE,wEAAwE;QACxE,iDAAiD;QACjD,IACG,IAAgC,CAAC,SAAS,CAAC,KAAK,WAAW;YAC5D,YAAY,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAG,WAA4C,EAAE,EAAE,CAAC,EACtF,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QAED,MAAM,IAAI,QAAQ,CAChB,qIAAqI,EACrI,GAAG,CACJ,CAAA;IACH,CAAC,CAAA;AACH,CAAC;AAED,SAAS,eAAe,CAAC,IAAa;IACpC,OAAO,CACL,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI,IAAI,CAC/D,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,OAAgB;IAClC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IACzD,OAAQ,OAAmC,CAAC,wBAAwB,CAAC,KAAK,IAAI,CAAA;AAChF,CAAC"}
1
+ {"version":3,"file":"block-status-writes.js","sourceRoot":"","sources":["../../src/hooks/block-status-writes.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAA;AAQvE,OAAO,EACL,KAAK,uBAAuB,EAG7B,MAAM,cAAc,CAAA;AAqBrB,eAAO,MAAM,gBAAgB,EAAE,UAAU,CAAC,uBAAuB,CA8G9D,CAAA"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAA;AAQvE,OAAO,EAAE,KAAK,uBAAuB,EAAmB,MAAM,cAAc,CAAA;AAqB5E,eAAO,MAAM,gBAAgB,EAAE,UAAU,CAAC,uBAAuB,CA8G9D,CAAA"}
package/dist/plugin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { getPluginRegistry } from '@forumone/throughline-plugin-contract';
2
2
  import { createMcpHandler, createNamedLogger, defaultLogger, getAuditWriter, } from '@forumone/throughline-core';
3
- import { resolveCollection, validateOptions, } from './options.js';
3
+ import { validateOptions } from './options.js';
4
4
  import { createBlockStatusWritesHook } from './hooks/block-status-writes.js';
5
5
  import { createRecordDraftWritesHook } from './hooks/draft-writes.js';
6
6
  import { createAdminEndpoints } from './endpoints/admin.js';
@@ -41,7 +41,7 @@ export const publishingPlugin = (rawOptions) => (incomingConfig) => {
41
41
  ],
42
42
  },
43
43
  ...(adminComponents
44
- ? { admin: withAdminControls(collection, routePrefix, options) }
44
+ ? { admin: withAdminControls(collection, routePrefix) }
45
45
  : {}),
46
46
  };
47
47
  });
@@ -115,8 +115,7 @@ export const publishingPlugin = (rawOptions) => (incomingConfig) => {
115
115
  * Hosts must run `payload generate:importmap` after adding the plugin (the
116
116
  * dev server does it automatically) so Payload can resolve these paths.
117
117
  */
118
- function withAdminControls(collection, routePrefix, options) {
119
- const resolved = resolveCollection(options, collection.slug);
118
+ function withAdminControls(collection, routePrefix) {
120
119
  const edit = collection.admin?.components?.edit ?? {};
121
120
  return {
122
121
  ...(collection.admin ?? {}),
@@ -129,10 +128,7 @@ function withAdminControls(collection, routePrefix, options) {
129
128
  PublishButton: {
130
129
  path: CLIENT_ENTRY,
131
130
  exportName: 'PublishButton',
132
- clientProps: {
133
- routePrefix,
134
- publishedAtField: resolved.publishedAtField,
135
- },
131
+ clientProps: { routePrefix },
136
132
  },
137
133
  }
138
134
  : {}),
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forumone/throughline-publishing",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
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",
@@ -63,6 +63,7 @@
63
63
  "@forumone/throughline-plugin-contract": "0.2.1"
64
64
  },
65
65
  "devDependencies": {
66
+ "@payloadcms/db-sqlite": "^3.83.0",
66
67
  "@payloadcms/ui": "^3.83.0",
67
68
  "@types/node": "^20.17.0",
68
69
  "@types/react": "^19.2.0",