@xeplr/workflow-engine 0.0.0-stage → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -2
  3. package/db/xcfgSetup.js +8 -0
  4. package/index.js +290 -0
  5. package/lib/actionCatalog.js +197 -0
  6. package/lib/actions/jobChainSteps.js +79 -0
  7. package/lib/actions/jobRun.js +171 -0
  8. package/lib/actions/screenShow.js +71 -0
  9. package/lib/auth.js +19 -0
  10. package/lib/db.js +82 -0
  11. package/lib/envExposed.js +87 -0
  12. package/lib/flows.js +916 -0
  13. package/lib/jobChains.js +426 -0
  14. package/lib/workflowRunner.js +652 -0
  15. package/migrations/0003_workflows.sql +35 -0
  16. package/migrations/0004_workflow_steps.sql +79 -0
  17. package/migrations/0005_workflow_runs.sql +49 -0
  18. package/migrations/0006_workflow_step_runs.sql +42 -0
  19. package/migrations/0007_workflow_resume_keys.sql +42 -0
  20. package/migrations/0008_workflow_run_edges.sql +43 -0
  21. package/migrations/0009_workflow_steps_layout.sql +11 -0
  22. package/migrations/0010_workflow_steps_sample_output.sql +17 -0
  23. package/migrations/0011_workflow_steps_params.sql +27 -0
  24. package/migrations/0012_workflows_kind.sql +27 -0
  25. package/migrations/0013_workflows_key.sql +48 -0
  26. package/migrations/0014_workflows_trigger.sql +30 -0
  27. package/migrations-tenants/0001_companies.sql +23 -0
  28. package/migrations-tenants/0002_workspaces.sql +22 -0
  29. package/models/Company.js +82 -0
  30. package/models/Workflow.js +67 -0
  31. package/models/WorkflowResumeKey.js +32 -0
  32. package/models/WorkflowRun.js +64 -0
  33. package/models/WorkflowRunEdge.js +49 -0
  34. package/models/WorkflowStep.js +66 -0
  35. package/models/WorkflowStepRun.js +49 -0
  36. package/models/Workspace.js +75 -0
  37. package/models/index.js +25 -0
  38. package/package.json +47 -4
@@ -0,0 +1,87 @@
1
+ // WHICH ENV VARS A WORKFLOW STEP MAY REFERENCE, as `{env.NAME}`.
2
+ //
3
+ // Nothing, unless named:
4
+ //
5
+ // WORKFLOW_ENV_EXPOSED=PUBLIC_BASE_URL,DEPLOY_TAG
6
+ //
7
+ // AN ALLOWLIST, NOT A DENYLIST, and that is the whole design. A denylist works
8
+ // on the day it is written and fails silently afterwards: the day somebody
9
+ // adds STRIPE_SECRET_KEY to the environment it is readable until a second
10
+ // person remembers to go and hide it. This way a new variable is invisible
11
+ // until it is deliberately published, so the failure mode of forgetting is
12
+ // "the template renders empty" rather than "the key leaked".
13
+ //
14
+ // WHY THIS MATTERS MORE THAN IT LOOKS. A resolved step input is not private:
15
+ //
16
+ // - POST /workflows/:id/steps/try ECHOES the interpolated input straight
17
+ // back to the browser, by design, so the builder can show what it sent
18
+ // (see lib/router.js). The step does not even have to be saved — `values`
19
+ // comes off the request body.
20
+ // - workflowRunner writes the resolved values into WorkflowStepRun.input,
21
+ // where they stay for the life of the run history.
22
+ //
23
+ // So without a list, "can author a workflow step" would silently mean "can
24
+ // read ENCRYPTION_KEY and AUTH_JWT_SECRET" — the two that decrypt every
25
+ // stored connection string and mint a token for any user. Both halves of that
26
+ // escalation look ordinary in an access-control table, which is exactly why
27
+ // it has to be shut off here rather than noticed later.
28
+ //
29
+ // KEEP SECRETS OFF THIS LIST. It is for values that are awkward to hardcode
30
+ // per environment and harmless to read: a public base URL, an environment
31
+ // tag, a bucket name. A credential belongs on the path where the ACTION reads
32
+ // it server-side at execution time and it never enters the template context
33
+ // at all — that is what `useCustomConnection: false` on the email actions
34
+ // does with SMTP_*, and it is strictly safer than any list.
35
+
36
+ // Exact names only. No prefixes and no globs: `PUBLIC_*` reads as a small
37
+ // convenience right up until somebody names a secret PUBLIC_something, and
38
+ // then the list no longer says what it exposes. A literal '*' matches a
39
+ // variable actually called '*', which does not exist — so the tempting
40
+ // shortcut fails closed rather than exposing everything.
41
+ function exposedNames() {
42
+ var raw = process.env.WORKFLOW_ENV_EXPOSED;
43
+ if (!raw) return [];
44
+ return String(raw).split(',')
45
+ .map(function(n) { return n.trim(); })
46
+ .filter(function(n) { return n.length > 0; });
47
+ }
48
+
49
+ /**
50
+ * The `env` branch of a step's interpolation context.
51
+ *
52
+ * Read per call rather than memoized at load: this module is required before
53
+ * the host has finished loading its .env in at least one boot order, and a
54
+ * snapshot taken then would be permanently empty with nothing to explain it.
55
+ * The cost is splitting a short string once per step.
56
+ *
57
+ * A name on the list that is not set is OMITTED rather than included as
58
+ * undefined — either way `{env.NAME}` renders as empty string, since that is
59
+ * what interpolate() does with any unknown path (see @xeplr/schema-handler's
60
+ * templating.js). Worth knowing when debugging: a misspelled name and an
61
+ * unexposed one look identical in the output.
62
+ */
63
+ function exposedEnv() {
64
+ var out = {};
65
+ exposedNames().forEach(function(name) {
66
+ if (process.env[name] !== undefined) out[name] = process.env[name];
67
+ });
68
+ return out;
69
+ }
70
+
71
+ /**
72
+ * Say at boot what is readable, so it is a fact in the log rather than a
73
+ * thing you would have to go and derive from an .env file. A list that
74
+ * accidentally names a secret is only catchable if somebody can see it.
75
+ */
76
+ function logExposure() {
77
+ var names = exposedNames();
78
+ if (!names.length) return;
79
+ var missing = names.filter(function(n) { return process.env[n] === undefined; });
80
+ console.log('[workflow] {env.*} exposes ' + names.length + ' var(s) to step templates: ' + names.join(', '));
81
+ if (missing.length) {
82
+ console.warn('[workflow] WORKFLOW_ENV_EXPOSED names ' + missing.length +
83
+ ' var(s) that are not set (they resolve to empty string): ' + missing.join(', '));
84
+ }
85
+ }
86
+
87
+ module.exports = { exposedNames: exposedNames, exposedEnv: exposedEnv, logExposure: logExposure };