@object-ui/app-shell 7.0.0 → 7.2.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/CHANGELOG.md +560 -0
- package/dist/console/AppContent.js +23 -17
- package/dist/console/ConsoleShell.d.ts +16 -0
- package/dist/console/ConsoleShell.js +43 -2
- package/dist/console/ai/AiChatPage.js +47 -16
- package/dist/console/ai/LiveCanvas.d.ts +8 -2
- package/dist/console/ai/LiveCanvas.js +6 -4
- package/dist/console/home/HomeLayout.js +5 -7
- package/dist/console/home/HomePage.js +1 -9
- package/dist/console/organizations/CreateWorkspaceDialog.js +15 -1
- package/dist/console/organizations/OrganizationsPage.js +22 -3
- package/dist/console/organizations/provisionEnvironment.d.ts +53 -0
- package/dist/console/organizations/provisionEnvironment.js +64 -0
- package/dist/environment/EnvironmentEntitlementDialog.d.ts +34 -0
- package/dist/environment/EnvironmentEntitlementDialog.js +37 -0
- package/dist/environment/EnvironmentListToolbar.d.ts +33 -0
- package/dist/environment/EnvironmentListToolbar.js +59 -0
- package/dist/environment/entitlements.d.ts +90 -0
- package/dist/environment/entitlements.js +91 -0
- package/dist/environment/useEnvironmentEntitlements.d.ts +32 -0
- package/dist/environment/useEnvironmentEntitlements.js +108 -0
- package/dist/hooks/useActionModal.js +15 -1
- package/dist/hooks/useAiSurface.d.ts +59 -0
- package/dist/hooks/useAiSurface.js +78 -0
- package/dist/hooks/useChatConversation.d.ts +30 -0
- package/dist/hooks/useChatConversation.js +63 -0
- package/dist/hooks/useConsoleActionRuntime.d.ts +3 -0
- package/dist/hooks/useConsoleActionRuntime.js +42 -10
- package/dist/index.d.ts +5 -2
- package/dist/index.js +10 -2
- package/dist/layout/AppHeader.js +28 -4
- package/dist/layout/ConsoleFloatingChatbot.d.ts +6 -4
- package/dist/layout/ConsoleFloatingChatbot.js +41 -10
- package/dist/layout/ConsoleLayout.js +5 -6
- package/dist/layout/ContextSelectors.js +59 -35
- package/dist/layout/agentPicker.d.ts +56 -0
- package/dist/layout/agentPicker.js +40 -0
- package/dist/preview/CommitTimeline.d.ts +15 -0
- package/dist/preview/CommitTimeline.js +82 -0
- package/dist/preview/DraftPreviewBar.js +20 -7
- package/dist/preview/UnpublishedAppBar.js +11 -7
- package/dist/preview/commitHistory.d.ts +28 -0
- package/dist/preview/commitHistory.js +48 -0
- package/dist/providers/ExpressionProvider.js +9 -3
- package/dist/providers/MetadataProvider.js +9 -0
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +1 -1
- package/dist/utils/recordFormNavigation.d.ts +60 -0
- package/dist/utils/recordFormNavigation.js +35 -0
- package/dist/utils/resolvePageVarTokens.d.ts +31 -0
- package/dist/utils/resolvePageVarTokens.js +72 -0
- package/dist/views/CreateViewDialog.js +14 -1
- package/dist/views/FlowRunner.d.ts +2 -30
- package/dist/views/FlowRunner.js +18 -50
- package/dist/views/ObjectView.js +26 -12
- package/dist/views/ScreenView.d.ts +70 -0
- package/dist/views/ScreenView.js +73 -0
- package/dist/views/metadata-admin/AssignedUsersSection.d.ts +28 -0
- package/dist/views/metadata-admin/AssignedUsersSection.js +151 -0
- package/dist/views/metadata-admin/DirectoryPage.js +2 -14
- package/dist/views/metadata-admin/JsonSourceEditor.d.ts +3 -1
- package/dist/views/metadata-admin/JsonSourceEditor.js +21 -3
- package/dist/views/metadata-admin/PackagesPage.d.ts +5 -0
- package/dist/views/metadata-admin/PackagesPage.js +58 -5
- package/dist/views/metadata-admin/PermissionMatrixEditor.js +2 -1
- package/dist/views/metadata-admin/ResourceEditPage.js +83 -24
- package/dist/views/metadata-admin/ResourceListPage.js +28 -19
- package/dist/views/metadata-admin/StudioHomePage.js +6 -14
- package/dist/views/metadata-admin/anchors.js +20 -2
- package/dist/views/metadata-admin/createBody.d.ts +26 -0
- package/dist/views/metadata-admin/createBody.js +30 -0
- package/dist/views/metadata-admin/i18n.js +108 -2
- package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.d.ts +10 -2
- package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.js +136 -8
- package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.js +99 -4
- package/dist/views/metadata-admin/inspectors/FlowExprIssue.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/FlowExprIssue.js +13 -0
- package/dist/views/metadata-admin/inspectors/FlowKeyValueField.d.ts +20 -2
- package/dist/views/metadata-admin/inspectors/FlowKeyValueField.js +71 -28
- package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.d.ts +4 -1
- package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.js +24 -9
- package/dist/views/metadata-admin/inspectors/FlowNodeInspector.js +81 -4
- package/dist/views/metadata-admin/inspectors/FlowObjectListField.d.ts +4 -1
- package/dist/views/metadata-admin/inspectors/FlowObjectListField.js +8 -3
- package/dist/views/metadata-admin/inspectors/ObjectDefaultInspector.js +5 -4
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.js +47 -12
- package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.d.ts +1 -1
- package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.js +60 -2
- package/dist/views/metadata-admin/inspectors/VariableTextInput.d.ts +47 -0
- package/dist/views/metadata-admin/inspectors/VariableTextInput.js +95 -0
- package/dist/views/metadata-admin/inspectors/_shared.d.ts +5 -1
- package/dist/views/metadata-admin/inspectors/_shared.js +2 -2
- package/dist/views/metadata-admin/inspectors/datasetFilterCondition.d.ts +24 -0
- package/dist/views/metadata-admin/inspectors/datasetFilterCondition.js +102 -0
- package/dist/views/metadata-admin/inspectors/flow-node-config.d.ts +16 -1
- package/dist/views/metadata-admin/inspectors/flow-node-config.js +67 -11
- package/dist/views/metadata-admin/inspectors/flow-ref-check.d.ts +39 -0
- package/dist/views/metadata-admin/inspectors/flow-ref-check.js +114 -0
- package/dist/views/metadata-admin/inspectors/flow-scope.d.ts +109 -0
- package/dist/views/metadata-admin/inspectors/flow-scope.js +199 -0
- package/dist/views/metadata-admin/inspectors/useDatasetFields.d.ts +14 -3
- package/dist/views/metadata-admin/inspectors/useDatasetFields.js +0 -0
- package/dist/views/metadata-admin/inspectors/useFlowScope.d.ts +23 -0
- package/dist/views/metadata-admin/inspectors/useFlowScope.js +45 -0
- package/dist/views/metadata-admin/issuePath.d.ts +22 -0
- package/dist/views/metadata-admin/issuePath.js +65 -0
- package/dist/views/metadata-admin/package-scope.d.ts +41 -0
- package/dist/views/metadata-admin/package-scope.js +59 -0
- package/dist/views/metadata-admin/preview-registry.d.ts +12 -0
- package/dist/views/metadata-admin/previews/DatasetPreview.js +21 -5
- package/dist/views/metadata-admin/previews/FlowCanvas.d.ts +26 -1
- package/dist/views/metadata-admin/previews/FlowCanvas.js +143 -16
- package/dist/views/metadata-admin/previews/FlowPreview.d.ts +1 -1
- package/dist/views/metadata-admin/previews/FlowPreview.js +47 -7
- package/dist/views/metadata-admin/previews/FlowSimulatorPanel.js +37 -3
- package/dist/views/metadata-admin/previews/ObjectFormCanvas.js +9 -4
- package/dist/views/metadata-admin/previews/PagePreview.js +112 -3
- package/dist/views/metadata-admin/previews/ProblemsPanel.d.ts +18 -0
- package/dist/views/metadata-admin/previews/ProblemsPanel.js +27 -0
- package/dist/views/metadata-admin/previews/ReportPreview.d.ts +9 -8
- package/dist/views/metadata-admin/previews/ReportPreview.js +33 -16
- package/dist/views/metadata-admin/previews/ScreenPreview.d.ts +38 -0
- package/dist/views/metadata-admin/previews/ScreenPreview.js +61 -0
- package/dist/views/metadata-admin/previews/flow-canvas-layout.d.ts +14 -0
- package/dist/views/metadata-admin/previews/flow-canvas-layout.js +37 -0
- package/dist/views/metadata-admin/previews/flow-canvas-parts.d.ts +17 -1
- package/dist/views/metadata-admin/previews/flow-canvas-parts.js +23 -6
- package/dist/views/metadata-admin/previews/flow-expr-problems.d.ts +19 -0
- package/dist/views/metadata-admin/previews/flow-expr-problems.js +97 -0
- package/dist/views/metadata-admin/previews/flow-problems.d.ts +84 -0
- package/dist/views/metadata-admin/previews/flow-problems.js +209 -0
- package/dist/views/metadata-admin/previews/object-fields-io.d.ts +21 -0
- package/dist/views/metadata-admin/previews/object-fields-io.js +37 -2
- package/dist/views/metadata-admin/previews/screen-spec.d.ts +43 -0
- package/dist/views/metadata-admin/previews/screen-spec.js +108 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-types.d.ts +20 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.d.ts +7 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.js +76 -2
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.d.ts +32 -3
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.js +119 -9
- package/package.json +38 -38
|
@@ -28,6 +28,63 @@ export function evalCondition(expr, variables) {
|
|
|
28
28
|
return { result: false, error: err.message || 'Evaluation failed.' };
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Find a directed cycle in `edges` over `nodeIds`, returned as the node path
|
|
33
|
+
* that closes the loop (e.g. `['a','b','a']`), or `null` when the graph is a
|
|
34
|
+
* DAG. Iterative DFS with a recursion-stack colour map; the first cycle found
|
|
35
|
+
* wins (enough to report — the author fixes one at a time).
|
|
36
|
+
*/
|
|
37
|
+
export function findCycle(nodeIds, edges) {
|
|
38
|
+
const adj = new Map();
|
|
39
|
+
for (const id of nodeIds)
|
|
40
|
+
adj.set(id, []);
|
|
41
|
+
for (const e of edges) {
|
|
42
|
+
if (adj.has(e.source) && adj.has(e.target))
|
|
43
|
+
adj.get(e.source).push(e.target);
|
|
44
|
+
}
|
|
45
|
+
const WHITE = 0, GRAY = 1, BLACK = 2;
|
|
46
|
+
const color = new Map(nodeIds.map((id) => [id, WHITE]));
|
|
47
|
+
const stack = [];
|
|
48
|
+
const visit = (start) => {
|
|
49
|
+
// Explicit stack of {node, next-child-index} frames so a deep graph can't
|
|
50
|
+
// blow the JS call stack.
|
|
51
|
+
const frames = [{ id: start, i: 0 }];
|
|
52
|
+
color.set(start, GRAY);
|
|
53
|
+
stack.push(start);
|
|
54
|
+
while (frames.length) {
|
|
55
|
+
const frame = frames[frames.length - 1];
|
|
56
|
+
const children = adj.get(frame.id) ?? [];
|
|
57
|
+
if (frame.i < children.length) {
|
|
58
|
+
const next = children[frame.i++];
|
|
59
|
+
const c = color.get(next);
|
|
60
|
+
if (c === GRAY) {
|
|
61
|
+
// Back into the active path → cycle. Slice from `next` to close it.
|
|
62
|
+
const from = stack.indexOf(next);
|
|
63
|
+
return [...stack.slice(from), next];
|
|
64
|
+
}
|
|
65
|
+
if (c === WHITE) {
|
|
66
|
+
color.set(next, GRAY);
|
|
67
|
+
stack.push(next);
|
|
68
|
+
frames.push({ id: next, i: 0 });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
color.set(frame.id, BLACK);
|
|
73
|
+
stack.pop();
|
|
74
|
+
frames.pop();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
for (const id of nodeIds) {
|
|
80
|
+
if (color.get(id) === WHITE) {
|
|
81
|
+
const cycle = visit(id);
|
|
82
|
+
if (cycle)
|
|
83
|
+
return cycle;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
31
88
|
/** Static structural checks; `errors` block Run, `warnings` are advisory. */
|
|
32
89
|
export function validateFlowDraft(nodes, edges) {
|
|
33
90
|
const errors = [];
|
|
@@ -44,10 +101,12 @@ export function validateFlowDraft(nodes, edges) {
|
|
|
44
101
|
idSet.add(id);
|
|
45
102
|
}
|
|
46
103
|
for (const e of edges) {
|
|
104
|
+
// Attach the endpoints so a dangling-edge error can badge the offending
|
|
105
|
+
// connection on the canvas (not just appear as a flow-level message).
|
|
47
106
|
if (!idSet.has(e.source))
|
|
48
|
-
errors.push({ level: 'error', message: `Edge source "${e.source}" does not exist.` });
|
|
107
|
+
errors.push({ level: 'error', edge: { source: e.source, target: e.target }, message: `Edge source "${e.source}" does not exist.` });
|
|
49
108
|
if (!idSet.has(e.target))
|
|
50
|
-
errors.push({ level: 'error', message: `Edge target "${e.target}" does not exist.` });
|
|
109
|
+
errors.push({ level: 'error', edge: { source: e.source, target: e.target }, message: `Edge target "${e.target}" does not exist.` });
|
|
51
110
|
}
|
|
52
111
|
// Entry resolution: prefer an explicit `start` node, else a node with no
|
|
53
112
|
// incoming edge. Zero or many → the author must fix it before running.
|
|
@@ -109,5 +168,20 @@ export function validateFlowDraft(nodes, edges) {
|
|
|
109
168
|
}
|
|
110
169
|
}
|
|
111
170
|
}
|
|
171
|
+
// DAG-modulo-back-edges (ADR-0044): the engine requires the flow graph MINUS
|
|
172
|
+
// declared back-edges to be acyclic. A declared revise loop (its closing edge
|
|
173
|
+
// marked `type: 'back'`) is excluded and passes; any *unmarked* cycle is an
|
|
174
|
+
// error — the author must opt in, edge by edge, exactly as `registerFlow`
|
|
175
|
+
// enforces server-side.
|
|
176
|
+
const forwardEdges = edges.filter((e) => e.type !== 'back');
|
|
177
|
+
const cycle = findCycle(ids.filter((id) => !!id), forwardEdges);
|
|
178
|
+
if (cycle) {
|
|
179
|
+
errors.push({
|
|
180
|
+
level: 'error',
|
|
181
|
+
nodeId: cycle[0],
|
|
182
|
+
cycle,
|
|
183
|
+
message: `Cycle detected (${cycle.join(' → ')}). Mark the connection that closes the loop as a back-edge (Connection type → Back-edge) to declare an intentional revise/rework loop.`,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
112
186
|
return { errors, warnings, startNodeId };
|
|
113
187
|
}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Supported faithfully: start, decision (edge-first CEL routing), the CRUD /
|
|
6
6
|
* get / http / connector / script(notification|code) side-effects (MOCKED), and
|
|
7
|
-
* end. `wait` and `
|
|
7
|
+
* end. `wait`, `screen`, and `approval` PAUSE for manual continuation (an approval
|
|
8
|
+
* resumes down the chosen decision's branch — approve / reject / revise). `loop` resolves its
|
|
8
9
|
* collection and exposes the iterator but is a labelled single pass (the edge
|
|
9
10
|
* model has no separate body/exit edge). `parallel_gateway` fans out WITHOUT
|
|
10
11
|
* join synchronization; the ADR-0031 structured containers (`parallel`,
|
|
@@ -27,10 +28,38 @@ export declare class FlowSimulator {
|
|
|
27
28
|
step(): SimStep | null;
|
|
28
29
|
/** Run to completion (or until a pause / error). */
|
|
29
30
|
runToEnd(): SimState;
|
|
30
|
-
/**
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Continue a flow paused on a `wait`, `screen`, or `approval` node.
|
|
33
|
+
* - `screenOutputs` — inputs captured from a paused screen.
|
|
34
|
+
* - `decision` — the branch an approval resumes down (ADR-0019/0044:
|
|
35
|
+
* `approve` / `reject` / `revise`). The run takes ONLY the out-edge whose
|
|
36
|
+
* label matches, mirroring how the engine resumes a suspended approval by
|
|
37
|
+
* branch label — instead of fanning out to every out-edge.
|
|
38
|
+
*/
|
|
39
|
+
resume(opts?: {
|
|
40
|
+
screenOutputs?: Record<string, unknown>;
|
|
41
|
+
decision?: string;
|
|
42
|
+
}): void;
|
|
43
|
+
/**
|
|
44
|
+
* Resume a suspended approval down the chosen decision's out-edge: the one
|
|
45
|
+
* whose `label` equals `decision` (case-insensitive — `approve` / `reject` /
|
|
46
|
+
* `revise`). With no match (or no decision) it falls back to fanning out —
|
|
47
|
+
* mirroring the engine's unmatched-`branchLabel` fallback — and logs that so
|
|
48
|
+
* the author notices the unrouted decision.
|
|
49
|
+
*/
|
|
50
|
+
private resumeApproval;
|
|
32
51
|
private execute;
|
|
33
52
|
private executeDecision;
|
|
53
|
+
/**
|
|
54
|
+
* assignment node — set flow variables. Normalizes the three authoring
|
|
55
|
+
* shapes the engine accepts (Studio's `{ assignments: { var: value } }`
|
|
56
|
+
* map, the example `{ assignments: [{ variable, value }] }` array, and the
|
|
57
|
+
* legacy flat `{ var: value }`) and interpolates `{var}` templates — so the
|
|
58
|
+
* Debug run mirrors runtime instead of silently no-oping.
|
|
59
|
+
*/
|
|
60
|
+
private executeAssignment;
|
|
61
|
+
/** Resolve `{var}` templates in an assignment value against live variables. */
|
|
62
|
+
private interpolateValue;
|
|
34
63
|
private executeLoop;
|
|
35
64
|
/** Resolve a `{var}` template ref or a plain variable name from `variables`. */
|
|
36
65
|
private resolveRef;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
2
|
import { evalCondition, validateFlowDraft } from './flow-sim-validate';
|
|
3
3
|
const MAX_STEPS = 500;
|
|
4
|
-
const PASS_THROUGH = new Set(['start'
|
|
4
|
+
const PASS_THROUGH = new Set(['start']);
|
|
5
5
|
const MOCKED_SIDE_EFFECT = new Set([
|
|
6
6
|
'create_record',
|
|
7
7
|
'update_record',
|
|
@@ -124,24 +124,58 @@ export class FlowSimulator {
|
|
|
124
124
|
}
|
|
125
125
|
return this.state;
|
|
126
126
|
}
|
|
127
|
-
/**
|
|
128
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Continue a flow paused on a `wait`, `screen`, or `approval` node.
|
|
129
|
+
* - `screenOutputs` — inputs captured from a paused screen.
|
|
130
|
+
* - `decision` — the branch an approval resumes down (ADR-0019/0044:
|
|
131
|
+
* `approve` / `reject` / `revise`). The run takes ONLY the out-edge whose
|
|
132
|
+
* label matches, mirroring how the engine resumes a suspended approval by
|
|
133
|
+
* branch label — instead of fanning out to every out-edge.
|
|
134
|
+
*/
|
|
135
|
+
resume(opts = {}) {
|
|
129
136
|
const s = this.state;
|
|
130
137
|
if (s.status !== 'paused' || !s.activeNodeId)
|
|
131
138
|
return;
|
|
132
139
|
const node = this.nodes.get(s.activeNodeId);
|
|
133
|
-
if (screenOutputs && node?.type === 'screen') {
|
|
134
|
-
Object.assign(s.variables, screenOutputs);
|
|
140
|
+
if (opts.screenOutputs && node?.type === 'screen') {
|
|
141
|
+
Object.assign(s.variables, opts.screenOutputs);
|
|
135
142
|
}
|
|
136
143
|
s.pausedReason = undefined;
|
|
137
144
|
s.status = 'running';
|
|
138
|
-
if (node)
|
|
145
|
+
if (node?.type === 'approval')
|
|
146
|
+
this.resumeApproval(node, opts.decision);
|
|
147
|
+
else if (node)
|
|
139
148
|
this.enqueueSuccessors(node);
|
|
140
149
|
if (s.frontier.length === 0) {
|
|
141
150
|
s.status = 'done';
|
|
142
151
|
s.activeNodeId = null;
|
|
143
152
|
}
|
|
144
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Resume a suspended approval down the chosen decision's out-edge: the one
|
|
156
|
+
* whose `label` equals `decision` (case-insensitive — `approve` / `reject` /
|
|
157
|
+
* `revise`). With no match (or no decision) it falls back to fanning out —
|
|
158
|
+
* mirroring the engine's unmatched-`branchLabel` fallback — and logs that so
|
|
159
|
+
* the author notices the unrouted decision.
|
|
160
|
+
*/
|
|
161
|
+
resumeApproval(node, decision) {
|
|
162
|
+
const out = this.edges.map((e, i) => ({ e, i })).filter((x) => x.e.source === node.id);
|
|
163
|
+
const want = (decision ?? '').trim().toLowerCase();
|
|
164
|
+
const chosen = want ? out.find((x) => (x.e.label ?? '').trim().toLowerCase() === want) : undefined;
|
|
165
|
+
if (chosen) {
|
|
166
|
+
this.traverse(chosen.e, chosen.i);
|
|
167
|
+
this.record(node.id, 'approval', node.label, 'ok', { note: `Decision: ${decision} → ${chosen.e.target}` });
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
for (const x of out)
|
|
171
|
+
this.traverse(x.e, x.i);
|
|
172
|
+
this.record(node.id, 'approval', node.label, 'ok', {
|
|
173
|
+
note: want
|
|
174
|
+
? `No out-edge labelled "${decision}"; took all branches (engine label-fallback).`
|
|
175
|
+
: 'No decision supplied; took all branches.',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
145
179
|
// ---- node execution -----------------------------------------------------
|
|
146
180
|
execute(node) {
|
|
147
181
|
const type = node.type;
|
|
@@ -151,6 +185,8 @@ export class FlowSimulator {
|
|
|
151
185
|
}
|
|
152
186
|
if (type === 'decision')
|
|
153
187
|
return this.executeDecision(node);
|
|
188
|
+
if (type === 'assignment')
|
|
189
|
+
return this.executeAssignment(node);
|
|
154
190
|
if (UNSUPPORTED.has(type)) {
|
|
155
191
|
this.enqueueSuccessors(node);
|
|
156
192
|
return this.record(node.id, type, node.label, 'skipped', {
|
|
@@ -163,15 +199,34 @@ export class FlowSimulator {
|
|
|
163
199
|
note: 'Parallel split — branches fan out (no join synchronization is simulated).',
|
|
164
200
|
});
|
|
165
201
|
}
|
|
202
|
+
if (type === 'approval') {
|
|
203
|
+
// ADR-0019: an approval node opens a request and SUSPENDS the run until a
|
|
204
|
+
// decision is recorded. Model that as a pause; the author resumes down the
|
|
205
|
+
// chosen approve / reject / revise out-edge (see resumeApproval) rather
|
|
206
|
+
// than fanning out to every out-edge at once.
|
|
207
|
+
this.state.status = 'paused';
|
|
208
|
+
this.state.pausedReason = 'approval';
|
|
209
|
+
return this.record(node.id, type, node.label, 'paused', { note: 'Approval reached — choose a decision to continue.' });
|
|
210
|
+
}
|
|
166
211
|
if (type === 'wait') {
|
|
167
212
|
this.state.status = 'paused';
|
|
168
213
|
this.state.pausedReason = 'wait';
|
|
169
214
|
return this.record(node.id, type, node.label, 'paused', { note: 'Wait reached — continue manually.' });
|
|
170
215
|
}
|
|
171
216
|
if (type === 'screen') {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
217
|
+
// Mirror the engine's `shouldPause`: a screen suspends only when it
|
|
218
|
+
// collects input (`fields`) or explicitly opts in (`waitForInput`).
|
|
219
|
+
// A field-less / `waitForInput:false` screen is a server pass-through.
|
|
220
|
+
const fields = Array.isArray(node.config?.fields) ? node.config.fields : [];
|
|
221
|
+
const waitForInput = node.config?.waitForInput;
|
|
222
|
+
const shouldPause = waitForInput === true || (fields.length > 0 && waitForInput !== false);
|
|
223
|
+
if (shouldPause) {
|
|
224
|
+
this.state.status = 'paused';
|
|
225
|
+
this.state.pausedReason = 'screen';
|
|
226
|
+
return this.record(node.id, type, node.label, 'paused', { note: 'Screen reached — provide inputs, then continue.' });
|
|
227
|
+
}
|
|
228
|
+
this.enqueueSuccessors(node);
|
|
229
|
+
return this.record(node.id, type, node.label, 'ok', { note: 'Screen has no input — passed through (matches runtime).' });
|
|
175
230
|
}
|
|
176
231
|
if (type === 'loop') {
|
|
177
232
|
const step = this.executeLoop(node);
|
|
@@ -233,6 +288,61 @@ export class FlowSimulator {
|
|
|
233
288
|
note: multiMatch ? 'Multiple conditions matched; the first declared branch was taken.' : undefined,
|
|
234
289
|
});
|
|
235
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* assignment node — set flow variables. Normalizes the three authoring
|
|
293
|
+
* shapes the engine accepts (Studio's `{ assignments: { var: value } }`
|
|
294
|
+
* map, the example `{ assignments: [{ variable, value }] }` array, and the
|
|
295
|
+
* legacy flat `{ var: value }`) and interpolates `{var}` templates — so the
|
|
296
|
+
* Debug run mirrors runtime instead of silently no-oping.
|
|
297
|
+
*/
|
|
298
|
+
executeAssignment(node) {
|
|
299
|
+
const cfg = node.config ?? {};
|
|
300
|
+
const raw = cfg.assignments;
|
|
301
|
+
const pairs = [];
|
|
302
|
+
if (Array.isArray(raw)) {
|
|
303
|
+
for (const item of raw) {
|
|
304
|
+
if (item && typeof item === 'object') {
|
|
305
|
+
const e = item;
|
|
306
|
+
const name = e.variable ?? e.name ?? e.key;
|
|
307
|
+
if (typeof name === 'string' && name)
|
|
308
|
+
pairs.push([name, e.value]);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
else if (raw && typeof raw === 'object') {
|
|
313
|
+
for (const [k, v] of Object.entries(raw))
|
|
314
|
+
pairs.push([k, v]);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
for (const [k, v] of Object.entries(cfg))
|
|
318
|
+
pairs.push([k, v]);
|
|
319
|
+
}
|
|
320
|
+
const wrote = {};
|
|
321
|
+
for (const [key, value] of pairs) {
|
|
322
|
+
const resolved = this.interpolateValue(value);
|
|
323
|
+
this.state.variables[key] = resolved;
|
|
324
|
+
wrote[key] = resolved;
|
|
325
|
+
}
|
|
326
|
+
this.enqueueSuccessors(node);
|
|
327
|
+
return this.record(node.id, 'assignment', node.label, 'ok', {
|
|
328
|
+
wrote: Object.keys(wrote).length ? wrote : undefined,
|
|
329
|
+
note: Object.keys(wrote).length ? undefined : 'No assignments defined.',
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
/** Resolve `{var}` templates in an assignment value against live variables. */
|
|
333
|
+
interpolateValue(value) {
|
|
334
|
+
if (typeof value !== 'string')
|
|
335
|
+
return value;
|
|
336
|
+
const whole = value.match(/^\{([^}]+)\}$/);
|
|
337
|
+
if (whole) {
|
|
338
|
+
const v = this.state.variables[whole[1].trim()];
|
|
339
|
+
return v !== undefined ? v : value;
|
|
340
|
+
}
|
|
341
|
+
return value.replace(/\{([^}]+)\}/g, (_m, k) => {
|
|
342
|
+
const v = this.state.variables[String(k).trim()];
|
|
343
|
+
return v === undefined || v === null ? '' : String(v);
|
|
344
|
+
});
|
|
345
|
+
}
|
|
236
346
|
executeLoop(node) {
|
|
237
347
|
const ref = str(node.config?.collection);
|
|
238
348
|
const iterVar = str(node.config?.iteratorVariable);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/app-shell",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Minimal application shell for ObjectUI - framework-agnostic rendering engine",
|
|
@@ -33,36 +33,36 @@
|
|
|
33
33
|
"qrcode": "^1.5.4",
|
|
34
34
|
"sonner": "^2.0.7",
|
|
35
35
|
"zod": "^4.4.3",
|
|
36
|
-
"@object-ui/auth": "7.
|
|
37
|
-
"@object-ui/collaboration": "7.
|
|
38
|
-
"@object-ui/components": "7.
|
|
39
|
-
"@object-ui/core": "7.
|
|
40
|
-
"@object-ui/data-objectstack": "7.
|
|
41
|
-
"@object-ui/fields": "7.
|
|
42
|
-
"@object-ui/i18n": "7.
|
|
43
|
-
"@object-ui/layout": "7.
|
|
44
|
-
"@object-ui/permissions": "7.
|
|
45
|
-
"@object-ui/plugin-editor": "7.
|
|
46
|
-
"@object-ui/providers": "7.
|
|
47
|
-
"@object-ui/react": "7.
|
|
48
|
-
"@object-ui/types": "7.
|
|
36
|
+
"@object-ui/auth": "7.2.0",
|
|
37
|
+
"@object-ui/collaboration": "7.2.0",
|
|
38
|
+
"@object-ui/components": "7.2.0",
|
|
39
|
+
"@object-ui/core": "7.2.0",
|
|
40
|
+
"@object-ui/data-objectstack": "7.2.0",
|
|
41
|
+
"@object-ui/fields": "7.2.0",
|
|
42
|
+
"@object-ui/i18n": "7.2.0",
|
|
43
|
+
"@object-ui/layout": "7.2.0",
|
|
44
|
+
"@object-ui/permissions": "7.2.0",
|
|
45
|
+
"@object-ui/plugin-editor": "7.2.0",
|
|
46
|
+
"@object-ui/providers": "7.2.0",
|
|
47
|
+
"@object-ui/react": "7.2.0",
|
|
48
|
+
"@object-ui/types": "7.2.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"react": "^18.0.0 || ^19.0.0",
|
|
52
52
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
53
53
|
"react-router-dom": "^6.0.0 || ^7.0.0",
|
|
54
|
-
"@object-ui/plugin-calendar": "^7.
|
|
55
|
-
"@object-ui/plugin-charts": "^7.
|
|
56
|
-
"@object-ui/plugin-chatbot": "^7.
|
|
57
|
-
"@object-ui/plugin-dashboard": "^7.
|
|
58
|
-
"@object-ui/plugin-designer": "^7.
|
|
59
|
-
"@object-ui/plugin-detail": "^7.
|
|
60
|
-
"@object-ui/plugin-form": "^7.
|
|
61
|
-
"@object-ui/plugin-grid": "^7.
|
|
62
|
-
"@object-ui/plugin-kanban": "^7.
|
|
63
|
-
"@object-ui/plugin-list": "^7.
|
|
64
|
-
"@object-ui/plugin-report": "^7.
|
|
65
|
-
"@object-ui/plugin-view": "^7.
|
|
54
|
+
"@object-ui/plugin-calendar": "^7.2.0",
|
|
55
|
+
"@object-ui/plugin-charts": "^7.2.0",
|
|
56
|
+
"@object-ui/plugin-chatbot": "^7.2.0",
|
|
57
|
+
"@object-ui/plugin-dashboard": "^7.2.0",
|
|
58
|
+
"@object-ui/plugin-designer": "^7.2.0",
|
|
59
|
+
"@object-ui/plugin-detail": "^7.2.0",
|
|
60
|
+
"@object-ui/plugin-form": "^7.2.0",
|
|
61
|
+
"@object-ui/plugin-grid": "^7.2.0",
|
|
62
|
+
"@object-ui/plugin-kanban": "^7.2.0",
|
|
63
|
+
"@object-ui/plugin-list": "^7.2.0",
|
|
64
|
+
"@object-ui/plugin-report": "^7.2.0",
|
|
65
|
+
"@object-ui/plugin-view": "^7.2.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/node": "^26.0.0",
|
|
@@ -75,18 +75,18 @@
|
|
|
75
75
|
"sonner": "^2.0.7",
|
|
76
76
|
"typescript": "^6.0.3",
|
|
77
77
|
"vite": "^8.0.16",
|
|
78
|
-
"@object-ui/plugin-calendar": "7.
|
|
79
|
-
"@object-ui/plugin-charts": "7.
|
|
80
|
-
"@object-ui/plugin-chatbot": "7.
|
|
81
|
-
"@object-ui/plugin-dashboard": "7.
|
|
82
|
-
"@object-ui/plugin-designer": "7.
|
|
83
|
-
"@object-ui/plugin-detail": "7.
|
|
84
|
-
"@object-ui/plugin-form": "7.
|
|
85
|
-
"@object-ui/plugin-grid": "7.
|
|
86
|
-
"@object-ui/plugin-kanban": "7.
|
|
87
|
-
"@object-ui/plugin-list": "7.
|
|
88
|
-
"@object-ui/plugin-report": "7.
|
|
89
|
-
"@object-ui/plugin-view": "7.
|
|
78
|
+
"@object-ui/plugin-calendar": "7.2.0",
|
|
79
|
+
"@object-ui/plugin-charts": "7.2.0",
|
|
80
|
+
"@object-ui/plugin-chatbot": "7.2.0",
|
|
81
|
+
"@object-ui/plugin-dashboard": "7.2.0",
|
|
82
|
+
"@object-ui/plugin-designer": "7.2.0",
|
|
83
|
+
"@object-ui/plugin-detail": "7.2.0",
|
|
84
|
+
"@object-ui/plugin-form": "7.2.0",
|
|
85
|
+
"@object-ui/plugin-grid": "7.2.0",
|
|
86
|
+
"@object-ui/plugin-kanban": "7.2.0",
|
|
87
|
+
"@object-ui/plugin-list": "7.2.0",
|
|
88
|
+
"@object-ui/plugin-report": "7.2.0",
|
|
89
|
+
"@object-ui/plugin-view": "7.2.0"
|
|
90
90
|
},
|
|
91
91
|
"keywords": [
|
|
92
92
|
"objectui",
|