@contractspec/example.workflow-system 3.7.7 → 3.8.2
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/README.md +3 -0
- package/dist/browser/handlers/index.js +43 -43
- package/dist/browser/handlers/workflow.handlers.js +43 -43
- package/dist/browser/index.js +572 -183
- package/dist/browser/shared/demo-scenario.js +213 -0
- package/dist/browser/ui/WorkflowDashboard.visualizations.js +239 -0
- package/dist/browser/ui/hooks/index.js +0 -47
- package/dist/browser/ui/hooks/useWorkflowList.js +5 -3
- package/dist/browser/ui/index.js +5 -3
- package/dist/browser/ui/renderers/index.js +409 -73
- package/dist/browser/ui/renderers/workflow.markdown.js +409 -73
- package/dist/browser/visualizations/catalog.js +132 -0
- package/dist/browser/visualizations/index.js +133 -0
- package/dist/browser/visualizations/selectors.js +195 -0
- package/dist/example.test.d.ts +1 -0
- package/dist/handlers/index.js +43 -43
- package/dist/handlers/workflow.handlers.js +43 -43
- package/dist/index.d.ts +1 -0
- package/dist/index.js +572 -183
- package/dist/shared/demo-scenario.d.ts +43 -0
- package/dist/shared/demo-scenario.js +214 -0
- package/dist/ui/WorkflowDashboard.visualizations.d.ts +4 -0
- package/dist/ui/WorkflowDashboard.visualizations.js +240 -0
- package/dist/ui/hooks/index.js +0 -47
- package/dist/ui/hooks/useWorkflowList.d.ts +2 -1
- package/dist/ui/hooks/useWorkflowList.js +5 -3
- package/dist/ui/index.js +5 -3
- package/dist/ui/renderers/index.js +409 -73
- package/dist/ui/renderers/workflow.markdown.js +409 -73
- package/dist/visualizations/catalog.d.ts +11 -0
- package/dist/visualizations/catalog.js +133 -0
- package/dist/visualizations/index.d.ts +2 -0
- package/dist/visualizations/index.js +134 -0
- package/dist/visualizations/selectors.d.ts +11 -0
- package/dist/visualizations/selectors.js +196 -0
- package/dist/visualizations/selectors.test.d.ts +1 -0
- package/package.json +69 -8
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/visualizations/catalog.ts
|
|
2
|
+
import {
|
|
3
|
+
defineVisualization,
|
|
4
|
+
VisualizationRegistry
|
|
5
|
+
} from "@contractspec/lib.contracts-spec/visualizations";
|
|
6
|
+
var INSTANCE_LIST_REF = {
|
|
7
|
+
key: "workflow.instance.list",
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
};
|
|
10
|
+
var META = {
|
|
11
|
+
version: "1.0.0",
|
|
12
|
+
domain: "workflow",
|
|
13
|
+
stability: "experimental",
|
|
14
|
+
owners: ["@example.workflow-system"],
|
|
15
|
+
tags: ["workflow", "visualization", "operations"]
|
|
16
|
+
};
|
|
17
|
+
var WorkflowInstanceStatusVisualization = defineVisualization({
|
|
18
|
+
meta: {
|
|
19
|
+
...META,
|
|
20
|
+
key: "workflow-system.visualization.instance-status",
|
|
21
|
+
title: "Instance Status Breakdown",
|
|
22
|
+
description: "Distribution of workflow instance states.",
|
|
23
|
+
goal: "Surface the current workload mix.",
|
|
24
|
+
context: "Workflow operations overview."
|
|
25
|
+
},
|
|
26
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
27
|
+
visualization: {
|
|
28
|
+
kind: "pie",
|
|
29
|
+
nameDimension: "status",
|
|
30
|
+
valueMeasure: "instances",
|
|
31
|
+
dimensions: [
|
|
32
|
+
{ key: "status", label: "Status", dataPath: "status", type: "category" }
|
|
33
|
+
],
|
|
34
|
+
measures: [
|
|
35
|
+
{
|
|
36
|
+
key: "instances",
|
|
37
|
+
label: "Instances",
|
|
38
|
+
dataPath: "instances",
|
|
39
|
+
format: "number"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
table: { caption: "Workflow instance counts by status." }
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
var WorkflowThroughputVisualization = defineVisualization({
|
|
46
|
+
meta: {
|
|
47
|
+
...META,
|
|
48
|
+
key: "workflow-system.visualization.throughput",
|
|
49
|
+
title: "Run Throughput",
|
|
50
|
+
description: "Daily workflow instance starts.",
|
|
51
|
+
goal: "Show operational throughput over time.",
|
|
52
|
+
context: "Workflow trend monitoring."
|
|
53
|
+
},
|
|
54
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
55
|
+
visualization: {
|
|
56
|
+
kind: "cartesian",
|
|
57
|
+
variant: "line",
|
|
58
|
+
xDimension: "day",
|
|
59
|
+
yMeasures: ["instances"],
|
|
60
|
+
dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
|
|
61
|
+
measures: [
|
|
62
|
+
{
|
|
63
|
+
key: "instances",
|
|
64
|
+
label: "Instances",
|
|
65
|
+
dataPath: "instances",
|
|
66
|
+
format: "number",
|
|
67
|
+
color: "#0f766e"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
table: { caption: "Daily workflow instance starts." }
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
var WorkflowActiveMetricVisualization = defineVisualization({
|
|
74
|
+
meta: {
|
|
75
|
+
...META,
|
|
76
|
+
key: "workflow-system.visualization.active-work",
|
|
77
|
+
title: "Active Work",
|
|
78
|
+
description: "Current in-flight or pending workflow instances.",
|
|
79
|
+
goal: "Expose active operational workload.",
|
|
80
|
+
context: "Workflow workload comparison."
|
|
81
|
+
},
|
|
82
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
83
|
+
visualization: {
|
|
84
|
+
kind: "metric",
|
|
85
|
+
measure: "value",
|
|
86
|
+
measures: [
|
|
87
|
+
{ key: "value", label: "Instances", dataPath: "value", format: "number" }
|
|
88
|
+
],
|
|
89
|
+
table: { caption: "Active workflow count." }
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
var WorkflowCompletedMetricVisualization = defineVisualization({
|
|
93
|
+
meta: {
|
|
94
|
+
...META,
|
|
95
|
+
key: "workflow-system.visualization.completed-work",
|
|
96
|
+
title: "Completed Work",
|
|
97
|
+
description: "Completed workflow instances in the current sample.",
|
|
98
|
+
goal: "Show output against active workload.",
|
|
99
|
+
context: "Workflow workload comparison."
|
|
100
|
+
},
|
|
101
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
102
|
+
visualization: {
|
|
103
|
+
kind: "metric",
|
|
104
|
+
measure: "value",
|
|
105
|
+
measures: [
|
|
106
|
+
{ key: "value", label: "Instances", dataPath: "value", format: "number" }
|
|
107
|
+
],
|
|
108
|
+
table: { caption: "Completed workflow count." }
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
var WorkflowVisualizationSpecs = [
|
|
112
|
+
WorkflowInstanceStatusVisualization,
|
|
113
|
+
WorkflowThroughputVisualization,
|
|
114
|
+
WorkflowActiveMetricVisualization,
|
|
115
|
+
WorkflowCompletedMetricVisualization
|
|
116
|
+
];
|
|
117
|
+
var WorkflowVisualizationRegistry = new VisualizationRegistry([
|
|
118
|
+
...WorkflowVisualizationSpecs
|
|
119
|
+
]);
|
|
120
|
+
var WorkflowVisualizationRefs = WorkflowVisualizationSpecs.map((spec) => ({
|
|
121
|
+
key: spec.meta.key,
|
|
122
|
+
version: spec.meta.version
|
|
123
|
+
}));
|
|
124
|
+
export {
|
|
125
|
+
WorkflowVisualizationSpecs,
|
|
126
|
+
WorkflowVisualizationRegistry,
|
|
127
|
+
WorkflowVisualizationRefs,
|
|
128
|
+
WorkflowThroughputVisualization,
|
|
129
|
+
WorkflowInstanceStatusVisualization,
|
|
130
|
+
WorkflowCompletedMetricVisualization,
|
|
131
|
+
WorkflowActiveMetricVisualization
|
|
132
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// src/visualizations/catalog.ts
|
|
2
|
+
import {
|
|
3
|
+
defineVisualization,
|
|
4
|
+
VisualizationRegistry
|
|
5
|
+
} from "@contractspec/lib.contracts-spec/visualizations";
|
|
6
|
+
var INSTANCE_LIST_REF = {
|
|
7
|
+
key: "workflow.instance.list",
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
};
|
|
10
|
+
var META = {
|
|
11
|
+
version: "1.0.0",
|
|
12
|
+
domain: "workflow",
|
|
13
|
+
stability: "experimental",
|
|
14
|
+
owners: ["@example.workflow-system"],
|
|
15
|
+
tags: ["workflow", "visualization", "operations"]
|
|
16
|
+
};
|
|
17
|
+
var WorkflowInstanceStatusVisualization = defineVisualization({
|
|
18
|
+
meta: {
|
|
19
|
+
...META,
|
|
20
|
+
key: "workflow-system.visualization.instance-status",
|
|
21
|
+
title: "Instance Status Breakdown",
|
|
22
|
+
description: "Distribution of workflow instance states.",
|
|
23
|
+
goal: "Surface the current workload mix.",
|
|
24
|
+
context: "Workflow operations overview."
|
|
25
|
+
},
|
|
26
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
27
|
+
visualization: {
|
|
28
|
+
kind: "pie",
|
|
29
|
+
nameDimension: "status",
|
|
30
|
+
valueMeasure: "instances",
|
|
31
|
+
dimensions: [
|
|
32
|
+
{ key: "status", label: "Status", dataPath: "status", type: "category" }
|
|
33
|
+
],
|
|
34
|
+
measures: [
|
|
35
|
+
{
|
|
36
|
+
key: "instances",
|
|
37
|
+
label: "Instances",
|
|
38
|
+
dataPath: "instances",
|
|
39
|
+
format: "number"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
table: { caption: "Workflow instance counts by status." }
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
var WorkflowThroughputVisualization = defineVisualization({
|
|
46
|
+
meta: {
|
|
47
|
+
...META,
|
|
48
|
+
key: "workflow-system.visualization.throughput",
|
|
49
|
+
title: "Run Throughput",
|
|
50
|
+
description: "Daily workflow instance starts.",
|
|
51
|
+
goal: "Show operational throughput over time.",
|
|
52
|
+
context: "Workflow trend monitoring."
|
|
53
|
+
},
|
|
54
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
55
|
+
visualization: {
|
|
56
|
+
kind: "cartesian",
|
|
57
|
+
variant: "line",
|
|
58
|
+
xDimension: "day",
|
|
59
|
+
yMeasures: ["instances"],
|
|
60
|
+
dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
|
|
61
|
+
measures: [
|
|
62
|
+
{
|
|
63
|
+
key: "instances",
|
|
64
|
+
label: "Instances",
|
|
65
|
+
dataPath: "instances",
|
|
66
|
+
format: "number",
|
|
67
|
+
color: "#0f766e"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
table: { caption: "Daily workflow instance starts." }
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
var WorkflowActiveMetricVisualization = defineVisualization({
|
|
74
|
+
meta: {
|
|
75
|
+
...META,
|
|
76
|
+
key: "workflow-system.visualization.active-work",
|
|
77
|
+
title: "Active Work",
|
|
78
|
+
description: "Current in-flight or pending workflow instances.",
|
|
79
|
+
goal: "Expose active operational workload.",
|
|
80
|
+
context: "Workflow workload comparison."
|
|
81
|
+
},
|
|
82
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
83
|
+
visualization: {
|
|
84
|
+
kind: "metric",
|
|
85
|
+
measure: "value",
|
|
86
|
+
measures: [
|
|
87
|
+
{ key: "value", label: "Instances", dataPath: "value", format: "number" }
|
|
88
|
+
],
|
|
89
|
+
table: { caption: "Active workflow count." }
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
var WorkflowCompletedMetricVisualization = defineVisualization({
|
|
93
|
+
meta: {
|
|
94
|
+
...META,
|
|
95
|
+
key: "workflow-system.visualization.completed-work",
|
|
96
|
+
title: "Completed Work",
|
|
97
|
+
description: "Completed workflow instances in the current sample.",
|
|
98
|
+
goal: "Show output against active workload.",
|
|
99
|
+
context: "Workflow workload comparison."
|
|
100
|
+
},
|
|
101
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
102
|
+
visualization: {
|
|
103
|
+
kind: "metric",
|
|
104
|
+
measure: "value",
|
|
105
|
+
measures: [
|
|
106
|
+
{ key: "value", label: "Instances", dataPath: "value", format: "number" }
|
|
107
|
+
],
|
|
108
|
+
table: { caption: "Completed workflow count." }
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
var WorkflowVisualizationSpecs = [
|
|
112
|
+
WorkflowInstanceStatusVisualization,
|
|
113
|
+
WorkflowThroughputVisualization,
|
|
114
|
+
WorkflowActiveMetricVisualization,
|
|
115
|
+
WorkflowCompletedMetricVisualization
|
|
116
|
+
];
|
|
117
|
+
var WorkflowVisualizationRegistry = new VisualizationRegistry([
|
|
118
|
+
...WorkflowVisualizationSpecs
|
|
119
|
+
]);
|
|
120
|
+
var WorkflowVisualizationRefs = WorkflowVisualizationSpecs.map((spec) => ({
|
|
121
|
+
key: spec.meta.key,
|
|
122
|
+
version: spec.meta.version
|
|
123
|
+
}));
|
|
124
|
+
export {
|
|
125
|
+
createWorkflowVisualizationSections,
|
|
126
|
+
WorkflowVisualizationSpecs,
|
|
127
|
+
WorkflowVisualizationRegistry,
|
|
128
|
+
WorkflowVisualizationRefs,
|
|
129
|
+
WorkflowThroughputVisualization,
|
|
130
|
+
WorkflowInstanceStatusVisualization,
|
|
131
|
+
WorkflowCompletedMetricVisualization,
|
|
132
|
+
WorkflowActiveMetricVisualization
|
|
133
|
+
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// src/visualizations/catalog.ts
|
|
2
|
+
import {
|
|
3
|
+
defineVisualization,
|
|
4
|
+
VisualizationRegistry
|
|
5
|
+
} from "@contractspec/lib.contracts-spec/visualizations";
|
|
6
|
+
var INSTANCE_LIST_REF = {
|
|
7
|
+
key: "workflow.instance.list",
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
};
|
|
10
|
+
var META = {
|
|
11
|
+
version: "1.0.0",
|
|
12
|
+
domain: "workflow",
|
|
13
|
+
stability: "experimental",
|
|
14
|
+
owners: ["@example.workflow-system"],
|
|
15
|
+
tags: ["workflow", "visualization", "operations"]
|
|
16
|
+
};
|
|
17
|
+
var WorkflowInstanceStatusVisualization = defineVisualization({
|
|
18
|
+
meta: {
|
|
19
|
+
...META,
|
|
20
|
+
key: "workflow-system.visualization.instance-status",
|
|
21
|
+
title: "Instance Status Breakdown",
|
|
22
|
+
description: "Distribution of workflow instance states.",
|
|
23
|
+
goal: "Surface the current workload mix.",
|
|
24
|
+
context: "Workflow operations overview."
|
|
25
|
+
},
|
|
26
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
27
|
+
visualization: {
|
|
28
|
+
kind: "pie",
|
|
29
|
+
nameDimension: "status",
|
|
30
|
+
valueMeasure: "instances",
|
|
31
|
+
dimensions: [
|
|
32
|
+
{ key: "status", label: "Status", dataPath: "status", type: "category" }
|
|
33
|
+
],
|
|
34
|
+
measures: [
|
|
35
|
+
{
|
|
36
|
+
key: "instances",
|
|
37
|
+
label: "Instances",
|
|
38
|
+
dataPath: "instances",
|
|
39
|
+
format: "number"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
table: { caption: "Workflow instance counts by status." }
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
var WorkflowThroughputVisualization = defineVisualization({
|
|
46
|
+
meta: {
|
|
47
|
+
...META,
|
|
48
|
+
key: "workflow-system.visualization.throughput",
|
|
49
|
+
title: "Run Throughput",
|
|
50
|
+
description: "Daily workflow instance starts.",
|
|
51
|
+
goal: "Show operational throughput over time.",
|
|
52
|
+
context: "Workflow trend monitoring."
|
|
53
|
+
},
|
|
54
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
55
|
+
visualization: {
|
|
56
|
+
kind: "cartesian",
|
|
57
|
+
variant: "line",
|
|
58
|
+
xDimension: "day",
|
|
59
|
+
yMeasures: ["instances"],
|
|
60
|
+
dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
|
|
61
|
+
measures: [
|
|
62
|
+
{
|
|
63
|
+
key: "instances",
|
|
64
|
+
label: "Instances",
|
|
65
|
+
dataPath: "instances",
|
|
66
|
+
format: "number",
|
|
67
|
+
color: "#0f766e"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
table: { caption: "Daily workflow instance starts." }
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
var WorkflowActiveMetricVisualization = defineVisualization({
|
|
74
|
+
meta: {
|
|
75
|
+
...META,
|
|
76
|
+
key: "workflow-system.visualization.active-work",
|
|
77
|
+
title: "Active Work",
|
|
78
|
+
description: "Current in-flight or pending workflow instances.",
|
|
79
|
+
goal: "Expose active operational workload.",
|
|
80
|
+
context: "Workflow workload comparison."
|
|
81
|
+
},
|
|
82
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
83
|
+
visualization: {
|
|
84
|
+
kind: "metric",
|
|
85
|
+
measure: "value",
|
|
86
|
+
measures: [
|
|
87
|
+
{ key: "value", label: "Instances", dataPath: "value", format: "number" }
|
|
88
|
+
],
|
|
89
|
+
table: { caption: "Active workflow count." }
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
var WorkflowCompletedMetricVisualization = defineVisualization({
|
|
93
|
+
meta: {
|
|
94
|
+
...META,
|
|
95
|
+
key: "workflow-system.visualization.completed-work",
|
|
96
|
+
title: "Completed Work",
|
|
97
|
+
description: "Completed workflow instances in the current sample.",
|
|
98
|
+
goal: "Show output against active workload.",
|
|
99
|
+
context: "Workflow workload comparison."
|
|
100
|
+
},
|
|
101
|
+
source: { primary: INSTANCE_LIST_REF, resultPath: "data" },
|
|
102
|
+
visualization: {
|
|
103
|
+
kind: "metric",
|
|
104
|
+
measure: "value",
|
|
105
|
+
measures: [
|
|
106
|
+
{ key: "value", label: "Instances", dataPath: "value", format: "number" }
|
|
107
|
+
],
|
|
108
|
+
table: { caption: "Completed workflow count." }
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
var WorkflowVisualizationSpecs = [
|
|
112
|
+
WorkflowInstanceStatusVisualization,
|
|
113
|
+
WorkflowThroughputVisualization,
|
|
114
|
+
WorkflowActiveMetricVisualization,
|
|
115
|
+
WorkflowCompletedMetricVisualization
|
|
116
|
+
];
|
|
117
|
+
var WorkflowVisualizationRegistry = new VisualizationRegistry([
|
|
118
|
+
...WorkflowVisualizationSpecs
|
|
119
|
+
]);
|
|
120
|
+
var WorkflowVisualizationRefs = WorkflowVisualizationSpecs.map((spec) => ({
|
|
121
|
+
key: spec.meta.key,
|
|
122
|
+
version: spec.meta.version
|
|
123
|
+
}));
|
|
124
|
+
|
|
125
|
+
// src/visualizations/selectors.ts
|
|
126
|
+
function toDayKey(value) {
|
|
127
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
128
|
+
return date.toISOString().slice(0, 10);
|
|
129
|
+
}
|
|
130
|
+
function createWorkflowVisualizationSections(instances) {
|
|
131
|
+
const statusCounts = new Map;
|
|
132
|
+
const throughput = new Map;
|
|
133
|
+
let activeCount = 0;
|
|
134
|
+
let completedCount = 0;
|
|
135
|
+
for (const instance of instances) {
|
|
136
|
+
statusCounts.set(instance.status, (statusCounts.get(instance.status) ?? 0) + 1);
|
|
137
|
+
const day = toDayKey(instance.startedAt);
|
|
138
|
+
throughput.set(day, (throughput.get(day) ?? 0) + 1);
|
|
139
|
+
if (instance.status === "PENDING" || instance.status === "IN_PROGRESS") {
|
|
140
|
+
activeCount += 1;
|
|
141
|
+
}
|
|
142
|
+
if (instance.status === "COMPLETED") {
|
|
143
|
+
completedCount += 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const primaryItems = [
|
|
147
|
+
{
|
|
148
|
+
key: "workflow-status",
|
|
149
|
+
spec: WorkflowInstanceStatusVisualization,
|
|
150
|
+
data: {
|
|
151
|
+
data: Array.from(statusCounts.entries()).map(([status, count]) => ({
|
|
152
|
+
status,
|
|
153
|
+
instances: count
|
|
154
|
+
}))
|
|
155
|
+
},
|
|
156
|
+
title: "Instance Status Breakdown",
|
|
157
|
+
description: "Status mix across workflow instances.",
|
|
158
|
+
height: 260
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
key: "workflow-throughput",
|
|
162
|
+
spec: WorkflowThroughputVisualization,
|
|
163
|
+
data: {
|
|
164
|
+
data: Array.from(throughput.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([day, count]) => ({ day, instances: count }))
|
|
165
|
+
},
|
|
166
|
+
title: "Run Throughput",
|
|
167
|
+
description: "Daily workflow starts from current instances."
|
|
168
|
+
}
|
|
169
|
+
];
|
|
170
|
+
const comparisonItems = [
|
|
171
|
+
{
|
|
172
|
+
key: "workflow-active",
|
|
173
|
+
spec: WorkflowActiveMetricVisualization,
|
|
174
|
+
data: { data: [{ value: activeCount }] },
|
|
175
|
+
title: "Active Work",
|
|
176
|
+
description: "Pending and in-progress workflows.",
|
|
177
|
+
height: 200
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
key: "workflow-completed",
|
|
181
|
+
spec: WorkflowCompletedMetricVisualization,
|
|
182
|
+
data: { data: [{ value: completedCount }] },
|
|
183
|
+
title: "Completed Work",
|
|
184
|
+
description: "Completed workflows in the current sample.",
|
|
185
|
+
height: 200
|
|
186
|
+
}
|
|
187
|
+
];
|
|
188
|
+
return {
|
|
189
|
+
primaryItems,
|
|
190
|
+
comparisonItems
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
export {
|
|
194
|
+
createWorkflowVisualizationSections
|
|
195
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/handlers/index.js
CHANGED
|
@@ -55,9 +55,19 @@ function rowToApproval(row) {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
function createWorkflowHandlers(db) {
|
|
58
|
+
function normalizeSql(sql) {
|
|
59
|
+
let placeholderIndex = 0;
|
|
60
|
+
return sql.replace(/\?/g, () => `$${++placeholderIndex}`);
|
|
61
|
+
}
|
|
62
|
+
async function queryRows(sql, params = []) {
|
|
63
|
+
return (await db.query(normalizeSql(sql), params)).rows;
|
|
64
|
+
}
|
|
65
|
+
async function execute(sql, params = []) {
|
|
66
|
+
await db.execute(normalizeSql(sql), params);
|
|
67
|
+
}
|
|
58
68
|
async function listDefinitions(input) {
|
|
59
69
|
const { projectId, status, search, limit = 20, offset = 0 } = input;
|
|
60
|
-
let whereClause =
|
|
70
|
+
let whereClause = 'WHERE "projectId" = ?';
|
|
61
71
|
const params = [projectId];
|
|
62
72
|
if (status && status !== "all") {
|
|
63
73
|
whereClause += " AND status = ?";
|
|
@@ -67,9 +77,9 @@ function createWorkflowHandlers(db) {
|
|
|
67
77
|
whereClause += " AND name LIKE ?";
|
|
68
78
|
params.push(`%${search}%`);
|
|
69
79
|
}
|
|
70
|
-
const countResult =
|
|
80
|
+
const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_definition ${whereClause}`, params);
|
|
71
81
|
const total = countResult[0]?.count ?? 0;
|
|
72
|
-
const rows =
|
|
82
|
+
const rows = await queryRows(`SELECT * FROM workflow_definition ${whereClause} ORDER BY "updatedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
|
|
73
83
|
return {
|
|
74
84
|
definitions: rows.map(rowToDefinition),
|
|
75
85
|
total
|
|
@@ -78,7 +88,7 @@ function createWorkflowHandlers(db) {
|
|
|
78
88
|
async function createDefinition(input, context) {
|
|
79
89
|
const id = generateId("wfdef");
|
|
80
90
|
const now = new Date().toISOString();
|
|
81
|
-
await
|
|
91
|
+
await execute(`INSERT INTO workflow_definition (id, "projectId", "organizationId", name, description, type, status, "createdAt", "updatedAt")
|
|
82
92
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
83
93
|
id,
|
|
84
94
|
context.projectId,
|
|
@@ -90,15 +100,15 @@ function createWorkflowHandlers(db) {
|
|
|
90
100
|
now,
|
|
91
101
|
now
|
|
92
102
|
]);
|
|
93
|
-
const rows =
|
|
103
|
+
const rows = await queryRows(`SELECT * FROM workflow_definition WHERE id = ?`, [id]);
|
|
94
104
|
return rowToDefinition(rows[0]);
|
|
95
105
|
}
|
|
96
106
|
async function addStep(input) {
|
|
97
107
|
const id = generateId("wfstep");
|
|
98
108
|
const now = new Date().toISOString();
|
|
99
|
-
const maxOrderResult =
|
|
109
|
+
const maxOrderResult = await queryRows(`SELECT MAX("stepOrder") as maxOrder FROM workflow_step WHERE "definitionId" = ?`, [input.definitionId]);
|
|
100
110
|
const nextOrder = (maxOrderResult[0]?.maxOrder ?? 0) + 1;
|
|
101
|
-
await
|
|
111
|
+
await execute(`INSERT INTO workflow_step (id, "definitionId", name, description, "stepOrder", type, "requiredRoles", "autoApproveCondition", "timeoutHours", "createdAt")
|
|
102
112
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
103
113
|
id,
|
|
104
114
|
input.definitionId,
|
|
@@ -111,11 +121,11 @@ function createWorkflowHandlers(db) {
|
|
|
111
121
|
input.timeoutHours ?? null,
|
|
112
122
|
now
|
|
113
123
|
]);
|
|
114
|
-
const rows =
|
|
124
|
+
const rows = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [id]);
|
|
115
125
|
return rowToStep(rows[0]);
|
|
116
126
|
}
|
|
117
127
|
async function getSteps(definitionId) {
|
|
118
|
-
const rows =
|
|
128
|
+
const rows = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder"`, [definitionId]);
|
|
119
129
|
return rows.map(rowToStep);
|
|
120
130
|
}
|
|
121
131
|
async function listInstances(input) {
|
|
@@ -127,10 +137,10 @@ function createWorkflowHandlers(db) {
|
|
|
127
137
|
limit = 20,
|
|
128
138
|
offset = 0
|
|
129
139
|
} = input;
|
|
130
|
-
let whereClause =
|
|
140
|
+
let whereClause = 'WHERE "projectId" = ?';
|
|
131
141
|
const params = [projectId];
|
|
132
142
|
if (definitionId) {
|
|
133
|
-
whereClause +=
|
|
143
|
+
whereClause += ' AND "definitionId" = ?';
|
|
134
144
|
params.push(definitionId);
|
|
135
145
|
}
|
|
136
146
|
if (status && status !== "all") {
|
|
@@ -138,12 +148,12 @@ function createWorkflowHandlers(db) {
|
|
|
138
148
|
params.push(status);
|
|
139
149
|
}
|
|
140
150
|
if (requestedBy) {
|
|
141
|
-
whereClause +=
|
|
151
|
+
whereClause += ' AND "requestedBy" = ?';
|
|
142
152
|
params.push(requestedBy);
|
|
143
153
|
}
|
|
144
|
-
const countResult =
|
|
154
|
+
const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_instance ${whereClause}`, params);
|
|
145
155
|
const total = countResult[0]?.count ?? 0;
|
|
146
|
-
const rows =
|
|
156
|
+
const rows = await queryRows(`SELECT * FROM workflow_instance ${whereClause} ORDER BY "startedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
|
|
147
157
|
return {
|
|
148
158
|
instances: rows.map(rowToInstance),
|
|
149
159
|
total
|
|
@@ -152,9 +162,9 @@ function createWorkflowHandlers(db) {
|
|
|
152
162
|
async function startInstance(input, context) {
|
|
153
163
|
const id = generateId("wfinst");
|
|
154
164
|
const now = new Date().toISOString();
|
|
155
|
-
const steps =
|
|
165
|
+
const steps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder" LIMIT 1`, [input.definitionId]);
|
|
156
166
|
const firstStepId = steps[0]?.id ?? null;
|
|
157
|
-
await
|
|
167
|
+
await execute(`INSERT INTO workflow_instance (id, "projectId", "definitionId", status, "currentStepId", data, "requestedBy", "startedAt")
|
|
158
168
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
159
169
|
id,
|
|
160
170
|
context.projectId,
|
|
@@ -166,36 +176,32 @@ function createWorkflowHandlers(db) {
|
|
|
166
176
|
now
|
|
167
177
|
]);
|
|
168
178
|
if (firstStepId) {
|
|
169
|
-
await
|
|
179
|
+
await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
|
|
170
180
|
VALUES (?, ?, ?, ?, ?)`, [generateId("wfappr"), id, firstStepId, "PENDING", now]);
|
|
171
181
|
}
|
|
172
|
-
const rows =
|
|
182
|
+
const rows = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [id]);
|
|
173
183
|
return rowToInstance(rows[0]);
|
|
174
184
|
}
|
|
175
185
|
async function approveStep(input, context) {
|
|
176
186
|
const now = new Date().toISOString();
|
|
177
|
-
const instances =
|
|
178
|
-
input.instanceId
|
|
179
|
-
])).rows;
|
|
187
|
+
const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
180
188
|
if (!instances[0]) {
|
|
181
189
|
throw new Error("NOT_FOUND");
|
|
182
190
|
}
|
|
183
191
|
const instance = instances[0];
|
|
184
|
-
await
|
|
185
|
-
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
192
|
+
await execute(`UPDATE workflow_approval SET status = 'APPROVED', "actorId" = ?, comment = ?, "decidedAt" = ?
|
|
193
|
+
WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
|
|
186
194
|
context.actorId,
|
|
187
195
|
input.comment ?? null,
|
|
188
196
|
now,
|
|
189
197
|
input.instanceId,
|
|
190
198
|
instance.currentStepId
|
|
191
199
|
]);
|
|
192
|
-
const currentStep =
|
|
193
|
-
|
|
194
|
-
])).rows;
|
|
195
|
-
const nextSteps = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? AND stepOrder > ? ORDER BY stepOrder LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0])).rows;
|
|
200
|
+
const currentStep = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [instance.currentStepId]);
|
|
201
|
+
const nextSteps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? AND "stepOrder" > ? ORDER BY "stepOrder" LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0]);
|
|
196
202
|
if (nextSteps[0]) {
|
|
197
|
-
await
|
|
198
|
-
await
|
|
203
|
+
await execute(`UPDATE workflow_instance SET "currentStepId" = ? WHERE id = ?`, [nextSteps[0].id, input.instanceId]);
|
|
204
|
+
await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
|
|
199
205
|
VALUES (?, ?, ?, ?, ?)`, [
|
|
200
206
|
generateId("wfappr"),
|
|
201
207
|
input.instanceId,
|
|
@@ -204,37 +210,31 @@ function createWorkflowHandlers(db) {
|
|
|
204
210
|
now
|
|
205
211
|
]);
|
|
206
212
|
} else {
|
|
207
|
-
await
|
|
213
|
+
await execute(`UPDATE workflow_instance SET status = 'COMPLETED', "currentStepId" = NULL, "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
|
|
208
214
|
}
|
|
209
|
-
const updated =
|
|
210
|
-
input.instanceId
|
|
211
|
-
])).rows;
|
|
215
|
+
const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
212
216
|
return rowToInstance(updated[0]);
|
|
213
217
|
}
|
|
214
218
|
async function rejectStep(input, context) {
|
|
215
219
|
const now = new Date().toISOString();
|
|
216
|
-
const instances =
|
|
217
|
-
input.instanceId
|
|
218
|
-
])).rows;
|
|
220
|
+
const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
219
221
|
if (!instances[0]) {
|
|
220
222
|
throw new Error("NOT_FOUND");
|
|
221
223
|
}
|
|
222
|
-
await
|
|
223
|
-
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
224
|
+
await execute(`UPDATE workflow_approval SET status = 'REJECTED', "actorId" = ?, comment = ?, "decidedAt" = ?
|
|
225
|
+
WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
|
|
224
226
|
context.actorId,
|
|
225
227
|
input.reason,
|
|
226
228
|
now,
|
|
227
229
|
input.instanceId,
|
|
228
230
|
instances[0].currentStepId
|
|
229
231
|
]);
|
|
230
|
-
await
|
|
231
|
-
const updated =
|
|
232
|
-
input.instanceId
|
|
233
|
-
])).rows;
|
|
232
|
+
await execute(`UPDATE workflow_instance SET status = 'REJECTED', "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
|
|
233
|
+
const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
234
234
|
return rowToInstance(updated[0]);
|
|
235
235
|
}
|
|
236
236
|
async function getApprovals(instanceId) {
|
|
237
|
-
const rows =
|
|
237
|
+
const rows = await queryRows(`SELECT * FROM workflow_approval WHERE "instanceId" = ? ORDER BY "createdAt"`, [instanceId]);
|
|
238
238
|
return rows.map(rowToApproval);
|
|
239
239
|
}
|
|
240
240
|
return {
|