@forumone/throughline-publishing 0.4.0 → 0.5.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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # @forumone/throughline-publishing
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 43c0636: Show a blocked publish on the fields that blocked it, and stop announcing the draft save
8
+
9
+ Every publish diagnostic arrived as a toast in the corner, which left an editor reading `layout.7.image` and counting blocks. The pipeline's issues have always named their fields; they are now dispatched into form state, so the field carries the message and the collapsed block row containing it carries an error count. An issue with no field — an embargo, a missing approval — stays in the toast, which still lists everything.
10
+
11
+ Only fields the form actually has are marked, and an issue that names something deeper (a populated relationship's image, a block index) marks the nearest field that owns it. Payload's reducer creates a field state entry for any path it is handed, so an invented path would become invented data on the next save.
12
+
13
+ A field the collection itself refuses is now a failed step rather than a thrown error: `failedAt: 'execute'`, `code: 'field-validation-failed'`, with Payload's field paths as `issues`. The publishing write is the first step that enforces `required` — a draft write deliberately does not — so an empty required field inside a block is caught there and nowhere earlier. `publishDocument` and the `publish` MCP tool now return that as a result instead of throwing.
14
+
15
+ The interim draft save the Publish button performs no longer shows its own success toast. It is a step inside publishing rather than something the editor asked for, and its notice used to land on top of the publish one. Payload's own Save Draft button is untouched.
16
+
3
17
  ## 0.4.0
4
18
 
5
19
  ### Minor Changes
package/README.md CHANGED
@@ -52,7 +52,8 @@ What you get:
52
52
  - **No API key in the editorial publish path.** The endpoint authenticates off the Payload session cookie.
53
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
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.
55
+ - **Real feedback, on the field that caused it.** A blocked publish renders the failing step, its issues, and its suggestion — and every issue naming a field is marked on that field, with an error count on the collapsed block row containing it. An issue with no field (an embargo, a missing approval) stays in the toast, which is where the full list still appears.
56
+ - **One notice per action.** The interim draft save the button performs does not announce itself; publishing says "published" once.
56
57
 
57
58
  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
 
@@ -74,6 +75,8 @@ With `adminComponents: false` the admin has **no** working publish path until yo
74
75
 
75
76
  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
 
78
+ A field the collection itself refuses is one of those blocks — `failedAt: 'execute'`, `code: 'field-validation-failed'`, with Payload's own field paths as `issues`. The publishing write is the first step that enforces `required`, because a draft write deliberately does not, so an empty required field inside a block is caught there and nowhere earlier.
79
+
77
80
  ## Publishing from host code
78
81
 
79
82
  `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:
@@ -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;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAE,6BAAkC,GAAG,KAAK,CAAC,SAAS,CAyIxF"}
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,CA2KxF"}
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useCallback, useState } from 'react';
4
4
  import { FormSubmit, toast, useConfig, useDocumentInfo, useForm, useFormModified, useTranslation, } from '@payloadcms/ui';
5
- import { callPublishingEndpoint, describeBlock } from './publishing-client.js';
5
+ import { callPublishingEndpoint, describeBlock, fieldErrorsFromBlock } from './publishing-client.js';
6
6
  /**
7
7
  * Replaces Payload's Publish button on collections the publishing plugin
8
8
  * governs.
@@ -22,7 +22,7 @@ export function PublishButton(props = {}) {
22
22
  const routePrefix = props.routePrefix ?? '/publishing';
23
23
  const { collectionSlug, hasPublishedDoc, hasPublishPermission, id, setHasPublishedDoc, setMostRecentVersionIsAutosaved, setUnpublishedVersionCount, unpublishedVersionCount, uploadStatus, } = useDocumentInfo();
24
24
  const { config } = useConfig();
25
- const { submit } = useForm();
25
+ const { dispatchFields, getFields, setIsValid, setSubmitted, submit } = useForm();
26
26
  const modified = useFormModified();
27
27
  const { t } = useTranslation();
28
28
  const [publishing, setPublishing] = useState(false);
@@ -47,6 +47,12 @@ export function PublishButton(props = {}) {
47
47
  method: 'PATCH',
48
48
  overrides: { _status: 'draft' },
49
49
  skipValidation: true,
50
+ // No toast for this write. It is a step inside publishing, not
51
+ // something the editor asked for, and its success toast used to
52
+ // land on top of the publish one — two overlapping notices for one
53
+ // action, the first of which announced the lesser outcome.
54
+ // Payload's own Save Draft button is untouched and still toasts.
55
+ disableSuccessStatus: true,
50
56
  });
51
57
  // `submit` returns void and toasts its own error on most failures,
52
58
  // but falls through with a non-ok response when the body carries no
@@ -67,7 +73,30 @@ export function PublishButton(props = {}) {
67
73
  return;
68
74
  }
69
75
  if (!result.body.published) {
70
- const { title, description } = describeBlock(result.body);
76
+ /*
77
+ The pipeline's issues already name their fields; until now they only
78
+ reached a toast, which left the editor reading `layout.7.image` and
79
+ counting blocks. Dispatched into form state they render where the
80
+ problem is — on the field, and as an error count on the collapsed
81
+ block row containing it.
82
+
83
+ `setSubmitted` is what makes them visible: a field shows its error
84
+ only once the form has been submitted, and a publish with no pending
85
+ edits never submits the form at all. `setIsValid(false)` mirrors what
86
+ Payload does with the field errors from an ordinary save.
87
+
88
+ The toast still lists everything, because an issue with no field — an
89
+ embargo, a missing approval — has nowhere else to go.
90
+ */
91
+ const fieldErrors = fieldErrorsFromBlock(result.body, Object.keys(getFields()));
92
+ if (fieldErrors.length > 0) {
93
+ dispatchFields({ type: 'ADD_SERVER_ERRORS', errors: fieldErrors });
94
+ setIsValid(false);
95
+ setSubmitted(true);
96
+ }
97
+ const { title, description } = describeBlock(result.body, {
98
+ markedFields: fieldErrors.length,
99
+ });
71
100
  toast.error(title, {
72
101
  ...(description ? { description } : {}),
73
102
  duration: 10_000,
@@ -105,13 +134,17 @@ export function PublishButton(props = {}) {
105
134
  }, [
106
135
  api,
107
136
  collectionSlug,
137
+ dispatchFields,
138
+ getFields,
108
139
  id,
109
140
  modified,
110
141
  publishing,
111
142
  routePrefix,
112
143
  serverURL,
113
144
  setHasPublishedDoc,
145
+ setIsValid,
114
146
  setMostRecentVersionIsAutosaved,
147
+ setSubmitted,
115
148
  setUnpublishedVersionCount,
116
149
  submit,
117
150
  t,
@@ -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":"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,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAOpG;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAAC,QAAuC,EAAE;IACrE,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,aAAa,CAAA;IAEtD,MAAM,EACJ,cAAc,EACd,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,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAA;IACjF,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,uEAAuE;YACvE,uEAAuE;YACvE,mEAAmE;YACnE,gDAAgD;YAChD,EAAE;YACF,gEAAgE;YAChE,uEAAuE;YACvE,+DAA+D;YAC/D,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;oBACpB,+DAA+D;oBAC/D,gEAAgE;oBAChE,mEAAmE;oBACnE,2DAA2D;oBAC3D,iEAAiE;oBACjE,oBAAoB,EAAE,IAAI;iBAC3B,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;;;;;;;;;;;;;;kBAcE;gBACF,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;gBAC/E,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC3B,cAAc,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;oBAClE,UAAU,CAAC,KAAK,CAAC,CAAA;oBACjB,YAAY,CAAC,IAAI,CAAC,CAAA;gBACpB,CAAC;gBAED,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE;oBACxD,YAAY,EAAE,WAAW,CAAC,MAAM;iBACjC,CAAC,CAAA;gBACF,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,iEAAiE;YACjE,sEAAsE;YACtE,sDAAsD;YACtD,kEAAkE;YAClE,qEAAqE;YACrE,qEAAqE;YACrE,EAAE;YACF,gEAAgE;YAChE,kBAAkB,CAAC,IAAI,CAAC,CAAA;YACxB,0BAA0B,CAAC,CAAC,CAAC,CAAA;YAC7B,+BAA+B,CAAC,KAAK,CAAC,CAAA;YAEtC,6DAA6D;YAC7D,iEAAiE;YACjE,uCAAuC;YACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAA;YAC3C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,EAAE;oBACpC,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;oBAChC,QAAQ,EAAE,MAAM;iBACjB,CAAC,CAAA;YACJ,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAA;YACvC,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,aAAa,CAAC,KAAK,CAAC,CAAA;QACtB,CAAC;IACH,CAAC,EAAE;QACD,GAAG;QACH,cAAc;QACd,cAAc;QACd,SAAS;QACT,EAAE;QACF,QAAQ;QACR,UAAU;QACV,WAAW;QACX,SAAS;QACT,kBAAkB;QAClB,UAAU;QACV,+BAA+B;QAC/B,YAAY;QACZ,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"}
@@ -42,13 +42,58 @@ export type PublishingCallResult = {
42
42
  * back as `ok: true` with a `published: false` body.
43
43
  */
44
44
  export declare function callPublishingEndpoint(args: CallPublishingEndpointArgs): Promise<PublishingCallResult>;
45
+ /**
46
+ * A field error in the shape Payload's form reducer accepts. `path` is a
47
+ * *form state* path — dotted, with numeric segments for array and block rows —
48
+ * which is not the same dialect the pipeline's issues speak.
49
+ *
50
+ * Structurally `ValidationFieldError` from `payload`, redeclared so this file
51
+ * needs no import of its own and stays unit-testable without a form.
52
+ */
53
+ export interface PublishingFieldError {
54
+ path: string;
55
+ message: string;
56
+ }
57
+ /**
58
+ * Rewrites a pipeline issue's field path into a form state path.
59
+ *
60
+ * The checks address array members with brackets — `layout[2].image` — because
61
+ * they walk a document. Payload's form state keys the same field
62
+ * `layout.2.image`. One is not a prefix of the other, so an unrewritten path
63
+ * matches no field and the error would land nowhere.
64
+ */
65
+ export declare function toFormPath(field: string): string;
66
+ /**
67
+ * Turns a pipeline block into errors attached to the fields that caused it.
68
+ *
69
+ * `formPaths` is what the form actually has — `Object.keys(getFields())` at the
70
+ * call site. Only those paths are used, and that restriction is load-bearing
71
+ * rather than defensive: Payload's reducer *creates* a field state entry for
72
+ * any path it is handed, and an invented entry becomes invented data on the
73
+ * next save. `speakers.0.portrait` is a real issue path — the pipeline walks
74
+ * the document at depth, so a related person's portrait is in the tree — and
75
+ * writing it into form state would turn a relationship's list of ids into a
76
+ * list of objects.
77
+ *
78
+ * An issue whose exact path is not a field falls back to the nearest ancestor
79
+ * that is, so a problem inside a populated relationship marks the relationship
80
+ * and a block-level composition failure marks the block field. That is also
81
+ * what puts the error count on a *collapsed* block row: the reducer propagates
82
+ * every error path up to its parents.
83
+ *
84
+ * Anything that resolves to no field at all is left out and stays in the toast,
85
+ * which is the whole reason the toast keeps listing the issues.
86
+ */
87
+ export declare function fieldErrorsFromBlock(body: PublishingResponse, formPaths: Iterable<string>): PublishingFieldError[];
45
88
  /**
46
89
  * Turns a pipeline block into something an editor can act on: which step
47
90
  * said no, what it objected to, and what to do about it. This is the
48
91
  * information the pipeline already returns — it just never had a way to
49
92
  * reach the screen.
50
93
  */
51
- export declare function describeBlock(body: PublishingResponse): {
94
+ export declare function describeBlock(body: PublishingResponse, options?: {
95
+ markedFields?: number;
96
+ }): {
52
97
  title: string;
53
98
  description: string;
54
99
  };
@@ -1 +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,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,EAAE,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"}
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,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,EAAE,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;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,kBAAkB,EACxB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC1B,oBAAoB,EAAE,CAgBxB;AAeD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,kBAAkB,EACxB,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC;IACD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB,CA4BA"}
@@ -36,6 +36,71 @@ export async function callPublishingEndpoint(args) {
36
36
  }
37
37
  return { ok: true, body };
38
38
  }
39
+ /**
40
+ * Rewrites a pipeline issue's field path into a form state path.
41
+ *
42
+ * The checks address array members with brackets — `layout[2].image` — because
43
+ * they walk a document. Payload's form state keys the same field
44
+ * `layout.2.image`. One is not a prefix of the other, so an unrewritten path
45
+ * matches no field and the error would land nowhere.
46
+ */
47
+ export function toFormPath(field) {
48
+ return field
49
+ .replace(/\[(\d+)\]/g, '.$1')
50
+ .replace(/\.{2,}/g, '.')
51
+ .replace(/^\.|\.$/g, '');
52
+ }
53
+ /**
54
+ * Turns a pipeline block into errors attached to the fields that caused it.
55
+ *
56
+ * `formPaths` is what the form actually has — `Object.keys(getFields())` at the
57
+ * call site. Only those paths are used, and that restriction is load-bearing
58
+ * rather than defensive: Payload's reducer *creates* a field state entry for
59
+ * any path it is handed, and an invented entry becomes invented data on the
60
+ * next save. `speakers.0.portrait` is a real issue path — the pipeline walks
61
+ * the document at depth, so a related person's portrait is in the tree — and
62
+ * writing it into form state would turn a relationship's list of ids into a
63
+ * list of objects.
64
+ *
65
+ * An issue whose exact path is not a field falls back to the nearest ancestor
66
+ * that is, so a problem inside a populated relationship marks the relationship
67
+ * and a block-level composition failure marks the block field. That is also
68
+ * what puts the error count on a *collapsed* block row: the reducer propagates
69
+ * every error path up to its parents.
70
+ *
71
+ * Anything that resolves to no field at all is left out and stays in the toast,
72
+ * which is the whole reason the toast keeps listing the issues.
73
+ */
74
+ export function fieldErrorsFromBlock(body, formPaths) {
75
+ const paths = formPaths instanceof Set ? formPaths : new Set(formPaths);
76
+ // Grouped, because several issues can resolve to one field — three bad
77
+ // blocks all land on `layout` — and the reducer keeps one message per path.
78
+ const byPath = new Map();
79
+ for (const issue of body.issues ?? []) {
80
+ if (!issue.field)
81
+ continue;
82
+ const resolved = resolveFieldPath(toFormPath(issue.field), paths);
83
+ if (!resolved)
84
+ continue;
85
+ const messages = byPath.get(resolved) ?? [];
86
+ if (!messages.includes(issue.message))
87
+ messages.push(issue.message);
88
+ byPath.set(resolved, messages);
89
+ }
90
+ return [...byPath].map(([path, messages]) => ({ path, message: messages.join('; ') }));
91
+ }
92
+ /** The path itself if the form has it, else its nearest ancestor that it does. */
93
+ function resolveFieldPath(path, formPaths) {
94
+ if (path === '' || path === '(root)')
95
+ return undefined;
96
+ const segments = path.split('.');
97
+ for (let end = segments.length; end > 0; end--) {
98
+ const candidate = segments.slice(0, end).join('.');
99
+ if (formPaths.has(candidate))
100
+ return candidate;
101
+ }
102
+ return undefined;
103
+ }
39
104
  const MAX_LISTED_ISSUES = 5;
40
105
  /**
41
106
  * Turns a pipeline block into something an editor can act on: which step
@@ -43,7 +108,7 @@ const MAX_LISTED_ISSUES = 5;
43
108
  * information the pipeline already returns — it just never had a way to
44
109
  * reach the screen.
45
110
  */
46
- export function describeBlock(body) {
111
+ export function describeBlock(body, options = {}) {
47
112
  const title = body.reason ?? `Publish blocked at the ${body.failedAt ?? 'policy'} check.`;
48
113
  const lines = [];
49
114
  if (body.failedAt && body.reason) {
@@ -59,6 +124,12 @@ export function describeBlock(body) {
59
124
  if (body.suggestion) {
60
125
  lines.push(`Suggestion: ${body.suggestion}`);
61
126
  }
127
+ // Said only when it is true. An editor who has read a toast listing five
128
+ // paths still has to find them, and the answer — they are marked on the
129
+ // fields — is not something the toast otherwise reveals.
130
+ if ((options.markedFields ?? 0) > 0) {
131
+ lines.push('Fields with a problem are highlighted in the form.');
132
+ }
62
133
  return { title, description: lines.join('\n') };
63
134
  }
64
135
  //# sourceMappingURL=publishing-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"publishing-client.js","sourceRoot":"","sources":["../../src/admin/publishing-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoCH;;;;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"}
1
+ {"version":3,"file":"publishing-client.js","sourceRoot":"","sources":["../../src/admin/publishing-client.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,15 @@ import type { PipelineStep } from '../types.js';
30
30
  * The event is emitted after the write and cannot fail the step: once
31
31
  * `_status` is `published` the publish has happened, and reporting failure
32
32
  * would send an editor back to re-publish live content.
33
+ *
34
+ * A field Payload refuses is a failed *step*, not a thrown error. The write is
35
+ * the first thing in the whole pipeline that enforces `required` — a draft
36
+ * write does not, which is the point of drafts — so an empty required field
37
+ * inside a block reaches here and nowhere earlier. Thrown, it left the
38
+ * transport to explain itself: the admin got a bare message and the MCP tool
39
+ * got an exception, when Payload had already said exactly which paths were
40
+ * wrong. Returned as issues, it travels the same road as every other block —
41
+ * onto the fields in the admin, into the tool's result, into the audit row.
33
42
  */
34
43
  export declare const executeStep: PipelineStep;
35
44
  //# sourceMappingURL=execute.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/execute.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,WAAW,EAAE,YAoCzB,CAAA"}
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/execute.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,aAAa,CAAA;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,eAAO,MAAM,WAAW,EAAE,YAoDzB,CAAA"}
@@ -30,6 +30,15 @@ import { sendEventSafely } from '../../events.js';
30
30
  * The event is emitted after the write and cannot fail the step: once
31
31
  * `_status` is `published` the publish has happened, and reporting failure
32
32
  * would send an editor back to re-publish live content.
33
+ *
34
+ * A field Payload refuses is a failed *step*, not a thrown error. The write is
35
+ * the first thing in the whole pipeline that enforces `required` — a draft
36
+ * write does not, which is the point of drafts — so an empty required field
37
+ * inside a block reaches here and nowhere earlier. Thrown, it left the
38
+ * transport to explain itself: the admin got a bare message and the MCP tool
39
+ * got an exception, when Payload had already said exactly which paths were
40
+ * wrong. Returned as issues, it travels the same road as every other block —
41
+ * onto the fields in the admin, into the tool's result, into the audit row.
33
42
  */
34
43
  export const executeStep = async (ctx) => {
35
44
  const now = new Date().toISOString();
@@ -37,18 +46,35 @@ export const executeStep = async (ctx) => {
37
46
  ? ctx.document[ctx.collection.publishedAtField]
38
47
  : null;
39
48
  const wasFirstPublish = previousPublishedAt === null;
40
- await ctx.payload.update({
41
- collection: ctx.collection.slug,
42
- id: ctx.documentId,
43
- data: {
44
- _status: 'published',
45
- ...(wasFirstPublish ? { [ctx.collection.publishedAtField]: now } : {}),
46
- },
47
- ...(ctx.actor.enforceAccessAs
48
- ? { user: ctx.actor.enforceAccessAs, overrideAccess: false }
49
- : {}),
50
- context: { bypassPublishingServer: true },
51
- });
49
+ try {
50
+ await ctx.payload.update({
51
+ collection: ctx.collection.slug,
52
+ id: ctx.documentId,
53
+ data: {
54
+ _status: 'published',
55
+ ...(wasFirstPublish ? { [ctx.collection.publishedAtField]: now } : {}),
56
+ },
57
+ ...(ctx.actor.enforceAccessAs
58
+ ? { user: ctx.actor.enforceAccessAs, overrideAccess: false }
59
+ : {}),
60
+ context: { bypassPublishingServer: true },
61
+ });
62
+ }
63
+ catch (error) {
64
+ const issues = fieldIssues(error);
65
+ // Only a field rejection is an answer. A database or access failure is
66
+ // not, and rethrowing keeps it out of the diagnostics an editor reads as
67
+ // "fix these fields".
68
+ if (!issues)
69
+ throw error;
70
+ return {
71
+ pass: false,
72
+ code: 'field-validation-failed',
73
+ reason: `${issues.length} field${issues.length === 1 ? '' : 's'} the collection will not accept`,
74
+ issues,
75
+ suggestion: 'These are required by the collection rather than by a policy check, so they must be filled before this document can be published.',
76
+ };
77
+ }
52
78
  // The write has landed. From here the publish has happened, so nothing
53
79
  // below may turn it back into a failure.
54
80
  const warning = await sendEventSafely(ctx.inngest, {
@@ -64,4 +90,34 @@ export const executeStep = async (ctx) => {
64
90
  });
65
91
  return { pass: true, ...(warning ? { warnings: [warning] } : {}) };
66
92
  };
93
+ /**
94
+ * Payload's `ValidationError` as issues, or `undefined` for anything else.
95
+ *
96
+ * Read structurally rather than with `instanceof`. The class would work in the
97
+ * ordinary install and fail in the one that matters — two copies of `payload`
98
+ * resolved under one tree make `instanceof` false for an error that is one in
99
+ * every other respect, and the failure mode is a silent rethrow of something
100
+ * an editor could have fixed. The shape is stable: `data.errors` is a list of
101
+ * `{ path, message }`, and the paths are already dotted form paths.
102
+ */
103
+ function fieldIssues(error) {
104
+ if (!error || typeof error !== 'object')
105
+ return undefined;
106
+ const data = error.data;
107
+ if (!data || typeof data !== 'object')
108
+ return undefined;
109
+ const errors = data.errors;
110
+ if (!Array.isArray(errors) || errors.length === 0)
111
+ return undefined;
112
+ const issues = [];
113
+ for (const entry of errors) {
114
+ if (!entry || typeof entry !== 'object')
115
+ return undefined;
116
+ const { path, message } = entry;
117
+ if (typeof path !== 'string' || typeof message !== 'string')
118
+ return undefined;
119
+ issues.push({ field: path, message, severity: 'error' });
120
+ }
121
+ return issues;
122
+ }
67
123
  //# sourceMappingURL=execute.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../../src/pipeline/steps/execute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAGjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,MAAM,WAAW,GAAiB,KAAK,EAAE,GAAG,EAAE,EAAE;IACrD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACpC,MAAM,mBAAmB,GACvB,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,KAAK,QAAQ;QAC/D,CAAC,CAAE,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAY;QAC3D,CAAC,CAAC,IAAI,CAAA;IACV,MAAM,eAAe,GAAG,mBAAmB,KAAK,IAAI,CAAA;IAEpD,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;QACvB,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,IAAI;QAC/B,EAAE,EAAE,GAAG,CAAC,UAAU;QAClB,IAAI,EAAE;YACJ,OAAO,EAAE,WAAW;YACpB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvE;QACD,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe;YAC3B,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,eAAe,EAAE,cAAc,EAAE,KAAK,EAAE;YAC5D,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE;KAC1C,CAAC,CAAA;IAEF,uEAAuE;IACvE,yCAAyC;IACzC,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,OAAO,EAAE;QACjD,IAAI,EAAE,wBAAwB;QAC9B,IAAI,EAAE;YACJ,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,IAAI;YAC/B,EAAE,EAAE,GAAG,CAAC,UAAU;YAClB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC;YACtE,WAAW,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,QAAQ;YAC3C,mBAAmB;YACnB,cAAc,EAAE,eAAe;SAChC;KACF,CAAC,CAAA;IAEF,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;AACpE,CAAC,CAAA"}
1
+ {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../../src/pipeline/steps/execute.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.4.0",
3
+ "version": "0.5.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",