@fsai-flow/workflow 0.0.1
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/.eslintrc.json +33 -0
- package/README.md +11 -0
- package/dist/README.md +11 -0
- package/dist/package.json +42 -0
- package/dist/src/index.d.ts +21 -0
- package/dist/src/index.js +33 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/Constants.d.ts +68 -0
- package/dist/src/lib/Constants.js +106 -0
- package/dist/src/lib/Constants.js.map +1 -0
- package/dist/src/lib/DeferredPromise.d.ts +6 -0
- package/dist/src/lib/DeferredPromise.js +11 -0
- package/dist/src/lib/DeferredPromise.js.map +1 -0
- package/dist/src/lib/Expression.d.ts +65 -0
- package/dist/src/lib/Expression.js +215 -0
- package/dist/src/lib/Expression.js.map +1 -0
- package/dist/src/lib/Interfaces.d.ts +1569 -0
- package/dist/src/lib/Interfaces.js +44 -0
- package/dist/src/lib/Interfaces.js.map +1 -0
- package/dist/src/lib/LoggerProxy.d.ts +9 -0
- package/dist/src/lib/LoggerProxy.js +40 -0
- package/dist/src/lib/LoggerProxy.js.map +1 -0
- package/dist/src/lib/MetadataUtils.d.ts +4 -0
- package/dist/src/lib/MetadataUtils.js +27 -0
- package/dist/src/lib/MetadataUtils.js.map +1 -0
- package/dist/src/lib/NodeErrors.d.ts +82 -0
- package/dist/src/lib/NodeErrors.js +289 -0
- package/dist/src/lib/NodeErrors.js.map +1 -0
- package/dist/src/lib/NodeHelpers.d.ts +198 -0
- package/dist/src/lib/NodeHelpers.js +1348 -0
- package/dist/src/lib/NodeHelpers.js.map +1 -0
- package/dist/src/lib/ObservableObject.d.ts +5 -0
- package/dist/src/lib/ObservableObject.js +61 -0
- package/dist/src/lib/ObservableObject.js.map +1 -0
- package/dist/src/lib/RoutingNode.d.ts +18 -0
- package/dist/src/lib/RoutingNode.js +508 -0
- package/dist/src/lib/RoutingNode.js.map +1 -0
- package/dist/src/lib/TelemetryHelpers.d.ts +3 -0
- package/dist/src/lib/TelemetryHelpers.js +69 -0
- package/dist/src/lib/TelemetryHelpers.js.map +1 -0
- package/dist/src/lib/TypeValidation.d.ts +21 -0
- package/dist/src/lib/TypeValidation.js +385 -0
- package/dist/src/lib/TypeValidation.js.map +1 -0
- package/dist/src/lib/VersionedNodeType.d.ts +9 -0
- package/dist/src/lib/VersionedNodeType.js +26 -0
- package/dist/src/lib/VersionedNodeType.js.map +1 -0
- package/dist/src/lib/Workflow.d.ts +248 -0
- package/dist/src/lib/Workflow.js +901 -0
- package/dist/src/lib/Workflow.js.map +1 -0
- package/dist/src/lib/WorkflowDataProxy.d.ts +87 -0
- package/dist/src/lib/WorkflowDataProxy.js +556 -0
- package/dist/src/lib/WorkflowDataProxy.js.map +1 -0
- package/dist/src/lib/WorkflowErrors.d.ts +9 -0
- package/dist/src/lib/WorkflowErrors.js +18 -0
- package/dist/src/lib/WorkflowErrors.js.map +1 -0
- package/dist/src/lib/WorkflowHooks.d.ts +11 -0
- package/dist/src/lib/WorkflowHooks.js +34 -0
- package/dist/src/lib/WorkflowHooks.js.map +1 -0
- package/dist/src/lib/errors/base/base.error.d.ts +30 -0
- package/dist/src/lib/errors/base/base.error.js +45 -0
- package/dist/src/lib/errors/base/base.error.js.map +1 -0
- package/dist/src/lib/errors/base/operational.error.d.ts +15 -0
- package/dist/src/lib/errors/base/operational.error.js +19 -0
- package/dist/src/lib/errors/base/operational.error.js.map +1 -0
- package/dist/src/lib/errors/error.types.d.ts +11 -0
- package/dist/src/lib/errors/error.types.js +3 -0
- package/dist/src/lib/errors/error.types.js.map +1 -0
- package/dist/src/lib/errors/index.d.ts +1 -0
- package/dist/src/lib/errors/index.js +6 -0
- package/dist/src/lib/errors/index.js.map +1 -0
- package/dist/src/lib/result.d.ts +19 -0
- package/dist/src/lib/result.js +36 -0
- package/dist/src/lib/result.js.map +1 -0
- package/dist/src/lib/utils.d.ts +50 -0
- package/dist/src/lib/utils.js +110 -0
- package/dist/src/lib/utils.js.map +1 -0
- package/eslint.config.js +19 -0
- package/jest.config.ts +10 -0
- package/package.json +40 -0
- package/project.json +19 -0
- package/src/index.ts +33 -0
- package/src/lib/Constants.ts +124 -0
- package/src/lib/DeferredPromise.ts +14 -0
- package/src/lib/Expression.ts +375 -0
- package/src/lib/Interfaces.ts +2229 -0
- package/src/lib/LoggerProxy.ts +43 -0
- package/src/lib/MetadataUtils.ts +34 -0
- package/src/lib/NodeErrors.ts +332 -0
- package/src/lib/NodeHelpers.ts +1666 -0
- package/src/lib/ObservableObject.ts +77 -0
- package/src/lib/RoutingNode.ts +862 -0
- package/src/lib/TelemetryHelpers.ts +86 -0
- package/src/lib/TypeValidation.ts +431 -0
- package/src/lib/VersionedNodeType.ts +30 -0
- package/src/lib/Workflow.ts +1266 -0
- package/src/lib/WorkflowDataProxy.ts +708 -0
- package/src/lib/WorkflowErrors.ts +18 -0
- package/src/lib/WorkflowHooks.ts +51 -0
- package/src/lib/errors/base/base.error.ts +68 -0
- package/src/lib/errors/base/operational.error.ts +21 -0
- package/src/lib/errors/error.types.ts +14 -0
- package/src/lib/errors/index.ts +1 -0
- package/src/lib/result.ts +34 -0
- package/src/lib/utils.ts +132 -0
- package/tests/Helpers.ts +667 -0
- package/tests/NodeHelpers.test.ts +3053 -0
- package/tests/ObservableObject.test.ts +171 -0
- package/tests/RoutingNode.test.ts +1680 -0
- package/tests/Workflow.test.ts +1284 -0
- package/tests/WorkflowDataProxy.test.ts +199 -0
- package/tsconfig.json +27 -0
- package/tsconfig.lib.json +11 -0
- package/tsconfig.spec.json +14 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-cycle
|
|
2
|
+
import {
|
|
3
|
+
IWorkflowBase,
|
|
4
|
+
IWorkflowExecuteHooks,
|
|
5
|
+
IWorkflowHooksOptionalParameters,
|
|
6
|
+
WorkflowExecuteMode,
|
|
7
|
+
} from './Interfaces';
|
|
8
|
+
|
|
9
|
+
export class WorkflowHooks {
|
|
10
|
+
mode: WorkflowExecuteMode;
|
|
11
|
+
|
|
12
|
+
workflowData: IWorkflowBase;
|
|
13
|
+
|
|
14
|
+
executionId: string;
|
|
15
|
+
|
|
16
|
+
sessionId?: string;
|
|
17
|
+
|
|
18
|
+
retryOf?: string;
|
|
19
|
+
|
|
20
|
+
hookFunctions: IWorkflowExecuteHooks;
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
hookFunctions: IWorkflowExecuteHooks,
|
|
24
|
+
mode: WorkflowExecuteMode,
|
|
25
|
+
executionId: string,
|
|
26
|
+
workflowData: IWorkflowBase,
|
|
27
|
+
optionalParameters?: IWorkflowHooksOptionalParameters,
|
|
28
|
+
) {
|
|
29
|
+
// eslint-disable-next-line no-param-reassign, @typescript-eslint/prefer-nullish-coalescing
|
|
30
|
+
optionalParameters = optionalParameters || {};
|
|
31
|
+
|
|
32
|
+
this.hookFunctions = hookFunctions;
|
|
33
|
+
this.mode = mode;
|
|
34
|
+
this.executionId = executionId;
|
|
35
|
+
this.workflowData = workflowData;
|
|
36
|
+
this.sessionId = optionalParameters.sessionId;
|
|
37
|
+
this.retryOf = optionalParameters.retryOf;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
41
|
+
async executeHookFunctions(hookName: string, parameters: any[]) {
|
|
42
|
+
// tslint:disable-line:no-any
|
|
43
|
+
if (this.hookFunctions[hookName] !== undefined && Array.isArray(this.hookFunctions[hookName])) {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, no-restricted-syntax
|
|
45
|
+
for (const hookFunction of this.hookFunctions[hookName]!) {
|
|
46
|
+
// eslint-disable-next-line no-await-in-loop
|
|
47
|
+
await hookFunction.apply(this, parameters);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Event } from '@sentry/node';
|
|
2
|
+
import callsites from 'callsites';
|
|
3
|
+
|
|
4
|
+
import type { ErrorTags, ErrorLevel, ReportingOptions } from '../error.types';
|
|
5
|
+
|
|
6
|
+
export type BaseErrorOptions = { description?: undefined | null } & ErrorOptions & ReportingOptions;
|
|
7
|
+
|
|
8
|
+
interface ErrorOptions {
|
|
9
|
+
cause?: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Base class for all errors
|
|
14
|
+
*/
|
|
15
|
+
export abstract class BaseError extends Error {
|
|
16
|
+
/**
|
|
17
|
+
* Error level. Defines which level the error should be logged/reported
|
|
18
|
+
* @default 'error'
|
|
19
|
+
*/
|
|
20
|
+
level: ErrorLevel;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Whether the error should be reported to Sentry.
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
readonly shouldReport: boolean;
|
|
27
|
+
|
|
28
|
+
readonly description: string | null | undefined;
|
|
29
|
+
|
|
30
|
+
readonly tags: ErrorTags;
|
|
31
|
+
|
|
32
|
+
readonly extra?: Event['extra'];
|
|
33
|
+
|
|
34
|
+
readonly packageName?: string;
|
|
35
|
+
|
|
36
|
+
readonly cause?: unknown;
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
message: string,
|
|
40
|
+
{
|
|
41
|
+
level = 'error',
|
|
42
|
+
description,
|
|
43
|
+
shouldReport,
|
|
44
|
+
tags = {},
|
|
45
|
+
extra,
|
|
46
|
+
...rest
|
|
47
|
+
}: BaseErrorOptions = {},
|
|
48
|
+
) {
|
|
49
|
+
super(message);
|
|
50
|
+
|
|
51
|
+
if (rest.cause !== undefined) {
|
|
52
|
+
this.cause = rest.cause;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.level = level;
|
|
56
|
+
this.shouldReport = shouldReport ?? (level === 'error' || level === 'fatal');
|
|
57
|
+
this.description = description;
|
|
58
|
+
this.tags = tags;
|
|
59
|
+
this.extra = extra;
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const filePath = callsites()[2].getFileName() ?? '';
|
|
63
|
+
const match = /packages\/([^\/]+)\//.exec(filePath)?.[1];
|
|
64
|
+
|
|
65
|
+
if (match) this.tags['packageName'] = match;
|
|
66
|
+
} catch {}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BaseErrorOptions } from './base.error';
|
|
2
|
+
import { BaseError } from './base.error';
|
|
3
|
+
|
|
4
|
+
export type OperationalErrorOptions = Omit<BaseErrorOptions, 'level'> & {
|
|
5
|
+
level?: 'info' | 'warning' | 'error';
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Error that indicates a transient issue, like a network request failing,
|
|
10
|
+
* a database query timing out, etc. These are expected to happen, are
|
|
11
|
+
* transient by nature and should be handled gracefully.
|
|
12
|
+
*
|
|
13
|
+
* Default level: warning
|
|
14
|
+
*/
|
|
15
|
+
export class OperationalError extends BaseError {
|
|
16
|
+
constructor(message: string, opts: OperationalErrorOptions = {}) {
|
|
17
|
+
opts.level = opts.level ?? 'warning';
|
|
18
|
+
|
|
19
|
+
super(message, opts);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Event } from '@sentry/node';
|
|
2
|
+
|
|
3
|
+
export type ErrorLevel = 'fatal' | 'error' | 'warning' | 'info';
|
|
4
|
+
|
|
5
|
+
export type ErrorTags = NonNullable<Event['tags']>;
|
|
6
|
+
|
|
7
|
+
export type ReportingOptions = {
|
|
8
|
+
/** Whether the error should be reported to Sentry */
|
|
9
|
+
shouldReport?: boolean;
|
|
10
|
+
level?: ErrorLevel;
|
|
11
|
+
tags?: ErrorTags;
|
|
12
|
+
extra?: Event['extra'];
|
|
13
|
+
executionId?: string;
|
|
14
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { OperationalError, type OperationalErrorOptions } from './base/operational.error';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type ResultOk<T> = { ok: true; result: T };
|
|
2
|
+
export type ResultError<E> = { ok: false; error: E };
|
|
3
|
+
export type Result<T, E> = ResultOk<T> | ResultError<E>;
|
|
4
|
+
|
|
5
|
+
export const createResultOk = <T>(data: T): ResultOk<T> => ({
|
|
6
|
+
ok: true,
|
|
7
|
+
result: data,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const createResultError = <E = unknown>(error: E): ResultError<E> => ({
|
|
11
|
+
ok: false,
|
|
12
|
+
error,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export function ensureError(error: unknown): Error {
|
|
16
|
+
return error instanceof Error
|
|
17
|
+
? error
|
|
18
|
+
: new Error('Error that was not an instance of Error was thrown, cause: ' + error);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Executes the given function and converts it to a Result object.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const result = toResult(() => fs.writeFileSync('file.txt', 'Hello, World!'));
|
|
26
|
+
*/
|
|
27
|
+
export const toResult = <T, E extends Error = Error>(fn: () => T): Result<T, E> => {
|
|
28
|
+
try {
|
|
29
|
+
return createResultOk<T>(fn());
|
|
30
|
+
} catch (e) {
|
|
31
|
+
const error = ensureError(e);
|
|
32
|
+
return createResultError<E>(error as E);
|
|
33
|
+
}
|
|
34
|
+
};
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parse as esprimaParse,
|
|
3
|
+
Syntax,
|
|
4
|
+
type Node as SyntaxNode,
|
|
5
|
+
type ExpressionStatement,
|
|
6
|
+
} from 'esprima-next';
|
|
7
|
+
import type { IDisplayOptions, INodeProperties } from './Interfaces';
|
|
8
|
+
import { merge } from 'lodash';
|
|
9
|
+
|
|
10
|
+
export function updateDisplayOptions(
|
|
11
|
+
displayOptions: IDisplayOptions,
|
|
12
|
+
properties: INodeProperties[],
|
|
13
|
+
) {
|
|
14
|
+
return properties.map((nodeProperty) => {
|
|
15
|
+
return {
|
|
16
|
+
...nodeProperty,
|
|
17
|
+
displayOptions: merge({}, nodeProperty.displayOptions, displayOptions),
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export function randomInt(max: number): number;
|
|
22
|
+
export function randomInt(min: number, max: number): number;
|
|
23
|
+
/**
|
|
24
|
+
* Generates a random integer within a specified range.
|
|
25
|
+
*
|
|
26
|
+
* @param {number} min - The lower bound of the range. If `max` is not provided, this value is used as the upper bound and the lower bound is set to 0.
|
|
27
|
+
* @param {number} [max] - The upper bound of the range, not inclusive.
|
|
28
|
+
* @returns {number} A random integer within the specified range.
|
|
29
|
+
*/
|
|
30
|
+
export function randomInt(min: number, max?: number): number {
|
|
31
|
+
if (max === undefined) {
|
|
32
|
+
max = min;
|
|
33
|
+
min = 0;
|
|
34
|
+
}
|
|
35
|
+
return min + (crypto.getRandomValues(new Uint32Array(1))[0] % (max - min));
|
|
36
|
+
}
|
|
37
|
+
type MutuallyExclusive<T, U> =
|
|
38
|
+
| (T & { [k in Exclude<keyof U, keyof T>]?: never })
|
|
39
|
+
| (U & { [k in Exclude<keyof T, keyof U>]?: never });
|
|
40
|
+
|
|
41
|
+
type JSONParseOptions<T> = { acceptJSObject?: boolean } & MutuallyExclusive<
|
|
42
|
+
{ errorMessage?: string },
|
|
43
|
+
{ fallbackValue?: T }
|
|
44
|
+
>;
|
|
45
|
+
|
|
46
|
+
export const jsonParse = <T>(jsonString: string, options?: JSONParseOptions<T>): T => {
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(jsonString) as T;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (options?.acceptJSObject) {
|
|
51
|
+
try {
|
|
52
|
+
const jsonStringCleaned = parseJSObject(jsonString);
|
|
53
|
+
return jsonStringCleaned as T;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
// Ignore this error and return the original error or the fallback value
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (options?.fallbackValue !== undefined) {
|
|
59
|
+
if (options.fallbackValue instanceof Function) {
|
|
60
|
+
return options.fallbackValue();
|
|
61
|
+
}
|
|
62
|
+
return options.fallbackValue;
|
|
63
|
+
} else if (options?.errorMessage) {
|
|
64
|
+
// TODO REFACTOR: Use ApplicationError from offical @fsai-flow/workflow repo
|
|
65
|
+
throw new Error(options.errorMessage);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
type JSONStringifyOptions = {
|
|
73
|
+
replaceCircularRefs?: boolean;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const replaceCircularReferences = <T>(value: T, knownObjects = new WeakSet()): T => {
|
|
77
|
+
if (typeof value !== 'object' || value === null || value instanceof RegExp) return value;
|
|
78
|
+
if ('toJSON' in value && typeof value.toJSON === 'function') return value.toJSON() as T;
|
|
79
|
+
if (knownObjects.has(value)) return '[Circular Reference]' as T;
|
|
80
|
+
knownObjects.add(value);
|
|
81
|
+
const copy = (Array.isArray(value) ? [] : {}) as T;
|
|
82
|
+
for (const key in value) {
|
|
83
|
+
copy[key] = replaceCircularReferences(value[key], knownObjects);
|
|
84
|
+
}
|
|
85
|
+
knownObjects.delete(value);
|
|
86
|
+
return copy;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const jsonStringify = (obj: unknown, options: JSONStringifyOptions = {}): string => {
|
|
90
|
+
return JSON.stringify(options?.replaceCircularRefs ? replaceCircularReferences(obj) : obj);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
function syntaxNodeToValue(expression?: SyntaxNode | null): unknown {
|
|
94
|
+
switch (expression?.type) {
|
|
95
|
+
case Syntax.ObjectExpression:
|
|
96
|
+
return Object.fromEntries(
|
|
97
|
+
expression.properties
|
|
98
|
+
.filter((prop) => prop.type === Syntax.Property)
|
|
99
|
+
.map(({ key, value }) => [syntaxNodeToValue(key), syntaxNodeToValue(value)]),
|
|
100
|
+
);
|
|
101
|
+
case Syntax.Identifier:
|
|
102
|
+
return expression.name;
|
|
103
|
+
case Syntax.Literal:
|
|
104
|
+
return expression.value;
|
|
105
|
+
case Syntax.ArrayExpression:
|
|
106
|
+
return expression.elements.map((exp) => syntaxNodeToValue(exp));
|
|
107
|
+
default:
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Parse any JavaScript ObjectExpression, including:
|
|
114
|
+
* - single quoted keys
|
|
115
|
+
* - unquoted keys
|
|
116
|
+
*/
|
|
117
|
+
function parseJSObject(objectAsString: string): object {
|
|
118
|
+
const jsExpression = esprimaParse(`(${objectAsString})`).body.find(
|
|
119
|
+
(node): node is ExpressionStatement =>
|
|
120
|
+
node.type === Syntax.ExpressionStatement && node.expression.type === Syntax.ObjectExpression,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
return syntaxNodeToValue(jsExpression?.expression) as object;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Checks if a value is an object with a specific key and provides a type guard for the key.
|
|
128
|
+
*/
|
|
129
|
+
export function hasKey<T extends PropertyKey>(value: unknown, key: T): value is Record<T, unknown> {
|
|
130
|
+
return value !== null && typeof value === 'object' && value.hasOwnProperty(key);
|
|
131
|
+
}
|
|
132
|
+
|