@objectstack/plugin-approvals 12.6.0 → 13.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 +24 -4
- package/dist/index.d.mts +27 -9
- package/dist/index.d.ts +27 -9
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/approval-node.test.ts +2 -2
- package/src/approval-node.ts +1 -1
- package/src/approval-revise.test.ts +3 -3
- package/src/approval-service.test.ts +10 -10
- package/src/approval-service.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/plugin-approvals",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Multi-step approval engine for ObjectStack — sys_approval_process + sys_approval_request + sys_approval_action + IApprovalService.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@objectstack/core": "
|
|
17
|
-
"@objectstack/formula": "
|
|
18
|
-
"@objectstack/metadata-core": "
|
|
19
|
-
"@objectstack/platform-objects": "
|
|
20
|
-
"@objectstack/spec": "
|
|
16
|
+
"@objectstack/core": "13.0.0",
|
|
17
|
+
"@objectstack/formula": "13.0.0",
|
|
18
|
+
"@objectstack/metadata-core": "13.0.0",
|
|
19
|
+
"@objectstack/platform-objects": "13.0.0",
|
|
20
|
+
"@objectstack/spec": "13.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "^26.1.0",
|
|
24
24
|
"typescript": "^6.0.3",
|
|
25
25
|
"vitest": "^4.1.10",
|
|
26
|
-
"@objectstack/service-automation": "
|
|
26
|
+
"@objectstack/service-automation": "13.0.0"
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"objectstack",
|
|
@@ -5,7 +5,7 @@ import { AutomationEngine } from '@objectstack/service-automation';
|
|
|
5
5
|
import { ApprovalService } from './approval-service.js';
|
|
6
6
|
import { registerApprovalNode } from './approval-node.js';
|
|
7
7
|
|
|
8
|
-
const SYSTEM_CTX = { isSystem: true,
|
|
8
|
+
const SYSTEM_CTX = { isSystem: true, positions: [], permissions: [] } as any;
|
|
9
9
|
|
|
10
10
|
const noopLogger = {
|
|
11
11
|
info() {}, warn() {}, error() {}, debug() {},
|
|
@@ -190,7 +190,7 @@ describe('Approval node bridge (ADR-0019)', () => {
|
|
|
190
190
|
const request = (await fake.find('sys_approval_request', { where: { status: 'pending' } }))[0];
|
|
191
191
|
|
|
192
192
|
await expect(
|
|
193
|
-
service.decideNode(request.id, { decision: 'approve', actorId: 'intruder' }, { isSystem: false,
|
|
193
|
+
service.decideNode(request.id, { decision: 'approve', actorId: 'intruder' }, { isSystem: false, positions: [], permissions: [] } as any),
|
|
194
194
|
).rejects.toThrow(/FORBIDDEN/);
|
|
195
195
|
});
|
|
196
196
|
});
|
package/src/approval-node.ts
CHANGED
|
@@ -48,7 +48,7 @@ interface MinimalLogger {
|
|
|
48
48
|
warn?: (msg: any, ...rest: any[]) => void;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const SYSTEM_CTX = { isSystem: true,
|
|
51
|
+
const SYSTEM_CTX = { isSystem: true, positions: [], permissions: [] } as const;
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* Register the `approval` node executor on the automation engine. Idempotent at
|
|
@@ -19,8 +19,8 @@ import { ApprovalService } from './approval-service.js';
|
|
|
19
19
|
import { registerApprovalNode } from './approval-node.js';
|
|
20
20
|
import { bindApprovalLockHook, APPROVALS_HOOK_PACKAGE } from './lifecycle-hooks.js';
|
|
21
21
|
|
|
22
|
-
const SYSTEM_CTX = { isSystem: true,
|
|
23
|
-
const USER_CTX = { isSystem: false,
|
|
22
|
+
const SYSTEM_CTX = { isSystem: true, positions: [], permissions: [] } as any;
|
|
23
|
+
const USER_CTX = { isSystem: false, positions: [], permissions: [] } as any;
|
|
24
24
|
|
|
25
25
|
const noopLogger = { info() {}, warn() {}, error() {}, debug() {} };
|
|
26
26
|
|
|
@@ -288,7 +288,7 @@ describe('Send back for revision (ADR-0044)', () => {
|
|
|
288
288
|
const editAttempt = () => hook!({
|
|
289
289
|
object: 'fin_expense',
|
|
290
290
|
input: { id: 'x1', data: { amount: 1200 } },
|
|
291
|
-
session: { isSystem: false,
|
|
291
|
+
session: { isSystem: false, positions: [] },
|
|
292
292
|
});
|
|
293
293
|
|
|
294
294
|
await expect(editAttempt()).rejects.toThrow(/RECORD_LOCKED/); // pending → locked
|
|
@@ -110,8 +110,8 @@ function makeFakeEngine() {
|
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
const CTX = { userId: 'u1', tenantId: 't1',
|
|
114
|
-
const SYS = { isSystem: true,
|
|
113
|
+
const CTX = { userId: 'u1', tenantId: 't1', positions: [], permissions: [] } as any;
|
|
114
|
+
const SYS = { isSystem: true, positions: [], permissions: [] } as any;
|
|
115
115
|
|
|
116
116
|
function nodeConfig(approvers: string[], extra: Record<string, any> = {}) {
|
|
117
117
|
return {
|
|
@@ -219,7 +219,7 @@ describe('ApprovalService (node era)', () => {
|
|
|
219
219
|
it('decideNode: blocks a non-approver in a non-system context', async () => {
|
|
220
220
|
const req = await svc.openNodeRequest(openInput(['u9']), CTX);
|
|
221
221
|
await expect(
|
|
222
|
-
svc.decideNode(req.id, { decision: 'approve', actorId: 'mallory' }, { isSystem: false,
|
|
222
|
+
svc.decideNode(req.id, { decision: 'approve', actorId: 'mallory' }, { isSystem: false, positions: [], permissions: [] } as any),
|
|
223
223
|
).rejects.toThrow(/FORBIDDEN/);
|
|
224
224
|
});
|
|
225
225
|
|
|
@@ -319,7 +319,7 @@ describe('ApprovalService (node era)', () => {
|
|
|
319
319
|
|
|
320
320
|
it('recall: blocks a non-submitter in a non-system context', async () => {
|
|
321
321
|
const req = await svc.openNodeRequest(openInput(['u9']), CTX);
|
|
322
|
-
await expect(svc.recall(req.id, { actorId: 'u9' }, {
|
|
322
|
+
await expect(svc.recall(req.id, { actorId: 'u9' }, { positions: [], permissions: [] } as any))
|
|
323
323
|
.rejects.toThrow(/FORBIDDEN/);
|
|
324
324
|
});
|
|
325
325
|
|
|
@@ -483,7 +483,7 @@ describe('ApprovalService (node era)', () => {
|
|
|
483
483
|
|
|
484
484
|
it('remind: only the submitter may nudge', async () => {
|
|
485
485
|
const req = await svc.openNodeRequest(openInput(['u9']), CTX);
|
|
486
|
-
await expect(svc.remind(req.id, { actorId: 'u9' }, {
|
|
486
|
+
await expect(svc.remind(req.id, { actorId: 'u9' }, { positions: [], permissions: [] } as any))
|
|
487
487
|
.rejects.toThrow(/FORBIDDEN/);
|
|
488
488
|
});
|
|
489
489
|
|
|
@@ -503,7 +503,7 @@ describe('ApprovalService (node era)', () => {
|
|
|
503
503
|
const req = await svc.openNodeRequest(openInput(['u9']), CTX);
|
|
504
504
|
await svc.comment(req.id, { actorId: 'u1', comment: 'Numbers attached.' }, CTX);
|
|
505
505
|
await svc.comment(req.id, { actorId: 'u9', comment: 'Thanks, reviewing.' }, CTX);
|
|
506
|
-
await expect(svc.comment(req.id, { actorId: 'outsider', comment: 'hi' }, {
|
|
506
|
+
await expect(svc.comment(req.id, { actorId: 'outsider', comment: 'hi' }, { positions: [], permissions: [] } as any))
|
|
507
507
|
.rejects.toThrow(/FORBIDDEN/);
|
|
508
508
|
const actions = await svc.listActions(req.id, SYS);
|
|
509
509
|
expect(actions.filter(a => a.action === 'comment')).toHaveLength(2);
|
|
@@ -882,7 +882,7 @@ describe('record-lock hook (node era)', () => {
|
|
|
882
882
|
engine.fire('beforeUpdate', {
|
|
883
883
|
object: 'opportunity',
|
|
884
884
|
input: { id: 'opp1', data: { amount: 200 } },
|
|
885
|
-
session: { isSystem: false,
|
|
885
|
+
session: { isSystem: false, positions: [], userId: 'u1' },
|
|
886
886
|
}),
|
|
887
887
|
).rejects.toThrow(/RECORD_LOCKED/);
|
|
888
888
|
});
|
|
@@ -892,7 +892,7 @@ describe('record-lock hook (node era)', () => {
|
|
|
892
892
|
engine.fire('beforeUpdate', {
|
|
893
893
|
object: 'opportunity',
|
|
894
894
|
input: { id: 'opp1', data: { approval_status: 'approved' } },
|
|
895
|
-
session: { isSystem: false,
|
|
895
|
+
session: { isSystem: false, positions: [] },
|
|
896
896
|
}),
|
|
897
897
|
).resolves.toBeUndefined();
|
|
898
898
|
});
|
|
@@ -902,7 +902,7 @@ describe('record-lock hook (node era)', () => {
|
|
|
902
902
|
engine.fire('beforeUpdate', {
|
|
903
903
|
object: 'opportunity',
|
|
904
904
|
input: { id: 'opp1', data: { amount: 200 } },
|
|
905
|
-
session: { isSystem: true,
|
|
905
|
+
session: { isSystem: true, positions: [] },
|
|
906
906
|
}),
|
|
907
907
|
).resolves.toBeUndefined();
|
|
908
908
|
});
|
|
@@ -922,7 +922,7 @@ describe('record-lock hook (node era)', () => {
|
|
|
922
922
|
engine.fire('beforeUpdate', {
|
|
923
923
|
object: 'opportunity',
|
|
924
924
|
input: { id: 'other_record', data: { amount: 200 } },
|
|
925
|
-
session: { isSystem: false,
|
|
925
|
+
session: { isSystem: false, positions: [] },
|
|
926
926
|
}),
|
|
927
927
|
).resolves.toBeUndefined();
|
|
928
928
|
});
|
package/src/approval-service.ts
CHANGED
|
@@ -97,7 +97,7 @@ export type ActionTokenOutcome =
|
|
|
97
97
|
| { ok: true; action: 'approve' | 'reject'; request: ApprovalRequestRow; approverId: string }
|
|
98
98
|
| { ok: false; reason: 'invalid' | 'expired' | 'consumed' | 'not_pending' | 'not_approver'; request?: ApprovalRequestRow };
|
|
99
99
|
|
|
100
|
-
const SYSTEM_CTX = { isSystem: true,
|
|
100
|
+
const SYSTEM_CTX = { isSystem: true, positions: [], permissions: [] } as const;
|
|
101
101
|
|
|
102
102
|
function uid(prefix: string): string {
|
|
103
103
|
const g: any = globalThis as any;
|