@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.
Files changed (113) hide show
  1. package/.eslintrc.json +33 -0
  2. package/README.md +11 -0
  3. package/dist/README.md +11 -0
  4. package/dist/package.json +42 -0
  5. package/dist/src/index.d.ts +21 -0
  6. package/dist/src/index.js +33 -0
  7. package/dist/src/index.js.map +1 -0
  8. package/dist/src/lib/Constants.d.ts +68 -0
  9. package/dist/src/lib/Constants.js +106 -0
  10. package/dist/src/lib/Constants.js.map +1 -0
  11. package/dist/src/lib/DeferredPromise.d.ts +6 -0
  12. package/dist/src/lib/DeferredPromise.js +11 -0
  13. package/dist/src/lib/DeferredPromise.js.map +1 -0
  14. package/dist/src/lib/Expression.d.ts +65 -0
  15. package/dist/src/lib/Expression.js +215 -0
  16. package/dist/src/lib/Expression.js.map +1 -0
  17. package/dist/src/lib/Interfaces.d.ts +1569 -0
  18. package/dist/src/lib/Interfaces.js +44 -0
  19. package/dist/src/lib/Interfaces.js.map +1 -0
  20. package/dist/src/lib/LoggerProxy.d.ts +9 -0
  21. package/dist/src/lib/LoggerProxy.js +40 -0
  22. package/dist/src/lib/LoggerProxy.js.map +1 -0
  23. package/dist/src/lib/MetadataUtils.d.ts +4 -0
  24. package/dist/src/lib/MetadataUtils.js +27 -0
  25. package/dist/src/lib/MetadataUtils.js.map +1 -0
  26. package/dist/src/lib/NodeErrors.d.ts +82 -0
  27. package/dist/src/lib/NodeErrors.js +289 -0
  28. package/dist/src/lib/NodeErrors.js.map +1 -0
  29. package/dist/src/lib/NodeHelpers.d.ts +198 -0
  30. package/dist/src/lib/NodeHelpers.js +1348 -0
  31. package/dist/src/lib/NodeHelpers.js.map +1 -0
  32. package/dist/src/lib/ObservableObject.d.ts +5 -0
  33. package/dist/src/lib/ObservableObject.js +61 -0
  34. package/dist/src/lib/ObservableObject.js.map +1 -0
  35. package/dist/src/lib/RoutingNode.d.ts +18 -0
  36. package/dist/src/lib/RoutingNode.js +508 -0
  37. package/dist/src/lib/RoutingNode.js.map +1 -0
  38. package/dist/src/lib/TelemetryHelpers.d.ts +3 -0
  39. package/dist/src/lib/TelemetryHelpers.js +69 -0
  40. package/dist/src/lib/TelemetryHelpers.js.map +1 -0
  41. package/dist/src/lib/TypeValidation.d.ts +21 -0
  42. package/dist/src/lib/TypeValidation.js +385 -0
  43. package/dist/src/lib/TypeValidation.js.map +1 -0
  44. package/dist/src/lib/VersionedNodeType.d.ts +9 -0
  45. package/dist/src/lib/VersionedNodeType.js +26 -0
  46. package/dist/src/lib/VersionedNodeType.js.map +1 -0
  47. package/dist/src/lib/Workflow.d.ts +248 -0
  48. package/dist/src/lib/Workflow.js +901 -0
  49. package/dist/src/lib/Workflow.js.map +1 -0
  50. package/dist/src/lib/WorkflowDataProxy.d.ts +87 -0
  51. package/dist/src/lib/WorkflowDataProxy.js +556 -0
  52. package/dist/src/lib/WorkflowDataProxy.js.map +1 -0
  53. package/dist/src/lib/WorkflowErrors.d.ts +9 -0
  54. package/dist/src/lib/WorkflowErrors.js +18 -0
  55. package/dist/src/lib/WorkflowErrors.js.map +1 -0
  56. package/dist/src/lib/WorkflowHooks.d.ts +11 -0
  57. package/dist/src/lib/WorkflowHooks.js +34 -0
  58. package/dist/src/lib/WorkflowHooks.js.map +1 -0
  59. package/dist/src/lib/errors/base/base.error.d.ts +30 -0
  60. package/dist/src/lib/errors/base/base.error.js +45 -0
  61. package/dist/src/lib/errors/base/base.error.js.map +1 -0
  62. package/dist/src/lib/errors/base/operational.error.d.ts +15 -0
  63. package/dist/src/lib/errors/base/operational.error.js +19 -0
  64. package/dist/src/lib/errors/base/operational.error.js.map +1 -0
  65. package/dist/src/lib/errors/error.types.d.ts +11 -0
  66. package/dist/src/lib/errors/error.types.js +3 -0
  67. package/dist/src/lib/errors/error.types.js.map +1 -0
  68. package/dist/src/lib/errors/index.d.ts +1 -0
  69. package/dist/src/lib/errors/index.js +6 -0
  70. package/dist/src/lib/errors/index.js.map +1 -0
  71. package/dist/src/lib/result.d.ts +19 -0
  72. package/dist/src/lib/result.js +36 -0
  73. package/dist/src/lib/result.js.map +1 -0
  74. package/dist/src/lib/utils.d.ts +50 -0
  75. package/dist/src/lib/utils.js +110 -0
  76. package/dist/src/lib/utils.js.map +1 -0
  77. package/eslint.config.js +19 -0
  78. package/jest.config.ts +10 -0
  79. package/package.json +40 -0
  80. package/project.json +19 -0
  81. package/src/index.ts +33 -0
  82. package/src/lib/Constants.ts +124 -0
  83. package/src/lib/DeferredPromise.ts +14 -0
  84. package/src/lib/Expression.ts +375 -0
  85. package/src/lib/Interfaces.ts +2229 -0
  86. package/src/lib/LoggerProxy.ts +43 -0
  87. package/src/lib/MetadataUtils.ts +34 -0
  88. package/src/lib/NodeErrors.ts +332 -0
  89. package/src/lib/NodeHelpers.ts +1666 -0
  90. package/src/lib/ObservableObject.ts +77 -0
  91. package/src/lib/RoutingNode.ts +862 -0
  92. package/src/lib/TelemetryHelpers.ts +86 -0
  93. package/src/lib/TypeValidation.ts +431 -0
  94. package/src/lib/VersionedNodeType.ts +30 -0
  95. package/src/lib/Workflow.ts +1266 -0
  96. package/src/lib/WorkflowDataProxy.ts +708 -0
  97. package/src/lib/WorkflowErrors.ts +18 -0
  98. package/src/lib/WorkflowHooks.ts +51 -0
  99. package/src/lib/errors/base/base.error.ts +68 -0
  100. package/src/lib/errors/base/operational.error.ts +21 -0
  101. package/src/lib/errors/error.types.ts +14 -0
  102. package/src/lib/errors/index.ts +1 -0
  103. package/src/lib/result.ts +34 -0
  104. package/src/lib/utils.ts +132 -0
  105. package/tests/Helpers.ts +667 -0
  106. package/tests/NodeHelpers.test.ts +3053 -0
  107. package/tests/ObservableObject.test.ts +171 -0
  108. package/tests/RoutingNode.test.ts +1680 -0
  109. package/tests/Workflow.test.ts +1284 -0
  110. package/tests/WorkflowDataProxy.test.ts +199 -0
  111. package/tsconfig.json +27 -0
  112. package/tsconfig.lib.json +11 -0
  113. 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
+ };
@@ -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
+