@rytass/bpm-core-shared 0.5.0 → 0.6.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 (50) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +3 -2
  3. package/src/index.js +13 -0
  4. package/src/index.js.map +1 -0
  5. package/src/lib/condition.d.ts +43 -0
  6. package/src/lib/condition.js +3 -0
  7. package/src/lib/condition.js.map +1 -0
  8. package/src/lib/form.d.ts +48 -0
  9. package/src/lib/form.js +3 -0
  10. package/src/lib/form.js.map +1 -0
  11. package/src/lib/identity.d.ts +24 -0
  12. package/src/lib/identity.js +3 -0
  13. package/src/lib/identity.js.map +1 -0
  14. package/src/lib/organization.d.ts +47 -0
  15. package/src/lib/organization.js +3 -0
  16. package/src/lib/organization.js.map +1 -0
  17. package/src/lib/status.d.ts +7 -0
  18. package/src/lib/status.js +3 -0
  19. package/src/lib/status.js.map +1 -0
  20. package/src/lib/workflow-command.d.ts +156 -0
  21. package/src/lib/workflow-command.js +292 -0
  22. package/src/lib/workflow-command.js.map +1 -0
  23. package/src/lib/workflow-graph.d.ts +96 -0
  24. package/src/lib/workflow-graph.js +605 -0
  25. package/src/lib/workflow-graph.js.map +1 -0
  26. package/src/lib/workflow-toolset.d.ts +105 -0
  27. package/src/lib/workflow-toolset.js +707 -0
  28. package/src/lib/workflow-toolset.js.map +1 -0
  29. package/src/lib/workflow.d.ts +162 -0
  30. package/src/lib/workflow.js +3 -0
  31. package/src/lib/workflow.js.map +1 -0
  32. package/eslint.config.mjs +0 -19
  33. package/jest.config.cts +0 -10
  34. package/project.json +0 -49
  35. package/src/lib/condition.ts +0 -55
  36. package/src/lib/form.ts +0 -77
  37. package/src/lib/identity.ts +0 -25
  38. package/src/lib/organization.ts +0 -52
  39. package/src/lib/shared.spec.ts +0 -13
  40. package/src/lib/status.ts +0 -29
  41. package/src/lib/workflow-command.spec.ts +0 -278
  42. package/src/lib/workflow-command.ts +0 -848
  43. package/src/lib/workflow-graph.ts +0 -974
  44. package/src/lib/workflow-toolset.spec.ts +0 -230
  45. package/src/lib/workflow-toolset.ts +0 -1083
  46. package/src/lib/workflow.ts +0 -216
  47. package/tsconfig.json +0 -23
  48. package/tsconfig.lib.json +0 -16
  49. package/tsconfig.spec.json +0 -16
  50. /package/src/{index.ts → index.d.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -8,6 +8,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  Releases are managed by [`nx release`](https://nx.dev/recipes/nx-release) with
9
9
  Conventional Commits — see `nx.json` for the release config.
10
10
 
11
+ ## 0.6.0 — 2026-08-04
12
+
13
+ ### Added
14
+
15
+ - **`preferClosestOrgUnit` optional flag** on the `ORG_MANAGER` and
16
+ `ORG_UNIT_MANAGER` variants of `ApproverResolver`. When enabled, the
17
+ workflow engine resolves only the deepest org-unit-level manager rules
18
+ on the winning priority tier, so ancestor-level catch-all rules do not
19
+ dilute the approver list. Defaults to `false` (unchanged behavior).
20
+ `parseApproverResolver` in `workflow-toolset.ts` parses the new field.
21
+
22
+ ### Why a minor
23
+
24
+ New optional type field — backward compatible, additive.
25
+
26
+ ## 0.5.1 — 2026-07-06
27
+
28
+ ### Fixed
29
+
30
+ - **Broken 0.5.0 tarball.** 0.5.0 was published from the project source root, so
31
+ the npm tarball shipped only TypeScript sources with no compiled `.js`/`.d.ts`
32
+ while `main`/`exports` point at `./src/index.js`. 0.5.1 is republished from the
33
+ build output (`dist/libs/shared`). Configured `nx-release-publish` with
34
+ `packageRoot: dist/libs/shared`. No source changes.
35
+
11
36
  ## 0.5.0 — 2026-07-06
12
37
 
13
38
  No source changes. Version aligned with `@rytass/bpm-core-client` and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rytass/bpm-core-shared",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Shared TypeScript contracts for the Rytass BPM approval workflow stack (workflow, form, condition, identity, organization, status types).",
5
5
  "keywords": [
6
6
  "bpm",
@@ -94,5 +94,6 @@
94
94
  },
95
95
  "dependencies": {
96
96
  "tslib": "^2.3.0"
97
- }
97
+ },
98
+ "type": "commonjs"
98
99
  }
package/src/index.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/condition"), exports);
5
+ tslib_1.__exportStar(require("./lib/form"), exports);
6
+ tslib_1.__exportStar(require("./lib/identity"), exports);
7
+ tslib_1.__exportStar(require("./lib/organization"), exports);
8
+ tslib_1.__exportStar(require("./lib/status"), exports);
9
+ tslib_1.__exportStar(require("./lib/workflow"), exports);
10
+ tslib_1.__exportStar(require("./lib/workflow-graph"), exports);
11
+ tslib_1.__exportStar(require("./lib/workflow-command"), exports);
12
+ tslib_1.__exportStar(require("./lib/workflow-toolset"), exports);
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/shared/src/index.ts"],"names":[],"mappings":";;;AAAA,0DAAgC;AAChC,qDAA2B;AAC3B,yDAA+B;AAC/B,6DAAmC;AACnC,uDAA6B;AAC7B,yDAA+B;AAC/B,+DAAqC;AACrC,iEAAuC;AACvC,iEAAuC"}
@@ -0,0 +1,43 @@
1
+ export type ConditionContextType = 'INITIATOR_POLICY' | 'ENTRY_CONDITION' | 'FLOW_CONDITION' | 'APPROVER_RESOLVER' | 'FORM_FIELD_CONDITION' | 'DELEGATION_CONDITION';
2
+ export interface SubjectContext {
3
+ readonly customFields: Readonly<Record<string, unknown>>;
4
+ readonly email: string;
5
+ readonly memberId: string;
6
+ readonly name: string;
7
+ readonly org: OrgContext;
8
+ readonly position: PositionContext;
9
+ readonly roles: readonly string[];
10
+ }
11
+ export interface OrgContext {
12
+ readonly code: string;
13
+ readonly costCenter?: string;
14
+ readonly id: string;
15
+ readonly location?: string;
16
+ readonly name: string;
17
+ readonly parentId: string | null;
18
+ readonly path: readonly string[];
19
+ readonly type: string;
20
+ }
21
+ export interface PositionContext {
22
+ readonly code: string;
23
+ readonly id: string;
24
+ readonly level: number;
25
+ readonly name: string;
26
+ }
27
+ export interface EnvContext {
28
+ readonly now: string;
29
+ }
30
+ export interface InstanceContext {
31
+ readonly id: string;
32
+ readonly startedAt: string;
33
+ readonly templateId: string;
34
+ readonly templateVersion: number;
35
+ }
36
+ export interface LastDecisionContext {
37
+ readonly action: 'approved' | 'rejected' | 'returned';
38
+ readonly comment: string;
39
+ readonly decidedAt: string;
40
+ readonly decidedBy: SubjectContext;
41
+ readonly nodeId: string;
42
+ readonly taskId: string;
43
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=condition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"condition.js","sourceRoot":"","sources":["../../../../../libs/shared/src/lib/condition.ts"],"names":[],"mappings":""}
@@ -0,0 +1,48 @@
1
+ export interface FormDefinitionSchema {
2
+ readonly fields: readonly FormFieldDefinition[];
3
+ readonly schemaVersion: 1;
4
+ }
5
+ export type FormFieldDefinition = TextFieldDefinition | NumberFieldDefinition | DateFieldDefinition | SelectFieldDefinition | BooleanFieldDefinition | FileUploadFieldDefinition;
6
+ interface BaseFormFieldDefinition<TType extends string> {
7
+ readonly defaultValue?: FormFieldValue;
8
+ readonly description?: string;
9
+ readonly fieldKey: string;
10
+ readonly label: string;
11
+ readonly placeholder?: string;
12
+ readonly readonlyWhen?: string;
13
+ readonly required: boolean;
14
+ readonly requiredWhen?: string;
15
+ readonly type: TType;
16
+ readonly visibleWhen?: string;
17
+ }
18
+ export type FormFieldValue = boolean | number | string | readonly string[] | null;
19
+ export type TextFieldDefinition = BaseFormFieldDefinition<'text' | 'textarea'> & {
20
+ readonly maxLength?: number;
21
+ readonly minLength?: number;
22
+ };
23
+ export type NumberFieldDefinition = BaseFormFieldDefinition<'number' | 'money'> & {
24
+ readonly maximum?: number;
25
+ readonly minimum?: number;
26
+ };
27
+ export type DateFieldDefinition = BaseFormFieldDefinition<'date' | 'datetime'>;
28
+ export type SelectFieldDefinition = BaseFormFieldDefinition<'select' | 'radio' | 'checkbox'> & {
29
+ readonly options: readonly FormFieldOption[];
30
+ };
31
+ export interface FormFieldOption {
32
+ readonly label: string;
33
+ readonly value: string;
34
+ }
35
+ export type BooleanFieldDefinition = BaseFormFieldDefinition<'boolean'>;
36
+ export type FileUploadFieldDefinition = BaseFormFieldDefinition<'file_upload'> & {
37
+ readonly acceptedMimeTypes?: readonly string[];
38
+ readonly maxFiles?: number;
39
+ };
40
+ export interface FormUiSchema {
41
+ readonly layout: readonly FormLayoutItem[];
42
+ readonly schemaVersion: 1;
43
+ }
44
+ export interface FormLayoutItem {
45
+ readonly fieldKey: string;
46
+ readonly width: 'FULL' | 'HALF' | 'THIRD';
47
+ }
48
+ export {};
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.js","sourceRoot":"","sources":["../../../../../libs/shared/src/lib/form.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ export interface MemberMetadata {
2
+ readonly memberId: string;
3
+ readonly name: string;
4
+ readonly email: string;
5
+ /**
6
+ * Opaque host-shaped JSON. BPM stores the object verbatim, serializes
7
+ * it back to GraphQL clients as `MemberProfile.customFieldsJson`, and
8
+ * never introspects keys — anything the host puts here flows through
9
+ * unchanged. Hosts using `createBPMMemberBaseResolverProvider` populate
10
+ * this field via the `options.readCustomFields?.(member)` hook;
11
+ * standalone `BPMMemberResolver` implementations return whatever shape
12
+ * the consumer UI needs. Use it as a side channel for picker labels,
13
+ * org context strings, avatar URLs, or any other host-specific
14
+ * metadata that should appear in member-display UIs.
15
+ */
16
+ readonly customFields: Readonly<Record<string, unknown>>;
17
+ }
18
+ export interface MemberMetadataCacheEntry {
19
+ readonly id: string;
20
+ readonly memberId: string;
21
+ readonly metadata: MemberMetadata;
22
+ readonly fetchedAt: string;
23
+ readonly expiresAt: string;
24
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../../../../libs/shared/src/lib/identity.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Org-unit type — matches the BPM GraphQL `OrgUnitType` enum SDL names
3
+ * (UPPERCASE). The server stores the value lower-cased internally
4
+ * (`'company'` etc. in the database row), but every read and write
5
+ * across the GraphQL wire and the REST surface uses the UPPERCASE form.
6
+ *
7
+ * Consumers of `@rytass/bpm-core-client/organization` must pass these
8
+ * UPPERCASE literals to `createOrgUnit` / `updateOrgUnit` (and the
9
+ * `readOrganizationDashboard` `orgUnitType` filter), and will receive
10
+ * UPPERCASE values back on `OrgUnitRecord.type`.
11
+ */
12
+ export type OrgUnitType = 'COMPANY' | 'DIVISION' | 'DEPARTMENT' | 'TEAM';
13
+ export type ManagerResolutionScopeType = 'MEMBER' | 'ORG_UNIT' | 'POSITION';
14
+ export interface OrgUnit {
15
+ readonly id: string;
16
+ readonly parentId: string | null;
17
+ readonly code: string;
18
+ readonly name: string;
19
+ readonly type: OrgUnitType;
20
+ readonly path: string;
21
+ readonly metadata: Readonly<Record<string, unknown>>;
22
+ }
23
+ export interface Position {
24
+ readonly id: string;
25
+ readonly code: string;
26
+ readonly name: string;
27
+ readonly level: number;
28
+ readonly metadata: Readonly<Record<string, unknown>>;
29
+ }
30
+ export interface Membership {
31
+ readonly id: string;
32
+ readonly memberId: string;
33
+ readonly orgUnitId: string;
34
+ readonly positionId: string | null;
35
+ readonly isPrimary: boolean;
36
+ readonly effectiveFrom: string;
37
+ readonly effectiveTo: string | null;
38
+ }
39
+ export interface ManagerResolution {
40
+ readonly id: string;
41
+ readonly scopeType: ManagerResolutionScopeType;
42
+ readonly scopeId: string;
43
+ readonly managerMemberId: string;
44
+ readonly priority: number;
45
+ readonly effectiveFrom: string;
46
+ readonly effectiveTo: string | null;
47
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=organization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization.js","sourceRoot":"","sources":["../../../../../libs/shared/src/lib/organization.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ export type VersionStatus = 'DRAFT' | 'PUBLISHED' | 'ARCHIVED';
2
+ export type ApprovalInstanceState = 'DRAFT' | 'RUNNING' | 'APPROVED' | 'REJECTED' | 'RETURNED' | 'CANCELLED' | 'EXPIRED';
3
+ export type WorkflowTokenStatus = 'ACTIVE' | 'WAITING' | 'CONSUMED';
4
+ export type TaskStatus = 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'TRANSFERRED' | 'CANCELLED';
5
+ export type TaskDecisionAction = 'APPROVED' | 'REJECTED' | 'RETURNED' | 'TRANSFERRED';
6
+ export type DelegationRuleStatus = 'ACTIVE' | 'REVOKED' | 'EXPIRED';
7
+ export type DelegationScopeType = 'ALL' | 'TEMPLATE_LIST' | 'CONDITION_BASED';
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../../libs/shared/src/lib/status.ts"],"names":[],"mappings":""}
@@ -0,0 +1,156 @@
1
+ import { FormDefinitionSchema } from './form';
2
+ import { ApproverResolver, ReturnResubmitStrategy, ServiceAction, WorkflowDefinition, WorkflowEdgeConditionOperator, WorkflowNodeTriggerMode } from './workflow';
3
+ import { NodePaletteType, WorkflowEdgeIdFactory } from './workflow-graph';
4
+ /**
5
+ * The serialisable command layer that the template designer — and the LLM
6
+ * assistant — drive the workflow through. Every mutation a user can perform on
7
+ * the canvas, node palette, or property form is expressed as one of these
8
+ * commands, and {@link applyWorkflowCommand} is the single pure reducer that
9
+ * applies them. The React controller and the LLM tool runtime both dispatch the
10
+ * exact same commands, guaranteeing the assistant can do anything a user can.
11
+ */
12
+ /** Everything the reducer needs as the single source of truth. */
13
+ export interface WorkflowDesignerState {
14
+ /** Form schema bound to the template; needed to compile edge conditions. */
15
+ readonly formSchema: FormDefinitionSchema | null;
16
+ readonly definition: WorkflowDefinition;
17
+ readonly editingEdgeId: string | null;
18
+ readonly formDefinitionVersionId: string | null;
19
+ readonly initiatorPolicyCel: string | null;
20
+ readonly selectedEdgeIds: readonly string[];
21
+ readonly selectedNodeId: string | null;
22
+ }
23
+ /** Where a newly added node should be wired in. */
24
+ export interface WorkflowNodeAnchor {
25
+ /** Insert the node onto this edge, splitting it. */
26
+ readonly edgeId?: string | null;
27
+ /** Insert the node after this node. */
28
+ readonly afterNodeId?: string | null;
29
+ }
30
+ export type WorkflowCommand = {
31
+ readonly type: 'addNode';
32
+ readonly nodeType: NodePaletteType;
33
+ readonly anchor?: WorkflowNodeAnchor;
34
+ } | {
35
+ readonly type: 'renameNode';
36
+ readonly nodeId: string;
37
+ readonly label: string;
38
+ } | {
39
+ readonly type: 'deleteNode';
40
+ readonly nodeId: string;
41
+ } | {
42
+ readonly type: 'setNodeTriggerMode';
43
+ readonly nodeId: string;
44
+ readonly triggerMode: WorkflowNodeTriggerMode;
45
+ } | {
46
+ readonly type: 'setUserTaskApprover';
47
+ readonly nodeId: string;
48
+ readonly approverResolver: ApproverResolver;
49
+ } | {
50
+ readonly type: 'setUserTaskReturnResubmitStrategy';
51
+ readonly nodeId: string;
52
+ readonly resubmitStrategy: ReturnResubmitStrategy;
53
+ } | {
54
+ readonly type: 'setUserTaskOptions';
55
+ readonly nodeId: string;
56
+ readonly allowAddSigner?: boolean;
57
+ readonly allowReject?: boolean;
58
+ readonly allowTransfer?: boolean;
59
+ readonly description?: string;
60
+ } | {
61
+ readonly type: 'setServiceAction';
62
+ readonly nodeId: string;
63
+ readonly action: ServiceAction;
64
+ } | {
65
+ readonly type: 'setEndState';
66
+ readonly nodeId: string;
67
+ readonly endState: 'APPROVED' | 'REJECTED';
68
+ } | {
69
+ readonly type: 'connectEdge';
70
+ readonly source: string;
71
+ readonly target: string;
72
+ } | {
73
+ readonly type: 'deleteEdge';
74
+ readonly edgeId: string;
75
+ } | {
76
+ readonly type: 'setEdgeCondition';
77
+ readonly edgeId: string;
78
+ readonly fieldKey?: string | null;
79
+ readonly operator?: WorkflowEdgeConditionOperator | null;
80
+ readonly value?: string | null;
81
+ } | {
82
+ readonly type: 'setEdgeDefault';
83
+ readonly edgeId: string;
84
+ readonly isDefault: boolean;
85
+ } | {
86
+ readonly type: 'bindForm';
87
+ readonly formDefinitionVersionId: string | null;
88
+ readonly formSchema: FormDefinitionSchema | null;
89
+ } | {
90
+ readonly type: 'setInitiatorPolicyCel';
91
+ readonly cel: string | null;
92
+ } | {
93
+ readonly type: 'selectNode';
94
+ readonly nodeId: string | null;
95
+ } | {
96
+ readonly type: 'selectEdges';
97
+ readonly edgeIds: readonly string[];
98
+ } | {
99
+ readonly type: 'autoLayout';
100
+ };
101
+ /** High-level intents that fold into a sequence of primitive commands. */
102
+ export type WorkflowMacroCommand = {
103
+ readonly type: 'insertApprovalStep';
104
+ readonly approverResolver: ApproverResolver;
105
+ readonly label?: string;
106
+ readonly anchor?: WorkflowNodeAnchor;
107
+ } | {
108
+ readonly type: 'insertNotification';
109
+ readonly action: ServiceAction;
110
+ readonly label?: string;
111
+ readonly anchor?: WorkflowNodeAnchor;
112
+ } | {
113
+ readonly type: 'insertConditionalBranch';
114
+ readonly label?: string;
115
+ readonly anchor?: WorkflowNodeAnchor;
116
+ };
117
+ export type AnyWorkflowCommand = WorkflowCommand | WorkflowMacroCommand;
118
+ /** Side-effect hints for the React controller (pixel layout/viewport). */
119
+ export interface WorkflowCommandEffects {
120
+ /** Topology changed — controller should re-run dagre layout + viewport. */
121
+ readonly layout: boolean;
122
+ }
123
+ export interface WorkflowCommandResult {
124
+ readonly state: WorkflowDesignerState;
125
+ /** Whether the command actually changed state. */
126
+ readonly changed: boolean;
127
+ /** Human-facing reason the command was a no-op / rejected, else null. */
128
+ readonly error: string | null;
129
+ /** Post-apply validation issue (same text the Save button uses), else null. */
130
+ readonly issue: string | null;
131
+ readonly effects: WorkflowCommandEffects;
132
+ }
133
+ export interface WorkflowCommandOptions {
134
+ /** Injectable edge id factory for deterministic runs (tests, replay). */
135
+ readonly createEdgeId?: WorkflowEdgeIdFactory;
136
+ }
137
+ /**
138
+ * Apply a single primitive command. Pure: same input → same output (given a
139
+ * deterministic edge id factory). Topology-changing commands set
140
+ * `effects.layout` so the controller can run dagre afterwards.
141
+ */
142
+ export declare function applyWorkflowCommand(state: WorkflowDesignerState, command: WorkflowCommand, options?: WorkflowCommandOptions): WorkflowCommandResult;
143
+ /**
144
+ * Apply a high-level macro by expanding it into primitive commands. The added
145
+ * node's id is deterministic (`<nodeType>_<nextIndex>`), so follow-up commands
146
+ * can target it without depending on the post-insert selection.
147
+ */
148
+ export declare function applyWorkflowMacroCommand(state: WorkflowDesignerState, command: WorkflowMacroCommand, options?: WorkflowCommandOptions): WorkflowCommandResult;
149
+ /** Apply a batch of primitive commands in order, threading state. */
150
+ export declare function applyWorkflowCommands(state: WorkflowDesignerState, commands: readonly WorkflowCommand[], options?: WorkflowCommandOptions): WorkflowCommandResult;
151
+ /**
152
+ * Expand a high-level macro into the primitive command sequence that realises
153
+ * it. The next node id is derived from the same deterministic rule
154
+ * {@link applyAddNode} uses, so post-add commands can target it directly.
155
+ */
156
+ export declare function expandMacroCommand(state: WorkflowDesignerState, command: WorkflowMacroCommand): readonly WorkflowCommand[];