@rytass/bpm-core-shared 0.17.5 → 0.17.7
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 +34 -0
- package/package.json +1 -1
- package/src/index.d.ts +3 -0
- package/src/index.js +3 -0
- package/src/index.js.map +1 -1
- package/src/lib/field-access-lint.d.ts +119 -0
- package/src/lib/field-access-lint.js +379 -0
- package/src/lib/field-access-lint.js.map +1 -0
- package/src/lib/field-access-matrix.d.ts +101 -0
- package/src/lib/field-access-matrix.js +213 -0
- package/src/lib/field-access-matrix.js.map +1 -0
- package/src/lib/field-access.d.ts +63 -0
- package/src/lib/field-access.js +140 -0
- package/src/lib/field-access.js.map +1 -0
- package/src/lib/workflow-command.d.ts +26 -1
- package/src/lib/workflow-command.js +52 -0
- package/src/lib/workflow-command.js.map +1 -1
- package/src/lib/workflow-graph.d.ts +48 -0
- package/src/lib/workflow-graph.js +58 -1
- package/src/lib/workflow-graph.js.map +1 -1
- package/src/lib/workflow-toolset.js +77 -1
- package/src/lib/workflow-toolset.js.map +1 -1
- package/src/lib/workflow.d.ts +20 -4
- package/src/lib/workflow.js.map +1 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { FieldAccessIssue, UserTaskFillConflict } from './field-access-lint';
|
|
2
|
+
import type { FormDefinitionSchema, FormFieldDefinition } from './form';
|
|
3
|
+
import type { FieldAccessEntry, WorkflowDefinition } from './workflow';
|
|
4
|
+
/**
|
|
5
|
+
* What a cell of the field access matrix offers. `DEFAULT` writes nothing for
|
|
6
|
+
* the node, so the field falls back to the derived value; the other four each
|
|
7
|
+
* stand for one `FieldAccessEntry` (`fill` other than `NONE` is always shown,
|
|
8
|
+
* since a hidden fill cannot be published).
|
|
9
|
+
*/
|
|
10
|
+
export type FieldAccessChoice = 'DEFAULT' | 'FILL_OPTIONAL' | 'FILL_REQUIRED' | 'HIDE' | 'SHOW';
|
|
11
|
+
export declare const FIELD_ACCESS_CHOICES: readonly FieldAccessChoice[];
|
|
12
|
+
/**
|
|
13
|
+
* What a viewer at one place in the flow gets for a field. `FILL` is the
|
|
14
|
+
* applicant's own field; `NOT_SHOWN` is an approver-filled field nobody has
|
|
15
|
+
* filled in yet, which BPM leaves off the screen; `HIDDEN` is a node that
|
|
16
|
+
* chose to hide the field (display only, never a permission).
|
|
17
|
+
*/
|
|
18
|
+
export type FieldAccessEffective = 'FILL' | 'FILL_OPTIONAL' | 'FILL_REQUIRED' | 'HIDDEN' | 'NOT_SHOWN' | 'READ_ONLY';
|
|
19
|
+
/**
|
|
20
|
+
* Why a cell reads the way it does: the node lists the field (`EXPLICIT`), an
|
|
21
|
+
* ordinary field every node sees read-only (`APPLICANT_FIELD`), an
|
|
22
|
+
* approver-filled field a fill node before this one has filled in
|
|
23
|
+
* (`FILLED_EARLIER`), or one no such node has yet (`NOT_YET_FILLED`).
|
|
24
|
+
*/
|
|
25
|
+
export type FieldAccessCellReason = 'APPLICANT_FIELD' | 'EXPLICIT' | 'FILLED_EARLIER' | 'NOT_YET_FILLED';
|
|
26
|
+
/** A hint the matrix shows that does not block publishing. */
|
|
27
|
+
export interface FieldAccessNotice {
|
|
28
|
+
readonly code: 'shownBeforeFilled';
|
|
29
|
+
readonly fieldKey: string;
|
|
30
|
+
readonly nodeId: string;
|
|
31
|
+
}
|
|
32
|
+
export interface FieldAccessMatrixColumn {
|
|
33
|
+
/**
|
|
34
|
+
* The label of the edge from an exclusive gateway straight into the node,
|
|
35
|
+
* so nodes on mutually exclusive branches can say which branch they are on.
|
|
36
|
+
*/
|
|
37
|
+
readonly branchLabel: string | null;
|
|
38
|
+
/** Why the node cannot let its approver fill fields in. */
|
|
39
|
+
readonly fillConflicts: readonly UserTaskFillConflict[];
|
|
40
|
+
/** Node-level issues: the legacy key, an invalid list, fill conflicts. */
|
|
41
|
+
readonly issues: readonly FieldAccessIssue[];
|
|
42
|
+
readonly label: string;
|
|
43
|
+
readonly nodeId: string;
|
|
44
|
+
/** 1-based position among the user tasks, in flow order (關卡 N). */
|
|
45
|
+
readonly stage: number;
|
|
46
|
+
}
|
|
47
|
+
export interface FieldAccessMatrixCell {
|
|
48
|
+
readonly choice: FieldAccessChoice;
|
|
49
|
+
/** The fill choices this cell cannot take, for the node or the field. */
|
|
50
|
+
readonly disabledChoices: readonly FieldAccessChoice[];
|
|
51
|
+
readonly effective: FieldAccessEffective;
|
|
52
|
+
readonly explicit: FieldAccessEntry | null;
|
|
53
|
+
readonly nodeId: string;
|
|
54
|
+
readonly reason: FieldAccessCellReason;
|
|
55
|
+
}
|
|
56
|
+
export interface FieldAccessMatrixRow {
|
|
57
|
+
readonly applicant: {
|
|
58
|
+
readonly effective: Extract<FieldAccessEffective, 'FILL' | 'NOT_SHOWN'>;
|
|
59
|
+
};
|
|
60
|
+
/** One per column, in column order. */
|
|
61
|
+
readonly cells: readonly FieldAccessMatrixCell[];
|
|
62
|
+
readonly field: FormFieldDefinition;
|
|
63
|
+
readonly fieldKey: string;
|
|
64
|
+
/** Why the form will not let an approver fill this field in. */
|
|
65
|
+
readonly fillBlockers: readonly FieldAccessIssue[];
|
|
66
|
+
/** The user tasks that fill this field in, in column order. */
|
|
67
|
+
readonly fillNodeIds: readonly string[];
|
|
68
|
+
/** Publish-blocking issues that name this field. */
|
|
69
|
+
readonly issues: readonly FieldAccessIssue[];
|
|
70
|
+
readonly notices: readonly FieldAccessNotice[];
|
|
71
|
+
}
|
|
72
|
+
export interface FieldAccessMatrix {
|
|
73
|
+
readonly columns: readonly FieldAccessMatrixColumn[];
|
|
74
|
+
/**
|
|
75
|
+
* Every publish-blocking field access issue of the definition, the same set
|
|
76
|
+
* the publish lint reports apart from its early-read rule, in lint order.
|
|
77
|
+
*/
|
|
78
|
+
readonly issues: readonly FieldAccessIssue[];
|
|
79
|
+
readonly rows: readonly FieldAccessMatrixRow[];
|
|
80
|
+
}
|
|
81
|
+
export declare function readFieldAccessChoice(entry: FieldAccessEntry | null): FieldAccessChoice;
|
|
82
|
+
/** The entry a choice writes for the field, or `null` for `DEFAULT`. */
|
|
83
|
+
export declare function readFieldAccessEntryForChoice(fieldKey: string, choice: FieldAccessChoice): FieldAccessEntry | null;
|
|
84
|
+
/**
|
|
85
|
+
* The designer's field access matrix: one row per top-level form field, one
|
|
86
|
+
* column per user task in flow order, and in each cell the node's explicit
|
|
87
|
+
* entry or, without one, what the engine's `resolveFieldAccess` would give a
|
|
88
|
+
* viewer at that node.
|
|
89
|
+
*
|
|
90
|
+
* The engine reveals an approver-filled field once a fill node has produced a
|
|
91
|
+
* task in the case; at design time that reads as "a fill node earlier in the
|
|
92
|
+
* column order reaches this one". A fill node on another branch, or one only
|
|
93
|
+
* reached by looping back, does not count, so the cell shows the more
|
|
94
|
+
* cautious `NOT_SHOWN`.
|
|
95
|
+
*
|
|
96
|
+
* The issues come from the same `field-access-lint` rules the publish lint
|
|
97
|
+
* runs, so the matrix and publishing never disagree; only the early-read rule
|
|
98
|
+
* (`approverFilledFieldReadEarly`), which reads every node's expressions, is
|
|
99
|
+
* left to publishing.
|
|
100
|
+
*/
|
|
101
|
+
export declare function readFieldAccessMatrix(definition: WorkflowDefinition, schema: FormDefinitionSchema | null): FieldAccessMatrix;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FIELD_ACCESS_CHOICES = void 0;
|
|
4
|
+
exports.readFieldAccessChoice = readFieldAccessChoice;
|
|
5
|
+
exports.readFieldAccessEntryForChoice = readFieldAccessEntryForChoice;
|
|
6
|
+
exports.readFieldAccessMatrix = readFieldAccessMatrix;
|
|
7
|
+
const field_access_lint_1 = require("./field-access-lint");
|
|
8
|
+
const field_access_1 = require("./field-access");
|
|
9
|
+
exports.FIELD_ACCESS_CHOICES = [
|
|
10
|
+
'DEFAULT',
|
|
11
|
+
'SHOW',
|
|
12
|
+
'HIDE',
|
|
13
|
+
'FILL_OPTIONAL',
|
|
14
|
+
'FILL_REQUIRED',
|
|
15
|
+
];
|
|
16
|
+
const FILL_CHOICES = [
|
|
17
|
+
'FILL_OPTIONAL',
|
|
18
|
+
'FILL_REQUIRED',
|
|
19
|
+
];
|
|
20
|
+
function readFieldAccessChoice(entry) {
|
|
21
|
+
if (entry === null) {
|
|
22
|
+
return 'DEFAULT';
|
|
23
|
+
}
|
|
24
|
+
if (entry.fill === 'REQUIRED') {
|
|
25
|
+
return 'FILL_REQUIRED';
|
|
26
|
+
}
|
|
27
|
+
if (entry.fill === 'OPTIONAL') {
|
|
28
|
+
return 'FILL_OPTIONAL';
|
|
29
|
+
}
|
|
30
|
+
return entry.display === 'HIDE' ? 'HIDE' : 'SHOW';
|
|
31
|
+
}
|
|
32
|
+
const CHOICE_FILLS = {
|
|
33
|
+
FILL_OPTIONAL: 'OPTIONAL',
|
|
34
|
+
FILL_REQUIRED: 'REQUIRED',
|
|
35
|
+
HIDE: 'NONE',
|
|
36
|
+
SHOW: 'NONE',
|
|
37
|
+
};
|
|
38
|
+
/** The entry a choice writes for the field, or `null` for `DEFAULT`. */
|
|
39
|
+
function readFieldAccessEntryForChoice(fieldKey, choice) {
|
|
40
|
+
return choice === 'DEFAULT'
|
|
41
|
+
? null
|
|
42
|
+
: {
|
|
43
|
+
display: choice === 'HIDE' ? 'HIDE' : 'SHOW',
|
|
44
|
+
fieldKey,
|
|
45
|
+
fill: CHOICE_FILLS[choice],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The user tasks in flow order: a topological walk from the nodes nothing
|
|
50
|
+
* leads into, ties broken by definition order. A cycle (a loop back for
|
|
51
|
+
* rework) has no topological order, so the walk then takes the earliest
|
|
52
|
+
* remaining node, in definition order, that a placed node leads into —
|
|
53
|
+
* entering the loop where the flow does — and carries on.
|
|
54
|
+
*/
|
|
55
|
+
function readFlowOrderedUserTasks(definition) {
|
|
56
|
+
const nodeIds = definition.nodes.map((node) => node.id);
|
|
57
|
+
const known = new Set(nodeIds);
|
|
58
|
+
const edges = definition.edges.filter((edge) => known.has(edge.source) &&
|
|
59
|
+
known.has(edge.target) &&
|
|
60
|
+
edge.source !== edge.target);
|
|
61
|
+
const walk = (remaining, ordered) => {
|
|
62
|
+
var _a, _b;
|
|
63
|
+
if (remaining.length === 0) {
|
|
64
|
+
return ordered;
|
|
65
|
+
}
|
|
66
|
+
const left = new Set(remaining);
|
|
67
|
+
const placed = new Set(ordered);
|
|
68
|
+
const next = (_b = (_a = remaining.find((nodeId) => !edges.some((edge) => edge.target === nodeId && left.has(edge.source)))) !== null && _a !== void 0 ? _a : remaining.find((nodeId) => edges.some((edge) => edge.target === nodeId && placed.has(edge.source)))) !== null && _b !== void 0 ? _b : remaining[0];
|
|
69
|
+
return walk(remaining.filter((nodeId) => nodeId !== next), [...ordered, next]);
|
|
70
|
+
};
|
|
71
|
+
const order = walk([...new Set(nodeIds)], []);
|
|
72
|
+
const userTasks = definition.nodes.filter((node) => node.type === 'userTask');
|
|
73
|
+
const firstById = new Map([...userTasks]
|
|
74
|
+
.reverse()
|
|
75
|
+
.map((node) => [node.id, node]));
|
|
76
|
+
return order.flatMap((nodeId) => {
|
|
77
|
+
const node = firstById.get(nodeId);
|
|
78
|
+
return node === undefined ? [] : [node];
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function readBranchLabel(definition, nodeId) {
|
|
82
|
+
const gatewayIds = new Set(definition.nodes
|
|
83
|
+
.filter((node) => node.type === 'exclusiveGateway')
|
|
84
|
+
.map((node) => node.id));
|
|
85
|
+
const label = definition.edges
|
|
86
|
+
.filter((edge) => edge.target === nodeId && gatewayIds.has(edge.source))
|
|
87
|
+
.map((edge) => { var _a, _b, _c; return (_c = (_b = (_a = edge.data) === null || _a === void 0 ? void 0 : _a.label) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : ''; })
|
|
88
|
+
.find((value) => value !== '');
|
|
89
|
+
return label !== null && label !== void 0 ? label : null;
|
|
90
|
+
}
|
|
91
|
+
function readExplicitEffective(entry) {
|
|
92
|
+
if (entry.fill === 'REQUIRED') {
|
|
93
|
+
return 'FILL_REQUIRED';
|
|
94
|
+
}
|
|
95
|
+
if (entry.fill === 'OPTIONAL') {
|
|
96
|
+
return 'FILL_OPTIONAL';
|
|
97
|
+
}
|
|
98
|
+
return entry.display === 'HIDE' ? 'HIDDEN' : 'READ_ONLY';
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The designer's field access matrix: one row per top-level form field, one
|
|
102
|
+
* column per user task in flow order, and in each cell the node's explicit
|
|
103
|
+
* entry or, without one, what the engine's `resolveFieldAccess` would give a
|
|
104
|
+
* viewer at that node.
|
|
105
|
+
*
|
|
106
|
+
* The engine reveals an approver-filled field once a fill node has produced a
|
|
107
|
+
* task in the case; at design time that reads as "a fill node earlier in the
|
|
108
|
+
* column order reaches this one". A fill node on another branch, or one only
|
|
109
|
+
* reached by looping back, does not count, so the cell shows the more
|
|
110
|
+
* cautious `NOT_SHOWN`.
|
|
111
|
+
*
|
|
112
|
+
* The issues come from the same `field-access-lint` rules the publish lint
|
|
113
|
+
* runs, so the matrix and publishing never disagree; only the early-read rule
|
|
114
|
+
* (`approverFilledFieldReadEarly`), which reads every node's expressions, is
|
|
115
|
+
* left to publishing.
|
|
116
|
+
*/
|
|
117
|
+
function readFieldAccessMatrix(definition, schema) {
|
|
118
|
+
var _a;
|
|
119
|
+
const userTasks = readFlowOrderedUserTasks(definition);
|
|
120
|
+
const issues = [
|
|
121
|
+
...definition.nodes
|
|
122
|
+
.filter((node) => node.type === 'userTask')
|
|
123
|
+
.flatMap((node) => (0, field_access_lint_1.readNodeFieldAccessIssues)(node)),
|
|
124
|
+
...(0, field_access_lint_1.readDefinitionFieldAccessIssues)(definition),
|
|
125
|
+
...(0, field_access_lint_1.readFormFieldAccessIssues)(definition, schema),
|
|
126
|
+
];
|
|
127
|
+
const columns = userTasks.map((node, index) => ({
|
|
128
|
+
branchLabel: readBranchLabel(definition, node.id),
|
|
129
|
+
fillConflicts: (0, field_access_lint_1.readUserTaskFillConflicts)(node),
|
|
130
|
+
issues: (0, field_access_lint_1.readNodeFieldAccessIssues)(node).filter((issue) => issue.fieldKeys.length === 0),
|
|
131
|
+
label: node.data.label,
|
|
132
|
+
nodeId: node.id,
|
|
133
|
+
stage: index + 1,
|
|
134
|
+
}));
|
|
135
|
+
const reachable = new Map(userTasks.map((node) => [
|
|
136
|
+
node.id,
|
|
137
|
+
(0, field_access_lint_1.readReachableWorkflowNodeIds)(definition.edges, [node.id]),
|
|
138
|
+
]));
|
|
139
|
+
const accessByNode = new Map(userTasks.map((node) => [
|
|
140
|
+
node.id,
|
|
141
|
+
new Map((0, field_access_1.readNodeFieldAccess)(node).map((entry) => [
|
|
142
|
+
entry.fieldKey,
|
|
143
|
+
entry,
|
|
144
|
+
])),
|
|
145
|
+
]));
|
|
146
|
+
const indexById = new Map(userTasks.map((node, index) => [node.id, index]));
|
|
147
|
+
const isFilledBefore = (fillNodeIds, nodeIndex) => fillNodeIds.some((fillNodeId) => {
|
|
148
|
+
var _a, _b, _c;
|
|
149
|
+
const fillIndex = (_a = indexById.get(fillNodeId)) !== null && _a !== void 0 ? _a : -1;
|
|
150
|
+
return (fillIndex < nodeIndex &&
|
|
151
|
+
((_c = (_b = reachable.get(fillNodeId)) === null || _b === void 0 ? void 0 : _b.has(userTasks[nodeIndex].id)) !== null && _c !== void 0 ? _c : false));
|
|
152
|
+
});
|
|
153
|
+
const rows = ((_a = schema === null || schema === void 0 ? void 0 : schema.fields) !== null && _a !== void 0 ? _a : []).map((field) => {
|
|
154
|
+
const { fieldKey } = field;
|
|
155
|
+
const fillNodeIds = userTasks
|
|
156
|
+
.filter((node) => (0, field_access_1.readNodeFillableFieldKeys)(node).has(fieldKey))
|
|
157
|
+
.map((node) => node.id);
|
|
158
|
+
const isApproverFilled = fillNodeIds.length > 0;
|
|
159
|
+
const fillBlockers = (0, field_access_lint_1.readFormFieldFillIssues)(field);
|
|
160
|
+
const fieldDisabledChoices = fillBlockers.length > 0
|
|
161
|
+
? FILL_CHOICES
|
|
162
|
+
: field.type === 'boolean'
|
|
163
|
+
? ['FILL_REQUIRED']
|
|
164
|
+
: [];
|
|
165
|
+
const cells = userTasks.map((node, index) => {
|
|
166
|
+
var _a, _b;
|
|
167
|
+
const explicit = (_b = (_a = accessByNode.get(node.id)) === null || _a === void 0 ? void 0 : _a.get(fieldKey)) !== null && _b !== void 0 ? _b : null;
|
|
168
|
+
const filledBefore = isFilledBefore(fillNodeIds, index);
|
|
169
|
+
const reason = explicit !== null
|
|
170
|
+
? 'EXPLICIT'
|
|
171
|
+
: !isApproverFilled
|
|
172
|
+
? 'APPLICANT_FIELD'
|
|
173
|
+
: filledBefore
|
|
174
|
+
? 'FILLED_EARLIER'
|
|
175
|
+
: 'NOT_YET_FILLED';
|
|
176
|
+
return {
|
|
177
|
+
choice: readFieldAccessChoice(explicit),
|
|
178
|
+
disabledChoices: columns[index].fillConflicts.length > 0
|
|
179
|
+
? FILL_CHOICES
|
|
180
|
+
: fieldDisabledChoices,
|
|
181
|
+
effective: explicit !== null
|
|
182
|
+
? readExplicitEffective(explicit)
|
|
183
|
+
: reason === 'NOT_YET_FILLED'
|
|
184
|
+
? 'NOT_SHOWN'
|
|
185
|
+
: 'READ_ONLY',
|
|
186
|
+
explicit,
|
|
187
|
+
nodeId: node.id,
|
|
188
|
+
reason,
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
return {
|
|
192
|
+
applicant: { effective: isApproverFilled ? 'NOT_SHOWN' : 'FILL' },
|
|
193
|
+
cells,
|
|
194
|
+
field,
|
|
195
|
+
fieldKey,
|
|
196
|
+
fillBlockers,
|
|
197
|
+
fillNodeIds,
|
|
198
|
+
issues: issues.filter((issue) => issue.fieldKeys.includes(fieldKey)),
|
|
199
|
+
notices: cells
|
|
200
|
+
.filter((cell, index) => isApproverFilled &&
|
|
201
|
+
cell.effective === 'READ_ONLY' &&
|
|
202
|
+
cell.reason === 'EXPLICIT' &&
|
|
203
|
+
!isFilledBefore(fillNodeIds, index))
|
|
204
|
+
.map((cell) => ({
|
|
205
|
+
code: 'shownBeforeFilled',
|
|
206
|
+
fieldKey,
|
|
207
|
+
nodeId: cell.nodeId,
|
|
208
|
+
})),
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
return { columns, issues, rows };
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=field-access-matrix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-access-matrix.js","sourceRoot":"","sources":["../../../../../libs/shared/src/lib/field-access-matrix.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { FieldAccessEntry, UserTaskNode, WorkflowDefinition } from './workflow';
|
|
2
|
+
/** What a viewer may do with one top-level form field (`resolveFieldAccess`). */
|
|
3
|
+
export interface ResolvedFieldAccess {
|
|
4
|
+
readonly editable: boolean;
|
|
5
|
+
readonly visible: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Reads `fieldAccess` off a user task without trusting its shape: saved drafts
|
|
9
|
+
* can hold anything, and no caller may throw because of one. A value that is
|
|
10
|
+
* not an array reads as `[]`; an entry that is not an object or has no
|
|
11
|
+
* non-empty `fieldKey` string is skipped; the first entry for a `fieldKey`
|
|
12
|
+
* wins, so every reader agrees on which one counts. An unknown `fill` reads as
|
|
13
|
+
* `NONE`, so a malformed entry never lets anyone write, and an unknown
|
|
14
|
+
* `display` reads as `SHOW`. The deprecated `fieldPermissions` is never read.
|
|
15
|
+
*/
|
|
16
|
+
export declare const readNodeFieldAccess: (node: UserTaskNode) => readonly FieldAccessEntry[];
|
|
17
|
+
/**
|
|
18
|
+
* The top-level form field a field path addresses: `items.price` and
|
|
19
|
+
* `items[0]` both address `items`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const readTopLevelFieldKey: (fieldPath: string) => string;
|
|
22
|
+
/**
|
|
23
|
+
* The top-level form field a `SET_FORM_FIELD` `fieldPath` writes. The engine
|
|
24
|
+
* accepts an optional leading `form.` or `formData.` segment and trims each
|
|
25
|
+
* segment, so `form.amount` and ` amount ` both write `amount`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const readFormFieldPathTopLevelKey: (fieldPath: string) => string;
|
|
28
|
+
export declare const readNodeFillableFieldKeys: (node: UserTaskNode) => ReadonlySet<string>;
|
|
29
|
+
export declare const readNodeRequiredFieldKeys: (node: UserTaskNode) => ReadonlySet<string>;
|
|
30
|
+
/** Every field some user task lets its approver fill in. */
|
|
31
|
+
export declare const readApproverFilledFieldKeys: (definition: WorkflowDefinition) => ReadonlySet<string>;
|
|
32
|
+
/**
|
|
33
|
+
* The approver-filled fields that have been revealed to the applicant: those a
|
|
34
|
+
* user task that already produced a non-ad-hoc task lets its approver fill in.
|
|
35
|
+
* Ids that are missing or point to a non-user task are skipped.
|
|
36
|
+
*/
|
|
37
|
+
export declare const readRevealedFieldKeys: (definition: WorkflowDefinition, nodeIdsWithNonAdhocTask: Iterable<string>) => ReadonlySet<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Resolves what a viewer may see and edit per top-level form field. Rules, the
|
|
40
|
+
* first match wins:
|
|
41
|
+
*
|
|
42
|
+
* 1. a non-ad-hoc task at a user task that lists the field: as configured;
|
|
43
|
+
* 2. an ad-hoc task at a user task that lists the field: the configured
|
|
44
|
+
* visibility, never editable;
|
|
45
|
+
* 3. a field some user task fills in: read-only once revealed, hidden before;
|
|
46
|
+
* 4. any other field: visible, read-only.
|
|
47
|
+
*/
|
|
48
|
+
export declare const resolveFieldAccess: (input: {
|
|
49
|
+
readonly definition: WorkflowDefinition;
|
|
50
|
+
readonly fieldKeys: readonly string[];
|
|
51
|
+
readonly isAdhocTask: boolean;
|
|
52
|
+
readonly nodeId: string | null;
|
|
53
|
+
readonly revealedFieldKeys: ReadonlySet<string>;
|
|
54
|
+
}) => Readonly<Record<string, ResolvedFieldAccess>>;
|
|
55
|
+
export declare const omitFormDataKeys: (formData: Readonly<Record<string, unknown>>, keys: ReadonlySet<string>) => Readonly<Record<string, unknown>>;
|
|
56
|
+
/**
|
|
57
|
+
* For each key: the stored value when `stored` has it as an own property,
|
|
58
|
+
* otherwise the key is left out of the result entirely (a submitted value for
|
|
59
|
+
* it is dropped, never trusted).
|
|
60
|
+
*/
|
|
61
|
+
export declare const overlayStoredFormDataKeys: (submitted: Readonly<Record<string, unknown>>, stored: Readonly<Record<string, unknown>>, keys: ReadonlySet<string>) => Readonly<Record<string, unknown>>;
|
|
62
|
+
/** `undefined`, `null`, a blank string and an empty array are empty. */
|
|
63
|
+
export declare const isFormDataValueEmpty: (value: unknown) => boolean;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isFormDataValueEmpty = exports.overlayStoredFormDataKeys = exports.omitFormDataKeys = exports.resolveFieldAccess = exports.readRevealedFieldKeys = exports.readApproverFilledFieldKeys = exports.readNodeRequiredFieldKeys = exports.readNodeFillableFieldKeys = exports.readFormFieldPathTopLevelKey = exports.readTopLevelFieldKey = exports.readNodeFieldAccess = void 0;
|
|
4
|
+
const hasOwn = (target, key) => Object.prototype.hasOwnProperty.call(target, key);
|
|
5
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
const isUserTaskNode = (node) => node.type === 'userTask';
|
|
7
|
+
const findUserTaskNode = (definition, nodeId) => definition.nodes.find((node) => isUserTaskNode(node) && node.id === nodeId);
|
|
8
|
+
/**
|
|
9
|
+
* Reads `fieldAccess` off a user task without trusting its shape: saved drafts
|
|
10
|
+
* can hold anything, and no caller may throw because of one. A value that is
|
|
11
|
+
* not an array reads as `[]`; an entry that is not an object or has no
|
|
12
|
+
* non-empty `fieldKey` string is skipped; the first entry for a `fieldKey`
|
|
13
|
+
* wins, so every reader agrees on which one counts. An unknown `fill` reads as
|
|
14
|
+
* `NONE`, so a malformed entry never lets anyone write, and an unknown
|
|
15
|
+
* `display` reads as `SHOW`. The deprecated `fieldPermissions` is never read.
|
|
16
|
+
*/
|
|
17
|
+
const readNodeFieldAccess = (node) => {
|
|
18
|
+
const raw = node.data.fieldAccess;
|
|
19
|
+
if (!Array.isArray(raw)) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
return raw.reduce((collected, entry) => {
|
|
23
|
+
if (!isRecord(entry) ||
|
|
24
|
+
typeof entry['fieldKey'] !== 'string' ||
|
|
25
|
+
entry['fieldKey'] === '' ||
|
|
26
|
+
collected.some((existing) => existing.fieldKey === entry['fieldKey'])) {
|
|
27
|
+
return collected;
|
|
28
|
+
}
|
|
29
|
+
const fill = entry['fill'];
|
|
30
|
+
return [
|
|
31
|
+
...collected,
|
|
32
|
+
{
|
|
33
|
+
display: entry['display'] === 'HIDE' ? 'HIDE' : 'SHOW',
|
|
34
|
+
fieldKey: entry['fieldKey'],
|
|
35
|
+
fill: fill === 'OPTIONAL' || fill === 'REQUIRED' ? fill : 'NONE',
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
}, []);
|
|
39
|
+
};
|
|
40
|
+
exports.readNodeFieldAccess = readNodeFieldAccess;
|
|
41
|
+
/**
|
|
42
|
+
* The top-level form field a field path addresses: `items.price` and
|
|
43
|
+
* `items[0]` both address `items`.
|
|
44
|
+
*/
|
|
45
|
+
const readTopLevelFieldKey = (fieldPath) => fieldPath.split('.')[0].split('[')[0];
|
|
46
|
+
exports.readTopLevelFieldKey = readTopLevelFieldKey;
|
|
47
|
+
/**
|
|
48
|
+
* The top-level form field a `SET_FORM_FIELD` `fieldPath` writes. The engine
|
|
49
|
+
* accepts an optional leading `form.` or `formData.` segment and trims each
|
|
50
|
+
* segment, so `form.amount` and ` amount ` both write `amount`.
|
|
51
|
+
*/
|
|
52
|
+
const readFormFieldPathTopLevelKey = (fieldPath) => {
|
|
53
|
+
const segments = fieldPath
|
|
54
|
+
.split('.')
|
|
55
|
+
.map((segment) => segment.trim())
|
|
56
|
+
.filter(Boolean);
|
|
57
|
+
const fieldSegments = segments[0] === 'form' || segments[0] === 'formData'
|
|
58
|
+
? segments.slice(1)
|
|
59
|
+
: segments;
|
|
60
|
+
return (0, exports.readTopLevelFieldKey)(fieldSegments.join('.'));
|
|
61
|
+
};
|
|
62
|
+
exports.readFormFieldPathTopLevelKey = readFormFieldPathTopLevelKey;
|
|
63
|
+
const readNodeFillableFieldKeys = (node) => new Set((0, exports.readNodeFieldAccess)(node)
|
|
64
|
+
.filter((entry) => entry.fill !== 'NONE')
|
|
65
|
+
.map((entry) => entry.fieldKey));
|
|
66
|
+
exports.readNodeFillableFieldKeys = readNodeFillableFieldKeys;
|
|
67
|
+
const readNodeRequiredFieldKeys = (node) => new Set((0, exports.readNodeFieldAccess)(node)
|
|
68
|
+
.filter((entry) => entry.fill === 'REQUIRED')
|
|
69
|
+
.map((entry) => entry.fieldKey));
|
|
70
|
+
exports.readNodeRequiredFieldKeys = readNodeRequiredFieldKeys;
|
|
71
|
+
/** Every field some user task lets its approver fill in. */
|
|
72
|
+
const readApproverFilledFieldKeys = (definition) => new Set(definition.nodes
|
|
73
|
+
.filter(isUserTaskNode)
|
|
74
|
+
.flatMap((node) => [...(0, exports.readNodeFillableFieldKeys)(node)]));
|
|
75
|
+
exports.readApproverFilledFieldKeys = readApproverFilledFieldKeys;
|
|
76
|
+
/**
|
|
77
|
+
* The approver-filled fields that have been revealed to the applicant: those a
|
|
78
|
+
* user task that already produced a non-ad-hoc task lets its approver fill in.
|
|
79
|
+
* Ids that are missing or point to a non-user task are skipped.
|
|
80
|
+
*/
|
|
81
|
+
const readRevealedFieldKeys = (definition, nodeIdsWithNonAdhocTask) => new Set([...new Set(nodeIdsWithNonAdhocTask)].flatMap((nodeId) => {
|
|
82
|
+
const node = findUserTaskNode(definition, nodeId);
|
|
83
|
+
return node === undefined ? [] : [...(0, exports.readNodeFillableFieldKeys)(node)];
|
|
84
|
+
}));
|
|
85
|
+
exports.readRevealedFieldKeys = readRevealedFieldKeys;
|
|
86
|
+
/**
|
|
87
|
+
* Resolves what a viewer may see and edit per top-level form field. Rules, the
|
|
88
|
+
* first match wins:
|
|
89
|
+
*
|
|
90
|
+
* 1. a non-ad-hoc task at a user task that lists the field: as configured;
|
|
91
|
+
* 2. an ad-hoc task at a user task that lists the field: the configured
|
|
92
|
+
* visibility, never editable;
|
|
93
|
+
* 3. a field some user task fills in: read-only once revealed, hidden before;
|
|
94
|
+
* 4. any other field: visible, read-only.
|
|
95
|
+
*/
|
|
96
|
+
const resolveFieldAccess = (input) => {
|
|
97
|
+
const node = input.nodeId === null
|
|
98
|
+
? undefined
|
|
99
|
+
: findUserTaskNode(input.definition, input.nodeId);
|
|
100
|
+
const listed = new Map((node === undefined ? [] : (0, exports.readNodeFieldAccess)(node)).map((entry) => [entry.fieldKey, entry]));
|
|
101
|
+
const approverFilled = (0, exports.readApproverFilledFieldKeys)(input.definition);
|
|
102
|
+
return Object.fromEntries(input.fieldKeys.map((fieldKey) => {
|
|
103
|
+
const entry = listed.get(fieldKey);
|
|
104
|
+
if (entry !== undefined) {
|
|
105
|
+
return [
|
|
106
|
+
fieldKey,
|
|
107
|
+
{
|
|
108
|
+
editable: !input.isAdhocTask && entry.fill !== 'NONE',
|
|
109
|
+
visible: entry.display === 'SHOW',
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
if (approverFilled.has(fieldKey)) {
|
|
114
|
+
return [
|
|
115
|
+
fieldKey,
|
|
116
|
+
{ editable: false, visible: input.revealedFieldKeys.has(fieldKey) },
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
return [fieldKey, { editable: false, visible: true }];
|
|
120
|
+
}));
|
|
121
|
+
};
|
|
122
|
+
exports.resolveFieldAccess = resolveFieldAccess;
|
|
123
|
+
const omitFormDataKeys = (formData, keys) => Object.fromEntries(Object.entries(formData).filter(([key]) => !keys.has(key)));
|
|
124
|
+
exports.omitFormDataKeys = omitFormDataKeys;
|
|
125
|
+
/**
|
|
126
|
+
* For each key: the stored value when `stored` has it as an own property,
|
|
127
|
+
* otherwise the key is left out of the result entirely (a submitted value for
|
|
128
|
+
* it is dropped, never trusted).
|
|
129
|
+
*/
|
|
130
|
+
const overlayStoredFormDataKeys = (submitted, stored, keys) => (Object.assign(Object.assign({}, (0, exports.omitFormDataKeys)(submitted, keys)), Object.fromEntries([...keys]
|
|
131
|
+
.filter((key) => hasOwn(stored, key))
|
|
132
|
+
.map((key) => [key, stored[key]]))));
|
|
133
|
+
exports.overlayStoredFormDataKeys = overlayStoredFormDataKeys;
|
|
134
|
+
/** `undefined`, `null`, a blank string and an empty array are empty. */
|
|
135
|
+
const isFormDataValueEmpty = (value) => value === undefined ||
|
|
136
|
+
value === null ||
|
|
137
|
+
(typeof value === 'string' && value.trim() === '') ||
|
|
138
|
+
(Array.isArray(value) && value.length === 0);
|
|
139
|
+
exports.isFormDataValueEmpty = isFormDataValueEmpty;
|
|
140
|
+
//# sourceMappingURL=field-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-access.js","sourceRoot":"","sources":["../../../../../libs/shared/src/lib/field-access.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FormDefinitionSchema } from './form';
|
|
2
|
-
import { ApproverResolver, DecisionPolicy, ExcludeInitiatorConfig, ReturnResubmitStrategy, ServiceAction, SlaConfig, WorkflowDefinition, WorkflowEdgeConditionOperator, WorkflowNodeTriggerMode } from './workflow';
|
|
2
|
+
import { ApproverResolver, DecisionPolicy, ExcludeInitiatorConfig, FieldAccessEntry, ReturnResubmitStrategy, ServiceAction, SlaConfig, WorkflowDefinition, WorkflowEdgeConditionOperator, WorkflowNodeTriggerMode } from './workflow';
|
|
3
3
|
import { NodePaletteType, WorkflowEdgeIdFactory } from './workflow-graph';
|
|
4
4
|
/**
|
|
5
5
|
* The serialisable command layer that the template designer — and the LLM
|
|
@@ -79,6 +79,27 @@ export type WorkflowCommand = {
|
|
|
79
79
|
readonly allowReject?: boolean;
|
|
80
80
|
readonly allowTransfer?: boolean;
|
|
81
81
|
readonly description?: string;
|
|
82
|
+
} | {
|
|
83
|
+
/**
|
|
84
|
+
* Writes the field access of every user task at once, the way the
|
|
85
|
+
* designer's field access matrix applies: each node gets the entries
|
|
86
|
+
* listed for it, in order, and a user task with none listed loses its
|
|
87
|
+
* `fieldAccess` key. Pass the whole matrix, not a change set —
|
|
88
|
+
* unless `nodeIds` is given: then only those user tasks are rewritten,
|
|
89
|
+
* every other node keeps what it has, and an entry for a node outside
|
|
90
|
+
* `nodeIds` is refused.
|
|
91
|
+
*/
|
|
92
|
+
readonly type: 'setFieldAccessMatrix';
|
|
93
|
+
readonly entries: readonly FieldAccessMatrixEntry[];
|
|
94
|
+
readonly nodeIds?: readonly string[];
|
|
95
|
+
} | {
|
|
96
|
+
/**
|
|
97
|
+
* Drops the deprecated `fieldPermissions` key from a user task. Nothing
|
|
98
|
+
* is converted into `fieldAccess`: the old key was never enforced, so
|
|
99
|
+
* what it held means nothing.
|
|
100
|
+
*/
|
|
101
|
+
readonly type: 'removeLegacyFieldPermissions';
|
|
102
|
+
readonly nodeId: string;
|
|
82
103
|
} | {
|
|
83
104
|
readonly type: 'setServiceAction';
|
|
84
105
|
readonly nodeId: string;
|
|
@@ -120,6 +141,10 @@ export type WorkflowCommand = {
|
|
|
120
141
|
} | {
|
|
121
142
|
readonly type: 'autoLayout';
|
|
122
143
|
};
|
|
144
|
+
/** One `fieldAccess` entry of `setFieldAccessMatrix`, with its node. */
|
|
145
|
+
export interface FieldAccessMatrixEntry extends FieldAccessEntry {
|
|
146
|
+
readonly nodeId: string;
|
|
147
|
+
}
|
|
123
148
|
/** High-level intents that fold into a sequence of primitive commands. */
|
|
124
149
|
export type WorkflowMacroCommand = {
|
|
125
150
|
readonly type: 'insertApprovalStep';
|