@abinnovision/payloadcms-mcpx 2.0.0-beta.9 → 2.0.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 (76) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +56 -36
  3. package/dist/admin/capability-matrix.d.mts +5 -3
  4. package/dist/admin/capability-matrix.mjs +374 -272
  5. package/dist/admin/capability-toggles.mjs +79 -42
  6. package/dist/admin/confirmations-panel.mjs +232 -0
  7. package/dist/admin/confirmations.d.mts +15 -0
  8. package/dist/admin/confirmations.mjs +71 -0
  9. package/dist/admin/endpoint-url.mjs +7 -0
  10. package/dist/admin/index.d.mts +2 -1
  11. package/dist/admin/index.mjs +2 -1
  12. package/dist/admin/setup-guide.d.mts +3 -3
  13. package/dist/admin/setup-guide.mjs +51 -33
  14. package/dist/api-keys/capability-matrix.d.mts +7 -9
  15. package/dist/api-keys/capability-matrix.mjs +15 -37
  16. package/dist/api-keys/collection.mjs +16 -18
  17. package/dist/api-keys/confirmation-view.mjs +7 -0
  18. package/dist/api-keys/fields.mjs +50 -57
  19. package/dist/auth/resolve.mjs +69 -40
  20. package/dist/capabilities.d.mts +2 -0
  21. package/dist/capabilities.mjs +64 -16
  22. package/dist/define-tool.d.mts +4 -4
  23. package/dist/define-tool.mjs +8 -1
  24. package/dist/endpoint/confirmations.mjs +124 -0
  25. package/dist/endpoint/handler.mjs +25 -27
  26. package/dist/endpoint/http.mjs +27 -0
  27. package/dist/endpoint/server.mjs +48 -29
  28. package/dist/endpoint/upload.mjs +104 -0
  29. package/dist/grants/confirmation.mjs +75 -0
  30. package/dist/grants/grant.mjs +137 -0
  31. package/dist/guards.mjs +8 -1
  32. package/dist/index.d.mts +2 -2
  33. package/dist/index.mjs +1 -1
  34. package/dist/options.mjs +68 -61
  35. package/dist/plugin.d.mts +3 -2
  36. package/dist/plugin.mjs +19 -4
  37. package/dist/request.mjs +5 -1
  38. package/dist/result.mjs +11 -1
  39. package/dist/schema/admin-hidden.mjs +4 -4
  40. package/dist/schema/describe.mjs +12 -18
  41. package/dist/schema/index.mjs +5 -6
  42. package/dist/schema/lexical-pointer.mjs +8 -8
  43. package/dist/schema/lexical.mjs +15 -13
  44. package/dist/schema/outline.mjs +4 -5
  45. package/dist/schema/path.mjs +4 -1
  46. package/dist/schema/pointer.mjs +11 -17
  47. package/dist/schema/shape.mjs +38 -50
  48. package/dist/schema/walk.mjs +57 -18
  49. package/dist/tools/builtin.mjs +7 -1
  50. package/dist/tools/create-document.mjs +38 -26
  51. package/dist/tools/delete-document.mjs +154 -0
  52. package/dist/tools/describe-schema.mjs +7 -5
  53. package/dist/tools/document.mjs +131 -45
  54. package/dist/tools/duplicate-document.mjs +50 -0
  55. package/dist/tools/find-documents.mjs +15 -20
  56. package/dist/tools/find-versions.mjs +11 -15
  57. package/dist/tools/get-document.mjs +31 -15
  58. package/dist/tools/links.mjs +53 -0
  59. package/dist/tools/list-capabilities.mjs +17 -7
  60. package/dist/tools/patch-document.mjs +57 -53
  61. package/dist/tools/publish-document.mjs +17 -29
  62. package/dist/tools/query-paths.mjs +1 -1
  63. package/dist/tools/run-confirmed.mjs +114 -0
  64. package/dist/tools/shared.mjs +53 -46
  65. package/dist/tools/validate-document.mjs +6 -8
  66. package/dist/tools/versions.mjs +11 -6
  67. package/dist/types.d.mts +158 -43
  68. package/dist/upload/file.mjs +177 -0
  69. package/dist/version.mjs +4 -2
  70. package/dist/write/draft-guard.mjs +35 -28
  71. package/dist/write/patch.mjs +4 -4
  72. package/dist/write/publish-intent.mjs +23 -11
  73. package/dist/write/row-ids.mjs +3 -1
  74. package/dist/write/write-data.mjs +18 -31
  75. package/package.json +14 -14
  76. package/dist/builtin-tool-names.mjs +0 -16
@@ -1,87 +1,96 @@
1
1
  "use client";
2
- import { CAPABILITIES_FIELD } from "../capabilities.mjs";
3
- import { CAPABILITY_OPERATIONS, capabilityPaths, cellPath, toolPath } from "../api-keys/capability-matrix.mjs";
4
- import { buildToggleActions, columnState, rowState, toolsState } from "./capability-toggles.mjs";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
- import { CheckboxInput, FieldDescription, FieldLabel, useForm, useFormFields } from "@payloadcms/ui";
2
+ import { ACCESS_CHAIN, CAPABILITIES_FIELD } from "../capabilities.mjs";
3
+ import { capabilityPaths, toolPath } from "../api-keys/capability-matrix.mjs";
4
+ import { translateStatic } from "../i18n.mjs";
5
+ import { accessLevelOf, accessLevelsOf, allAccessLevel, allDeleteMode, buildToggleActions, deleteModeOf, toolsState } from "./capability-toggles.mjs";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { Button, CheckboxInput, FieldDescription, FieldLabel, Pill, useConfig, useForm, useFormFields, useTranslation } from "@payloadcms/ui";
7
8
  import { useCallback, useMemo } from "react";
8
9
  //#region src/admin/capability-matrix.tsx
9
- const NOT_EXPOSED = "The plugin config does not expose this.";
10
10
  const BASE_CLASS = "mcpx-capabilities";
11
+ const LIVE_WRITE = "Writes go live immediately.";
12
+ const cx = (...names) => names.filter(Boolean).join(" ");
11
13
  const SHEET = `
12
14
  .${BASE_CLASS} tbody tr:hover { background: var(--theme-elevation-50); }
13
- .${BASE_CLASS}__toggle:hover { text-decoration: underline; }
14
- .${BASE_CLASS} section:last-of-type { margin-bottom: 0; }
15
+ .${BASE_CLASS}__sections {
16
+ display: flex;
17
+ flex-wrap: wrap;
18
+ gap: var(--base) calc(var(--base) * 2);
19
+ align-items: flex-start;
20
+ }
21
+ .${BASE_CLASS}__table {
22
+ table-layout: fixed;
23
+ width: auto;
24
+ border-collapse: separate;
25
+ border-spacing: 0;
26
+ text-align: left;
27
+ }
28
+ .${BASE_CLASS}__table thead { background: var(--theme-elevation-50); }
29
+ .${BASE_CLASS}__table th,
30
+ .${BASE_CLASS}__table td {
31
+ padding: calc(var(--base) * 0.6);
32
+ vertical-align: middle;
33
+ font-weight: normal;
34
+ text-align: center;
35
+ }
36
+ .${BASE_CLASS}__table thead th { color: var(--theme-elevation-400); }
37
+ .${BASE_CLASS}__table .${BASE_CLASS}__head { white-space: nowrap; }
38
+ .${BASE_CLASS}__table .${BASE_CLASS}__start { text-align: start; }
39
+ .${BASE_CLASS}__table .${BASE_CLASS}__row,
40
+ .${BASE_CLASS}__table .${BASE_CLASS}__title {
41
+ padding-inline-start: calc(var(--base) * 0.8);
42
+ text-align: inherit;
43
+ }
44
+ .${BASE_CLASS}__table .${BASE_CLASS}__row { word-break: break-word; }
45
+ .${BASE_CLASS}__table thead .${BASE_CLASS}__row { color: var(--theme-elevation-500); }
46
+ .${BASE_CLASS}__table .${BASE_CLASS}__title {
47
+ font-size: 0.8em;
48
+ letter-spacing: 0.08em;
49
+ text-transform: uppercase;
50
+ }
51
+ .${BASE_CLASS}__table td:last-child { padding-inline-end: calc(var(--base) * 0.8); }
52
+ .${BASE_CLASS}__table thead tr:first-child th:first-child { border-start-start-radius: var(--style-radius-s); }
53
+ .${BASE_CLASS}__table thead tr:first-child th:last-child { border-start-end-radius: var(--style-radius-s); }
54
+ .${BASE_CLASS}__table thead tr:last-child > * { border-bottom: 1px solid var(--theme-border-color); }
55
+ .${BASE_CLASS}__hint { display: block; color: var(--theme-elevation-400); }
56
+ .${BASE_CLASS}__badge {
57
+ display: inline-flex;
58
+ margin-inline-start: calc(var(--base) * 0.4);
59
+ vertical-align: middle;
60
+ cursor: help;
61
+ }
62
+ .${BASE_CLASS}__delete {
63
+ display: inline-flex;
64
+ align-items: center;
65
+ gap: calc(var(--base) * 0.4);
66
+ }
67
+ .${BASE_CLASS}__segments {
68
+ display: inline-flex;
69
+ border: 1px solid var(--theme-elevation-150);
70
+ border-radius: var(--style-radius-s);
71
+ background: var(--theme-input-bg);
72
+ }
73
+ .${BASE_CLASS}__segments .btn { border: 0; border-radius: 0; white-space: nowrap; }
74
+ .${BASE_CLASS}__segments .btn + .btn { border-inline-start: 1px solid var(--theme-elevation-150); }
75
+ .${BASE_CLASS}__segments .btn:first-child { border-start-start-radius: inherit; border-end-start-radius: inherit; }
76
+ .${BASE_CLASS}__segments .btn:last-child { border-start-end-radius: inherit; border-end-end-radius: inherit; }
77
+ .${BASE_CLASS}__segments .btn--style-transparent { --hover-bg: var(--theme-elevation-100); }
78
+ .${BASE_CLASS}__shield {
79
+ display: inline-flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ padding: calc(var(--base) * 0.2);
83
+ border: 0;
84
+ border-radius: var(--style-radius-s);
85
+ background: transparent;
86
+ color: var(--theme-elevation-800);
87
+ line-height: 0;
88
+ cursor: pointer;
89
+ }
90
+ .${BASE_CLASS}__shield:hover:not(:disabled) { background: var(--theme-elevation-100); }
91
+ .${BASE_CLASS}__shield:disabled { cursor: not-allowed; opacity: 0.6; }
92
+ .${BASE_CLASS}__shield.${BASE_CLASS}__danger { color: var(--theme-error-500); }
15
93
  `;
16
- const CELL_PADDING = "calc(var(--base) * 0.6)";
17
- const EDGE_PADDING = "calc(var(--base) * 0.8)";
18
- const styles = {
19
- section: { marginBottom: "var(--base)" },
20
- table: {
21
- tableLayout: "fixed",
22
- width: "auto",
23
- borderCollapse: "separate",
24
- borderSpacing: 0,
25
- textAlign: "left"
26
- },
27
- head: { background: "var(--theme-elevation-50)" },
28
- headTitle: {
29
- padding: `${CELL_PADDING} ${CELL_PADDING} ${CELL_PADDING} ${EDGE_PADDING}`,
30
- verticalAlign: "middle",
31
- fontSize: "0.8em",
32
- letterSpacing: "0.08em",
33
- textTransform: "uppercase",
34
- color: "var(--theme-elevation-400)",
35
- fontWeight: "normal"
36
- },
37
- headOperation: {
38
- padding: CELL_PADDING,
39
- verticalAlign: "middle",
40
- color: "var(--theme-elevation-400)",
41
- fontWeight: "normal",
42
- textAlign: "center",
43
- whiteSpace: "nowrap"
44
- },
45
- headLabel: {
46
- padding: `${CELL_PADDING} ${CELL_PADDING} ${CELL_PADDING} ${EDGE_PADDING}`,
47
- verticalAlign: "middle",
48
- color: "var(--theme-elevation-500)",
49
- fontWeight: "normal"
50
- },
51
- rule: { borderBottom: "1px solid var(--theme-border-color)" },
52
- topStart: { borderStartStartRadius: "var(--style-radius-s)" },
53
- topEnd: { borderStartEndRadius: "var(--style-radius-s)" },
54
- rowHeader: {
55
- padding: `${CELL_PADDING} ${CELL_PADDING} ${CELL_PADDING} ${EDGE_PADDING}`,
56
- verticalAlign: "middle",
57
- fontWeight: "normal",
58
- wordBreak: "break-word"
59
- },
60
- rowButton: {
61
- padding: 0,
62
- border: 0,
63
- background: "none",
64
- color: "inherit",
65
- font: "inherit",
66
- cursor: "pointer",
67
- textAlign: "start",
68
- textDecoration: "none"
69
- },
70
- hint: {
71
- display: "block",
72
- color: "var(--theme-elevation-400)"
73
- },
74
- cell: {
75
- padding: CELL_PADDING,
76
- verticalAlign: "middle",
77
- textAlign: "center"
78
- },
79
- lastCell: { paddingInlineEnd: EDGE_PADDING },
80
- dash: {
81
- color: "var(--theme-elevation-400)",
82
- cursor: "help"
83
- }
84
- };
85
94
  const Box = ({ label, onChange, readOnly, state }) => /*#__PURE__*/ jsx(CheckboxInput, {
86
95
  checked: state === "on",
87
96
  name: label,
@@ -89,200 +98,300 @@ const Box = ({ label, onChange, readOnly, state }) => /*#__PURE__*/ jsx(Checkbox
89
98
  partialChecked: state === "mixed",
90
99
  readOnly
91
100
  });
92
- const LABEL_WIDTH = "calc(var(--base) * 9)";
101
+ const deleteBoxState = (mode) => mode === void 0 ? "mixed" : mode === "off" ? "off" : "on";
102
+ const nextDeleteMode = (mode) => mode === "off" || mode === void 0 ? "approve" : "off";
103
+ const ACCESS_OPTIONS = [{
104
+ id: "none",
105
+ label: "None",
106
+ title: "No access"
107
+ }, ...ACCESS_CHAIN.map((operation) => ({
108
+ id: operation.id,
109
+ label: operation.label,
110
+ title: operation.description
111
+ }))];
112
+ const ARROW_STEPS = {
113
+ ArrowDown: 1,
114
+ ArrowLeft: -1,
115
+ ArrowRight: 1,
116
+ ArrowUp: -1
117
+ };
118
+ const Segments = ({ included = [], label, onSelect, options, readOnly, selected }) => {
119
+ const focusable = options.some((option) => option.id === selected) ? selected : options[0]?.id;
120
+ const onKeyDown = (event) => {
121
+ const step = ARROW_STEPS[event.key];
122
+ if (readOnly || step === void 0) return;
123
+ event.preventDefault();
124
+ const radios = [...event.currentTarget.querySelectorAll("[role=\"radio\"]")];
125
+ const current = radios.findIndex((radio) => radio === document.activeElement);
126
+ const next = Math.min(Math.max(current + step, 0), radios.length - 1);
127
+ const option = options[next];
128
+ if (option) {
129
+ radios[next]?.focus();
130
+ onSelect(option.id);
131
+ }
132
+ };
133
+ return /*#__PURE__*/ jsx("div", {
134
+ "aria-label": label,
135
+ className: `${BASE_CLASS}__segments`,
136
+ onKeyDown,
137
+ role: "radiogroup",
138
+ children: options.map((option) => {
139
+ const checked = option.id === selected;
140
+ return /*#__PURE__*/ jsx(Button, {
141
+ buttonStyle: checked ? "primary" : included.includes(option.id) ? "pill" : "transparent",
142
+ disabled: readOnly,
143
+ extraButtonProps: {
144
+ "aria-checked": checked,
145
+ role: "radio",
146
+ tabIndex: option.id === focusable ? 0 : -1,
147
+ title: option.title
148
+ },
149
+ margin: false,
150
+ onClick: () => {
151
+ onSelect(option.id);
152
+ },
153
+ size: "small",
154
+ children: option.label
155
+ }, option.id);
156
+ })
157
+ });
158
+ };
159
+ const includedBelow = (options, level) => {
160
+ const selected = options.findIndex((option) => option.id === level);
161
+ return selected < 1 ? [] : options.slice(1, selected).map((option) => option.id);
162
+ };
163
+ const accessOptions = (rows) => ACCESS_OPTIONS.filter((option) => rows.some((row) => accessLevelsOf(row).includes(option.id)));
164
+ const LABEL_WIDTH = "calc(var(--base) * 11)";
93
165
  const OPERATION_WIDTH = "calc(var(--base) * 3.5)";
94
- const Columns = ({ count }) => /*#__PURE__*/ jsxs("colgroup", { children: [/*#__PURE__*/ jsx("col", { style: { width: LABEL_WIDTH } }), Array.from({ length: count }, (_, index) => /*#__PURE__*/ jsx("col", { style: { width: OPERATION_WIDTH } }, index))] });
95
- const cellStyle = (isLast) => isLast ? {
96
- ...styles.cell,
97
- ...styles.lastCell
98
- } : styles.cell;
99
- const Dash = () => /*#__PURE__*/ jsx("span", {
100
- "aria-label": NOT_EXPOSED,
101
- style: styles.dash,
102
- title: NOT_EXPOSED,
103
- children: "—"
166
+ const ACCESS_WIDTH = "calc(var(--base) * 13)";
167
+ const DELETE_WIDTH = "calc(var(--base) * 4)";
168
+ const LiveBadge = () => /*#__PURE__*/ jsx("span", {
169
+ className: `${BASE_CLASS}__badge`,
170
+ title: LIVE_WRITE,
171
+ children: /*#__PURE__*/ jsxs(Pill, {
172
+ pillStyle: "warning",
173
+ size: "small",
174
+ children: [/*#__PURE__*/ jsx("span", {
175
+ "aria-hidden": "true",
176
+ children: "Live"
177
+ }), /*#__PURE__*/ jsx("span", {
178
+ className: "sr-only",
179
+ children: LIVE_WRITE
180
+ })]
181
+ })
104
182
  });
183
+ const APPROVED_ALWAYS = "Always approved: the plugin config does not allow unattended deletes";
184
+ const ApprovalToggle = ({ label, locked = false, onToggle, pressed, readOnly }) => {
185
+ const title = locked ? APPROVED_ALWAYS : pressed ? "A person approves each delete" : "Each delete runs without approval";
186
+ return /*#__PURE__*/ jsxs("button", {
187
+ "aria-pressed": pressed,
188
+ className: cx(`${BASE_CLASS}__shield`, !pressed && `${BASE_CLASS}__danger`),
189
+ disabled: readOnly || locked,
190
+ onClick: onToggle,
191
+ title,
192
+ type: "button",
193
+ children: [/*#__PURE__*/ jsxs("svg", {
194
+ "aria-hidden": "true",
195
+ fill: "none",
196
+ height: "16",
197
+ stroke: "currentColor",
198
+ strokeLinecap: "round",
199
+ strokeLinejoin: "round",
200
+ strokeWidth: "2",
201
+ viewBox: "0 0 24 24",
202
+ width: "16",
203
+ children: [/*#__PURE__*/ jsx("path", { d: "M12 3l8 3v6c0 4.5-3.2 8-8 9-4.8-1-8-4.5-8-9V6z" }), pressed ? /*#__PURE__*/ jsx("path", { d: "M9 12l2 2 4-4" }) : null]
204
+ }), /*#__PURE__*/ jsx("span", {
205
+ className: "sr-only",
206
+ children: `${label}: ${title}`
207
+ })]
208
+ });
209
+ };
210
+ const MatrixTable = ({ all, children, heads, start = false, title, widths }) => /*#__PURE__*/ jsx("section", { children: /*#__PURE__*/ jsxs("table", {
211
+ className: `${BASE_CLASS}__table`,
212
+ children: [
213
+ /*#__PURE__*/ jsxs("colgroup", { children: [/*#__PURE__*/ jsx("col", { style: { width: LABEL_WIDTH } }), widths.map((width, index) => /*#__PURE__*/ jsx("col", { style: { width } }, index))] }),
214
+ /*#__PURE__*/ jsxs("thead", { children: [/*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
215
+ className: `${BASE_CLASS}__title`,
216
+ scope: "col",
217
+ children: title
218
+ }), heads.map((head) => /*#__PURE__*/ jsx("th", {
219
+ className: `${BASE_CLASS}__head${start ? ` ${BASE_CLASS}__start` : ""}`,
220
+ scope: "col",
221
+ children: head
222
+ }, head))] }), /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
223
+ className: `${BASE_CLASS}__row`,
224
+ scope: "row",
225
+ children: "All"
226
+ }), all] })] }),
227
+ /*#__PURE__*/ jsx("tbody", { children })
228
+ ]
229
+ }) });
105
230
  const NamespaceTable = ({ matrix, namespace, path, readOnly, toggle, values }) => {
106
231
  const rows = matrix[namespace.id];
107
- const columns = CAPABILITY_OPERATIONS.filter((operation) => rows.some((row) => row[operation.id]));
108
- return /*#__PURE__*/ jsx("section", {
109
- style: styles.section,
110
- children: /*#__PURE__*/ jsxs("table", {
111
- style: styles.table,
112
- children: [
113
- /*#__PURE__*/ jsx(Columns, { count: columns.length }),
114
- /*#__PURE__*/ jsxs("thead", {
115
- style: styles.head,
116
- children: [/*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
117
- scope: "col",
118
- style: {
119
- ...styles.headTitle,
120
- ...styles.topStart
121
- },
122
- children: namespace.title
123
- }), columns.map((operation, column) => /*#__PURE__*/ jsx("th", {
124
- scope: "col",
125
- style: column === columns.length - 1 ? {
126
- ...styles.headOperation,
127
- ...styles.topEnd
128
- } : styles.headOperation,
129
- title: operation.description,
130
- children: operation.label
131
- }, operation.id))] }), /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
132
- scope: "row",
133
- style: {
134
- ...styles.headLabel,
135
- ...styles.rule
232
+ const withDelete = rows.some((row) => row.delete);
233
+ const allOptions = accessOptions(rows);
234
+ const allLevel = allAccessLevel(matrix, path, namespace.id, values);
235
+ const entries = namespace.title.toLowerCase();
236
+ const { i18n } = useTranslation();
237
+ const { getEntityConfig } = useConfig();
238
+ const nameOf = (row) => translateStatic(namespace.id === "collections" ? getEntityConfig({ collectionSlug: row.slug }).labels.plural : getEntityConfig({ globalSlug: row.slug }).label, i18n) ?? row.slug;
239
+ const allDelete = allDeleteMode(matrix, path, namespace.id, values);
240
+ return /*#__PURE__*/ jsx(MatrixTable, {
241
+ all: /*#__PURE__*/ jsxs(Fragment, { children: [/*#__PURE__*/ jsx("td", {
242
+ className: `${BASE_CLASS}__start`,
243
+ children: /*#__PURE__*/ jsx(Segments, {
244
+ included: includedBelow(allOptions, allLevel),
245
+ label: `Access for every ${entries} entry`,
246
+ onSelect: (level) => {
247
+ toggle({
248
+ kind: "access",
249
+ namespace: namespace.id,
250
+ level
251
+ });
252
+ },
253
+ options: allOptions,
254
+ readOnly,
255
+ selected: allLevel
256
+ })
257
+ }), withDelete ? /*#__PURE__*/ jsx("td", {
258
+ className: `${BASE_CLASS}__start`,
259
+ children: /*#__PURE__*/ jsx(Box, {
260
+ label: `Delete for every ${entries} entry`,
261
+ onChange: () => {
262
+ toggle({
263
+ kind: "deleteMode",
264
+ namespace: namespace.id,
265
+ mode: nextDeleteMode(allDelete)
266
+ });
267
+ },
268
+ readOnly,
269
+ state: deleteBoxState(allDelete)
270
+ })
271
+ }) : null] }),
272
+ heads: withDelete ? ["Access", "Delete"] : ["Access"],
273
+ start: true,
274
+ title: namespace.title,
275
+ widths: withDelete ? [ACCESS_WIDTH, DELETE_WIDTH] : [ACCESS_WIDTH],
276
+ children: rows.map((row) => {
277
+ const options = accessOptions([row]);
278
+ const level = accessLevelOf(path, namespace.id, row, values);
279
+ const mode = deleteModeOf(path, namespace.id, row, values);
280
+ const name = nameOf(row);
281
+ return /*#__PURE__*/ jsxs("tr", { children: [
282
+ /*#__PURE__*/ jsxs("th", {
283
+ className: `${BASE_CLASS}__row`,
284
+ scope: "row",
285
+ children: [
286
+ name,
287
+ row.live ? /*#__PURE__*/ jsx(LiveBadge, {}) : null,
288
+ name === row.slug ? null : /*#__PURE__*/ jsx("span", {
289
+ className: `${BASE_CLASS}__hint`,
290
+ children: row.slug
291
+ })
292
+ ]
293
+ }),
294
+ /*#__PURE__*/ jsx("td", {
295
+ className: `${BASE_CLASS}__start`,
296
+ children: /*#__PURE__*/ jsx(Segments, {
297
+ included: includedBelow(options, level),
298
+ label: `Access for ${name}`,
299
+ onSelect: (next) => {
300
+ toggle({
301
+ kind: "access",
302
+ namespace: namespace.id,
303
+ fieldName: row.fieldName,
304
+ level: next
305
+ });
136
306
  },
137
- children: "All"
138
- }), columns.map((operation, column) => {
139
- const state = columnState(matrix, path, namespace.id, operation.id, values);
140
- return /*#__PURE__*/ jsx("td", {
141
- style: {
142
- ...cellStyle(column === columns.length - 1),
143
- ...styles.rule
144
- },
145
- children: /*#__PURE__*/ jsx(Box, {
146
- label: `${operation.label} every ${namespace.title.toLowerCase()} entry`,
147
- onChange: () => {
148
- toggle({
149
- kind: "column",
150
- namespace: namespace.id,
151
- operation: operation.id,
152
- value: state !== "on"
153
- });
154
- },
155
- readOnly,
156
- state
157
- })
158
- }, operation.id);
159
- })] })]
307
+ options,
308
+ readOnly,
309
+ selected: level
310
+ })
160
311
  }),
161
- /*#__PURE__*/ jsx("tbody", { children: rows.map((row) => {
162
- const state = rowState(path, namespace.id, row, values);
163
- return /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsxs("th", {
164
- scope: "row",
165
- style: styles.rowHeader,
166
- children: [/*#__PURE__*/ jsx("button", {
167
- className: `${BASE_CLASS}__toggle`,
168
- disabled: readOnly,
169
- onClick: () => {
312
+ withDelete ? /*#__PURE__*/ jsx("td", {
313
+ className: `${BASE_CLASS}__start`,
314
+ children: row.delete ? /*#__PURE__*/ jsxs("span", {
315
+ className: `${BASE_CLASS}__delete`,
316
+ children: [/*#__PURE__*/ jsx(Box, {
317
+ label: `Delete ${name}`,
318
+ onChange: () => {
170
319
  toggle({
171
- kind: "row",
320
+ kind: "deleteMode",
172
321
  namespace: namespace.id,
173
322
  fieldName: row.fieldName,
174
- value: state !== "on"
323
+ mode: nextDeleteMode(mode)
175
324
  });
176
325
  },
177
- style: styles.rowButton,
178
- title: `Toggle every capability for ${row.label}`,
179
- type: "button",
180
- children: row.label
181
- }), row.hint ? /*#__PURE__*/ jsx("span", {
182
- style: styles.hint,
183
- children: row.hint
184
- }) : null]
185
- }), columns.map((operation, column) => /*#__PURE__*/ jsx("td", {
186
- style: cellStyle(column === columns.length - 1),
187
- children: row[operation.id] ? /*#__PURE__*/ jsx(Box, {
188
- label: `${operation.label} ${row.label}`,
189
- onChange: () => {
326
+ readOnly,
327
+ state: deleteBoxState(mode)
328
+ }), mode === "off" ? null : /*#__PURE__*/ jsx(ApprovalToggle, {
329
+ label: `Approval for deleting ${name}`,
330
+ locked: !row.deleteUnattended,
331
+ onToggle: () => {
190
332
  toggle({
191
- kind: "cell",
333
+ kind: "deleteMode",
192
334
  namespace: namespace.id,
193
335
  fieldName: row.fieldName,
194
- operation: operation.id,
195
- value: values[cellPath(path, namespace.id, row.fieldName, operation.id)] !== true
336
+ mode: mode === "trash" ? "approve" : "trash"
196
337
  });
197
338
  },
198
- readOnly,
199
- state: values[cellPath(path, namespace.id, row.fieldName, operation.id)] === true ? "on" : "off"
200
- }) : /*#__PURE__*/ jsx(Dash, {})
201
- }, operation.id))] }, row.fieldName);
202
- }) })
203
- ]
339
+ pressed: mode !== "trash",
340
+ readOnly
341
+ })]
342
+ }) : null
343
+ }) : null
344
+ ] }, row.fieldName);
204
345
  })
205
346
  });
206
347
  };
207
348
  const ToolsTable = ({ matrix, path, readOnly, toggle, values }) => {
208
349
  const toolState = toolsState(matrix, path, values);
209
- return /*#__PURE__*/ jsx("section", {
210
- style: styles.section,
211
- children: /*#__PURE__*/ jsxs("table", {
212
- style: styles.table,
213
- children: [
214
- /*#__PURE__*/ jsx(Columns, { count: 1 }),
215
- /*#__PURE__*/ jsxs("thead", {
216
- style: styles.head,
217
- children: [/*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
218
- scope: "col",
219
- style: {
220
- ...styles.headTitle,
221
- ...styles.topStart
222
- },
223
- children: "Tools"
224
- }), /*#__PURE__*/ jsx("th", {
225
- scope: "col",
226
- style: {
227
- ...styles.headOperation,
228
- ...styles.topEnd
229
- },
230
- children: "Enabled"
231
- })] }), /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsx("th", {
232
- scope: "row",
233
- style: {
234
- ...styles.headLabel,
235
- ...styles.rule
236
- },
237
- children: "All"
238
- }), /*#__PURE__*/ jsx("td", {
239
- style: {
240
- ...cellStyle(true),
241
- ...styles.rule
242
- },
243
- children: /*#__PURE__*/ jsx(Box, {
244
- label: "Enable every tool",
245
- onChange: () => {
246
- toggle({
247
- kind: "tools",
248
- value: toolState !== "on"
249
- });
250
- },
251
- readOnly,
252
- state: toolState
253
- })
254
- })] })]
255
- }),
256
- /*#__PURE__*/ jsx("tbody", { children: matrix.tools.map((tool) => /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsxs("th", {
257
- scope: "row",
258
- style: styles.rowHeader,
259
- children: [tool.name, /*#__PURE__*/ jsx("span", {
260
- style: styles.hint,
261
- children: tool.description
262
- })]
263
- }), /*#__PURE__*/ jsx("td", {
264
- style: cellStyle(true),
265
- children: /*#__PURE__*/ jsx(Box, {
266
- label: `Enable ${tool.name}`,
267
- onChange: () => {
268
- toggle({
269
- kind: "tool",
270
- name: tool.name,
271
- value: values[toolPath(path, tool.name)] !== true
272
- });
273
- },
274
- readOnly,
275
- state: values[toolPath(path, tool.name)] === true ? "on" : "off"
276
- })
277
- })] }, tool.name)) })
278
- ]
350
+ return /*#__PURE__*/ jsx(MatrixTable, {
351
+ all: /*#__PURE__*/ jsx("td", { children: /*#__PURE__*/ jsx(Box, {
352
+ label: "Enable every tool",
353
+ onChange: () => {
354
+ toggle({
355
+ kind: "tools",
356
+ value: toolState !== "on"
357
+ });
358
+ },
359
+ readOnly,
360
+ state: toolState
361
+ }) }),
362
+ heads: ["Enabled"],
363
+ title: "Tools",
364
+ widths: [OPERATION_WIDTH],
365
+ children: matrix.tools.map((tool) => {
366
+ const granted = values[toolPath(path, tool.name)] === true;
367
+ return /*#__PURE__*/ jsxs("tr", { children: [/*#__PURE__*/ jsxs("th", {
368
+ className: `${BASE_CLASS}__row`,
369
+ scope: "row",
370
+ children: [tool.name, /*#__PURE__*/ jsx("span", {
371
+ className: `${BASE_CLASS}__hint`,
372
+ children: tool.description
373
+ })]
374
+ }), /*#__PURE__*/ jsx("td", { children: /*#__PURE__*/ jsx(Box, {
375
+ label: `Enable ${tool.name}`,
376
+ onChange: () => {
377
+ toggle({
378
+ kind: "tool",
379
+ name: tool.name,
380
+ value: !granted
381
+ });
382
+ },
383
+ readOnly,
384
+ state: granted ? "on" : "off"
385
+ }) })] }, tool.name);
279
386
  })
280
387
  });
281
388
  };
282
389
  /**
283
- * What an API key may do, as one table per namespace: a row per entity, a
284
- * column per operation and a bulk toggle in each column header. The nested
285
- * checkbox fields back every cell, so the stored document is unaffected.
390
+ * What an API key may do, as one table per namespace: a row per entity with
391
+ * an Access control and a Delete checkbox with an approval shield, and an
392
+ * "All" row that sets every row.
393
+ * The nested checkbox fields back every control, so the stored document is
394
+ * unaffected.
286
395
  */ const McpxCapabilityMatrix = ({ field, matrix, path = CAPABILITIES_FIELD, readOnly = false, withinTab = false }) => {
287
396
  const { dispatchFields, setModified } = useForm();
288
397
  const paths = useMemo(() => capabilityPaths(matrix, path), [matrix, path]);
@@ -308,45 +417,38 @@ const ToolsTable = ({ matrix, path, readOnly, toggle, values }) => {
308
417
  title: "Globals"
309
418
  }].filter((namespace) => matrix[namespace.id].length > 0);
310
419
  return /*#__PURE__*/ jsxs("div", {
311
- className: [
312
- "field-type",
313
- "group-field",
314
- "group-field--top-level",
315
- withinTab && "group-field--within-tab",
316
- BASE_CLASS
317
- ].filter(Boolean).join(" "),
420
+ className: cx("field-type", "group-field", "group-field--top-level", withinTab && "group-field--within-tab", BASE_CLASS),
318
421
  children: [/*#__PURE__*/ jsx("style", { children: SHEET }), /*#__PURE__*/ jsxs("div", {
319
422
  className: "group-field__wrap",
320
- children: [
321
- /*#__PURE__*/ jsx("div", {
322
- className: "group-field__header",
323
- children: /*#__PURE__*/ jsxs("header", { children: [/*#__PURE__*/ jsx("h3", {
324
- className: "group-field__title",
325
- children: /*#__PURE__*/ jsx(FieldLabel, {
326
- as: "span",
327
- label: "Capabilities"
328
- })
329
- }), typeof field?.admin?.description === "string" ? /*#__PURE__*/ jsx(FieldDescription, {
330
- description: field.admin.description,
331
- path
332
- }) : null] })
333
- }),
334
- namespaces.map((namespace) => /*#__PURE__*/ jsx(NamespaceTable, {
423
+ children: [/*#__PURE__*/ jsx("div", {
424
+ className: "group-field__header",
425
+ children: /*#__PURE__*/ jsxs("header", { children: [withinTab ? null : /*#__PURE__*/ jsx("h3", {
426
+ className: "group-field__title",
427
+ children: /*#__PURE__*/ jsx(FieldLabel, {
428
+ as: "span",
429
+ label: "Capabilities"
430
+ })
431
+ }), typeof field?.admin?.description === "string" ? /*#__PURE__*/ jsx(FieldDescription, {
432
+ description: field.admin.description,
433
+ path
434
+ }) : null] })
435
+ }), /*#__PURE__*/ jsxs("div", {
436
+ className: `${BASE_CLASS}__sections`,
437
+ children: [namespaces.map((namespace) => /*#__PURE__*/ jsx(NamespaceTable, {
335
438
  matrix,
336
439
  namespace,
337
440
  path,
338
441
  readOnly,
339
442
  toggle,
340
443
  values
341
- }, namespace.id)),
342
- matrix.tools.length > 0 ? /*#__PURE__*/ jsx(ToolsTable, {
444
+ }, namespace.id)), matrix.tools.length > 0 ? /*#__PURE__*/ jsx(ToolsTable, {
343
445
  matrix,
344
446
  path,
345
447
  readOnly,
346
448
  toggle,
347
449
  values
348
- }) : null
349
- ]
450
+ }) : null]
451
+ })]
350
452
  })]
351
453
  });
352
454
  };