@solidactions/sdk 0.1.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/.claude/settings.local.json +7 -0
- package/.clavix/outputs/dbos-http-sdk/full-prd.md +142 -0
- package/.clavix/outputs/dbos-http-sdk/quick-prd.md +12 -0
- package/.clavix/outputs/dbos-http-sdk/tasks.md +630 -0
- package/.clavix/outputs/prompts/dbos-http-api-20260110-033219.md +91 -0
- package/.husky/pre-commit +1 -0
- package/.prettierignore +3 -0
- package/.prettierrc +9 -0
- package/CODE_OF_CONDUCT.md +49 -0
- package/CONTRIBUTING.md +47 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/dbos-config.schema.json +132 -0
- package/dist/schemas/system_db_schema.d.ts +73 -0
- package/dist/schemas/system_db_schema.d.ts.map +1 -0
- package/dist/schemas/system_db_schema.js +3 -0
- package/dist/schemas/system_db_schema.js.map +1 -0
- package/dist/src/adminserver.d.ts +79 -0
- package/dist/src/adminserver.d.ts.map +1 -0
- package/dist/src/adminserver.js +495 -0
- package/dist/src/adminserver.js.map +1 -0
- package/dist/src/authdecorators.d.ts +2 -0
- package/dist/src/authdecorators.d.ts.map +1 -0
- package/dist/src/authdecorators.js +48 -0
- package/dist/src/authdecorators.js.map +1 -0
- package/dist/src/cli/cli.d.ts +9 -0
- package/dist/src/cli/cli.d.ts.map +1 -0
- package/dist/src/cli/cli.js +116 -0
- package/dist/src/cli/cli.js.map +1 -0
- package/dist/src/cli/commands.d.ts +3 -0
- package/dist/src/cli/commands.d.ts.map +1 -0
- package/dist/src/cli/commands.js +46 -0
- package/dist/src/cli/commands.js.map +1 -0
- package/dist/src/client.d.ts +85 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +186 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/conductor/conductor.d.ts +28 -0
- package/dist/src/conductor/conductor.d.ts.map +1 -0
- package/dist/src/conductor/conductor.js +376 -0
- package/dist/src/conductor/conductor.js.map +1 -0
- package/dist/src/conductor/protocol.d.ts +238 -0
- package/dist/src/conductor/protocol.d.ts.map +1 -0
- package/dist/src/conductor/protocol.js +353 -0
- package/dist/src/conductor/protocol.js.map +1 -0
- package/dist/src/config.d.ts +91 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +199 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/context.d.ts +62 -0
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +118 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/database_utils.d.ts +17 -0
- package/dist/src/database_utils.d.ts.map +1 -0
- package/dist/src/database_utils.js +53 -0
- package/dist/src/database_utils.js.map +1 -0
- package/dist/src/datasource.d.ts +109 -0
- package/dist/src/datasource.d.ts.map +1 -0
- package/dist/src/datasource.js +204 -0
- package/dist/src/datasource.js.map +1 -0
- package/dist/src/dbos-executor.d.ts +189 -0
- package/dist/src/dbos-executor.d.ts.map +1 -0
- package/dist/src/dbos-executor.js +817 -0
- package/dist/src/dbos-executor.js.map +1 -0
- package/dist/src/dbos.d.ts +519 -0
- package/dist/src/dbos.d.ts.map +1 -0
- package/dist/src/dbos.js +1282 -0
- package/dist/src/dbos.js.map +1 -0
- package/dist/src/debouncer.d.ts +33 -0
- package/dist/src/debouncer.d.ts.map +1 -0
- package/dist/src/debouncer.js +170 -0
- package/dist/src/debouncer.js.map +1 -0
- package/dist/src/debugpoint.d.ts +26 -0
- package/dist/src/debugpoint.d.ts.map +1 -0
- package/dist/src/debugpoint.js +65 -0
- package/dist/src/debugpoint.js.map +1 -0
- package/dist/src/decorators.d.ts +219 -0
- package/dist/src/decorators.d.ts.map +1 -0
- package/dist/src/decorators.js +873 -0
- package/dist/src/decorators.js.map +1 -0
- package/dist/src/error.d.ts +130 -0
- package/dist/src/error.d.ts.map +1 -0
- package/dist/src/error.js +290 -0
- package/dist/src/error.js.map +1 -0
- package/dist/src/http_client.d.ts +82 -0
- package/dist/src/http_client.d.ts.map +1 -0
- package/dist/src/http_client.js +286 -0
- package/dist/src/http_client.js.map +1 -0
- package/dist/src/http_system_database.d.ts +84 -0
- package/dist/src/http_system_database.d.ts.map +1 -0
- package/dist/src/http_system_database.js +429 -0
- package/dist/src/http_system_database.js.map +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +53 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/scheduler/crontab.d.ts +14 -0
- package/dist/src/scheduler/crontab.d.ts.map +1 -0
- package/dist/src/scheduler/crontab.js +308 -0
- package/dist/src/scheduler/crontab.js.map +1 -0
- package/dist/src/scheduler/scheduler.d.ts +41 -0
- package/dist/src/scheduler/scheduler.d.ts.map +1 -0
- package/dist/src/scheduler/scheduler.js +165 -0
- package/dist/src/scheduler/scheduler.js.map +1 -0
- package/dist/src/serialization.d.ts +57 -0
- package/dist/src/serialization.d.ts.map +1 -0
- package/dist/src/serialization.js +306 -0
- package/dist/src/serialization.js.map +1 -0
- package/dist/src/solidactions-executor.d.ts +177 -0
- package/dist/src/solidactions-executor.d.ts.map +1 -0
- package/dist/src/solidactions-executor.js +817 -0
- package/dist/src/solidactions-executor.js.map +1 -0
- package/dist/src/solidactions.d.ts +519 -0
- package/dist/src/solidactions.d.ts.map +1 -0
- package/dist/src/solidactions.js +1284 -0
- package/dist/src/solidactions.js.map +1 -0
- package/dist/src/step.d.ts +16 -0
- package/dist/src/step.d.ts.map +1 -0
- package/dist/src/step.js +3 -0
- package/dist/src/step.js.map +1 -0
- package/dist/src/system_database.d.ts +141 -0
- package/dist/src/system_database.d.ts.map +1 -0
- package/dist/src/system_database.js +25 -0
- package/dist/src/system_database.js.map +1 -0
- package/dist/src/telemetry/collector.d.ts +13 -0
- package/dist/src/telemetry/collector.d.ts.map +1 -0
- package/dist/src/telemetry/collector.js +63 -0
- package/dist/src/telemetry/collector.js.map +1 -0
- package/dist/src/telemetry/exporters.d.ts +13 -0
- package/dist/src/telemetry/exporters.d.ts.map +1 -0
- package/dist/src/telemetry/exporters.js +101 -0
- package/dist/src/telemetry/exporters.js.map +1 -0
- package/dist/src/telemetry/logs.d.ts +52 -0
- package/dist/src/telemetry/logs.d.ts.map +1 -0
- package/dist/src/telemetry/logs.js +287 -0
- package/dist/src/telemetry/logs.js.map +1 -0
- package/dist/src/telemetry/traces.d.ts +52 -0
- package/dist/src/telemetry/traces.d.ts.map +1 -0
- package/dist/src/telemetry/traces.js +150 -0
- package/dist/src/telemetry/traces.js.map +1 -0
- package/dist/src/utils.d.ts +26 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +136 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/src/wfqueue.d.ts +64 -0
- package/dist/src/wfqueue.d.ts.map +1 -0
- package/dist/src/wfqueue.js +147 -0
- package/dist/src/wfqueue.js.map +1 -0
- package/dist/src/workflow.d.ts +154 -0
- package/dist/src/workflow.d.ts.map +1 -0
- package/dist/src/workflow.js +99 -0
- package/dist/src/workflow.js.map +1 -0
- package/dist/src/workflow_management.d.ts +15 -0
- package/dist/src/workflow_management.d.ts.map +1 -0
- package/dist/src/workflow_management.js +87 -0
- package/dist/src/workflow_management.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/api-schema.md +1441 -0
- package/docs/migration-guide.md +460 -0
- package/docs/phase-14-changes.md +156 -0
- package/docs/solidsteps-ai-prompt.md +534 -0
- package/eslint.config.cjs +50 -0
- package/package.json +84 -0
- package/solidactions-ai-prompt.md +1504 -0
- package/solidactions-config.schema.json +132 -0
- package/solidactions-test-config.yaml +15 -0
|
@@ -0,0 +1,1284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SolidActions = exports.runInternalStep = exports.getExecutor = void 0;
|
|
7
|
+
const context_1 = require("./context");
|
|
8
|
+
const solidactions_executor_1 = require("./solidactions-executor");
|
|
9
|
+
const traces_1 = require("./telemetry/traces");
|
|
10
|
+
const workflow_1 = require("./workflow");
|
|
11
|
+
const logs_1 = require("./telemetry/logs");
|
|
12
|
+
const error_1 = require("./error");
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
const decorators_1 = require("./decorators");
|
|
15
|
+
const utils_1 = require("./utils");
|
|
16
|
+
const serialization_1 = require("./serialization");
|
|
17
|
+
const adminserver_1 = require("./adminserver");
|
|
18
|
+
const node_crypto_1 = require("node:crypto");
|
|
19
|
+
const conductor_1 = require("./conductor/conductor");
|
|
20
|
+
const system_database_1 = require("./system_database");
|
|
21
|
+
const authdecorators_1 = require("./authdecorators");
|
|
22
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
23
|
+
function getExecutor() {
|
|
24
|
+
if (!solidactions_executor_1.SolidActionsExecutor.globalInstance) {
|
|
25
|
+
throw new error_1.SolidActionsExecutorNotInitializedError();
|
|
26
|
+
}
|
|
27
|
+
return solidactions_executor_1.SolidActionsExecutor.globalInstance;
|
|
28
|
+
}
|
|
29
|
+
exports.getExecutor = getExecutor;
|
|
30
|
+
function runInternalStep(callback, funcName, childWFID, assignedFuncID) {
|
|
31
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
32
|
+
if (SolidActions.isInStep()) {
|
|
33
|
+
// OK to use directly
|
|
34
|
+
return callback();
|
|
35
|
+
}
|
|
36
|
+
else if (SolidActions.isInWorkflow()) {
|
|
37
|
+
return solidactions_executor_1.SolidActionsExecutor.globalInstance.runInternalStep(callback, funcName, SolidActions.workflowID, assignedFuncID ?? (0, context_1.functionIDGetIncrement)(), childWFID);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError(`Invalid call to \`${funcName}\` inside a \`transaction\` or \`procedure\``);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return callback();
|
|
44
|
+
}
|
|
45
|
+
exports.runInternalStep = runInternalStep;
|
|
46
|
+
class SolidActions {
|
|
47
|
+
///////
|
|
48
|
+
// Lifecycle
|
|
49
|
+
///////
|
|
50
|
+
static adminServer = undefined;
|
|
51
|
+
static conductor = undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Set configuration of `SolidActions` prior to `launch`
|
|
54
|
+
* @param config - configuration of services needed by SolidActions
|
|
55
|
+
*/
|
|
56
|
+
static setConfig(config) {
|
|
57
|
+
(0, node_assert_1.default)(!SolidActions.isInitialized(), 'Cannot call SolidActions.setConfig after SolidActions.launch');
|
|
58
|
+
SolidActions.#solidActionsConfig = config;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if SolidActions has been `launch`ed (and not `shutdown`)
|
|
62
|
+
* @returns `true` if SolidActions has been launched, or `false` otherwise
|
|
63
|
+
*/
|
|
64
|
+
static isInitialized() {
|
|
65
|
+
return !!solidactions_executor_1.SolidActionsExecutor.globalInstance?.initialized;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Launch SolidActions, starting recovery and request handling
|
|
69
|
+
* @param options - Launch options for connecting to SolidActions Conductor
|
|
70
|
+
*/
|
|
71
|
+
static async launch(options) {
|
|
72
|
+
const debugMode = options?.debugMode ?? process.env.SOLIDACTIONS_DEBUG_WORKFLOW_ID !== undefined;
|
|
73
|
+
const configFile = await (0, config_1.readConfigFile)();
|
|
74
|
+
// If no setConfig() was called, try to auto-configure from solidsteps.yaml + env vars
|
|
75
|
+
if (!SolidActions.#solidActionsConfig) {
|
|
76
|
+
const solidStepsConfig = await (0, config_1.readSolidStepsConfig)();
|
|
77
|
+
if (solidStepsConfig.project || process.env.SOLIDACTIONS_API_URL) {
|
|
78
|
+
// Auto-configure: use project name from solidsteps.yaml, API config from env vars
|
|
79
|
+
try {
|
|
80
|
+
const httpConfig = (0, config_1.getHttpConfig)(configFile);
|
|
81
|
+
SolidActions.#solidActionsConfig = {
|
|
82
|
+
name: solidStepsConfig.project || configFile.name,
|
|
83
|
+
api: {
|
|
84
|
+
url: httpConfig.apiUrl,
|
|
85
|
+
key: httpConfig.apiKey,
|
|
86
|
+
timeout: httpConfig.timeout,
|
|
87
|
+
maxRetries: httpConfig.maxRetries,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// If we can't get HTTP config, fall back to normal config file handling
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
let internalConfig = SolidActions.#solidActionsConfig
|
|
97
|
+
? (0, config_1.translateSolidActionsConfig)(SolidActions.#solidActionsConfig, debugMode)
|
|
98
|
+
: (0, config_1.getSolidActionsConfig)(configFile);
|
|
99
|
+
let runtimeConfig = SolidActions.#solidActionsConfig
|
|
100
|
+
? (0, config_1.translateRuntimeConfig)(SolidActions.#solidActionsConfig)
|
|
101
|
+
: (0, config_1.getRuntimeConfig)(configFile);
|
|
102
|
+
if (process.env.SOLIDACTIONS__CLOUD === 'true' || process.env.SOLIDACTIONS__CLOUD === 'true') {
|
|
103
|
+
[internalConfig, runtimeConfig] = (0, config_1.overwriteConfigForCloud)(internalConfig, runtimeConfig, configFile);
|
|
104
|
+
}
|
|
105
|
+
utils_1.globalParams.enableOTLP = SolidActions.#solidActionsConfig?.enableOTLP ?? (0, utils_1.defaultEnableOTLP)();
|
|
106
|
+
if (!(0, traces_1.isTraceContextWorking)())
|
|
107
|
+
(0, traces_1.installTraceContextManager)(internalConfig.name);
|
|
108
|
+
// Do nothing if SolidActions is already initialized
|
|
109
|
+
if (SolidActions.isInitialized()) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
(0, decorators_1.finalizeClassRegistrations)();
|
|
113
|
+
(0, decorators_1.insertAllMiddleware)();
|
|
114
|
+
// Globally set the application version and executor ID.
|
|
115
|
+
// In SolidActions Cloud, instead use the value supplied through environment variables.
|
|
116
|
+
if (process.env.SOLIDACTIONS__CLOUD !== 'true') {
|
|
117
|
+
if (SolidActions.#solidActionsConfig?.applicationVersion) {
|
|
118
|
+
utils_1.globalParams.appVersion = SolidActions.#solidActionsConfig.applicationVersion;
|
|
119
|
+
}
|
|
120
|
+
else if (SolidActions.#solidActionsConfig?.enablePatching) {
|
|
121
|
+
utils_1.globalParams.appVersion = 'PATCHING_ENABLED';
|
|
122
|
+
}
|
|
123
|
+
if (SolidActions.#solidActionsConfig?.executorID) {
|
|
124
|
+
utils_1.globalParams.executorID = SolidActions.#solidActionsConfig.executorID;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (options?.conductorKey) {
|
|
128
|
+
// Always use a generated executor ID in Conductor.
|
|
129
|
+
utils_1.globalParams.executorID = (0, node_crypto_1.randomUUID)();
|
|
130
|
+
}
|
|
131
|
+
solidactions_executor_1.SolidActionsExecutor.globalInstance = new solidactions_executor_1.SolidActionsExecutor(internalConfig, { debugMode });
|
|
132
|
+
(0, decorators_1.recordSolidActionsLaunch)();
|
|
133
|
+
const executor = solidactions_executor_1.SolidActionsExecutor.globalInstance;
|
|
134
|
+
await executor.init();
|
|
135
|
+
const debugWorkflowId = process.env.SOLIDACTIONS_DEBUG_WORKFLOW_ID;
|
|
136
|
+
if (debugWorkflowId) {
|
|
137
|
+
SolidActions.logger.info(`Debugging workflow "${debugWorkflowId}"`);
|
|
138
|
+
const handle = await executor.executeWorkflowId(debugWorkflowId);
|
|
139
|
+
await handle.getResult();
|
|
140
|
+
SolidActions.logger.info(`Workflow Debugging complete. Exiting process.`);
|
|
141
|
+
await executor.destroy();
|
|
142
|
+
process.exit(0);
|
|
143
|
+
return; // return for cases where process.exit is mocked
|
|
144
|
+
}
|
|
145
|
+
await solidactions_executor_1.SolidActionsExecutor.globalInstance.initEventReceivers();
|
|
146
|
+
if (options?.conductorKey) {
|
|
147
|
+
if (!options.conductorURL) {
|
|
148
|
+
const solidActionsDomain = process.env.SOLIDACTIONS_DOMAIN || 'cloud.dbos.dev';
|
|
149
|
+
options.conductorURL = `wss://${solidActionsDomain}/conductor/v1alpha1`;
|
|
150
|
+
}
|
|
151
|
+
SolidActions.conductor = new conductor_1.Conductor(solidactions_executor_1.SolidActionsExecutor.globalInstance, options.conductorKey, options.conductorURL);
|
|
152
|
+
SolidActions.conductor.dispatchLoop();
|
|
153
|
+
}
|
|
154
|
+
// Start the SolidActions admin server
|
|
155
|
+
const logger = SolidActions.logger;
|
|
156
|
+
if (runtimeConfig.runAdminServer) {
|
|
157
|
+
const adminApp = adminserver_1.SolidActionsAdminServer.setupAdminApp(executor);
|
|
158
|
+
try {
|
|
159
|
+
await adminserver_1.SolidActionsAdminServer.checkPortAvailabilityIPv4Ipv6(runtimeConfig.admin_port, logger);
|
|
160
|
+
// Wrap the listen call in a promise to properly catch errors
|
|
161
|
+
SolidActions.adminServer = await new Promise((resolve, reject) => {
|
|
162
|
+
const server = adminApp.listen(runtimeConfig?.admin_port, () => {
|
|
163
|
+
SolidActions.logger.debug(`SolidActions Admin Server is running at http://localhost:${runtimeConfig?.admin_port}`);
|
|
164
|
+
resolve(server);
|
|
165
|
+
});
|
|
166
|
+
server.on('error', (err) => {
|
|
167
|
+
reject(err);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
logger.warn(`Unable to start SolidActions admin server on port ${runtimeConfig.admin_port}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Logs all workflows that can be invoked externally.
|
|
178
|
+
*/
|
|
179
|
+
static logRegisteredEndpoints() {
|
|
180
|
+
if (!solidactions_executor_1.SolidActionsExecutor.globalInstance)
|
|
181
|
+
return;
|
|
182
|
+
for (const lcl of (0, decorators_1.getLifecycleListeners)()) {
|
|
183
|
+
lcl.logRegisteredEndpoints?.();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Shut down SolidActions processing:
|
|
188
|
+
* Stops receiving external workflow requests
|
|
189
|
+
* Disconnects from administration / Conductor
|
|
190
|
+
* Stops workflow processing and disconnects from databases
|
|
191
|
+
*/
|
|
192
|
+
static async shutdown() {
|
|
193
|
+
// Stop the admin server
|
|
194
|
+
if (SolidActions.adminServer) {
|
|
195
|
+
SolidActions.adminServer.close();
|
|
196
|
+
SolidActions.adminServer = undefined;
|
|
197
|
+
}
|
|
198
|
+
// Stop the conductor
|
|
199
|
+
if (SolidActions.conductor) {
|
|
200
|
+
SolidActions.conductor.stop();
|
|
201
|
+
while (!SolidActions.conductor.isClosed) {
|
|
202
|
+
await (0, utils_1.sleepms)(500);
|
|
203
|
+
}
|
|
204
|
+
SolidActions.conductor = undefined;
|
|
205
|
+
}
|
|
206
|
+
// Stop the executor
|
|
207
|
+
if (solidactions_executor_1.SolidActionsExecutor.globalInstance) {
|
|
208
|
+
await solidactions_executor_1.SolidActionsExecutor.globalInstance.deactivateEventReceivers();
|
|
209
|
+
await solidactions_executor_1.SolidActionsExecutor.globalInstance.destroy();
|
|
210
|
+
solidactions_executor_1.SolidActionsExecutor.globalInstance = undefined;
|
|
211
|
+
}
|
|
212
|
+
// Reset the global app version and executor ID
|
|
213
|
+
utils_1.globalParams.appVersion = process.env.SOLIDACTIONS__APPVERSION || '';
|
|
214
|
+
utils_1.globalParams.wasComputed = false;
|
|
215
|
+
utils_1.globalParams.appID = process.env.SOLIDACTIONS__APPID || '';
|
|
216
|
+
utils_1.globalParams.executorID = process.env.SOLIDACTIONS__VMID || 'local';
|
|
217
|
+
(0, decorators_1.recordSolidActionsShutdown)();
|
|
218
|
+
}
|
|
219
|
+
//////
|
|
220
|
+
// Convenience APIs for SolidSteps workflows
|
|
221
|
+
//////
|
|
222
|
+
/**
|
|
223
|
+
* Get the workflow input from WORKFLOW_INPUT environment variable.
|
|
224
|
+
* This is set by the SolidSteps runner from the webhook payload.
|
|
225
|
+
* @returns Parsed input or empty object if not set
|
|
226
|
+
*/
|
|
227
|
+
static getInput() {
|
|
228
|
+
const raw = process.env.WORKFLOW_INPUT;
|
|
229
|
+
if (!raw) {
|
|
230
|
+
return {};
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
return JSON.parse(raw);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return {};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Run a workflow with minimal boilerplate.
|
|
241
|
+
* Handles launch(), startWorkflow(), getResult(), and shutdown().
|
|
242
|
+
*
|
|
243
|
+
* @param workflow - The workflow function to run
|
|
244
|
+
* @param options - Optional settings for input and workflow ID
|
|
245
|
+
* @returns Promise that resolves when workflow completes (or rejects on error)
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* async function myWorkflow(input: MyInput): Promise<MyOutput> {
|
|
250
|
+
* // workflow logic
|
|
251
|
+
* }
|
|
252
|
+
*
|
|
253
|
+
* const wf = SolidActions.registerWorkflow(myWorkflow, { name: 'my-workflow' });
|
|
254
|
+
* await SolidActions.run(wf);
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
static async run(workflow, options) {
|
|
258
|
+
try {
|
|
259
|
+
await SolidActions.launch();
|
|
260
|
+
const input = options?.input ?? SolidActions.getInput();
|
|
261
|
+
const handle = await SolidActions.startWorkflow(workflow, {
|
|
262
|
+
workflowID: options?.workflowID,
|
|
263
|
+
})(input);
|
|
264
|
+
await handle.getResult();
|
|
265
|
+
await SolidActions.shutdown();
|
|
266
|
+
process.exit(0);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
console.error('Workflow failed:', error);
|
|
270
|
+
try {
|
|
271
|
+
await SolidActions.shutdown();
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// Ignore shutdown errors
|
|
275
|
+
}
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Interface for signal URLs returned by getSignalUrls()
|
|
281
|
+
*/
|
|
282
|
+
static getSignalUrls(topic) {
|
|
283
|
+
const baseApiUrl = process.env.SOLIDACTIONS_API_URL?.replace('/api/internal', '') ||
|
|
284
|
+
process.env.SOLIDACTIONS_API_URL?.replace('/api/internal', '') ||
|
|
285
|
+
process.env.APP_URL ||
|
|
286
|
+
'http://localhost:8000';
|
|
287
|
+
const workflowId = SolidActions.workflowID;
|
|
288
|
+
if (!workflowId) {
|
|
289
|
+
throw new error_1.SolidActionsError('getSignalUrls() must be called from within a workflow');
|
|
290
|
+
}
|
|
291
|
+
const topicParam = topic ? `&topic=${encodeURIComponent(topic)}` : '';
|
|
292
|
+
const base = `${baseApiUrl}/api/signal/${workflowId}`;
|
|
293
|
+
return {
|
|
294
|
+
base,
|
|
295
|
+
approve: `${base}?choice=approve${topicParam}`,
|
|
296
|
+
reject: `${base}?choice=reject${topicParam}`,
|
|
297
|
+
custom: (action) => `${base}?choice=${encodeURIComponent(action)}${topicParam}`,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/** Stop listening for external events (for testing) */
|
|
301
|
+
static async deactivateEventReceivers() {
|
|
302
|
+
return solidactions_executor_1.SolidActionsExecutor.globalInstance?.deactivateEventReceivers();
|
|
303
|
+
}
|
|
304
|
+
/** Start listening for external events (for testing) */
|
|
305
|
+
static async initEventReceivers() {
|
|
306
|
+
return solidactions_executor_1.SolidActionsExecutor.globalInstance?.initEventReceivers();
|
|
307
|
+
}
|
|
308
|
+
// Global SolidActions executor instance
|
|
309
|
+
static get #executor() {
|
|
310
|
+
return getExecutor();
|
|
311
|
+
}
|
|
312
|
+
//////
|
|
313
|
+
// Globals
|
|
314
|
+
//////
|
|
315
|
+
static #solidActionsConfig;
|
|
316
|
+
//////
|
|
317
|
+
// Context
|
|
318
|
+
//////
|
|
319
|
+
/** Get the current SolidActions Logger, appropriate to the current context */
|
|
320
|
+
static get logger() {
|
|
321
|
+
const lctx = (0, context_1.getCurrentContextStore)();
|
|
322
|
+
if (lctx?.logger)
|
|
323
|
+
return lctx.logger;
|
|
324
|
+
const executor = solidactions_executor_1.SolidActionsExecutor.globalInstance;
|
|
325
|
+
if (executor)
|
|
326
|
+
return executor.logger;
|
|
327
|
+
return new logs_1.GlobalLogger();
|
|
328
|
+
}
|
|
329
|
+
/** Get the current SolidActions Tracer, for starting spans */
|
|
330
|
+
static get tracer() {
|
|
331
|
+
const executor = solidactions_executor_1.SolidActionsExecutor.globalInstance;
|
|
332
|
+
if (executor)
|
|
333
|
+
return executor.tracer;
|
|
334
|
+
}
|
|
335
|
+
/** Get the current SolidActions tracing span, appropriate to the current context */
|
|
336
|
+
static get span() {
|
|
337
|
+
return (0, traces_1.getActiveSpan)();
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Get the current request object (such as an HTTP request)
|
|
341
|
+
* This is intended for use in event libraries that know the type of the current request,
|
|
342
|
+
* and set it using `withTracedContext` or `runWithContext`
|
|
343
|
+
*/
|
|
344
|
+
static requestObject() {
|
|
345
|
+
return (0, context_1.getCurrentContextStore)()?.request;
|
|
346
|
+
}
|
|
347
|
+
/** Get the current HTTP request (within `@SolidActions.getApi` et al) */
|
|
348
|
+
static getRequest() {
|
|
349
|
+
return this.requestObject();
|
|
350
|
+
}
|
|
351
|
+
/** Get the current HTTP request (within `@SolidActions.getApi` et al) */
|
|
352
|
+
static get request() {
|
|
353
|
+
const r = SolidActions.getRequest();
|
|
354
|
+
if (!r)
|
|
355
|
+
throw new error_1.SolidActionsError('`SolidActions.request` accessed from outside of HTTP requests');
|
|
356
|
+
return r;
|
|
357
|
+
}
|
|
358
|
+
/** Get the current application version */
|
|
359
|
+
static get applicationVersion() {
|
|
360
|
+
return utils_1.globalParams.appVersion;
|
|
361
|
+
}
|
|
362
|
+
/** Get the current workflow ID */
|
|
363
|
+
static get workflowID() {
|
|
364
|
+
return (0, context_1.getCurrentContextStore)()?.workflowId;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Get the current run ID (alias for workflowID).
|
|
368
|
+
* This is the unique identifier assigned by SolidSteps at trigger time.
|
|
369
|
+
*/
|
|
370
|
+
static get runID() {
|
|
371
|
+
return SolidActions.workflowID;
|
|
372
|
+
}
|
|
373
|
+
/** Get the current step number, within the current workflow */
|
|
374
|
+
static get stepID() {
|
|
375
|
+
if (SolidActions.isInStep()) {
|
|
376
|
+
return (0, context_1.getCurrentContextStore)()?.curStepFunctionId;
|
|
377
|
+
}
|
|
378
|
+
else if (SolidActions.isInTransaction()) {
|
|
379
|
+
return (0, context_1.getCurrentContextStore)()?.curTxFunctionId;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
static get stepStatus() {
|
|
386
|
+
return (0, context_1.getCurrentContextStore)()?.stepStatus;
|
|
387
|
+
}
|
|
388
|
+
/** Get the current authenticated user */
|
|
389
|
+
static get authenticatedUser() {
|
|
390
|
+
return (0, context_1.getCurrentContextStore)()?.authenticatedUser ?? '';
|
|
391
|
+
}
|
|
392
|
+
/** Get the roles granted to the current authenticated user */
|
|
393
|
+
static get authenticatedRoles() {
|
|
394
|
+
return (0, context_1.getCurrentContextStore)()?.authenticatedRoles ?? [];
|
|
395
|
+
}
|
|
396
|
+
/** Get the role assumed by the current user giving authorization to execute the current function */
|
|
397
|
+
static get assumedRole() {
|
|
398
|
+
return (0, context_1.getCurrentContextStore)()?.assumedRole ?? '';
|
|
399
|
+
}
|
|
400
|
+
/** @returns true if called from within a transaction, false otherwise */
|
|
401
|
+
static isInTransaction() {
|
|
402
|
+
return (0, context_1.getCurrentContextStore)()?.curTxFunctionId !== undefined;
|
|
403
|
+
}
|
|
404
|
+
/** @returns true if called from within a step, false otherwise */
|
|
405
|
+
static isInStep() {
|
|
406
|
+
return (0, context_1.getCurrentContextStore)()?.curStepFunctionId !== undefined;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* @returns true if called from within a workflow
|
|
410
|
+
* (regardless of whether the workflow is currently executing a step,
|
|
411
|
+
* transaction, or procedure), false otherwise
|
|
412
|
+
*/
|
|
413
|
+
static isWithinWorkflow() {
|
|
414
|
+
return (0, context_1.getCurrentContextStore)()?.workflowId !== undefined;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* @returns true if called from within a workflow that is not currently executing
|
|
418
|
+
* a step, transaction, or procedure, or false otherwise
|
|
419
|
+
*/
|
|
420
|
+
static isInWorkflow() {
|
|
421
|
+
return SolidActions.isWithinWorkflow() && !SolidActions.isInTransaction() && !SolidActions.isInStep();
|
|
422
|
+
}
|
|
423
|
+
//////
|
|
424
|
+
// Access to system DB, for event receivers etc.
|
|
425
|
+
//////
|
|
426
|
+
/**
|
|
427
|
+
* Get a state item from the system database, which provides a key/value store interface for event dispatchers.
|
|
428
|
+
* The full key for the database state should include the service, function, and item.
|
|
429
|
+
* Values are versioned. A version can either be a sequence number (long integer), or a time (high precision floating point).
|
|
430
|
+
* If versions are in use, any upsert is discarded if the version field is less than what is already stored.
|
|
431
|
+
*
|
|
432
|
+
* Examples of state that could be kept:
|
|
433
|
+
* Offsets into kafka topics, per topic partition
|
|
434
|
+
* Last time for which a scheduling service completed schedule dispatch
|
|
435
|
+
*
|
|
436
|
+
* @param service - should be unique to the event receiver keeping state, to separate from others
|
|
437
|
+
* @param workflowFnName - function name; should be the fully qualified / unique function name dispatched
|
|
438
|
+
* @param key - The subitem kept by event receiver service for the function, allowing multiple values to be stored per function
|
|
439
|
+
* @returns The latest system database state for the specified service+workflow+item
|
|
440
|
+
*/
|
|
441
|
+
static async getEventDispatchState(svc, wfn, key) {
|
|
442
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('getEventDispatchState');
|
|
443
|
+
return await SolidActions.#executor.getEventDispatchState(svc, wfn, key);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Set a state item into the system database, which provides a key/value store interface for event dispatchers.
|
|
447
|
+
* The full key for the database state should include the service, function, and item; these fields are part of `state`.
|
|
448
|
+
* Values are versioned. A version can either be a sequence number (long integer), or a time (high precision floating point).
|
|
449
|
+
* If versions are in use, any upsert is discarded if the version field is less than what is already stored.
|
|
450
|
+
*
|
|
451
|
+
* @param state - the service, workflow, item, version, and value to write to the database
|
|
452
|
+
* @returns The upsert returns the current record, which may be useful if it is more recent than the `state` provided.
|
|
453
|
+
*/
|
|
454
|
+
static async upsertEventDispatchState(state) {
|
|
455
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('upsertEventDispatchState');
|
|
456
|
+
return await SolidActions.#executor.upsertEventDispatchState(state);
|
|
457
|
+
}
|
|
458
|
+
//////
|
|
459
|
+
// Workflow and other operations
|
|
460
|
+
//////
|
|
461
|
+
/**
|
|
462
|
+
* Get the workflow status given a workflow ID
|
|
463
|
+
* @param workflowID - ID of the workflow
|
|
464
|
+
* @returns status of the workflow as `WorkflowStatus`, or `null` if there is no workflow with `workflowID`
|
|
465
|
+
*/
|
|
466
|
+
static getWorkflowStatus(workflowID) {
|
|
467
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('getWorkflowStatus');
|
|
468
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
469
|
+
if (SolidActions.isInStep()) {
|
|
470
|
+
// OK to use directly
|
|
471
|
+
return SolidActions.#executor.getWorkflowStatus(workflowID);
|
|
472
|
+
}
|
|
473
|
+
else if (SolidActions.isInWorkflow()) {
|
|
474
|
+
return SolidActions.#executor.getWorkflowStatus(workflowID, SolidActions.workflowID, (0, context_1.functionIDGetIncrement)());
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `getWorkflowStatus` inside a `transaction` or `procedure`');
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return SolidActions.#executor.getWorkflowStatus(workflowID);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Get the workflow result, given a workflow ID
|
|
484
|
+
* @param workflowID - ID of the workflow
|
|
485
|
+
* @param timeoutSeconds - Maximum time to wait for result
|
|
486
|
+
* @returns The return value of the workflow, or throws the exception thrown by the workflow, or `null` if times out
|
|
487
|
+
*/
|
|
488
|
+
static async getResult(workflowID, timeoutSeconds) {
|
|
489
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('getResult');
|
|
490
|
+
let timerFuncID = undefined;
|
|
491
|
+
if (SolidActions.isWithinWorkflow() && timeoutSeconds !== undefined) {
|
|
492
|
+
timerFuncID = (0, context_1.functionIDGetIncrement)();
|
|
493
|
+
}
|
|
494
|
+
return await SolidActions.getResultInternal(workflowID, timeoutSeconds, timerFuncID, undefined);
|
|
495
|
+
}
|
|
496
|
+
static async getResultInternal(workflowID, timeoutSeconds, timerFuncID, assignedFuncID) {
|
|
497
|
+
return await runInternalStep(async () => {
|
|
498
|
+
const rres = await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.awaitWorkflowResult(workflowID, timeoutSeconds, SolidActions.workflowID, timerFuncID);
|
|
499
|
+
if (!rres)
|
|
500
|
+
return null;
|
|
501
|
+
if (rres?.cancelled) {
|
|
502
|
+
throw new error_1.SolidActionsAwaitedWorkflowCancelledError(workflowID);
|
|
503
|
+
}
|
|
504
|
+
return solidactions_executor_1.SolidActionsExecutor.reviveResultOrError(rres, SolidActions.#executor.serializer);
|
|
505
|
+
}, 'SolidActions.getResult', workflowID, assignedFuncID);
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Create a workflow handle with a given workflow ID.
|
|
509
|
+
* This call always returns a handle, even if the workflow does not exist.
|
|
510
|
+
* The resulting handle will check the database to provide any workflow information.
|
|
511
|
+
* @param workflowID - ID of the workflow
|
|
512
|
+
* @returns `WorkflowHandle` that can be used to poll for the status or result of any workflow with `workflowID`
|
|
513
|
+
*/
|
|
514
|
+
static retrieveWorkflow(workflowID) {
|
|
515
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('retrieveWorkflow');
|
|
516
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
517
|
+
if (!SolidActions.isInWorkflow()) {
|
|
518
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `retrieveWorkflow` inside a `transaction` or `step`');
|
|
519
|
+
}
|
|
520
|
+
return new workflow_1.RetrievedHandle(solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase, workflowID);
|
|
521
|
+
}
|
|
522
|
+
return SolidActions.#executor.retrieveWorkflow(workflowID);
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Query the system database for all workflows matching the provided predicate
|
|
526
|
+
* @param input - `GetWorkflowsInput` predicate for filtering returned workflows
|
|
527
|
+
* @returns `WorkflowStatus` array containing details of the matching workflows
|
|
528
|
+
*/
|
|
529
|
+
static async listWorkflows(input) {
|
|
530
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('listWorkflows');
|
|
531
|
+
return await runInternalStep(async () => {
|
|
532
|
+
return await SolidActions.#executor.listWorkflows(input);
|
|
533
|
+
}, 'SolidActions.listWorkflows');
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Retrieve the steps of a workflow
|
|
537
|
+
* @param workflowID - ID of the workflow
|
|
538
|
+
* @returns `StepInfo` array listing the executed steps of the workflow. If the workflow is not found, `undefined` is returned.
|
|
539
|
+
*/
|
|
540
|
+
static async listWorkflowSteps(workflowID) {
|
|
541
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('listWorkflowSteps');
|
|
542
|
+
return await runInternalStep(async () => {
|
|
543
|
+
return await SolidActions.#executor.listWorkflowSteps(workflowID);
|
|
544
|
+
}, 'SolidActions.listWorkflowSteps');
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Cancel a workflow given its ID.
|
|
548
|
+
* If the workflow is currently running, `SolidActionsWorkflowCancelledError` will be
|
|
549
|
+
* thrown from its next SolidActions call.
|
|
550
|
+
* @param workflowID - ID of the workflow
|
|
551
|
+
*/
|
|
552
|
+
static async cancelWorkflow(workflowID) {
|
|
553
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('cancelWorkflow');
|
|
554
|
+
return await runInternalStep(async () => {
|
|
555
|
+
return await SolidActions.#executor.cancelWorkflow(workflowID);
|
|
556
|
+
}, 'SolidActions.cancelWorkflow');
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Resume a workflow given its ID.
|
|
560
|
+
* @param workflowID - ID of the workflow
|
|
561
|
+
*/
|
|
562
|
+
static async resumeWorkflow(workflowID) {
|
|
563
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('resumeWorkflow');
|
|
564
|
+
await runInternalStep(async () => {
|
|
565
|
+
return await SolidActions.#executor.resumeWorkflow(workflowID);
|
|
566
|
+
}, 'SolidActions.resumeWorkflow');
|
|
567
|
+
return this.retrieveWorkflow(workflowID);
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Fork a workflow given its ID.
|
|
571
|
+
* @param workflowID - ID of the workflow
|
|
572
|
+
* @param startStep - Step ID to start the forked workflow from
|
|
573
|
+
* @param applicationVersion - Version of the application to use for the forked workflow
|
|
574
|
+
* @returns A handle to the forked workflow
|
|
575
|
+
* @throws SolidActionsInvalidStepIDError if the `startStep` is greater than the maximum step ID of the workflow
|
|
576
|
+
*/
|
|
577
|
+
static async forkWorkflow(workflowID, startStep, options) {
|
|
578
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('forkWorkflow');
|
|
579
|
+
const forkedID = await runInternalStep(async () => {
|
|
580
|
+
return await SolidActions.#executor.forkWorkflow(workflowID, startStep, options);
|
|
581
|
+
}, 'SolidActions.forkWorkflow');
|
|
582
|
+
return this.retrieveWorkflow(forkedID);
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Sleep for the specified amount of time.
|
|
586
|
+
* If called from within a workflow, the sleep is "durable",
|
|
587
|
+
* meaning that the workflow will sleep until the wakeup time
|
|
588
|
+
* (calculated by adding `durationMS` to the original invocation time),
|
|
589
|
+
* regardless of workflow recovery.
|
|
590
|
+
* @param durationMS - Length of sleep, in milliseconds.
|
|
591
|
+
*/
|
|
592
|
+
static async sleepms(durationMS) {
|
|
593
|
+
if (SolidActions.isWithinWorkflow() && !SolidActions.isInStep()) {
|
|
594
|
+
if (SolidActions.isInTransaction()) {
|
|
595
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.sleep` inside a `transaction`');
|
|
596
|
+
}
|
|
597
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
|
598
|
+
if (durationMS <= 0) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
return await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.durableSleepms(SolidActions.workflowID, functionID, durationMS);
|
|
602
|
+
}
|
|
603
|
+
await (0, utils_1.sleepms)(durationMS);
|
|
604
|
+
}
|
|
605
|
+
/** @see sleepms */
|
|
606
|
+
static async sleepSeconds(durationSec) {
|
|
607
|
+
return SolidActions.sleepms(durationSec * 1000);
|
|
608
|
+
}
|
|
609
|
+
/** @see sleepms */
|
|
610
|
+
static async sleep(durationMS) {
|
|
611
|
+
return SolidActions.sleepms(durationMS);
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Get the current time in milliseconds, similar to `Date.now()`.
|
|
615
|
+
* This function is deterministic and can be used within workflows.
|
|
616
|
+
*/
|
|
617
|
+
static async now() {
|
|
618
|
+
if (SolidActions.isInWorkflow()) {
|
|
619
|
+
return runInternalStep(async () => Promise.resolve(Date.now()), 'SolidActions.now');
|
|
620
|
+
}
|
|
621
|
+
return Date.now();
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Generate a random (v4) UUUID, similar to `node:crypto.randomUUID`.
|
|
625
|
+
* This function is deterministic and can be used within workflows.
|
|
626
|
+
*/
|
|
627
|
+
static async randomUUID() {
|
|
628
|
+
if (SolidActions.isInWorkflow()) {
|
|
629
|
+
return runInternalStep(async () => Promise.resolve((0, node_crypto_1.randomUUID)()), 'SolidActions.randomUUID');
|
|
630
|
+
}
|
|
631
|
+
return (0, node_crypto_1.randomUUID)();
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Use the provided `workflowID` as the identifier for first workflow started
|
|
635
|
+
* within the `callback` function.
|
|
636
|
+
* @param workflowID - ID to assign to the first workflow started
|
|
637
|
+
* @param callback - Function to run, which would start a workflow
|
|
638
|
+
* @returns - Return value from `callback`
|
|
639
|
+
*/
|
|
640
|
+
static async withNextWorkflowID(workflowID, callback) {
|
|
641
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('workflows');
|
|
642
|
+
return SolidActions.#withTopContext({ idAssignedForNextWorkflow: workflowID }, callback);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Use the provided `authedUser` and `authedRoles` as the authenticated user for
|
|
646
|
+
* any security checks or calls to `SolidActions.authenticatedUser`
|
|
647
|
+
* or `SolidActions.authenticatedRoles` placed within the `callback` function.
|
|
648
|
+
* @param authedUser - Authenticated user
|
|
649
|
+
* @param authedRoles - Authenticated roles
|
|
650
|
+
* @param callback - Function to run with authentication context in place
|
|
651
|
+
* @returns - Return value from `callback`
|
|
652
|
+
*/
|
|
653
|
+
static async withAuthedContext(authedUser, authedRoles, callback) {
|
|
654
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('auth');
|
|
655
|
+
return SolidActions.#withTopContext({
|
|
656
|
+
authenticatedUser: authedUser,
|
|
657
|
+
authenticatedRoles: authedRoles,
|
|
658
|
+
}, callback);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* This generic setter helps users calling SolidActions operation to pass a name,
|
|
662
|
+
* later used in seeding a parent OTel span for the operation.
|
|
663
|
+
* @param callerName - Tracing caller name
|
|
664
|
+
* @param callback - Function to run with tracing context in place
|
|
665
|
+
* @returns - Return value from `callback`
|
|
666
|
+
*/
|
|
667
|
+
static async withNamedContext(callerName, callback) {
|
|
668
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('tracing');
|
|
669
|
+
return SolidActions.#withTopContext({ operationCaller: callerName }, callback);
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Specify workflow timeout for any workflows started within the `callback`.
|
|
673
|
+
* @param timeoutMS - timeout length for all workflows started within `callback` will be run
|
|
674
|
+
* @param callback - Function to run, which would call or start workflows
|
|
675
|
+
* @returns - Return value from `callback`
|
|
676
|
+
*/
|
|
677
|
+
static async withWorkflowTimeout(timeoutMS, callback) {
|
|
678
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('workflows');
|
|
679
|
+
return SolidActions.#withTopContext({ workflowTimeoutMS: timeoutMS }, callback);
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Run a workflow with the option to set any of the contextual items
|
|
683
|
+
*
|
|
684
|
+
* @param options - Overrides for options
|
|
685
|
+
* @param callback - Function to run, which would call or start workflows
|
|
686
|
+
* @returns - Return value from `callback`
|
|
687
|
+
*/
|
|
688
|
+
static async runWithContext(options, callback) {
|
|
689
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('contexts');
|
|
690
|
+
return SolidActions.#withTopContext(options, callback);
|
|
691
|
+
}
|
|
692
|
+
static async #withTopContext(options, callback) {
|
|
693
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
|
694
|
+
if (pctx) {
|
|
695
|
+
// Save existing values and overwrite with new; hard to do cleanly but is actually type correct
|
|
696
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
697
|
+
const existing = {};
|
|
698
|
+
for (const k of Object.keys(options)) {
|
|
699
|
+
if (Object.hasOwn(pctx, k))
|
|
700
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
701
|
+
existing[k] = options[k];
|
|
702
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
703
|
+
pctx[k] = options[k];
|
|
704
|
+
}
|
|
705
|
+
try {
|
|
706
|
+
return await callback();
|
|
707
|
+
}
|
|
708
|
+
finally {
|
|
709
|
+
for (const k of Object.keys(options)) {
|
|
710
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
711
|
+
if (Object.hasOwn(existing, k))
|
|
712
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
|
|
713
|
+
pctx[k] = existing[k];
|
|
714
|
+
else
|
|
715
|
+
delete pctx[k];
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
return await (0, context_1.runWithTopContext)(options, callback);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
static startWorkflow(target, params) {
|
|
724
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('workflows');
|
|
725
|
+
const instance = typeof target === 'function' ? null : target;
|
|
726
|
+
if (instance && typeof instance !== 'function' && !(instance instanceof decorators_1.ConfiguredInstance)) {
|
|
727
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Attempt to call `startWorkflow` on an object that is not a `ConfiguredInstance`');
|
|
728
|
+
}
|
|
729
|
+
const regOps = (0, decorators_1.getRegisteredOperations)(target);
|
|
730
|
+
const handler = {
|
|
731
|
+
apply(target, _thisArg, args) {
|
|
732
|
+
const regOp = (0, decorators_1.getFunctionRegistration)(target);
|
|
733
|
+
if (!regOp) {
|
|
734
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
735
|
+
const name = typeof target === 'function' ? target.name : target.toString();
|
|
736
|
+
throw new error_1.SolidActionsNotRegisteredError(name, `${name} is not a registered SolidActions workflow function`);
|
|
737
|
+
}
|
|
738
|
+
return SolidActions.#invokeWorkflow(instance, regOp, args, params);
|
|
739
|
+
},
|
|
740
|
+
get(target, p, receiver) {
|
|
741
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
742
|
+
const func = Reflect.get(target, p, receiver);
|
|
743
|
+
const regOp = (0, decorators_1.getFunctionRegistration)(func) ?? regOps.find((op) => op.name === p);
|
|
744
|
+
if (regOp) {
|
|
745
|
+
return (...args) => SolidActions.#invokeWorkflow(instance, regOp, args, params);
|
|
746
|
+
}
|
|
747
|
+
const name = typeof p === 'string' ? p : String(p);
|
|
748
|
+
throw new error_1.SolidActionsNotRegisteredError(name, `${name} is not a registered SolidActions workflow function`);
|
|
749
|
+
},
|
|
750
|
+
};
|
|
751
|
+
return new Proxy(target, handler);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Send `message` on optional `topic` to the workflow with `destinationID`
|
|
755
|
+
* This can be done from inside or outside of SolidActions workflow functions
|
|
756
|
+
* Use the optional `idempotencyKey` to guarantee that the message is sent exactly once
|
|
757
|
+
* @see `SolidActions.recv`
|
|
758
|
+
*
|
|
759
|
+
* @param destinationID - ID of the workflow that will `recv` the message
|
|
760
|
+
* @param message - Message to send, which must be serializable as JSON
|
|
761
|
+
* @param topic - Optional topic; if specified the `recv` command can specify the same topic to receive selectively
|
|
762
|
+
* @param idempotencyKey - Optional key for sending the message exactly once
|
|
763
|
+
*/
|
|
764
|
+
static async send(destinationID, message, topic, idempotencyKey) {
|
|
765
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('send');
|
|
766
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
767
|
+
if (!SolidActions.isInWorkflow()) {
|
|
768
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.send` inside a `step` or `transaction`');
|
|
769
|
+
}
|
|
770
|
+
if (idempotencyKey) {
|
|
771
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.send` with an idempotency key from within a workflow');
|
|
772
|
+
}
|
|
773
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
|
774
|
+
return await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.send(SolidActions.workflowID, functionID, destinationID, SolidActions.#executor.serializer.stringify(message), topic);
|
|
775
|
+
}
|
|
776
|
+
return SolidActions.#executor.runSendTempWF(destinationID, message, topic, idempotencyKey); // Temp WF variant
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Receive a message on optional `topic` from within a workflow.
|
|
780
|
+
* This must be called from within a workflow; this workflow's ID is used to check for messages sent by `SolidActions.send`
|
|
781
|
+
* This can be configured to time out.
|
|
782
|
+
* Messages are received in the order in which they are sent (per-sender / causal order).
|
|
783
|
+
* @see `SolidActions.send`
|
|
784
|
+
*
|
|
785
|
+
* @param topic - Optional topic; if specified the `recv` command can specify the same topic to receive selectively
|
|
786
|
+
* @param timeoutSeconds - Optional timeout; if no message is received before the timeout, `null` will be returned
|
|
787
|
+
* @template T - The type of message that is expected to be received
|
|
788
|
+
* @returns Any message received, or `null` if the timeout expires
|
|
789
|
+
*/
|
|
790
|
+
static async recv(topic, timeoutSeconds) {
|
|
791
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('recv');
|
|
792
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
793
|
+
if (!SolidActions.isInWorkflow()) {
|
|
794
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.recv` inside a `step` or `transaction`');
|
|
795
|
+
}
|
|
796
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
|
797
|
+
const timeoutFunctionID = (0, context_1.functionIDGetIncrement)();
|
|
798
|
+
return SolidActions.#executor.serializer.parse(await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.recv(SolidActions.workflowID, functionID, timeoutFunctionID, topic, timeoutSeconds));
|
|
799
|
+
}
|
|
800
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Attempt to call `SolidActions.recv` outside of a workflow'); // Only workflows can recv
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Set an event, from within a SolidActions workflow. This value can be retrieved with `SolidActions.getEvent`.
|
|
804
|
+
* If the event `key` already exists, its `value` is updated.
|
|
805
|
+
* This function can only be called from within a workflow.
|
|
806
|
+
* @see `SolidActions.getEvent`
|
|
807
|
+
*
|
|
808
|
+
* @param key - The key for the event; at most one value is associated with a key at any given time.
|
|
809
|
+
* @param value - The value to associate with `key`
|
|
810
|
+
*/
|
|
811
|
+
static async setEvent(key, value) {
|
|
812
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('setEvent');
|
|
813
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
814
|
+
if (!SolidActions.isInWorkflow()) {
|
|
815
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.setEvent` inside a `step` or `transaction`');
|
|
816
|
+
}
|
|
817
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
|
818
|
+
return solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.setEvent(SolidActions.workflowID, functionID, key, SolidActions.#executor.serializer.stringify(value));
|
|
819
|
+
}
|
|
820
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Attempt to call `SolidActions.setEvent` outside of a workflow'); // Only workflows can set event
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Get the value of a workflow event, or wait for it to be set.
|
|
824
|
+
* This function can be called inside or outside of SolidActions workflow functions.
|
|
825
|
+
* If this function is called from within a workflow, its result is durably checkpointed.
|
|
826
|
+
* @see `SolidActions.setEvent`
|
|
827
|
+
*
|
|
828
|
+
* @param workflowID - The ID of the workflow with the corresponding `setEvent`
|
|
829
|
+
* @param key - The key for the event; at most one value is associated with a key at any given time.
|
|
830
|
+
* @param timeoutSeconds - Optional timeout; if a value for `key` is not set before the timeout, `null` will be returned
|
|
831
|
+
* @template T - The expected type for the value assigned to `key`
|
|
832
|
+
* @returns The value to associate with `key`, or `null` if the timeout is hit
|
|
833
|
+
*/
|
|
834
|
+
static async getEvent(workflowID, key, timeoutSeconds) {
|
|
835
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('getEvent');
|
|
836
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
837
|
+
if (!SolidActions.isInWorkflow()) {
|
|
838
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.getEvent` inside a `step` or `transaction`');
|
|
839
|
+
}
|
|
840
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
|
841
|
+
const timeoutFunctionID = (0, context_1.functionIDGetIncrement)();
|
|
842
|
+
const params = {
|
|
843
|
+
workflowID: SolidActions.workflowID,
|
|
844
|
+
functionID,
|
|
845
|
+
timeoutFunctionID,
|
|
846
|
+
};
|
|
847
|
+
return SolidActions.#executor.serializer.parse(await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.getEvent(workflowID, key, timeoutSeconds ?? solidactions_executor_1.SolidActionsExecutor.defaultNotificationTimeoutSec, params));
|
|
848
|
+
}
|
|
849
|
+
return SolidActions.#executor.getEvent(workflowID, key, timeoutSeconds);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Write a value to a stream.
|
|
853
|
+
* @param key - The stream key/name within the workflow
|
|
854
|
+
* @param value - A serializable value to write to the stream
|
|
855
|
+
*/
|
|
856
|
+
static async writeStream(key, value) {
|
|
857
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('writeStream');
|
|
858
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
859
|
+
if (SolidActions.isInWorkflow()) {
|
|
860
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
|
861
|
+
return await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.writeStreamFromWorkflow(SolidActions.workflowID, functionID, key, value);
|
|
862
|
+
}
|
|
863
|
+
else if (SolidActions.isInStep()) {
|
|
864
|
+
return await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.writeStreamFromStep(SolidActions.workflowID, key, value);
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.writeStream` outside of a workflow or step');
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.writeStream` outside of a workflow or step');
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Close a stream by writing a sentinel value.
|
|
876
|
+
* @param key - The stream key/name within the workflow
|
|
877
|
+
*/
|
|
878
|
+
static async closeStream(key) {
|
|
879
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('closeStream');
|
|
880
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
881
|
+
if (SolidActions.isInWorkflow()) {
|
|
882
|
+
const functionID = (0, context_1.functionIDGetIncrement)();
|
|
883
|
+
return await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.closeStream(SolidActions.workflowID, functionID, key);
|
|
884
|
+
}
|
|
885
|
+
else {
|
|
886
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.closeStream` outside of a workflow or step');
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to `SolidActions.closeStream` outside of a workflow');
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Read values from a stream as an async generator.
|
|
895
|
+
* This function reads values from a stream identified by the workflowID and key,
|
|
896
|
+
* yielding each value in order until the stream is closed or the workflow terminates.
|
|
897
|
+
* @param workflowID - The workflow instance ID that owns the stream
|
|
898
|
+
* @param key - The stream key/name within the workflow
|
|
899
|
+
* @returns An async generator that yields each value in the stream until the stream is closed
|
|
900
|
+
*/
|
|
901
|
+
static async *readStream(workflowID, key) {
|
|
902
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('readStream');
|
|
903
|
+
let offset = 0;
|
|
904
|
+
while (true) {
|
|
905
|
+
try {
|
|
906
|
+
const value = await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.readStream(workflowID, key, offset);
|
|
907
|
+
if (value === system_database_1.SOLIDACTIONS_STREAM_CLOSED_SENTINEL) {
|
|
908
|
+
break;
|
|
909
|
+
}
|
|
910
|
+
yield value;
|
|
911
|
+
offset += 1;
|
|
912
|
+
}
|
|
913
|
+
catch (error) {
|
|
914
|
+
if (error instanceof Error && error.message.includes('No value found')) {
|
|
915
|
+
// Poll the offset until a value arrives or the workflow terminates
|
|
916
|
+
const status = await SolidActions.getWorkflowStatus(workflowID);
|
|
917
|
+
if (!status || !(0, workflow_1.isWorkflowActive)(status.status)) {
|
|
918
|
+
break;
|
|
919
|
+
}
|
|
920
|
+
await (0, utils_1.sleepms)(1000); // 1 second polling interval
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
throw error;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
//////
|
|
928
|
+
// Decorators
|
|
929
|
+
//////
|
|
930
|
+
/**
|
|
931
|
+
* Allow a class to be assigned a name
|
|
932
|
+
*/
|
|
933
|
+
static className(name) {
|
|
934
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
935
|
+
function clsdec(ctor) {
|
|
936
|
+
const clsreg = (0, decorators_1.getClassRegistration)(ctor, true);
|
|
937
|
+
if (clsreg.reg?.name && clsreg.reg.name !== name && clsreg.reg.name !== ctor.name) {
|
|
938
|
+
throw new error_1.SolidActionsConflictingRegistrationError(`Attempt to assign name ${name} to class ${ctor.name}, which has already been aliased to ${clsreg.reg.name}`);
|
|
939
|
+
}
|
|
940
|
+
clsreg.reg.name = name;
|
|
941
|
+
}
|
|
942
|
+
return clsdec;
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Decorator designating a method as a SolidActions workflow
|
|
946
|
+
* Durable execution will be applied within calls to the workflow function
|
|
947
|
+
* This also registers the function so that it is available during recovery
|
|
948
|
+
* @param config - Configuration information for the workflow
|
|
949
|
+
*/
|
|
950
|
+
static workflow(config = {}) {
|
|
951
|
+
function decorator(target, propertyKey, inDescriptor) {
|
|
952
|
+
const { descriptor, registration } = (0, decorators_1.wrapSolidActionsFunctionAndRegisterByTarget)(target, propertyKey, config.name ?? propertyKey, inDescriptor);
|
|
953
|
+
const invoker = SolidActions.#getWorkflowInvoker(registration, config);
|
|
954
|
+
descriptor.value = invoker;
|
|
955
|
+
registration.wrappedFunction = invoker;
|
|
956
|
+
(0, decorators_1.registerFunctionWrapper)(invoker, registration);
|
|
957
|
+
return descriptor;
|
|
958
|
+
}
|
|
959
|
+
return decorator;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Create a SolidActions workflow function from a provided function.
|
|
963
|
+
* Similar to the SolidActions.workflow, but without requiring a decorator
|
|
964
|
+
* Durable execution will be applied to calls to the function returned by registerWorkflow
|
|
965
|
+
* This also registers the function so that it is available during recovery
|
|
966
|
+
* @param func - The function to register as a workflow
|
|
967
|
+
* @param name - The name of the registered workflow
|
|
968
|
+
* @param options - Configuration information for the registered workflow
|
|
969
|
+
*/
|
|
970
|
+
static registerWorkflow(func, config) {
|
|
971
|
+
const registration = (0, decorators_1.wrapSolidActionsFunctionAndRegisterByUniqueName)(config?.ctorOrProto, config?.className, config?.name ?? func.name, config?.name ?? func.name, func);
|
|
972
|
+
return SolidActions.#getWorkflowInvoker(registration, config);
|
|
973
|
+
}
|
|
974
|
+
static async #invokeWorkflow($this, regOP, args, params = {}, startWfFuncId) {
|
|
975
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('workflows');
|
|
976
|
+
const wfId = (0, context_1.getNextWFID)(params.workflowID);
|
|
977
|
+
const ppctx = (0, context_1.getCurrentContextStore)();
|
|
978
|
+
const queueName = params.queueName ?? ppctx?.queueAssignedForWorkflows;
|
|
979
|
+
const timeoutMS = params.timeoutMS ?? ppctx?.workflowTimeoutMS;
|
|
980
|
+
const instance = $this === undefined || typeof $this === 'function' ? undefined : $this;
|
|
981
|
+
if (instance && !(instance instanceof decorators_1.ConfiguredInstance)) {
|
|
982
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Attempt to call a `workflow` function on an object that is not a `ConfiguredInstance`');
|
|
983
|
+
}
|
|
984
|
+
// If this is called from within a workflow, this is a child workflow,
|
|
985
|
+
// For OAOO, we will need a consistent ID formed from the parent WF and call number
|
|
986
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
987
|
+
if (!SolidActions.isInWorkflow()) {
|
|
988
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to a `workflow` function from within a `step` or `transaction`');
|
|
989
|
+
}
|
|
990
|
+
const funcId = startWfFuncId ?? (0, context_1.functionIDGetIncrement)();
|
|
991
|
+
const pctx = (0, context_1.getCurrentContextStore)();
|
|
992
|
+
const pwfid = pctx.workflowId;
|
|
993
|
+
const wfParams = {
|
|
994
|
+
workflowUUID: wfId || pwfid + '-' + funcId,
|
|
995
|
+
configuredInstance: instance,
|
|
996
|
+
queueName,
|
|
997
|
+
timeoutMS,
|
|
998
|
+
// Detach child deadline if a null timeout is configured
|
|
999
|
+
deadlineEpochMS: params.timeoutMS === null || pctx?.workflowTimeoutMS === null ? undefined : pctx?.deadlineEpochMS,
|
|
1000
|
+
enqueueOptions: params.enqueueOptions,
|
|
1001
|
+
};
|
|
1002
|
+
return await invokeRegOp(wfParams, pwfid, funcId);
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
const wfParams = {
|
|
1006
|
+
workflowUUID: wfId,
|
|
1007
|
+
queueName,
|
|
1008
|
+
enqueueOptions: params.enqueueOptions,
|
|
1009
|
+
configuredInstance: instance,
|
|
1010
|
+
timeoutMS,
|
|
1011
|
+
};
|
|
1012
|
+
return await invokeRegOp(wfParams, undefined, undefined);
|
|
1013
|
+
}
|
|
1014
|
+
function invokeRegOp(wfParams, workflowID, funcNum) {
|
|
1015
|
+
if (regOP.workflowConfig) {
|
|
1016
|
+
const func = regOP.registeredFunction;
|
|
1017
|
+
return solidactions_executor_1.SolidActionsExecutor.globalInstance.internalWorkflow(func, wfParams, workflowID, funcNum, ...args);
|
|
1018
|
+
}
|
|
1019
|
+
if (regOP.stepConfig) {
|
|
1020
|
+
const func = regOP.registeredFunction;
|
|
1021
|
+
return solidactions_executor_1.SolidActionsExecutor.globalInstance.startStepTempWF(func, wfParams, workflowID, funcNum, ...args);
|
|
1022
|
+
}
|
|
1023
|
+
throw new error_1.SolidActionsNotRegisteredError(regOP.name, `${regOP.name} is not a registered SolidActions workflow, step, or transaction function`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
static #getWorkflowInvoker(registration, config) {
|
|
1027
|
+
registration.setWorkflowConfig(config ?? {});
|
|
1028
|
+
const invoker = async function (...rawArgs) {
|
|
1029
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('workflows');
|
|
1030
|
+
if (SolidActions.isInWorkflow()) {
|
|
1031
|
+
const startWfFuncId = (0, context_1.functionIDGetIncrement)();
|
|
1032
|
+
const getResFuncID = (0, context_1.functionIDGetIncrement)();
|
|
1033
|
+
const handle = await SolidActions.#invokeWorkflow(this, registration, rawArgs, undefined, startWfFuncId);
|
|
1034
|
+
return await handle.getResult(getResFuncID);
|
|
1035
|
+
}
|
|
1036
|
+
const handle = await SolidActions.#invokeWorkflow(this, registration, rawArgs);
|
|
1037
|
+
return await handle.getResult();
|
|
1038
|
+
};
|
|
1039
|
+
(0, decorators_1.registerFunctionWrapper)(invoker, registration);
|
|
1040
|
+
Object.defineProperty(invoker, 'name', {
|
|
1041
|
+
value: registration.name,
|
|
1042
|
+
});
|
|
1043
|
+
return invoker;
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Decorator designating a method as a SolidActions step.
|
|
1047
|
+
* A durable checkpoint will be made after the step completes
|
|
1048
|
+
* This ensures "at least once" execution of the step, and that the step will not
|
|
1049
|
+
* be executed again once the checkpoint is recorded
|
|
1050
|
+
*
|
|
1051
|
+
* @param config - Configuration information for the step, particularly the retry policy
|
|
1052
|
+
*/
|
|
1053
|
+
static step(config = {}) {
|
|
1054
|
+
function decorator(target, propertyKey, inDescriptor) {
|
|
1055
|
+
const { descriptor, registration } = (0, decorators_1.wrapSolidActionsFunctionAndRegisterByTarget)(target, propertyKey, config.name, inDescriptor);
|
|
1056
|
+
registration.setStepConfig(config);
|
|
1057
|
+
const invokeWrapper = async function (...rawArgs) {
|
|
1058
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('steps');
|
|
1059
|
+
let inst = undefined;
|
|
1060
|
+
if (this === undefined || typeof this === 'function') {
|
|
1061
|
+
// This is static
|
|
1062
|
+
}
|
|
1063
|
+
else {
|
|
1064
|
+
inst = this;
|
|
1065
|
+
if (!(inst instanceof decorators_1.ConfiguredInstance)) {
|
|
1066
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Attempt to call a `step` function on an object that is not a `ConfiguredInstance`');
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
1070
|
+
if (SolidActions.isInTransaction()) {
|
|
1071
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to a `step` function from within a `transaction`');
|
|
1072
|
+
}
|
|
1073
|
+
if (SolidActions.isInStep()) {
|
|
1074
|
+
// There should probably be checks here about the compatibility of the StepConfig...
|
|
1075
|
+
return registration.registeredFunction.call(this, ...rawArgs);
|
|
1076
|
+
}
|
|
1077
|
+
return await solidactions_executor_1.SolidActionsExecutor.globalInstance.callStepFunction(registration.registeredFunction, undefined, undefined, inst ?? null, ...rawArgs);
|
|
1078
|
+
}
|
|
1079
|
+
const wfId = (0, context_1.getNextWFID)(undefined);
|
|
1080
|
+
const wfParams = {
|
|
1081
|
+
configuredInstance: inst,
|
|
1082
|
+
workflowUUID: wfId,
|
|
1083
|
+
};
|
|
1084
|
+
return await SolidActions.#executor.runStepTempWF(registration.registeredFunction, wfParams, ...rawArgs);
|
|
1085
|
+
};
|
|
1086
|
+
descriptor.value = invokeWrapper;
|
|
1087
|
+
registration.wrappedFunction = invokeWrapper;
|
|
1088
|
+
(0, decorators_1.registerFunctionWrapper)(invokeWrapper, registration);
|
|
1089
|
+
Object.defineProperty(invokeWrapper, 'name', {
|
|
1090
|
+
value: registration.name,
|
|
1091
|
+
});
|
|
1092
|
+
return descriptor;
|
|
1093
|
+
}
|
|
1094
|
+
return decorator;
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Create a check pointed SolidActions step function from a provided function
|
|
1098
|
+
* Similar to the SolidActions.step decorator, but without requiring a decorator
|
|
1099
|
+
* A durable checkpoint will be made after the step completes
|
|
1100
|
+
* This ensures "at least once" execution of the step, and that the step will not
|
|
1101
|
+
* be executed again once the checkpoint is recorded
|
|
1102
|
+
* @param func - The function to register as a step
|
|
1103
|
+
* @param config - Configuration information for the step, particularly the retry policy and name
|
|
1104
|
+
*/
|
|
1105
|
+
static registerStep(func, config = {}) {
|
|
1106
|
+
const name = config.name ?? func.name;
|
|
1107
|
+
const reg = (0, decorators_1.wrapSolidActionsFunctionAndRegister)(config?.ctorOrProto, config?.className, name, name, func);
|
|
1108
|
+
const invokeWrapper = async function (...rawArgs) {
|
|
1109
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('steps');
|
|
1110
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
1111
|
+
const inst = this;
|
|
1112
|
+
const callFunc = reg.registeredFunction ?? reg.origFunction;
|
|
1113
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
1114
|
+
if (SolidActions.isInTransaction()) {
|
|
1115
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to a `step` function from within a `transaction`');
|
|
1116
|
+
}
|
|
1117
|
+
if (SolidActions.isInStep()) {
|
|
1118
|
+
// There should probably be checks here about the compatibility of the StepConfig...
|
|
1119
|
+
return callFunc.call(this, ...rawArgs);
|
|
1120
|
+
}
|
|
1121
|
+
return await solidactions_executor_1.SolidActionsExecutor.globalInstance.callStepFunction(callFunc, name, config, inst ?? null, ...rawArgs);
|
|
1122
|
+
}
|
|
1123
|
+
if ((0, context_1.getNextWFID)(undefined)) {
|
|
1124
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError(`Invalid call to step '${name}' outside of a workflow; with directive to start a workflow.`);
|
|
1125
|
+
}
|
|
1126
|
+
return callFunc.call(this, ...rawArgs);
|
|
1127
|
+
};
|
|
1128
|
+
(0, decorators_1.registerFunctionWrapper)(invokeWrapper, reg);
|
|
1129
|
+
Object.defineProperty(invokeWrapper, 'name', { value: name });
|
|
1130
|
+
return invokeWrapper;
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Run the enclosed `callback` as a checkpointed step within a SolidActions workflow
|
|
1134
|
+
* @param callback - function containing code to run
|
|
1135
|
+
* @param config - Configuration information for the step, particularly the retry policy
|
|
1136
|
+
* @param config.name - The name of the step; if not provided, the function name will be used
|
|
1137
|
+
* @returns - result (either obtained from invoking function, or retrieved if run before)
|
|
1138
|
+
*/
|
|
1139
|
+
static runStep(func, config = {}) {
|
|
1140
|
+
(0, decorators_1.ensureSolidActionsIsLaunched)('steps');
|
|
1141
|
+
const name = config.name ?? func.name;
|
|
1142
|
+
if (SolidActions.isWithinWorkflow()) {
|
|
1143
|
+
if (SolidActions.isInTransaction()) {
|
|
1144
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Invalid call to a runStep from within a `transaction`');
|
|
1145
|
+
}
|
|
1146
|
+
if (SolidActions.isInStep()) {
|
|
1147
|
+
// There should probably be checks here about the compatibility of the StepConfig...
|
|
1148
|
+
return func();
|
|
1149
|
+
}
|
|
1150
|
+
return solidactions_executor_1.SolidActionsExecutor.globalInstance.callStepFunction(func, name, config, null);
|
|
1151
|
+
}
|
|
1152
|
+
if ((0, context_1.getNextWFID)(undefined)) {
|
|
1153
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError(`Invalid call to step '${name}' outside of a workflow; with directive to start a workflow.`);
|
|
1154
|
+
}
|
|
1155
|
+
return func();
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Register serialization recipe; this is used to save/retrieve objects from the SolidActions system
|
|
1159
|
+
* database. This includes workflow inputs, function return values, messages, and events.
|
|
1160
|
+
*/
|
|
1161
|
+
static registerSerialization(serReg) {
|
|
1162
|
+
if (SolidActions.isInitialized()) {
|
|
1163
|
+
throw new TypeError(`Serializers/deserializers should not be registered after SolidActions.launch()`);
|
|
1164
|
+
}
|
|
1165
|
+
(0, serialization_1.registerSerializationRecipe)(serReg);
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Decorate a class with the default list of required roles.
|
|
1169
|
+
* This class-level default can be overridden on a per-function basis with `requiredRole`.
|
|
1170
|
+
* @param anyOf - The list of roles allowed access; authorization is granted if the authenticated user has any role on the list
|
|
1171
|
+
*/
|
|
1172
|
+
static defaultRequiredRole(anyOf) {
|
|
1173
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1174
|
+
function clsdec(ctor) {
|
|
1175
|
+
const clsreg = (0, decorators_1.associateClassWithExternal)(decorators_1.SOLIDACTIONS_AUTH, ctor);
|
|
1176
|
+
clsreg.requiredRole = anyOf;
|
|
1177
|
+
(0, authdecorators_1.registerAuthChecker)();
|
|
1178
|
+
}
|
|
1179
|
+
return clsdec;
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Decorate a method with the default list of required roles.
|
|
1183
|
+
* @see `SolidActions.defaultRequiredRole`
|
|
1184
|
+
* @param anyOf - The list of roles allowed access; authorization is granted if the authenticated user has any role on the list
|
|
1185
|
+
*/
|
|
1186
|
+
static requiredRole(anyOf) {
|
|
1187
|
+
function apidec(target, propertyKey, inDescriptor) {
|
|
1188
|
+
const rr = (0, decorators_1.associateMethodWithExternal)(decorators_1.SOLIDACTIONS_AUTH, target, undefined, propertyKey.toString(), inDescriptor.value);
|
|
1189
|
+
rr.regInfo.requiredRole = anyOf;
|
|
1190
|
+
(0, authdecorators_1.registerAuthChecker)();
|
|
1191
|
+
inDescriptor.value = rr.registration.wrappedFunction ?? rr.registration.registeredFunction;
|
|
1192
|
+
return inDescriptor;
|
|
1193
|
+
}
|
|
1194
|
+
return apidec;
|
|
1195
|
+
}
|
|
1196
|
+
/////
|
|
1197
|
+
// Patching
|
|
1198
|
+
/////
|
|
1199
|
+
/**
|
|
1200
|
+
* Check if a workflow execution has been patched.
|
|
1201
|
+
*
|
|
1202
|
+
* Patching allows reexecution of workflows to accommate changes to the workflow logic.
|
|
1203
|
+
*
|
|
1204
|
+
* Patches check the system database to see which code branch to take. As this adds overhead,
|
|
1205
|
+
* they may eventually be removed; see `deprecatePatch`.
|
|
1206
|
+
*
|
|
1207
|
+
* @param patchName Name of the patch to check.
|
|
1208
|
+
* @returns true if this is the patched(new) workflow variant, or false if the execution predates the patch
|
|
1209
|
+
*/
|
|
1210
|
+
static async patch(patchName) {
|
|
1211
|
+
if (!SolidActions.isInWorkflow()) {
|
|
1212
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('`SolidActions.patch` must be called from a workflow, and not within a step');
|
|
1213
|
+
}
|
|
1214
|
+
if (!SolidActions.#solidActionsConfig?.enablePatching) {
|
|
1215
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Patching is not enabled. See `enablePatching` in `SolidActionsConfig`');
|
|
1216
|
+
}
|
|
1217
|
+
const patched = await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.checkPatch(SolidActions.workflowID, (0, context_1.functionIDGet)(), patchName, false);
|
|
1218
|
+
if (patched.hasEntry) {
|
|
1219
|
+
(0, context_1.functionIDGetIncrement)();
|
|
1220
|
+
}
|
|
1221
|
+
return patched.isPatched;
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Check if a workflow execution has been patched, within a plan to eventually remove the unpatched (old) variant.
|
|
1225
|
+
*
|
|
1226
|
+
* `patch` may be changed to `deprecatePatch` after all unpatched workflows have completed and will not be reexecuted.
|
|
1227
|
+
* Once all workflows started with `patch` have completed (in favor of those using `deprecatePatch`), the `deprecatePatch` may then be removed.
|
|
1228
|
+
*
|
|
1229
|
+
* @param patchName Name of the patch to check.
|
|
1230
|
+
* @returns true if this is the patched(new) workflow variant, which it should always be if all unpatched workflows have been retired
|
|
1231
|
+
*/
|
|
1232
|
+
static async deprecatePatch(patchName) {
|
|
1233
|
+
if (!SolidActions.isInWorkflow()) {
|
|
1234
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('`SolidActions.deprecatePatch` must be called from a workflow, and not within a step');
|
|
1235
|
+
}
|
|
1236
|
+
if (!SolidActions.#solidActionsConfig?.enablePatching) {
|
|
1237
|
+
throw new error_1.SolidActionsInvalidWorkflowTransitionError('Patching is not enabled. See `enablePatching` in `SolidActionsConfig`');
|
|
1238
|
+
}
|
|
1239
|
+
const patched = await solidactions_executor_1.SolidActionsExecutor.globalInstance.systemDatabase.checkPatch(SolidActions.workflowID, (0, context_1.functionIDGet)(), patchName, true);
|
|
1240
|
+
if (patched.hasEntry) {
|
|
1241
|
+
(0, context_1.functionIDGetIncrement)();
|
|
1242
|
+
}
|
|
1243
|
+
return patched.isPatched;
|
|
1244
|
+
}
|
|
1245
|
+
/////
|
|
1246
|
+
// Registration, etc
|
|
1247
|
+
/////
|
|
1248
|
+
/**
|
|
1249
|
+
* Register a lifecycle listener
|
|
1250
|
+
*/
|
|
1251
|
+
static registerLifecycleCallback(lcl) {
|
|
1252
|
+
(0, decorators_1.registerLifecycleCallback)(lcl);
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Register a middleware provider
|
|
1256
|
+
*/
|
|
1257
|
+
static registerMiddlewareInstaller(mwp) {
|
|
1258
|
+
(0, decorators_1.registerMiddlewareInstaller)(mwp);
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Register information to be associated with a SolidActions class
|
|
1262
|
+
*/
|
|
1263
|
+
static associateClassWithInfo(external, cls) {
|
|
1264
|
+
return (0, decorators_1.associateClassWithExternal)(external, cls);
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Register information to be associated with a SolidActions function
|
|
1268
|
+
*/
|
|
1269
|
+
static associateFunctionWithInfo(external, func, target) {
|
|
1270
|
+
return (0, decorators_1.associateMethodWithExternal)(external, target.ctorOrProto, target.className, target.name ?? func.name, func);
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Register information to be associated with a SolidActions function
|
|
1274
|
+
*/
|
|
1275
|
+
static associateParamWithInfo(external, func, target) {
|
|
1276
|
+
return (0, decorators_1.associateParameterWithExternal)(external, target.ctorOrProto, target.className, target.name ?? func?.name ?? '<unknown>', func, target.param);
|
|
1277
|
+
}
|
|
1278
|
+
/** Get registrations */
|
|
1279
|
+
static getAssociatedInfo(external, cls, funcName) {
|
|
1280
|
+
return (0, decorators_1.getRegistrationsForExternal)(external, cls, funcName);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
exports.SolidActions = SolidActions;
|
|
1284
|
+
//# sourceMappingURL=solidactions.js.map
|