@swell/cli 2.9.0 → 2.9.3
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/dist/commands/app/dev.js +48 -0
- package/dist/commands/create/function.d.ts +3 -0
- package/dist/commands/create/function.js +80 -27
- package/dist/commands/inspect/functions.d.ts +4 -1
- package/dist/commands/inspect/functions.js +11 -0
- package/dist/commands/inspect/index.js +1 -0
- package/dist/commands/inspect/workflow-runs.d.ts +34 -0
- package/dist/commands/inspect/workflow-runs.js +319 -0
- package/dist/commands/inspect/workflows.d.ts +33 -0
- package/dist/commands/inspect/workflows.js +99 -0
- package/dist/commands/logs.d.ts +1 -0
- package/dist/commands/logs.js +20 -2
- package/dist/commands/schema.js +21 -2
- package/dist/create-app-command.js +1 -1
- package/dist/inspect-resource-command.d.ts +4 -1
- package/dist/inspect-resource-command.js +15 -7
- package/dist/lib/api.js +12 -0
- package/dist/lib/apps/app-config.js +25 -2
- package/dist/lib/apps/index.js +5 -2
- package/dist/lib/bundle.d.ts +2 -0
- package/dist/lib/bundle.js +312 -0
- package/dist/lib/create/function.d.ts +2 -2
- package/dist/lib/create/function.js +45 -1
- package/dist/lib/function-source-analysis.d.ts +16 -0
- package/dist/lib/function-source-analysis.js +311 -0
- package/dist/lib/logs/index.d.ts +2 -0
- package/dist/lib/logs/index.js +27 -2
- package/dist/lib/swell-function-wrapper.d.ts +11 -0
- package/dist/lib/swell-function-wrapper.js +105 -1
- package/dist/lib/workflows/operations.d.ts +68 -0
- package/dist/lib/workflows/operations.js +162 -0
- package/oclif.manifest.json +191 -13
- package/package.json +4 -4
|
@@ -3,7 +3,8 @@ import isEmpty from 'lodash/isEmpty.js';
|
|
|
3
3
|
import { detectFilenameMime } from 'mime-detect';
|
|
4
4
|
import * as fs from 'node:fs';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
|
-
import { bundleFunction, bundleComponent } from '../bundle.js';
|
|
6
|
+
import { bundleFunction, bundleComponent, bundleWorkflow } from '../bundle.js';
|
|
7
|
+
import { analyzeFunctionSource, hasKindDiagnostic, workflowStaticConfigError, } from '../function-source-analysis.js';
|
|
7
8
|
import { AllConfigPaths, ConfigType, filePathExists, hashFile, } from './index.js';
|
|
8
9
|
export class IgnoringFileError extends Error {
|
|
9
10
|
constructor(message) {
|
|
@@ -257,10 +258,32 @@ export class AppConfigFunction extends AppConfig {
|
|
|
257
258
|
if (!fileData) {
|
|
258
259
|
return;
|
|
259
260
|
}
|
|
260
|
-
const
|
|
261
|
+
const analysis = await analyzeFunctionSource(this.appPath);
|
|
262
|
+
if (analysis.kind === 'workflow') {
|
|
263
|
+
if (analysis.diagnostics.length > 0) {
|
|
264
|
+
throw new Error(analysis.diagnostics.join('\n'));
|
|
265
|
+
}
|
|
266
|
+
const { code, config } = await bundleWorkflow(this.appPath, analysis);
|
|
267
|
+
postData.file = {
|
|
268
|
+
data: fileData,
|
|
269
|
+
};
|
|
270
|
+
postData.build_file = {
|
|
271
|
+
content_type: 'application/javascript+module',
|
|
272
|
+
data: code,
|
|
273
|
+
};
|
|
274
|
+
postData.values = config;
|
|
275
|
+
return postData;
|
|
276
|
+
}
|
|
277
|
+
if (hasKindDiagnostic(analysis)) {
|
|
278
|
+
throw new Error(analysis.diagnostics.join('\n'));
|
|
279
|
+
}
|
|
280
|
+
const { code, config } = await bundleFunction(this.appPath);
|
|
261
281
|
if (!config) {
|
|
262
282
|
throw new IgnoringFileError('Function must export a `config` object.');
|
|
263
283
|
}
|
|
284
|
+
if (config.kind === 'workflow') {
|
|
285
|
+
throw workflowStaticConfigError();
|
|
286
|
+
}
|
|
264
287
|
// Save the original file and the bundled version
|
|
265
288
|
postData.file = {
|
|
266
289
|
data: fileData,
|
package/dist/lib/apps/index.js
CHANGED
|
@@ -354,8 +354,11 @@ export function appAssetImage(appPath, fileName) {
|
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
356
|
}
|
|
357
|
+
function getNameFromFilePath(filePath) {
|
|
358
|
+
return path.parse(filePath).name.replaceAll('_', '-');
|
|
359
|
+
}
|
|
357
360
|
export function appConfigFromFile(filePath, configType, appPath) {
|
|
358
|
-
const
|
|
361
|
+
const name = getNameFromFilePath(filePath);
|
|
359
362
|
// Notification basenames must not contain dots: the inspect identifier
|
|
360
363
|
// grammar (app.<slug>.<model>.<name>) splits on '.', so a dotted name
|
|
361
364
|
// would silently misparse on lookup.
|
|
@@ -376,7 +379,7 @@ export function appConfigFromFile(filePath, configType, appPath) {
|
|
|
376
379
|
return config;
|
|
377
380
|
}
|
|
378
381
|
export function findAppConfig(app, filePath, configType) {
|
|
379
|
-
const
|
|
382
|
+
const name = getNameFromFilePath(filePath);
|
|
380
383
|
const config = app?.configs?.find((c) => c && c.type === configType && c.name === name);
|
|
381
384
|
return config;
|
|
382
385
|
}
|
package/dist/lib/bundle.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
import type { FunctionSourceAnalysis } from './function-source-analysis.js';
|
|
1
2
|
export declare function bundleFunction(filePath: string): Promise<any>;
|
|
3
|
+
export declare function bundleWorkflow(filePath: string, analysis: FunctionSourceAnalysis): Promise<any>;
|
|
2
4
|
export declare function bundleComponent(filePath: string): Promise<any>;
|
package/dist/lib/bundle.js
CHANGED
|
@@ -36,6 +36,32 @@ export async function bundleFunction(filePath) {
|
|
|
36
36
|
throw new Error(`Unable to compile function ${filePath}: ${error.message}`);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
export async function bundleWorkflow(filePath, analysis) {
|
|
40
|
+
try {
|
|
41
|
+
const entrySource = getSwellWorkflowEntrypoint(filePath);
|
|
42
|
+
const buildResult = await esbuild.build({
|
|
43
|
+
bundle: true,
|
|
44
|
+
stdin: {
|
|
45
|
+
contents: entrySource,
|
|
46
|
+
loader: 'js',
|
|
47
|
+
resolveDir: path.dirname(filePath),
|
|
48
|
+
},
|
|
49
|
+
external: ['cloudflare:workers', 'cloudflare:workflows'],
|
|
50
|
+
format: 'esm',
|
|
51
|
+
metafile: true,
|
|
52
|
+
platform: 'browser',
|
|
53
|
+
target: ['es2022'],
|
|
54
|
+
write: false,
|
|
55
|
+
logLevel: 'silent',
|
|
56
|
+
});
|
|
57
|
+
const { metafile: { inputs }, outputFiles: [{ text: code }], } = buildResult;
|
|
58
|
+
const dependencies = Object.keys(inputs).filter((input) => !input.startsWith('node_modules/'));
|
|
59
|
+
return { code, config: analysis.config, dependencies };
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw new Error(`Unable to compile workflow ${filePath}: ${error.message}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
39
65
|
export async function bundleComponent(filePath) {
|
|
40
66
|
try {
|
|
41
67
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
@@ -80,6 +106,292 @@ function getSwellFunctionWrapper() {
|
|
|
80
106
|
const __dirname = path.dirname(__filename);
|
|
81
107
|
return fs.readFileSync(path.join(__dirname, './swell-function-wrapper.js'), 'utf8');
|
|
82
108
|
}
|
|
109
|
+
function getSwellWorkflowEntrypoint(filePath) {
|
|
110
|
+
const workflowImport = `./${path.basename(filePath)}`;
|
|
111
|
+
return `
|
|
112
|
+
import { WorkflowEntrypoint } from 'cloudflare:workers';
|
|
113
|
+
import { NonRetryableError } from 'cloudflare:workflows';
|
|
114
|
+
import UserWorkflow from ${JSON.stringify(workflowImport)};
|
|
115
|
+
|
|
116
|
+
const RUNTIME_PROXY_PATH = '/:workflows/runtime/request';
|
|
117
|
+
const RUNTIME_TOKEN_PATTERN = /\\bwf_rt_[0-9A-Za-z]+_[0-9A-Za-z]{12,}\\b/g;
|
|
118
|
+
|
|
119
|
+
class SwellWorkflowError extends Error {
|
|
120
|
+
constructor(body) {
|
|
121
|
+
const error = body && body.error ? body.error : body;
|
|
122
|
+
super(error && error.message ? error.message : 'Workflow runtime request failed');
|
|
123
|
+
this.name = 'SwellError';
|
|
124
|
+
this.body = body;
|
|
125
|
+
this.code = error && error.code;
|
|
126
|
+
this.status = error && error.status;
|
|
127
|
+
this.retryable = Boolean(error && error.retryable);
|
|
128
|
+
this.category = error && error.category;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export class SwellWorkflowEntrypoint extends WorkflowEntrypoint {
|
|
133
|
+
async run(event, step) {
|
|
134
|
+
const invocation = getInvocation(event);
|
|
135
|
+
const runtime = invocation.runtime;
|
|
136
|
+
const runtimeToken = invocation.runtime_token;
|
|
137
|
+
const startedAt = Date.now();
|
|
138
|
+
const proxy = new SwellWorkflowRuntimeProxy(runtime, runtimeToken);
|
|
139
|
+
const workflow = new UserWorkflow();
|
|
140
|
+
const req = buildWorkflowRequest(invocation, proxy);
|
|
141
|
+
const workflowStep = buildWorkflowStep(step, proxy);
|
|
142
|
+
|
|
143
|
+
await proxy.log({ phase: 'run', status: 'started' });
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const result = await workflow.run(req, workflowStep);
|
|
147
|
+
await proxy.log({ phase: 'run', status: 'completed', time: Date.now() - startedAt });
|
|
148
|
+
await proxy.lifecycle('completed');
|
|
149
|
+
return result;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
await proxy.log({
|
|
152
|
+
phase: 'run',
|
|
153
|
+
status: 'failed',
|
|
154
|
+
time: Date.now() - startedAt,
|
|
155
|
+
error: formatError(error),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
await proxy.lifecycle('failed', formatError(error));
|
|
160
|
+
} catch (closeoutError) {
|
|
161
|
+
await proxy.log({
|
|
162
|
+
phase: 'lifecycle',
|
|
163
|
+
status: 'failed',
|
|
164
|
+
error: formatError(closeoutError),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function getInvocation(event) {
|
|
174
|
+
const payload = event && Object.prototype.hasOwnProperty.call(event, 'payload')
|
|
175
|
+
? event.payload
|
|
176
|
+
: event;
|
|
177
|
+
|
|
178
|
+
if (!payload || typeof payload !== 'object') {
|
|
179
|
+
throw new Error('Workflow invocation payload is required');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!payload.runtime || !payload.runtime_token) {
|
|
183
|
+
throw new Error('Workflow runtime identity is required');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return payload;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function buildWorkflowRequest(invocation, proxy) {
|
|
190
|
+
const runtime = invocation.runtime;
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
id: runtime.request_id,
|
|
194
|
+
appId: runtime.app_slug || runtime.app_id,
|
|
195
|
+
store: {
|
|
196
|
+
id: runtime.store_id,
|
|
197
|
+
},
|
|
198
|
+
data: invocation.data,
|
|
199
|
+
workflow: {
|
|
200
|
+
workflow_id: runtime.workflow_id,
|
|
201
|
+
workflow_name: runtime.workflow_name,
|
|
202
|
+
workflow_instance_id: runtime.workflow_instance_id,
|
|
203
|
+
trigger: runtime.trigger,
|
|
204
|
+
request_id: runtime.request_id,
|
|
205
|
+
},
|
|
206
|
+
isLocalDev: false,
|
|
207
|
+
swell: {
|
|
208
|
+
get: (path, data) => proxy.api('get', path, data),
|
|
209
|
+
post: (path, data) => proxy.api('post', path, data),
|
|
210
|
+
put: (path, data) => proxy.api('put', path, data),
|
|
211
|
+
delete: (path, data) => proxy.api('delete', path, data),
|
|
212
|
+
settings: () => proxy.settings(),
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function buildWorkflowStep(cloudflareStep, proxy) {
|
|
218
|
+
return Object.freeze({
|
|
219
|
+
do(name, optionsOrCallback, maybeCallback) {
|
|
220
|
+
const hasOptions = typeof optionsOrCallback !== 'function';
|
|
221
|
+
const options = hasOptions ? optionsOrCallback : undefined;
|
|
222
|
+
const callback = hasOptions ? maybeCallback : optionsOrCallback;
|
|
223
|
+
|
|
224
|
+
const wrappedCallback = async () => {
|
|
225
|
+
await proxy.log({ phase: 'step', step_name: name, status: 'started' });
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const result = await callback();
|
|
229
|
+
await proxy.log({ phase: 'step', step_name: name, status: 'completed' });
|
|
230
|
+
return result;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
await proxy.log({
|
|
233
|
+
phase: 'step',
|
|
234
|
+
step_name: name,
|
|
235
|
+
status: 'failed',
|
|
236
|
+
error: formatError(error),
|
|
237
|
+
});
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
return hasOptions
|
|
243
|
+
? cloudflareStep.do(name, options, wrappedCallback)
|
|
244
|
+
: cloudflareStep.do(name, wrappedCallback);
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
sleep(name, duration) {
|
|
248
|
+
return cloudflareStep.sleep(name, duration);
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
sleepUntil(name, date) {
|
|
252
|
+
return cloudflareStep.sleepUntil(name, date);
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
class SwellWorkflowRuntimeProxy {
|
|
258
|
+
constructor(runtime, runtimeToken) {
|
|
259
|
+
this.runtime = runtime;
|
|
260
|
+
this.runtimeToken = runtimeToken;
|
|
261
|
+
this.apiHost = getRuntimeApiHost(runtime);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
api(method, path, data) {
|
|
265
|
+
return this.request({
|
|
266
|
+
type: 'api',
|
|
267
|
+
method,
|
|
268
|
+
path,
|
|
269
|
+
data,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
settings() {
|
|
274
|
+
return this.request({
|
|
275
|
+
type: 'settings',
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
lifecycle(status, error) {
|
|
280
|
+
return this.request({
|
|
281
|
+
type: 'lifecycle',
|
|
282
|
+
status,
|
|
283
|
+
error,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async log(entry) {
|
|
288
|
+
try {
|
|
289
|
+
return await this.request({
|
|
290
|
+
type: 'workflow_log',
|
|
291
|
+
entries: [sanitizeLogEntry(entry)],
|
|
292
|
+
});
|
|
293
|
+
} catch {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async request(operation) {
|
|
299
|
+
const response = await fetch(\`\${this.apiHost}\${RUNTIME_PROXY_PATH}\`, {
|
|
300
|
+
method: 'POST',
|
|
301
|
+
headers: {
|
|
302
|
+
Authorization: \`Bearer \${this.runtimeToken}\`,
|
|
303
|
+
'Content-Type': 'application/json',
|
|
304
|
+
'User-Agent': 'swell-workflows/1.0',
|
|
305
|
+
},
|
|
306
|
+
body: JSON.stringify({
|
|
307
|
+
runtime: runtimeIdentity(this.runtime),
|
|
308
|
+
operation,
|
|
309
|
+
}),
|
|
310
|
+
});
|
|
311
|
+
const text = await response.text();
|
|
312
|
+
const body = parseJson(text);
|
|
313
|
+
|
|
314
|
+
if (!response.ok) {
|
|
315
|
+
throw mapRuntimeError(body, response.status);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return body;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function getRuntimeApiHost(runtime) {
|
|
323
|
+
const apiHost = runtime && runtime.api_host;
|
|
324
|
+
|
|
325
|
+
if (!apiHost || typeof apiHost !== 'string') {
|
|
326
|
+
throw new Error('Workflow runtime API host is required');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (!/^https?:\\/\\//.test(apiHost)) {
|
|
330
|
+
throw new Error('Workflow runtime API host must be an HTTP URL');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return apiHost.replace(/\\/$/, '');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function runtimeIdentity(runtime) {
|
|
337
|
+
return {
|
|
338
|
+
app_id: runtime.app_id,
|
|
339
|
+
store_id: runtime.store_id,
|
|
340
|
+
environment_id: runtime.environment_id || null,
|
|
341
|
+
workflow_id: runtime.workflow_id,
|
|
342
|
+
workflow_name: runtime.workflow_name,
|
|
343
|
+
workflow_instance_id: runtime.workflow_instance_id,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function parseJson(text) {
|
|
348
|
+
try {
|
|
349
|
+
return JSON.parse(text);
|
|
350
|
+
} catch {
|
|
351
|
+
return text;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function mapRuntimeError(body, status) {
|
|
356
|
+
const error = body && body.error ? body.error : {};
|
|
357
|
+
const runtimeError = new SwellWorkflowError({
|
|
358
|
+
error: {
|
|
359
|
+
...error,
|
|
360
|
+
status: error.status || status,
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
if (error.retryable === false) {
|
|
365
|
+
return new NonRetryableError(runtimeError.message);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return runtimeError;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function sanitizeLogEntry(entry) {
|
|
372
|
+
return {
|
|
373
|
+
...entry,
|
|
374
|
+
error: entry && entry.error ? formatError(entry.error) : undefined,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function formatError(error) {
|
|
379
|
+
if (!error) {
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const code = error.code ? String(error.code).replace(RUNTIME_TOKEN_PATTERN, '[redacted]') : undefined;
|
|
384
|
+
const message = String(error.message || error)
|
|
385
|
+
.replace(RUNTIME_TOKEN_PATTERN, '[redacted]')
|
|
386
|
+
.slice(0, 1000);
|
|
387
|
+
|
|
388
|
+
return {
|
|
389
|
+
code,
|
|
390
|
+
message,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
`;
|
|
394
|
+
}
|
|
83
395
|
async function minifyCode(code) {
|
|
84
396
|
try {
|
|
85
397
|
const result = await esbuild.transform(code, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type FunctionType = 'cron' | 'model' | 'route';
|
|
1
|
+
type FunctionType = 'cron' | 'model' | 'route' | 'workflow';
|
|
2
2
|
type FunctionLanguage = 'js' | 'ts';
|
|
3
3
|
interface CreateFunctionArgs {
|
|
4
4
|
functionName: string;
|
|
@@ -14,5 +14,5 @@ interface WriteFunctionFileParams {
|
|
|
14
14
|
schedule: string;
|
|
15
15
|
trigger: FunctionType;
|
|
16
16
|
}
|
|
17
|
-
declare const getFunctionTemplate: ({ description, events, extension, language, route, schedule, trigger, }: WriteFunctionFileParams) => string;
|
|
17
|
+
declare const getFunctionTemplate: ({ description, events, extension, functionName, language, route, schedule, trigger, }: WriteFunctionFileParams) => string;
|
|
18
18
|
export { CreateFunctionArgs, FunctionLanguage, FunctionType, WriteFunctionFileParams, getFunctionTemplate, };
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
const getFunctionTemplate = ({ description, events, extension, language, route, schedule, trigger, }) => {
|
|
1
|
+
const getFunctionTemplate = ({ description, events, extension, functionName, language, route, schedule, trigger, }) => {
|
|
2
|
+
if (trigger === 'workflow') {
|
|
3
|
+
return getWorkflowTemplate({
|
|
4
|
+
description,
|
|
5
|
+
functionName,
|
|
6
|
+
language,
|
|
7
|
+
});
|
|
8
|
+
}
|
|
2
9
|
let typeAppend = ``;
|
|
3
10
|
if (trigger === 'model') {
|
|
4
11
|
const eventsList = events
|
|
@@ -37,4 +44,41 @@ export default async function (req${language === 'ts' ? ': SwellRequest' : ''})
|
|
|
37
44
|
}
|
|
38
45
|
`;
|
|
39
46
|
};
|
|
47
|
+
const getWorkflowTemplate = ({ description, functionName, language, }) => {
|
|
48
|
+
const className = toWorkflowClassName(functionName);
|
|
49
|
+
const reqParam = language === 'ts' ? 'req: SwellWorkflowRequest' : 'req';
|
|
50
|
+
const stepParam = language === 'ts' ? 'step: SwellWorkflowStep' : 'step';
|
|
51
|
+
return `export const config = {
|
|
52
|
+
kind: 'workflow',
|
|
53
|
+
description: '${description}'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default class ${className} {
|
|
57
|
+
async run(${reqParam}, ${stepParam}) {
|
|
58
|
+
await step.do('load settings', async () => {
|
|
59
|
+
await req.swell.settings();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
data: req.data,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
};
|
|
69
|
+
const toWorkflowClassName = (functionName) => {
|
|
70
|
+
const baseName = functionName
|
|
71
|
+
.split(/[/\\]/)
|
|
72
|
+
.pop()
|
|
73
|
+
?.replace(/\.[cm]?[jt]sx?$/i, '');
|
|
74
|
+
const className = (baseName || 'workflow')
|
|
75
|
+
.split(/[^\dA-Za-z]+/g)
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
.map((part) => `${part[0]?.toUpperCase() || ''}${part.slice(1)}`)
|
|
78
|
+
.join('');
|
|
79
|
+
if (!className) {
|
|
80
|
+
return 'Workflow';
|
|
81
|
+
}
|
|
82
|
+
return /^[$A-Z_a-z]/.test(className) ? className : `Workflow${className}`;
|
|
83
|
+
};
|
|
40
84
|
export { getFunctionTemplate, };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type FunctionSourceKind = 'function' | 'workflow' | 'unknown';
|
|
2
|
+
export interface FunctionSourceAnalysis {
|
|
3
|
+
config?: Record<string, unknown>;
|
|
4
|
+
kind: FunctionSourceKind;
|
|
5
|
+
defaultExport?: {
|
|
6
|
+
name?: string;
|
|
7
|
+
reason?: string;
|
|
8
|
+
validWorkflowClass: boolean;
|
|
9
|
+
};
|
|
10
|
+
diagnostics: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function analyzeFunctionSource(filePath: string): Promise<FunctionSourceAnalysis>;
|
|
13
|
+
export declare function analyzeFunctionSourceText(sourceText: string, filePath?: string): FunctionSourceAnalysis;
|
|
14
|
+
export declare function getFunctionTriggers(config: Record<string, unknown>): string[];
|
|
15
|
+
export declare function hasKindDiagnostic(analysis: FunctionSourceAnalysis): boolean;
|
|
16
|
+
export declare function workflowStaticConfigError(): Error;
|