@objectstack/plugin-approvals 16.0.0-rc.0 → 16.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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +220 -0
- package/dist/index.d.mts +44 -22
- package/dist/index.d.ts +44 -22
- package/dist/index.js +51 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +51 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/approval-service.test.ts +27 -0
- package/src/approval-service.ts +22 -0
- package/src/sys-approval-approver.object.ts +1 -1
- package/src/sys-approval-request.object.test.ts +14 -5
- package/src/sys-approval-request.object.ts +26 -17
- package/src/sys-approval-token.object.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/plugin-approvals@16.0.0
|
|
2
|
+
> @objectstack/plugin-approvals@16.0.0 build /home/runner/work/framework/framework/packages/plugins/plugin-approvals
|
|
3
3
|
> tsup --config ../../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m156.89 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m275.84 KB[39m
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 309ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.js [22m[32m158.72 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m277.08 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 309ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 27084ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m420.38 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m420.38 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,225 @@
|
|
|
1
1
|
# @objectstack/plugin-approvals
|
|
2
2
|
|
|
3
|
+
## 16.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e412fb6: feat(approvals): declare file attachments on approve/reject decisions
|
|
8
|
+
|
|
9
|
+
The declared `approval_approve` / `approval_reject` actions on
|
|
10
|
+
`sys_approval_request` gain an optional multi-file `attachments` param
|
|
11
|
+
(`type: 'file'`, `multiple`). The console renders `type:'file'` action params
|
|
12
|
+
through the shared upload widget (objectui ADR-0059) and POSTs the resolved
|
|
13
|
+
`attachments: string[]`, so a reviewer can attach supporting files to a
|
|
14
|
+
decision through the generic declared-action dialog — letting the approvals
|
|
15
|
+
inbox retire its hand-wired attachment composer (objectui#2698).
|
|
16
|
+
|
|
17
|
+
Purely additive metadata: the decision route already forwards
|
|
18
|
+
`body.attachments` to `ApprovalService.decide`, and the
|
|
19
|
+
`sys_approval_action.attachments` column (file, multiple) already persists them
|
|
20
|
+
(#3266/#3274). No service or route change.
|
|
21
|
+
|
|
22
|
+
- 8efa395: feat(approvals): server-computed `viewer` capability for precise decision-action gating
|
|
23
|
+
|
|
24
|
+
`getRequest` / `listRequests` now attach a per-viewer block —
|
|
25
|
+
`viewer: { can_act, is_submitter }` — computed from the caller's context
|
|
26
|
+
(`ApprovalRequestRow.viewer`):
|
|
27
|
+
|
|
28
|
+
- `can_act` — the caller is a _current pending approver_ (their user id is in the
|
|
29
|
+
request's resolved `pending_approvers` while it is still `pending`). This is
|
|
30
|
+
the same check the decision methods authorize with, so it already reflects
|
|
31
|
+
position/team/manager resolution — strictly more accurate than a client-side
|
|
32
|
+
identity guess.
|
|
33
|
+
- `is_submitter` — the caller submitted the request.
|
|
34
|
+
|
|
35
|
+
The declared decision actions on `sys_approval_request` now gate on it: approver
|
|
36
|
+
actions (approve/reject/reassign/send-back/request-info) use
|
|
37
|
+
`record.viewer.can_act`; submitter levers (remind/recall/resubmit) use
|
|
38
|
+
`record.viewer.is_submitter`. Previously approver actions only trimmed the
|
|
39
|
+
non-pending case, so a submitter viewing their own pending request saw buttons
|
|
40
|
+
they couldn't use (the backend 403'd); a position-addressed approver could be
|
|
41
|
+
wrongly hidden by the old client heuristic. Where `viewer` is absent (a row
|
|
42
|
+
surfaced outside a service read with a user context), the predicate fails closed.
|
|
43
|
+
|
|
44
|
+
- 3a18b60: feat(approvals): rename the `role` approver type to `org_membership_level` (#3133)
|
|
45
|
+
|
|
46
|
+
`ApproverType.role` was the last platform surface projecting the reserved word
|
|
47
|
+
"role" (ADR-0090 D3). It is not covered by D3's better-auth exception: that
|
|
48
|
+
exception protects better-auth's own `sys_member.role` **column**, which we do
|
|
49
|
+
not own — `ApproverType` is our own enum, an authoring surface, and D3 mandates
|
|
50
|
+
that the projection of that concept is spelled `org_membership_level` and
|
|
51
|
+
labelled "organization membership", **never "role"**.
|
|
52
|
+
|
|
53
|
+
The sentence licensing the leak was also false: ADR-0090 D3 claims
|
|
54
|
+
`sys_member.role` is "already relabelled `org_membership_level` in the platform
|
|
55
|
+
projection", but `org_membership_level` existed nowhere in the codebase and
|
|
56
|
+
ADR-0057 D7 lists that relabel under "Deferred (evidence-gated, P4)". The
|
|
57
|
+
projection never landed, so the word reached authors.
|
|
58
|
+
|
|
59
|
+
The name manufactured a real, silent failure — "hotcrm class": every other
|
|
60
|
+
surface renamed to `position` (`sys_role`, `ShareRecipientType.role`,
|
|
61
|
+
`ctx.roles[]`), so `{ type: 'role', value: 'sales_manager' }` reads as the
|
|
62
|
+
legacy spelling of a position. It resolves against the membership tier, finds
|
|
63
|
+
no member row, falls back to an inert `role:sales_manager` literal, and the
|
|
64
|
+
request waits forever on an approver that cannot exist.
|
|
65
|
+
|
|
66
|
+
- **spec**: `ApproverType` gains `org_membership_level`; `role` stays as a
|
|
67
|
+
deprecated alias for one window (a published 15.x flow keeps loading) with
|
|
68
|
+
`DEPRECATED_APPROVER_TYPES` + `canonicalApproverType()` as the single source
|
|
69
|
+
for the mapping. Removed in the next major.
|
|
70
|
+
- **plugin-approvals**: resolves on the canonical type and warns on the
|
|
71
|
+
deprecated spelling. The `type:value` fallback literal keeps the **authored**
|
|
72
|
+
spelling — stored `sys_approval_approver` rows and `pending_approvers` slots
|
|
73
|
+
from 15.x carry `role:<v>`, and rewriting it would orphan them.
|
|
74
|
+
- **lint**: `approval-role-not-membership-tier` → `approval-approver-not-membership-tier`
|
|
75
|
+
(the rule id carried the reserved word too), plus a new
|
|
76
|
+
`approval-approver-type-deprecated`. The two are mutually exclusive: a bad
|
|
77
|
+
_value_ wins, because prescribing `org_membership_level` for a position name
|
|
78
|
+
would be wrong advice — the fix there is `position`.
|
|
79
|
+
|
|
80
|
+
Authoring `type: 'role'` keeps working and now says so out loud. Rewrite it as
|
|
81
|
+
`org_membership_level`; if the value is an org position, the fix is `position`.
|
|
82
|
+
|
|
83
|
+
### Patch Changes
|
|
84
|
+
|
|
85
|
+
- 22013aa: **Split the overloaded `managedBy: 'system'` bucket into engine-owned vs. admin-writable, and enforce engine-owned writes (ADR-0103, #3220).** The `system` bucket conflated two incompatible write policies: rows a platform service owns end to end (never user-written), and platform-defined schema whose rows are legitimately admin/user-writable. It carried the same all-false affordance row as `better-auth`/`append-only` but, unlike `better-auth`, had no engine enforcement — a wildcard admin could raw-write these rows through the generic data API (ADR-0049 gap).
|
|
86
|
+
|
|
87
|
+
Rather than add a new `managedBy` enum value (which would fall through to fully-editable `platform` defaults on already-deployed Console clients), the write policy is now the **resolved affordance** (`resolveCrudAffordances` = bucket default + `userActions`), and _engine-owned_ is defined as a `system`/`append-only` object that grants no write:
|
|
88
|
+
|
|
89
|
+
- **Writable set declares `userActions`** — the RBAC link tables (`sys_user_position`, `sys_user_permission_set`, `sys_position_permission_set`), `sys_user_preference`, `sys_approval_delegation`, and the messaging config grids (`sys_notification_preference` / `…_subscription` / `…_template`) now declare `userActions: { create, edit, delete: true }`. The affordance is a declaration only — the `DelegatedAdminGate` / RLS / permission sets remain the authz.
|
|
90
|
+
- **Engine-owned objects locked to reads** — `apiMethods: ['get','list']` added where absent (jobs, notifications, approval request/approver/token/action, `sys_record_share`, `sys_automation_run`, mail/settings/secret audit, the messaging delivery pipeline). `sys_secret` is explicitly read-locked (an empty `apiMethods` array fails open).
|
|
91
|
+
- **`sys_import_job`** stays engine-owned: the REST import route now writes its job rows `isSystem`-elevated (attribution preserved via the explicit `created_by` stamp) and the object is locked to `['get','list']`.
|
|
92
|
+
- **New engine write guard** (`assertEngineOwnedWriteAllowed`, plugin-security) fail-closed rejects user-context generic writes to engine-owned `system`/`append-only` objects, keyed off the resolved affordance; `isSystem` and context-less engine/service writes bypass by construction. Wired into the security middleware alongside the other data-layer gates.
|
|
93
|
+
- **`reconcileManagedApiMethods`** (objectql registry) now runs for **every** managed bucket, not just `better-auth`: any advertised write verb an object's resolved affordances forbid is stripped at registration with a warning (the drift backstop, ADR-0049).
|
|
94
|
+
- **`/me/permissions` clamp** (plugin-hono-server) now clamps `system`/`append-only` as well as `better-auth`, so the client hint reflects `permission ∩ guard`.
|
|
95
|
+
|
|
96
|
+
**Potentially breaking:** a downstream/third-party `system` object that advertised generic write verbs relying on today's fail-open behaviour will have those verbs stripped (with a warning) and user-context generic writes to it rejected. Declare `userActions` opening the verbs the object legitimately takes from a user context. `better-auth` keeps plugin-auth's identity write guard unchanged; the row-level `managed_by` provenance vocabulary (ADR-0066) is a different axis and is untouched.
|
|
97
|
+
|
|
98
|
+
- 62a2117: **Split the overloaded `managedBy: 'system'` bucket with an explicit `engine-owned` value (ADR-0103 addendum, #3343).** ADR-0103 deferred the enum split ("revisitable later as a rename") because a new `managedBy` value would fall through to the fully-editable `platform` default on deployed Console clients. Both reasons against it are now retired — the server-side write guard / `apiMethods` reconciliation / `/me/permissions` clamp make that fallthrough cosmetic (the write is rejected regardless of what the client renders), and objectui#2712 closed the UI union — so v16 lands it, **additively**.
|
|
99
|
+
|
|
100
|
+
- **New enum value `engine-owned`** with the same all-locked default affordance row as `system` (`create/import/edit/delete: false`, `exportCsv: true`). It joins `ENGINE_OWNED_BUCKETS` (the engine write guard) and `GUARDED_WRITE_BUCKETS` (the `/me/permissions` clamp); the guard, `reconcileManagedApiMethods`, and the clamp mechanisms are unchanged — `engine-owned` is an explicit member of the set they already covered by resolved affordance.
|
|
101
|
+
- **20 objects relabelled `system → engine-owned`** — the ones the engine owns end to end and that declared no write-opening `userActions` (the metadata store, jobs, approval runtime rows, sharing rows, `sys_automation_run`, the messaging delivery/receipt pipeline, `sys_secret`, settings). One-line, behaviour-identical per object.
|
|
102
|
+
- **8 admin/user-writable objects keep `managedBy: 'system'`** (the RBAC link tables, `sys_user_preference`, `sys_approval_delegation`, the messaging config grids) — `system` now reads as "engine-managed schema, writable via `userActions`".
|
|
103
|
+
|
|
104
|
+
Behaviour-, enforcement- and wire-identical: resolved affordances, the guard verdict, the 405 `apiMethods` reconciliation, and the permissions clamp are the same before and after — this is a self-documenting relabel, not a policy change. No data migration (`managedBy` is schema metadata) and no code branches on the `'system'` literal. Retiring the overloaded `system` entirely (moving the 8 writable objects to a dedicated bucket) is a breaking rename deferred to v17.
|
|
105
|
+
|
|
106
|
+
- Updated dependencies [f972574]
|
|
107
|
+
- Updated dependencies [6289ec3]
|
|
108
|
+
- Updated dependencies [22013aa]
|
|
109
|
+
- Updated dependencies [3ad3dd5]
|
|
110
|
+
- Updated dependencies [8efa395]
|
|
111
|
+
- Updated dependencies [3a18b60]
|
|
112
|
+
- Updated dependencies [a8aa34c]
|
|
113
|
+
- Updated dependencies [e057f42]
|
|
114
|
+
- Updated dependencies [a3823b2]
|
|
115
|
+
- Updated dependencies [bc65105]
|
|
116
|
+
- Updated dependencies [43a3efb]
|
|
117
|
+
- Updated dependencies [524696a]
|
|
118
|
+
- Updated dependencies [6b51346]
|
|
119
|
+
- Updated dependencies [80273c8]
|
|
120
|
+
- Updated dependencies [bfa3c3f]
|
|
121
|
+
- Updated dependencies [5e3301d]
|
|
122
|
+
- Updated dependencies [dd9f223]
|
|
123
|
+
- Updated dependencies [46e876c]
|
|
124
|
+
- Updated dependencies [7125007]
|
|
125
|
+
- Updated dependencies [5f05de2]
|
|
126
|
+
- Updated dependencies [021ba4c]
|
|
127
|
+
- Updated dependencies [158aa14]
|
|
128
|
+
- Updated dependencies [62a2117]
|
|
129
|
+
- Updated dependencies [d2723e2]
|
|
130
|
+
- Updated dependencies [fefcd54]
|
|
131
|
+
- Updated dependencies [beaf2de]
|
|
132
|
+
- Updated dependencies [06cb319]
|
|
133
|
+
- Updated dependencies [369eb6e]
|
|
134
|
+
- Updated dependencies [06ff734]
|
|
135
|
+
- Updated dependencies [b659111]
|
|
136
|
+
- Updated dependencies [5754a23]
|
|
137
|
+
- Updated dependencies [6c270a6]
|
|
138
|
+
- Updated dependencies [290e2f0]
|
|
139
|
+
- Updated dependencies [668dd17]
|
|
140
|
+
- Updated dependencies [8abf133]
|
|
141
|
+
- Updated dependencies [e0859b1]
|
|
142
|
+
- Updated dependencies [04ecd4e]
|
|
143
|
+
- Updated dependencies [4d5a892]
|
|
144
|
+
- Updated dependencies [16cebeb]
|
|
145
|
+
- Updated dependencies [86d30af]
|
|
146
|
+
- Updated dependencies [8923843]
|
|
147
|
+
- Updated dependencies [ea32ec7]
|
|
148
|
+
- Updated dependencies [a2795f6]
|
|
149
|
+
- Updated dependencies [f16b492]
|
|
150
|
+
- Updated dependencies [4b6fde8]
|
|
151
|
+
- Updated dependencies [2018df9]
|
|
152
|
+
- Updated dependencies [fc5a3a2]
|
|
153
|
+
- Updated dependencies [8ff9210]
|
|
154
|
+
- @objectstack/spec@16.0.0
|
|
155
|
+
- @objectstack/platform-objects@16.0.0
|
|
156
|
+
- @objectstack/core@16.0.0
|
|
157
|
+
- @objectstack/formula@16.0.0
|
|
158
|
+
- @objectstack/metadata-core@16.0.0
|
|
159
|
+
|
|
160
|
+
## 16.0.0-rc.1
|
|
161
|
+
|
|
162
|
+
### Minor Changes
|
|
163
|
+
|
|
164
|
+
- e412fb6: feat(approvals): declare file attachments on approve/reject decisions
|
|
165
|
+
|
|
166
|
+
The declared `approval_approve` / `approval_reject` actions on
|
|
167
|
+
`sys_approval_request` gain an optional multi-file `attachments` param
|
|
168
|
+
(`type: 'file'`, `multiple`). The console renders `type:'file'` action params
|
|
169
|
+
through the shared upload widget (objectui ADR-0059) and POSTs the resolved
|
|
170
|
+
`attachments: string[]`, so a reviewer can attach supporting files to a
|
|
171
|
+
decision through the generic declared-action dialog — letting the approvals
|
|
172
|
+
inbox retire its hand-wired attachment composer (objectui#2698).
|
|
173
|
+
|
|
174
|
+
Purely additive metadata: the decision route already forwards
|
|
175
|
+
`body.attachments` to `ApprovalService.decide`, and the
|
|
176
|
+
`sys_approval_action.attachments` column (file, multiple) already persists them
|
|
177
|
+
(#3266/#3274). No service or route change.
|
|
178
|
+
|
|
179
|
+
- 8efa395: feat(approvals): server-computed `viewer` capability for precise decision-action gating
|
|
180
|
+
|
|
181
|
+
`getRequest` / `listRequests` now attach a per-viewer block —
|
|
182
|
+
`viewer: { can_act, is_submitter }` — computed from the caller's context
|
|
183
|
+
(`ApprovalRequestRow.viewer`):
|
|
184
|
+
|
|
185
|
+
- `can_act` — the caller is a _current pending approver_ (their user id is in the
|
|
186
|
+
request's resolved `pending_approvers` while it is still `pending`). This is
|
|
187
|
+
the same check the decision methods authorize with, so it already reflects
|
|
188
|
+
position/team/manager resolution — strictly more accurate than a client-side
|
|
189
|
+
identity guess.
|
|
190
|
+
- `is_submitter` — the caller submitted the request.
|
|
191
|
+
|
|
192
|
+
The declared decision actions on `sys_approval_request` now gate on it: approver
|
|
193
|
+
actions (approve/reject/reassign/send-back/request-info) use
|
|
194
|
+
`record.viewer.can_act`; submitter levers (remind/recall/resubmit) use
|
|
195
|
+
`record.viewer.is_submitter`. Previously approver actions only trimmed the
|
|
196
|
+
non-pending case, so a submitter viewing their own pending request saw buttons
|
|
197
|
+
they couldn't use (the backend 403'd); a position-addressed approver could be
|
|
198
|
+
wrongly hidden by the old client heuristic. Where `viewer` is absent (a row
|
|
199
|
+
surfaced outside a service read with a user context), the predicate fails closed.
|
|
200
|
+
|
|
201
|
+
### Patch Changes
|
|
202
|
+
|
|
203
|
+
- 62a2117: **Split the overloaded `managedBy: 'system'` bucket with an explicit `engine-owned` value (ADR-0103 addendum, #3343).** ADR-0103 deferred the enum split ("revisitable later as a rename") because a new `managedBy` value would fall through to the fully-editable `platform` default on deployed Console clients. Both reasons against it are now retired — the server-side write guard / `apiMethods` reconciliation / `/me/permissions` clamp make that fallthrough cosmetic (the write is rejected regardless of what the client renders), and objectui#2712 closed the UI union — so v16 lands it, **additively**.
|
|
204
|
+
|
|
205
|
+
- **New enum value `engine-owned`** with the same all-locked default affordance row as `system` (`create/import/edit/delete: false`, `exportCsv: true`). It joins `ENGINE_OWNED_BUCKETS` (the engine write guard) and `GUARDED_WRITE_BUCKETS` (the `/me/permissions` clamp); the guard, `reconcileManagedApiMethods`, and the clamp mechanisms are unchanged — `engine-owned` is an explicit member of the set they already covered by resolved affordance.
|
|
206
|
+
- **20 objects relabelled `system → engine-owned`** — the ones the engine owns end to end and that declared no write-opening `userActions` (the metadata store, jobs, approval runtime rows, sharing rows, `sys_automation_run`, the messaging delivery/receipt pipeline, `sys_secret`, settings). One-line, behaviour-identical per object.
|
|
207
|
+
- **8 admin/user-writable objects keep `managedBy: 'system'`** (the RBAC link tables, `sys_user_preference`, `sys_approval_delegation`, the messaging config grids) — `system` now reads as "engine-managed schema, writable via `userActions`".
|
|
208
|
+
|
|
209
|
+
Behaviour-, enforcement- and wire-identical: resolved affordances, the guard verdict, the 405 `apiMethods` reconciliation, and the permissions clamp are the same before and after — this is a self-documenting relabel, not a policy change. No data migration (`managedBy` is schema metadata) and no code branches on the `'system'` literal. Retiring the overloaded `system` entirely (moving the 8 writable objects to a dedicated bucket) is a breaking rename deferred to v17.
|
|
210
|
+
|
|
211
|
+
- Updated dependencies [6289ec3]
|
|
212
|
+
- Updated dependencies [8efa395]
|
|
213
|
+
- Updated dependencies [bfa3c3f]
|
|
214
|
+
- Updated dependencies [7125007]
|
|
215
|
+
- Updated dependencies [62a2117]
|
|
216
|
+
- Updated dependencies [06ff734]
|
|
217
|
+
- @objectstack/spec@16.0.0-rc.1
|
|
218
|
+
- @objectstack/platform-objects@16.0.0-rc.1
|
|
219
|
+
- @objectstack/formula@16.0.0-rc.1
|
|
220
|
+
- @objectstack/metadata-core@16.0.0-rc.1
|
|
221
|
+
- @objectstack/core@16.0.0-rc.1
|
|
222
|
+
|
|
3
223
|
## 16.0.0-rc.0
|
|
4
224
|
|
|
5
225
|
### Minor Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -220,7 +220,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
220
220
|
pluralLabel?: string | undefined;
|
|
221
221
|
description?: string | undefined;
|
|
222
222
|
icon?: string | undefined;
|
|
223
|
-
managedBy?: "platform" | "system" | "config" | "append-only" | "better-auth" | undefined;
|
|
223
|
+
managedBy?: "platform" | "system" | "config" | "engine-owned" | "append-only" | "better-auth" | undefined;
|
|
224
224
|
ownership?: "none" | "org" | "user" | undefined;
|
|
225
225
|
userActions?: {
|
|
226
226
|
create?: boolean | undefined;
|
|
@@ -418,7 +418,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
418
418
|
}[] | undefined;
|
|
419
419
|
filter?: {
|
|
420
420
|
field: string;
|
|
421
|
-
operator:
|
|
421
|
+
operator: "in" | "contains" | "after" | "starts_with" | "ends_with" | "not_in" | "not_contains" | "between" | "is_null" | "is_not_null" | "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "greater_than_or_equal" | "less_than_or_equal" | "before";
|
|
422
422
|
value?: string | number | boolean | (string | number)[] | null | undefined;
|
|
423
423
|
}[] | undefined;
|
|
424
424
|
description?: string | undefined;
|
|
@@ -577,7 +577,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
577
577
|
view?: string | undefined;
|
|
578
578
|
filter?: {
|
|
579
579
|
field: string;
|
|
580
|
-
operator:
|
|
580
|
+
operator: "in" | "contains" | "after" | "starts_with" | "ends_with" | "not_in" | "not_contains" | "between" | "is_null" | "is_not_null" | "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "greater_than_or_equal" | "less_than_or_equal" | "before";
|
|
581
581
|
value?: string | number | boolean | (string | number)[] | null | undefined;
|
|
582
582
|
}[] | undefined;
|
|
583
583
|
order?: number | undefined;
|
|
@@ -833,7 +833,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
833
833
|
readonly pluralLabel: "Approval Requests";
|
|
834
834
|
readonly icon: "inbox";
|
|
835
835
|
readonly isSystem: true;
|
|
836
|
-
readonly managedBy: "
|
|
836
|
+
readonly managedBy: "engine-owned";
|
|
837
837
|
readonly description: "Live approval instance tracked per submission";
|
|
838
838
|
readonly displayNameField: "id";
|
|
839
839
|
readonly nameField: "id";
|
|
@@ -3349,8 +3349,14 @@ declare const SysApprovalRequest: Omit<{
|
|
|
3349
3349
|
readonly label: "Comment";
|
|
3350
3350
|
readonly type: "textarea";
|
|
3351
3351
|
readonly required: false;
|
|
3352
|
+
}, {
|
|
3353
|
+
readonly name: "attachments";
|
|
3354
|
+
readonly label: "Attachments";
|
|
3355
|
+
readonly type: "file";
|
|
3356
|
+
readonly multiple: true;
|
|
3357
|
+
readonly required: false;
|
|
3352
3358
|
}];
|
|
3353
|
-
readonly visible: "record.
|
|
3359
|
+
readonly visible: "record.viewer.can_act";
|
|
3354
3360
|
readonly locations: ["record_section", "list_item"];
|
|
3355
3361
|
readonly successMessage: "Approved.";
|
|
3356
3362
|
readonly refreshAfter: true;
|
|
@@ -3366,8 +3372,14 @@ declare const SysApprovalRequest: Omit<{
|
|
|
3366
3372
|
readonly label: "Comment";
|
|
3367
3373
|
readonly type: "textarea";
|
|
3368
3374
|
readonly required: false;
|
|
3375
|
+
}, {
|
|
3376
|
+
readonly name: "attachments";
|
|
3377
|
+
readonly label: "Attachments";
|
|
3378
|
+
readonly type: "file";
|
|
3379
|
+
readonly multiple: true;
|
|
3380
|
+
readonly required: false;
|
|
3369
3381
|
}];
|
|
3370
|
-
readonly visible: "record.
|
|
3382
|
+
readonly visible: "record.viewer.can_act";
|
|
3371
3383
|
readonly confirmText: "Reject this request? A rejection is final for every approver.";
|
|
3372
3384
|
readonly locations: ["record_section", "list_item"];
|
|
3373
3385
|
readonly successMessage: "Rejected.";
|
|
@@ -3391,7 +3403,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
3391
3403
|
readonly type: "textarea";
|
|
3392
3404
|
readonly required: false;
|
|
3393
3405
|
}];
|
|
3394
|
-
readonly visible: "record.
|
|
3406
|
+
readonly visible: "record.viewer.can_act";
|
|
3395
3407
|
readonly locations: ["record_section"];
|
|
3396
3408
|
readonly successMessage: "Reassigned.";
|
|
3397
3409
|
readonly refreshAfter: true;
|
|
@@ -3408,7 +3420,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
3408
3420
|
readonly type: "textarea";
|
|
3409
3421
|
readonly required: false;
|
|
3410
3422
|
}];
|
|
3411
|
-
readonly visible: "record.
|
|
3423
|
+
readonly visible: "record.viewer.can_act";
|
|
3412
3424
|
readonly locations: ["record_section"];
|
|
3413
3425
|
readonly successMessage: "Sent back for revision.";
|
|
3414
3426
|
readonly refreshAfter: true;
|
|
@@ -3425,7 +3437,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
3425
3437
|
readonly type: "textarea";
|
|
3426
3438
|
readonly required: true;
|
|
3427
3439
|
}];
|
|
3428
|
-
readonly visible: "record.
|
|
3440
|
+
readonly visible: "record.viewer.can_act";
|
|
3429
3441
|
readonly locations: ["record_section"];
|
|
3430
3442
|
readonly successMessage: "Information requested.";
|
|
3431
3443
|
readonly refreshAfter: true;
|
|
@@ -3442,7 +3454,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
3442
3454
|
readonly type: "textarea";
|
|
3443
3455
|
readonly required: false;
|
|
3444
3456
|
}];
|
|
3445
|
-
readonly visible: "record.status == \"pending\" && record.
|
|
3457
|
+
readonly visible: "record.status == \"pending\" && record.viewer.is_submitter";
|
|
3446
3458
|
readonly locations: ["record_section"];
|
|
3447
3459
|
readonly successMessage: "Reminder sent.";
|
|
3448
3460
|
readonly refreshAfter: true;
|
|
@@ -3459,7 +3471,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
3459
3471
|
readonly type: "textarea";
|
|
3460
3472
|
readonly required: false;
|
|
3461
3473
|
}];
|
|
3462
|
-
readonly visible: "(record.status == \"pending\" || record.status == \"returned\") && record.
|
|
3474
|
+
readonly visible: "(record.status == \"pending\" || record.status == \"returned\") && record.viewer.is_submitter";
|
|
3463
3475
|
readonly confirmText: "Recall this request? Approvers can no longer act on it and the record is unlocked.";
|
|
3464
3476
|
readonly locations: ["record_section"];
|
|
3465
3477
|
readonly successMessage: "Recalled.";
|
|
@@ -3477,7 +3489,7 @@ declare const SysApprovalRequest: Omit<{
|
|
|
3477
3489
|
readonly type: "textarea";
|
|
3478
3490
|
readonly required: false;
|
|
3479
3491
|
}];
|
|
3480
|
-
readonly visible: "record.status == \"returned\" && record.
|
|
3492
|
+
readonly visible: "record.status == \"returned\" && record.viewer.is_submitter";
|
|
3481
3493
|
readonly locations: ["record_section"];
|
|
3482
3494
|
readonly successMessage: "Resubmitted.";
|
|
3483
3495
|
readonly refreshAfter: true;
|
|
@@ -3694,7 +3706,7 @@ declare const SysApprovalAction: Omit<{
|
|
|
3694
3706
|
pluralLabel?: string | undefined;
|
|
3695
3707
|
description?: string | undefined;
|
|
3696
3708
|
icon?: string | undefined;
|
|
3697
|
-
managedBy?: "platform" | "system" | "config" | "append-only" | "better-auth" | undefined;
|
|
3709
|
+
managedBy?: "platform" | "system" | "config" | "engine-owned" | "append-only" | "better-auth" | undefined;
|
|
3698
3710
|
ownership?: "none" | "org" | "user" | undefined;
|
|
3699
3711
|
userActions?: {
|
|
3700
3712
|
create?: boolean | undefined;
|
|
@@ -3892,7 +3904,7 @@ declare const SysApprovalAction: Omit<{
|
|
|
3892
3904
|
}[] | undefined;
|
|
3893
3905
|
filter?: {
|
|
3894
3906
|
field: string;
|
|
3895
|
-
operator:
|
|
3907
|
+
operator: "in" | "contains" | "after" | "starts_with" | "ends_with" | "not_in" | "not_contains" | "between" | "is_null" | "is_not_null" | "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "greater_than_or_equal" | "less_than_or_equal" | "before";
|
|
3896
3908
|
value?: string | number | boolean | (string | number)[] | null | undefined;
|
|
3897
3909
|
}[] | undefined;
|
|
3898
3910
|
description?: string | undefined;
|
|
@@ -4051,7 +4063,7 @@ declare const SysApprovalAction: Omit<{
|
|
|
4051
4063
|
view?: string | undefined;
|
|
4052
4064
|
filter?: {
|
|
4053
4065
|
field: string;
|
|
4054
|
-
operator:
|
|
4066
|
+
operator: "in" | "contains" | "after" | "starts_with" | "ends_with" | "not_in" | "not_contains" | "between" | "is_null" | "is_not_null" | "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "greater_than_or_equal" | "less_than_or_equal" | "before";
|
|
4055
4067
|
value?: string | number | boolean | (string | number)[] | null | undefined;
|
|
4056
4068
|
}[] | undefined;
|
|
4057
4069
|
order?: number | undefined;
|
|
@@ -5940,7 +5952,7 @@ declare const SysApprovalApprover: Omit<{
|
|
|
5940
5952
|
pluralLabel?: string | undefined;
|
|
5941
5953
|
description?: string | undefined;
|
|
5942
5954
|
icon?: string | undefined;
|
|
5943
|
-
managedBy?: "platform" | "system" | "config" | "append-only" | "better-auth" | undefined;
|
|
5955
|
+
managedBy?: "platform" | "system" | "config" | "engine-owned" | "append-only" | "better-auth" | undefined;
|
|
5944
5956
|
ownership?: "none" | "org" | "user" | undefined;
|
|
5945
5957
|
userActions?: {
|
|
5946
5958
|
create?: boolean | undefined;
|
|
@@ -6138,7 +6150,7 @@ declare const SysApprovalApprover: Omit<{
|
|
|
6138
6150
|
}[] | undefined;
|
|
6139
6151
|
filter?: {
|
|
6140
6152
|
field: string;
|
|
6141
|
-
operator:
|
|
6153
|
+
operator: "in" | "contains" | "after" | "starts_with" | "ends_with" | "not_in" | "not_contains" | "between" | "is_null" | "is_not_null" | "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "greater_than_or_equal" | "less_than_or_equal" | "before";
|
|
6142
6154
|
value?: string | number | boolean | (string | number)[] | null | undefined;
|
|
6143
6155
|
}[] | undefined;
|
|
6144
6156
|
description?: string | undefined;
|
|
@@ -6297,7 +6309,7 @@ declare const SysApprovalApprover: Omit<{
|
|
|
6297
6309
|
view?: string | undefined;
|
|
6298
6310
|
filter?: {
|
|
6299
6311
|
field: string;
|
|
6300
|
-
operator:
|
|
6312
|
+
operator: "in" | "contains" | "after" | "starts_with" | "ends_with" | "not_in" | "not_contains" | "between" | "is_null" | "is_not_null" | "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "greater_than_or_equal" | "less_than_or_equal" | "before";
|
|
6301
6313
|
value?: string | number | boolean | (string | number)[] | null | undefined;
|
|
6302
6314
|
}[] | undefined;
|
|
6303
6315
|
order?: number | undefined;
|
|
@@ -6553,7 +6565,7 @@ declare const SysApprovalApprover: Omit<{
|
|
|
6553
6565
|
readonly pluralLabel: "Approval Approvers";
|
|
6554
6566
|
readonly icon: "users";
|
|
6555
6567
|
readonly isSystem: true;
|
|
6556
|
-
readonly managedBy: "
|
|
6568
|
+
readonly managedBy: "engine-owned";
|
|
6557
6569
|
readonly description: "Normalized pending-approver rows for indexed inbox queries";
|
|
6558
6570
|
readonly displayNameField: "id";
|
|
6559
6571
|
readonly nameField: "id";
|
|
@@ -7457,7 +7469,7 @@ declare const SysApprovalDelegation: Omit<{
|
|
|
7457
7469
|
pluralLabel?: string | undefined;
|
|
7458
7470
|
description?: string | undefined;
|
|
7459
7471
|
icon?: string | undefined;
|
|
7460
|
-
managedBy?: "platform" | "system" | "config" | "append-only" | "better-auth" | undefined;
|
|
7472
|
+
managedBy?: "platform" | "system" | "config" | "engine-owned" | "append-only" | "better-auth" | undefined;
|
|
7461
7473
|
ownership?: "none" | "org" | "user" | undefined;
|
|
7462
7474
|
userActions?: {
|
|
7463
7475
|
create?: boolean | undefined;
|
|
@@ -7655,7 +7667,7 @@ declare const SysApprovalDelegation: Omit<{
|
|
|
7655
7667
|
}[] | undefined;
|
|
7656
7668
|
filter?: {
|
|
7657
7669
|
field: string;
|
|
7658
|
-
operator:
|
|
7670
|
+
operator: "in" | "contains" | "after" | "starts_with" | "ends_with" | "not_in" | "not_contains" | "between" | "is_null" | "is_not_null" | "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "greater_than_or_equal" | "less_than_or_equal" | "before";
|
|
7659
7671
|
value?: string | number | boolean | (string | number)[] | null | undefined;
|
|
7660
7672
|
}[] | undefined;
|
|
7661
7673
|
description?: string | undefined;
|
|
@@ -7814,7 +7826,7 @@ declare const SysApprovalDelegation: Omit<{
|
|
|
7814
7826
|
view?: string | undefined;
|
|
7815
7827
|
filter?: {
|
|
7816
7828
|
field: string;
|
|
7817
|
-
operator:
|
|
7829
|
+
operator: "in" | "contains" | "after" | "starts_with" | "ends_with" | "not_in" | "not_contains" | "between" | "is_null" | "is_not_null" | "is_empty" | "is_not_empty" | "equals" | "not_equals" | "greater_than" | "less_than" | "greater_than_or_equal" | "less_than_or_equal" | "before";
|
|
7818
7830
|
value?: string | number | boolean | (string | number)[] | null | undefined;
|
|
7819
7831
|
}[] | undefined;
|
|
7820
7832
|
order?: number | undefined;
|
|
@@ -9780,6 +9792,16 @@ declare class ApprovalService implements IApprovalService {
|
|
|
9780
9792
|
* Display-only and best-effort — errors leave the row untouched.
|
|
9781
9793
|
*/
|
|
9782
9794
|
private attachDecisionProgress;
|
|
9795
|
+
/**
|
|
9796
|
+
* Attach the per-viewer capability block (#3310) from the caller's context.
|
|
9797
|
+
* `can_act` mirrors the exact authorization the decision methods enforce — the
|
|
9798
|
+
* caller's user id is in the resolved `pending_approvers` while the request is
|
|
9799
|
+
* still `pending` (position/team/manager approvers are already resolved to
|
|
9800
|
+
* concrete user ids at open time, so a plain membership test is faithful).
|
|
9801
|
+
* `is_submitter` is a straight owner check. System/tokenless contexts get a
|
|
9802
|
+
* both-false block. Cheap + synchronous — safe on list reads.
|
|
9803
|
+
*/
|
|
9804
|
+
private attachViewers;
|
|
9783
9805
|
/**
|
|
9784
9806
|
* Derive approval-step progress from the owning flow's graph (single-read
|
|
9785
9807
|
* enrichment only — list reads skip it). Walks from the start node
|