@primero.ai/temporal-graph-tools 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +203 -0
  2. package/dist/examples/bundle/activities.d.ts +34 -0
  3. package/dist/examples/bundle/activities.d.ts.map +1 -0
  4. package/dist/examples/bundle/activities.js +35 -0
  5. package/dist/examples/bundle/workflows.d.ts +3 -0
  6. package/dist/examples/bundle/workflows.d.ts.map +1 -0
  7. package/dist/examples/bundle/workflows.js +14 -0
  8. package/dist/examples/trigger-workflows.d.ts +2 -0
  9. package/dist/examples/trigger-workflows.d.ts.map +1 -0
  10. package/dist/examples/trigger-workflows.js +43 -0
  11. package/dist/examples/worker.d.ts +3 -0
  12. package/dist/examples/worker.d.ts.map +1 -0
  13. package/dist/examples/worker.js +29 -0
  14. package/dist/src/bundler.d.ts +25 -0
  15. package/dist/src/bundler.d.ts.map +1 -0
  16. package/dist/src/bundler.js +208 -0
  17. package/dist/src/index.d.ts +6 -0
  18. package/dist/src/index.d.ts.map +1 -0
  19. package/dist/src/index.js +5 -0
  20. package/dist/src/types.d.ts +45 -0
  21. package/dist/src/types.d.ts.map +1 -0
  22. package/dist/src/types.js +64 -0
  23. package/dist/src/utils/deep-equal.d.ts +2 -0
  24. package/dist/src/utils/deep-equal.d.ts.map +1 -0
  25. package/dist/src/utils/deep-equal.js +54 -0
  26. package/dist/src/workflow/builder.d.ts +27 -0
  27. package/dist/src/workflow/builder.d.ts.map +1 -0
  28. package/dist/src/workflow/builder.js +230 -0
  29. package/dist/src/workflow/collection.d.ts +3 -0
  30. package/dist/src/workflow/collection.d.ts.map +1 -0
  31. package/dist/src/workflow/collection.js +39 -0
  32. package/dist/src/workflow-bundler.d.ts +6 -0
  33. package/dist/src/workflow-bundler.d.ts.map +1 -0
  34. package/dist/src/workflow-bundler.js +163 -0
  35. package/package.json +96 -0
@@ -0,0 +1,45 @@
1
+ import type { ActivityOptions } from '@temporalio/workflow';
2
+ export type CreateActivity<TInput = unknown, TOutput = unknown> = (input: TInput) => TOutput | Promise<TOutput>;
3
+ export type ActivityConfig<Id extends string = string> = ActivityOptions & {
4
+ id?: Id;
5
+ };
6
+ export declare const ACTIVITY_SOURCE_SYMBOL: unique symbol;
7
+ export type ConfiguredActivityReference<TInput = unknown, TOutput = unknown, Id extends string = string> = {
8
+ activity: CreateActivity<TInput, TOutput>;
9
+ config: ActivityConfig<Id>;
10
+ };
11
+ export type ActivityReference<TInput = unknown, TOutput = unknown, Id extends string = string> = CreateActivity<TInput, TOutput> | ConfiguredActivityReference<TInput, TOutput, Id>;
12
+ export declare function getActivitySourceFile(activity: CreateActivity<unknown, unknown>): string | undefined;
13
+ export type ActivityBundle = {
14
+ implementation: CreateActivity<unknown, unknown>;
15
+ config?: ActivityConfig;
16
+ name?: string;
17
+ sourceFile?: string;
18
+ };
19
+ export type WorkflowSourceArtifact = {
20
+ workflowName: string;
21
+ workflowSource: string;
22
+ };
23
+ export type WorkflowBuildResult = WorkflowSourceArtifact & {
24
+ activities: Record<string, ActivityBundle>;
25
+ };
26
+ export type TemporalWorkflowBuildOptions = {
27
+ workflowName: string;
28
+ activitiesImportPath?: string;
29
+ proxyOptions?: ActivityOptions | string;
30
+ };
31
+ export declare function createActivity<TInput, TOutput>(activity: CreateActivity<TInput, TOutput>): CreateActivity<TInput, TOutput>;
32
+ export declare function createActivity<TInput, TOutput, Id extends string>(activity: CreateActivity<TInput, TOutput>, config: ActivityConfig<Id>): ConfiguredActivityReference<TInput, TOutput, Id>;
33
+ type TupleToObject<Refs> = Refs extends readonly [infer Head, ...infer Tail] ? (Head extends ConfiguredActivityReference<never, infer Output, infer Id> ? {
34
+ [K in Id]: Output;
35
+ } : Record<never, never>) & TupleToObject<Tail extends readonly ConfiguredActivityReference<never, unknown, string>[] ? Tail : []> : object;
36
+ type Simplify<T> = {
37
+ [K in keyof T]: T[K];
38
+ };
39
+ export type ParallelOutputs<TInput, Refs extends readonly ConfiguredActivityReference<TInput, unknown, string>[]> = Simplify<TupleToObject<Refs>>;
40
+ export type WorkflowCollectionBuildResult = {
41
+ workflows: WorkflowSourceArtifact[];
42
+ activities: Record<string, ActivityBundle>;
43
+ };
44
+ export {};
45
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ const ACTIVITY_SELF_SEGMENT = '/temporal-graph-tools/';
3
+ export const ACTIVITY_SOURCE_SYMBOL = Symbol.for('@primero.ai/temporal-graph-tools/activity-source');
4
+ function captureActivitySource() {
5
+ // eslint-disable-next-line @typescript-eslint/unbound-method
6
+ const originalPrepare = Error.prepareStackTrace;
7
+ try {
8
+ Error.prepareStackTrace = (_, structuredStackTrace) => structuredStackTrace;
9
+ const error = new Error();
10
+ Error.captureStackTrace(error, createActivity);
11
+ const callsites = error.stack;
12
+ if (!callsites) {
13
+ return undefined;
14
+ }
15
+ for (const site of callsites) {
16
+ const fileName = typeof site.getFileName === 'function' ? site.getFileName.call(site) : undefined;
17
+ const scriptName = typeof site.getScriptNameOrSourceURL === 'function'
18
+ ? site.getScriptNameOrSourceURL.call(site)
19
+ : undefined;
20
+ const resolved = fileName !== null && fileName !== void 0 ? fileName : scriptName;
21
+ if (!resolved) {
22
+ continue;
23
+ }
24
+ const normalized = resolved.startsWith('file:') ? fileURLToPath(resolved) : resolved;
25
+ const comparable = normalized.replace(/\\/g, '/').replace(/^file:\/\//, '');
26
+ if (comparable.includes(ACTIVITY_SELF_SEGMENT)) {
27
+ continue;
28
+ }
29
+ return normalized;
30
+ }
31
+ return undefined;
32
+ }
33
+ finally {
34
+ Error.prepareStackTrace = originalPrepare;
35
+ }
36
+ }
37
+ function recordActivityMetadata(activity) {
38
+ if (Reflect.get(activity, ACTIVITY_SOURCE_SYMBOL)) {
39
+ return;
40
+ }
41
+ const metadata = {
42
+ sourceFile: captureActivitySource(),
43
+ };
44
+ Reflect.defineProperty(activity, ACTIVITY_SOURCE_SYMBOL, {
45
+ value: metadata,
46
+ configurable: false,
47
+ enumerable: false,
48
+ writable: false,
49
+ });
50
+ }
51
+ export function getActivitySourceFile(activity) {
52
+ const metadata = Reflect.get(activity, ACTIVITY_SOURCE_SYMBOL);
53
+ return metadata === null || metadata === void 0 ? void 0 : metadata.sourceFile;
54
+ }
55
+ export function createActivity(activity, config) {
56
+ recordActivityMetadata(activity);
57
+ if (!config) {
58
+ return activity;
59
+ }
60
+ return {
61
+ activity,
62
+ config,
63
+ };
64
+ }
@@ -0,0 +1,2 @@
1
+ export declare function deepEqual(left: unknown, right: unknown): boolean;
2
+ //# sourceMappingURL=deep-equal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deep-equal.d.ts","sourceRoot":"","sources":["../../../src/utils/deep-equal.ts"],"names":[],"mappings":"AAWA,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CA2DhE"}
@@ -0,0 +1,54 @@
1
+ function isPlainObject(value) {
2
+ if (value === null || typeof value !== 'object') {
3
+ return false;
4
+ }
5
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
6
+ const prototype = Object.getPrototypeOf(value);
7
+ return prototype === Object.prototype || prototype === null;
8
+ }
9
+ export function deepEqual(left, right) {
10
+ if (Object.is(left, right)) {
11
+ return true;
12
+ }
13
+ if (left === null || right === null) {
14
+ return left === right;
15
+ }
16
+ if (left instanceof Date && right instanceof Date) {
17
+ return left.getTime() === right.getTime();
18
+ }
19
+ if (left instanceof RegExp && right instanceof RegExp) {
20
+ return left.source === right.source && left.flags === right.flags;
21
+ }
22
+ if (Array.isArray(left) || Array.isArray(right)) {
23
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
24
+ return false;
25
+ }
26
+ for (let index = 0; index < left.length; index += 1) {
27
+ if (!deepEqual(left[index], right[index])) {
28
+ return false;
29
+ }
30
+ }
31
+ return true;
32
+ }
33
+ if (isPlainObject(left) || isPlainObject(right)) {
34
+ if (!isPlainObject(left) || !isPlainObject(right)) {
35
+ return false;
36
+ }
37
+ const leftEntries = Object.entries(left).filter(([, value]) => value !== undefined);
38
+ const rightEntries = Object.entries(right).filter(([, value]) => value !== undefined);
39
+ if (leftEntries.length !== rightEntries.length) {
40
+ return false;
41
+ }
42
+ const sortedLeft = leftEntries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
43
+ const sortedRight = rightEntries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
44
+ for (let index = 0; index < sortedLeft.length; index += 1) {
45
+ const [leftKey, leftValue] = sortedLeft[index];
46
+ const [rightKey, rightValue] = sortedRight[index];
47
+ if (leftKey !== rightKey || !deepEqual(leftValue, rightValue)) {
48
+ return false;
49
+ }
50
+ }
51
+ return true;
52
+ }
53
+ return false;
54
+ }
@@ -0,0 +1,27 @@
1
+ import type { ActivityConfig, ActivityReference, ConfiguredActivityReference, ParallelOutputs, TemporalWorkflowBuildOptions, WorkflowBuildResult } from '../types.js';
2
+ export declare class WorkflowBuilder<TCurrentOutput> {
3
+ private readonly activityBundles;
4
+ private readonly plan;
5
+ private readonly baseOptions;
6
+ private started;
7
+ private autoIncrement;
8
+ constructor(options: TemporalWorkflowBuildOptions);
9
+ then<TNextOutput>(reference: ActivityReference<TCurrentOutput, TNextOutput>, config?: ActivityConfig): WorkflowBuilder<TNextOutput>;
10
+ parallel<const References extends readonly ConfiguredActivityReference<TCurrentOutput, unknown, string>[]>(references: References): WorkflowBuilder<ParallelOutputs<TCurrentOutput, References>>;
11
+ commit(): WorkflowBuildResult;
12
+ private normalizeActivityReference;
13
+ private registerActivity;
14
+ private createActivityBundle;
15
+ private prepareConfig;
16
+ private generateTemporalWorkflowSource;
17
+ private formatProxyOptions;
18
+ private ensureUniqueIdentifier;
19
+ private formatPropertyKey;
20
+ private assignActivityKey;
21
+ private normalizeActivityKey;
22
+ private activityBundlesEqual;
23
+ private deriveActivityKey;
24
+ private assertStarted;
25
+ }
26
+ export declare const createWorkflowBuilder: <TInitialInput>(options: TemporalWorkflowBuildOptions) => WorkflowBuilder<TInitialInput>;
27
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../src/workflow/builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,cAAc,EACd,iBAAiB,EACjB,2BAA2B,EAE3B,eAAe,EACf,4BAA4B,EAC5B,mBAAmB,EACpB,MAAM,aAAa,CAAA;AAepB,qBAAa,eAAe,CAAC,cAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqC;IAErE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IAEnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8B;IAE1D,OAAO,CAAC,OAAO,CAAQ;IAEvB,OAAO,CAAC,aAAa,CAAI;gBAEb,OAAO,EAAE,4BAA4B;IAejD,IAAI,CAAC,WAAW,EACd,SAAS,EAAE,iBAAiB,CAAC,cAAc,EAAE,WAAW,CAAC,EACzD,MAAM,CAAC,EAAE,cAAc,GACtB,eAAe,CAAC,WAAW,CAAC;IAU/B,QAAQ,CACN,KAAK,CAAC,UAAU,SAAS,SAAS,2BAA2B,CAC3D,cAAc,EACd,OAAO,EACP,MAAM,CACP,EAAE,EACH,UAAU,EAAE,UAAU,GAAG,eAAe,CAAC,eAAe,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAiBvF,MAAM,IAAI,mBAAmB;IAgB7B,OAAO,CAAC,0BAA0B;IAiBlC,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,8BAA8B;IAwFtC,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,sBAAsB;IAqC9B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,iBAAiB;IAkCzB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,aAAa;CAKtB;AAED,eAAO,MAAM,qBAAqB,GAAI,aAAa,EACjD,SAAS,4BAA4B,KACpC,eAAe,CAAC,aAAa,CAE/B,CAAA"}
@@ -0,0 +1,230 @@
1
+ import { getActivitySourceFile } from '../types.js';
2
+ import { deepEqual } from '../utils/deep-equal.js';
3
+ export class WorkflowBuilder {
4
+ constructor(options) {
5
+ this.activityBundles = {};
6
+ this.plan = [];
7
+ this.started = false;
8
+ this.autoIncrement = 0;
9
+ const workflowName = options === null || options === void 0 ? void 0 : options.workflowName;
10
+ if (typeof workflowName !== 'string' || workflowName.trim().length === 0) {
11
+ throw new Error('createWorkflowBuilder requires options.workflowName to be a non-empty string.');
12
+ }
13
+ this.baseOptions = {
14
+ ...options,
15
+ workflowName: workflowName.trim(),
16
+ };
17
+ }
18
+ then(reference, config) {
19
+ const entry = this.normalizeActivityReference(reference, config);
20
+ const normalized = this.registerActivity(entry);
21
+ this.plan.push({ type: 'step', step: normalized });
22
+ this.started = true;
23
+ return this;
24
+ }
25
+ parallel(references) {
26
+ this.assertStarted('parallel');
27
+ if (!Array.isArray(references) || references.length === 0) {
28
+ throw new Error('parallel() requires at least one activity reference.');
29
+ }
30
+ const normalized = references.map((ref) => this.registerActivity(this.normalizeActivityReference(ref)));
31
+ this.plan.push({ type: 'parallel', steps: normalized });
32
+ this.started = true;
33
+ return this;
34
+ }
35
+ commit() {
36
+ if (!this.started) {
37
+ throw new Error('Cannot build a workflow without any steps. Call then() before build().');
38
+ }
39
+ const { source: workflowSource, workflowName } = this.generateTemporalWorkflowSource({
40
+ ...this.baseOptions,
41
+ });
42
+ return {
43
+ workflowName,
44
+ activities: { ...this.activityBundles },
45
+ workflowSource,
46
+ };
47
+ }
48
+ normalizeActivityReference(reference, inlineConfig) {
49
+ var _a;
50
+ if (typeof reference === 'function') {
51
+ return {
52
+ activity: reference,
53
+ config: { ...(inlineConfig !== null && inlineConfig !== void 0 ? inlineConfig : {}) },
54
+ };
55
+ }
56
+ return {
57
+ activity: reference.activity,
58
+ config: { ...((_a = reference.config) !== null && _a !== void 0 ? _a : {}), ...(inlineConfig !== null && inlineConfig !== void 0 ? inlineConfig : {}) },
59
+ };
60
+ }
61
+ registerActivity(entry) {
62
+ var _a;
63
+ const baseKey = (_a = entry.config.id) !== null && _a !== void 0 ? _a : this.deriveActivityKey(entry.activity);
64
+ const key = this.assignActivityKey(baseKey, entry);
65
+ return { key };
66
+ }
67
+ createActivityBundle(entry, key) {
68
+ var _a;
69
+ const config = this.prepareConfig(entry.config);
70
+ const name = (_a = entry.activity.name) === null || _a === void 0 ? void 0 : _a.trim();
71
+ const sourceFile = getActivitySourceFile(entry.activity);
72
+ return {
73
+ implementation: entry.activity,
74
+ ...(name ? { name } : { name: key }),
75
+ ...(config ? { config } : {}),
76
+ ...(sourceFile ? { sourceFile } : {}),
77
+ };
78
+ }
79
+ prepareConfig(config) {
80
+ const entries = Object.entries(config).filter(([, value]) => value !== undefined);
81
+ if (entries.length === 0) {
82
+ return undefined;
83
+ }
84
+ return Object.fromEntries(entries);
85
+ }
86
+ generateTemporalWorkflowSource(options) {
87
+ var _a;
88
+ const topLevelIdentifiers = new Set();
89
+ const workflowName = this.ensureUniqueIdentifier(options.workflowName, options.workflowName, topLevelIdentifiers);
90
+ const activitiesIdentifier = this.ensureUniqueIdentifier('activities', 'activities', topLevelIdentifiers);
91
+ const activitiesImportPath = (_a = options.activitiesImportPath) !== null && _a !== void 0 ? _a : './activities';
92
+ const proxyOptionsLiteral = this.formatProxyOptions(options.proxyOptions);
93
+ const lines = [];
94
+ lines.push(`import { proxyActivities } from '@temporalio/workflow'`);
95
+ lines.push(`import type { Activities } from '${activitiesImportPath}'`);
96
+ lines.push('');
97
+ lines.push(`const ${activitiesIdentifier} = proxyActivities<Activities>(${proxyOptionsLiteral})`);
98
+ lines.push('');
99
+ lines.push(`export async function ${workflowName}(input: unknown): Promise<unknown> {`);
100
+ let currentValue = 'input';
101
+ const localIdentifiers = new Set(['input']);
102
+ this.plan.forEach((stage, stageIndex) => {
103
+ if (stage.type === 'step') {
104
+ const resultVar = this.ensureUniqueIdentifier(undefined, `step${stageIndex}`, localIdentifiers);
105
+ lines.push(` const ${resultVar} = await ${activitiesIdentifier}.${stage.step.key}(${currentValue});`);
106
+ lines.push('');
107
+ currentValue = resultVar;
108
+ return;
109
+ }
110
+ const individualVars = stage.steps.map((step, index) => this.ensureUniqueIdentifier(undefined, `parallel${stageIndex}_${index}`, localIdentifiers));
111
+ lines.push(` const [${individualVars.join(', ')}] = await Promise.all([`);
112
+ stage.steps.forEach((step, index) => {
113
+ const suffix = index === stage.steps.length - 1 ? '' : ',';
114
+ lines.push(` ${activitiesIdentifier}.${step.key}(${currentValue})${suffix}`);
115
+ });
116
+ lines.push(' ])');
117
+ const aggregateVar = this.ensureUniqueIdentifier(undefined, `parallel${stageIndex}`, localIdentifiers);
118
+ lines.push(` const ${aggregateVar} = {`);
119
+ stage.steps.forEach((step, index) => {
120
+ const propertyKey = this.formatPropertyKey(step.key);
121
+ lines.push(` ${propertyKey}: ${individualVars[index]},`);
122
+ });
123
+ lines.push(' }');
124
+ lines.push('');
125
+ currentValue = aggregateVar;
126
+ });
127
+ lines.push(` return ${currentValue};`);
128
+ lines.push('}');
129
+ return {
130
+ workflowName,
131
+ source: lines.join('\n'),
132
+ };
133
+ }
134
+ formatProxyOptions(options) {
135
+ if (typeof options === 'string' && options.trim().length > 0) {
136
+ return options.trim();
137
+ }
138
+ if (options === undefined) {
139
+ return `{
140
+ startToCloseTimeout: '1 minute',
141
+ }`;
142
+ }
143
+ return JSON.stringify(options, null, 2);
144
+ }
145
+ ensureUniqueIdentifier(raw, fallback, used) {
146
+ const fallbackValue = fallback || 'value';
147
+ const base = raw && raw.trim().length > 0 ? raw.trim() : fallbackValue;
148
+ let sanitized = base.replace(/[^A-Za-z0-9_]/g, '_');
149
+ if (!/^[A-Za-z_]/.test(sanitized)) {
150
+ sanitized = `_${sanitized}`;
151
+ }
152
+ if (sanitized.length === 0 || /^_+$/.test(sanitized)) {
153
+ sanitized = fallbackValue.replace(/[^A-Za-z0-9_]/g, '_');
154
+ if (!/^[A-Za-z_]/.test(sanitized)) {
155
+ sanitized = `_${sanitized}`;
156
+ }
157
+ if (sanitized.length === 0 || /^_+$/.test(sanitized)) {
158
+ sanitized = 'value';
159
+ }
160
+ }
161
+ let candidate = sanitized;
162
+ let counter = 1;
163
+ while (used.has(candidate)) {
164
+ candidate = `${sanitized}_${counter++}`;
165
+ }
166
+ used.add(candidate);
167
+ return candidate;
168
+ }
169
+ formatPropertyKey(key) {
170
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
171
+ return key;
172
+ }
173
+ return `'${key.replace(/'/g, "\\'")}'`;
174
+ }
175
+ assignActivityKey(candidate, entry) {
176
+ const normalized = this.normalizeActivityKey(candidate);
177
+ const existing = this.activityBundles[normalized];
178
+ const initialBundle = this.createActivityBundle(entry, normalized);
179
+ if (!existing) {
180
+ this.activityBundles[normalized] = initialBundle;
181
+ return normalized;
182
+ }
183
+ if (this.activityBundlesEqual(existing, initialBundle)) {
184
+ return normalized;
185
+ }
186
+ let counter = 1;
187
+ while (true) {
188
+ const nextKey = `${normalized}_${counter++}`;
189
+ const nextBundle = this.createActivityBundle(entry, nextKey);
190
+ const existingBundle = this.activityBundles[nextKey];
191
+ if (!existingBundle) {
192
+ this.activityBundles[nextKey] = nextBundle;
193
+ return nextKey;
194
+ }
195
+ if (this.activityBundlesEqual(existingBundle, nextBundle)) {
196
+ return nextKey;
197
+ }
198
+ }
199
+ }
200
+ normalizeActivityKey(candidate) {
201
+ const key = candidate.trim();
202
+ if (key.length === 0) {
203
+ throw new Error('Activity keys must be non-empty strings.');
204
+ }
205
+ return key;
206
+ }
207
+ activityBundlesEqual(existing, candidate) {
208
+ var _a, _b, _c, _d;
209
+ const configsEqual = deepEqual((_a = existing.config) !== null && _a !== void 0 ? _a : undefined, (_b = candidate.config) !== null && _b !== void 0 ? _b : undefined);
210
+ const namesEqual = ((_c = existing.name) !== null && _c !== void 0 ? _c : null) === ((_d = candidate.name) !== null && _d !== void 0 ? _d : null);
211
+ return existing.implementation === candidate.implementation && configsEqual && namesEqual;
212
+ }
213
+ deriveActivityKey(activity) {
214
+ var _a;
215
+ const candidate = (_a = activity.name) === null || _a === void 0 ? void 0 : _a.trim();
216
+ if (candidate) {
217
+ return candidate;
218
+ }
219
+ this.autoIncrement += 1;
220
+ return `step_${this.autoIncrement}`;
221
+ }
222
+ assertStarted(method) {
223
+ if (!this.started) {
224
+ throw new Error(`Cannot call ${method}() before defining the first step.`);
225
+ }
226
+ }
227
+ }
228
+ export const createWorkflowBuilder = (options) => {
229
+ return new WorkflowBuilder(options);
230
+ };
@@ -0,0 +1,3 @@
1
+ import type { WorkflowBuildResult, WorkflowCollectionBuildResult } from '../types.js';
2
+ export declare function collectWorkflowBuildResults(results: readonly WorkflowBuildResult[]): WorkflowCollectionBuildResult;
3
+ //# sourceMappingURL=collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../../src/workflow/collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mBAAmB,EACnB,6BAA6B,EAE9B,MAAM,aAAa,CAAA;AAGpB,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,SAAS,mBAAmB,EAAE,GACtC,6BAA6B,CAmD/B"}
@@ -0,0 +1,39 @@
1
+ import { deepEqual } from '../utils/deep-equal.js';
2
+ export function collectWorkflowBuildResults(results) {
3
+ if (results.length === 0) {
4
+ throw new Error('collectWorkflowBuildResults requires at least one workflow build result.');
5
+ }
6
+ const workflows = [];
7
+ const activities = {};
8
+ const workflowNames = new Set();
9
+ results.forEach((result, index) => {
10
+ if (workflowNames.has(result.workflowName)) {
11
+ throw new Error(`Duplicate workflow name detected at index ${index}: '${result.workflowName}'. Workflow names must be unique.`);
12
+ }
13
+ workflowNames.add(result.workflowName);
14
+ workflows.push({
15
+ workflowName: result.workflowName,
16
+ workflowSource: result.workflowSource,
17
+ });
18
+ Object.entries(result.activities).forEach(([key, bundle]) => {
19
+ var _a, _b, _c, _d, _e, _f;
20
+ const existing = activities[key];
21
+ if (!existing) {
22
+ activities[key] = bundle;
23
+ return;
24
+ }
25
+ const configMatches = deepEqual((_a = existing.config) !== null && _a !== void 0 ? _a : undefined, (_b = bundle.config) !== null && _b !== void 0 ? _b : undefined);
26
+ const sourceMatches = ((_c = existing.sourceFile) !== null && _c !== void 0 ? _c : null) === ((_d = bundle.sourceFile) !== null && _d !== void 0 ? _d : null);
27
+ if (existing.implementation !== bundle.implementation ||
28
+ !configMatches ||
29
+ ((_e = existing.name) !== null && _e !== void 0 ? _e : null) !== ((_f = bundle.name) !== null && _f !== void 0 ? _f : null) ||
30
+ !sourceMatches) {
31
+ throw new Error(`Activity '${key}' is defined multiple times with conflicting implementations.`);
32
+ }
33
+ });
34
+ });
35
+ return {
36
+ workflows,
37
+ activities,
38
+ };
39
+ }
@@ -0,0 +1,6 @@
1
+ import type { WorkflowBuildResult, WorkflowSourceArtifact } from './types.js';
2
+ export type ActivityImplementations = Record<string, (...args: unknown[]) => unknown>;
3
+ export type ActivityBundles = WorkflowBuildResult['activities'];
4
+ export declare function instantiateActivities(bundles: ActivityBundles): Promise<ActivityImplementations>;
5
+ export declare function buildWorkflowBundleCode(workflow: string | WorkflowSourceArtifact | WorkflowSourceArtifact[], filename?: string): Promise<string>;
6
+ //# sourceMappingURL=workflow-bundler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-bundler.d.ts","sourceRoot":"","sources":["../../src/workflow-bundler.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAE7E,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAA;AAErF,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAA;AAE/D,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,uBAAuB,CAAC,CAQlC;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,GAAG,sBAAsB,GAAG,sBAAsB,EAAE,EACpE,QAAQ,SAAgB,GACvB,OAAO,CAAC,MAAM,CAAC,CAMjB"}
@@ -0,0 +1,163 @@
1
+ import * as realFS from 'node:fs';
2
+ import path, { basename } from 'node:path';
3
+ import { WorkflowCodeBundler, } from '@temporalio/worker/lib/workflow/bundler.js';
4
+ import { createFsFromVolume, Volume } from 'memfs';
5
+ import { Union } from 'unionfs';
6
+ export async function instantiateActivities(bundles) {
7
+ const implementations = {};
8
+ Object.entries(bundles).forEach(([key, bundle]) => {
9
+ implementations[key] = bundle.implementation;
10
+ });
11
+ return implementations;
12
+ }
13
+ export async function buildWorkflowBundleCode(workflow, filename = 'workflow.js') {
14
+ const workflows = normalizeWorkflows(workflow);
15
+ const bundler = new VirtualWorkflowCodeBundler(workflows);
16
+ const { code } = await bundler.createBundle();
17
+ return ensureInlineSourceMap(code, filename);
18
+ }
19
+ function normalizeWorkflows(workflow) {
20
+ if (Array.isArray(workflow)) {
21
+ if (workflow.length === 0) {
22
+ throw new Error('buildWorkflowBundleCode requires at least one workflow source.');
23
+ }
24
+ return workflow;
25
+ }
26
+ if (typeof workflow === 'string') {
27
+ return [
28
+ {
29
+ workflowName: 'workflow',
30
+ workflowSource: workflow,
31
+ },
32
+ ];
33
+ }
34
+ return [workflow];
35
+ }
36
+ function sanitizeFileName(candidate) {
37
+ const base = candidate.trim().replace(/[^A-Za-z0-9_.-]/g, '_');
38
+ if (base.length > 0 && !/^_+$/.test(base)) {
39
+ return `${base}.ts`;
40
+ }
41
+ return 'workflow.ts';
42
+ }
43
+ function ensureUniqueFileName(baseName, used) {
44
+ let candidate = baseName;
45
+ let counter = 1;
46
+ while (used.has(candidate)) {
47
+ const dotIndex = baseName.lastIndexOf('.');
48
+ const prefix = dotIndex > 0 ? baseName.slice(0, dotIndex) : baseName;
49
+ const extension = dotIndex > 0 ? baseName.slice(dotIndex) : '';
50
+ candidate = `${prefix}_${counter++}${extension}`;
51
+ }
52
+ used.add(candidate);
53
+ return candidate;
54
+ }
55
+ function stripExtension(fileName) {
56
+ const parsed = basename(fileName);
57
+ if (parsed.endsWith('.ts')) {
58
+ return parsed.slice(0, -3);
59
+ }
60
+ return parsed;
61
+ }
62
+ function ensureInlineSourceMap(code, filename) {
63
+ const marker = '//# sourceMappingURL=data:application/json;base64,';
64
+ if (!code.includes(marker)) {
65
+ const stubMap = {
66
+ version: 3,
67
+ file: filename,
68
+ sources: ['workflow.ts'],
69
+ sourcesContent: [code],
70
+ names: [],
71
+ mappings: '',
72
+ };
73
+ const base64 = Buffer.from(JSON.stringify(stubMap)).toString('base64');
74
+ return `${code}\n${marker}${base64}`;
75
+ }
76
+ const markerIndex = code.lastIndexOf(marker);
77
+ const base64 = code.slice(markerIndex + marker.length).trim();
78
+ const decoded = Buffer.from(base64, 'base64').toString('utf8');
79
+ const sourceMap = JSON.parse(decoded);
80
+ if (!sourceMap.file || sourceMap.file.length === 0) {
81
+ sourceMap.file = filename;
82
+ }
83
+ if (!Array.isArray(sourceMap.sources) || sourceMap.sources.length === 0) {
84
+ sourceMap.sources = [filename];
85
+ }
86
+ const updatedBase64 = Buffer.from(JSON.stringify(sourceMap)).toString('base64');
87
+ return code.slice(0, markerIndex + marker.length) + updatedBase64;
88
+ }
89
+ class VirtualWorkflowCodeBundler extends WorkflowCodeBundler {
90
+ constructor(workflows) {
91
+ const entry = path.join(VirtualWorkflowCodeBundler.virtualRoot, 'index.ts');
92
+ super({ workflowsPath: entry });
93
+ this.workflows = workflows;
94
+ this.virtualEntrypoint = entry;
95
+ }
96
+ async createBundle() {
97
+ const volume = new Volume();
98
+ const unionFs = new Union();
99
+ this.populateVirtualWorkflows(volume);
100
+ const readdir = Object.assign((...params) => {
101
+ const args = [...params];
102
+ const callbackCandidate = args.pop();
103
+ if (typeof callbackCandidate !== 'function') {
104
+ return realFS.readdir(...params);
105
+ }
106
+ const callback = callbackCandidate;
107
+ const wrappedCallback = (err, files) => {
108
+ if (err !== null) {
109
+ callback(err, files);
110
+ return;
111
+ }
112
+ if (!Array.isArray(files)) {
113
+ callback(null, files);
114
+ return;
115
+ }
116
+ if (!files.every((entry) => typeof entry === 'string')) {
117
+ callback(null, files);
118
+ return;
119
+ }
120
+ const typedFiles = files;
121
+ const filtered = typedFiles.filter((file) => /\.[jt]s$/.test(path.extname(file)) && !file.endsWith('.d.ts'));
122
+ callback(null, filtered);
123
+ };
124
+ const patchedArgs = [...args, wrappedCallback];
125
+ return realFS.readdir(...patchedArgs);
126
+ }, { __promisify__: realFS.readdir.__promisify__ });
127
+ const memoryFs = createFsFromVolume(volume);
128
+ const layeredFs = { ...realFS, readdir };
129
+ unionFs.use(memoryFs);
130
+ unionFs.use(layeredFs);
131
+ const distDir = '/dist';
132
+ const entrypointPath = this.makeEntrypointPath(unionFs, this.virtualEntrypoint);
133
+ this.genEntrypoint(volume, entrypointPath);
134
+ const bundleFilePath = await this.bundle(unionFs, memoryFs, entrypointPath, distDir);
135
+ let code = memoryFs.readFileSync(bundleFilePath, 'utf8');
136
+ code = code.replace('var __webpack_module_cache__ = {}', 'var __webpack_module_cache__ = globalThis.__webpack_module_cache__');
137
+ const sizeInMb = `${(code.length / (1024 * 1024)).toFixed(2)}MB`;
138
+ this.logger.info('Workflow bundle created', { size: sizeInMb });
139
+ return {
140
+ sourceMap: 'deprecated: this is no longer in use\n',
141
+ code,
142
+ };
143
+ }
144
+ populateVirtualWorkflows(volume) {
145
+ const usedFileNames = new Set();
146
+ const moduleSpecifiers = [];
147
+ const baseDir = VirtualWorkflowCodeBundler.virtualRoot;
148
+ this.workflows.forEach((entry, index) => {
149
+ var _a;
150
+ const fileName = this.workflows.length === 1
151
+ ? 'workflow.ts'
152
+ : ensureUniqueFileName(sanitizeFileName((_a = entry.workflowName) !== null && _a !== void 0 ? _a : `workflow_${index}`), usedFileNames);
153
+ const filePath = path.join(baseDir, fileName);
154
+ volume.mkdirSync(path.dirname(filePath), { recursive: true });
155
+ volume.writeFileSync(filePath, `${entry.workflowSource}\n`);
156
+ moduleSpecifiers.push(`./${stripExtension(fileName)}`);
157
+ });
158
+ const entrySource = moduleSpecifiers.map((specifier) => `export * from '${specifier}'`).join('\n') + '\n';
159
+ volume.mkdirSync(path.dirname(this.virtualEntrypoint), { recursive: true });
160
+ volume.writeFileSync(this.virtualEntrypoint, entrySource);
161
+ }
162
+ }
163
+ VirtualWorkflowCodeBundler.virtualRoot = path.join(process.cwd(), '__temporal_virtual__', 'workflows');