@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
package/README.md ADDED
@@ -0,0 +1,203 @@
1
+ # @primero.ai/temporal-graph-tools
2
+
3
+ TypeScript utilities for assembling Temporal workflows from plain activity
4
+ functions. Build a workflow plan, capture the generated source code, hydrate
5
+ activity implementations, and bundle everything for a worker without
6
+ hand-writing workflow files.
7
+
8
+ ## Highlights
9
+
10
+ - Fluent builder for chaining sequential steps and running activity stages in
11
+ parallel with type-safe input/output propagation.
12
+ - Automatic activity key generation and optional per-activity configuration so
13
+ the generated workflow source stays deterministic.
14
+ - One-call bundler that validates multiple plans, hydrates activities, and
15
+ produces bundled workflow code ready for a worker.
16
+ - Emits Temporal workflow source that proxies activities and stitches staged
17
+ plans together.
18
+ - Low-level helpers remain available if you prefer to collect results, build
19
+ bundles, or hydrate activities manually.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @primero.ai/temporal-graph-tools
25
+ # or
26
+ pnpm add @primero.ai/temporal-graph-tools
27
+ # or
28
+ bun add @primero.ai/temporal-graph-tools
29
+ ```
30
+
31
+ The package targets Node.js 18+ and ships ESM builds.
32
+
33
+ ## Quick start
34
+
35
+ ```ts
36
+ import {
37
+ bundleWorkflows,
38
+ createActivity,
39
+ createWorkflowBuilder,
40
+ } from '@primero.ai/temporal-graph-tools'
41
+
42
+ type FetchUserInput = { userId: string }
43
+ type FetchUserOutput = { profile: { id: string; name: string } }
44
+
45
+ const fetchUserProfile = createActivity(
46
+ async ({ userId }: FetchUserInput): Promise<FetchUserOutput> => {
47
+ return { profile: { id: userId, name: `User ${userId}` } }
48
+ },
49
+ { id: 'fetchUserProfile' },
50
+ )
51
+
52
+ const sendWelcomeEmail = createActivity(
53
+ async ({ profile }: FetchUserOutput) => {
54
+ return { sent: true, name: profile.name }
55
+ },
56
+ { id: 'sendWelcomeEmail' },
57
+ )
58
+
59
+ async function compile() {
60
+ const builder = createWorkflowBuilder<FetchUserInput>({
61
+ workflowName: 'customerOnboardingWorkflow',
62
+ proxyOptions: { startToCloseTimeout: '2 minutes' },
63
+ })
64
+
65
+ const plan = builder.then(fetchUserProfile).then(sendWelcomeEmail).commit()
66
+ const { activities, workflowBundle } = await bundleWorkflows([plan])
67
+
68
+ // Use the emitted artifacts with a Temporal worker
69
+ return { workflowBundle, activities }
70
+ }
71
+ ```
72
+
73
+ See the complete onboarding example in `examples/` for a richer flow that uses a
74
+ parallel stage and hooks a worker up to the generated artifacts.
75
+
76
+ ### Example scripts
77
+
78
+ After installing dependencies you can explore the sample project:
79
+
80
+ ```bash
81
+ bun install
82
+ bun run worker # Starts a Temporal worker (needs a Temporal cluster)
83
+ bun run trigger-workflows # Launches the sample workflows through the client
84
+ ```
85
+
86
+ Set `TEMPORAL_ADDRESS`, `TEMPORAL_NAMESPACE`, and `TEMPORAL_TASK_QUEUE` in
87
+ `.env` to point the worker at your cluster. Use `bun run trigger-workflows` to
88
+ start the compiled workflows through the Temporal client once the worker is
89
+ running.
90
+
91
+ ## Workflow builder API
92
+
93
+ ### `createWorkflowBuilder<TInput>(options)`
94
+
95
+ Creates a `WorkflowBuilder` instance typed with the initial workflow input.
96
+ `options` must include:
97
+
98
+ - `workflowName`: Name of the exported workflow function. This value must be a
99
+ non-empty string and unique across the plans you later bundle.
100
+
101
+ Optional fields:
102
+
103
+ - `activitiesImportPath`: Module specifier used in the generated workflow import
104
+ (`'./activities'` by default).
105
+ - `proxyOptions`: Either a `@temporalio/workflow` `ActivityOptions` object or a
106
+ string literal dropped into the generated code. If omitted, a one-minute
107
+ `startToCloseTimeout` is emitted.
108
+
109
+ ### `builder.then(activity, config?)`
110
+
111
+ Appends a sequential activity. The helper accepts either a bare activity
112
+ function or a value created with `createActivity`. When both inline and
113
+ preconfigured options are provided they are merged; `config.id` determines the
114
+ activity key.
115
+
116
+ ### `builder.parallel([activityA, activityB, ...])`
117
+
118
+ Executes multiple activities against the current stage output and returns an
119
+ object keyed by each activity's `id`. A parallel stage can only be added after
120
+ at least one `then` call.
121
+
122
+ ### `builder.commit(options?)`
123
+
124
+ Finalises the plan and returns:
125
+
126
+ - `workflowName`: The sanitized name of the exported workflow function.
127
+ - `workflowSource`: Generated TypeScript for the Temporal workflow function.
128
+ - `activities`: Map of activity keys to the original implementations and config
129
+ metadata. Implementations remain live references so any captured helpers stay
130
+ intact.
131
+
132
+ Additional `options` override the builder defaults for this invocation.
133
+
134
+ ## Activity helpers
135
+
136
+ ### `createActivity(activityFn, config?)`
137
+
138
+ Wraps an activity function so it can be reused with the builder. When a config
139
+ object is provided its `id` becomes the activity key; without options the
140
+ function name (or an auto-incremented fallback) is used. The helper is also
141
+ re-exported as `createActivity` for codebases that prefer plural naming.
142
+
143
+ ## Workflow bundler utilities
144
+
145
+ ### `bundleWorkflows(plans, options?)`
146
+
147
+ High-level helper that accepts one or more `WorkflowBuildResult` instances,
148
+ validates them, hydrates all activities, and bundles the generated workflow
149
+ sources. Returns:
150
+
151
+ - `activities`: Map of activity keys to runnable implementations.
152
+ - `workflowBundle`: Object containing the bundled JavaScript (in `code`) with an
153
+ inline source map.
154
+
155
+ Use this when you want a single call that prepares everything for a Temporal
156
+ worker. Under the hood it relies on the lower-level helpers documented below.
157
+
158
+ ```ts
159
+ const plans = [onboardingPlan, greetingPlan]
160
+ const { activities, workflowBundle } = await bundleWorkflows(plans, {
161
+ filename: 'team-workflows.js',
162
+ })
163
+ ```
164
+
165
+ ### `collectWorkflowBuildResults(results)`
166
+
167
+ Merges the output of multiple `builder.commit()` calls into a single object.
168
+ Workflow names must be unique (duplicates are rejected), and activity IDs either
169
+ unique or guaranteed to have identical implementation/config pairs. The result
170
+ can feed directly into `instantiateActivities` or `buildWorkflowBundleCode`.
171
+
172
+ ### `instantiateActivities(bundles)`
173
+
174
+ Accepts the `activities` map returned by `builder.commit()` (or
175
+ `collectWorkflowBuildResults`) and produces actual implementations. Each entry
176
+ is the original function reference supplied to the builder, so any captured
177
+ state remains intact.
178
+
179
+ ### `buildWorkflowBundleCode(source, filename?)`
180
+
181
+ Runs Temporal's `bundleWorkflowCode` against the generated workflow source(s)
182
+ and returns bundled JavaScript with an inline source map. `source` can be:
183
+
184
+ - A raw workflow source string (preserved for backward compatibility).
185
+ - A single `WorkflowBuildResult` or `WorkflowSourceArtifact`.
186
+ - An array of `WorkflowSourceArtifact` values (for multiple workflows).
187
+
188
+ `filename` controls the `file` attribute recorded in the map. When omitted the
189
+ helper generates deterministic filenames per workflow and normalizes the map so
190
+ Temporal tooling can attribute stack traces correctly.
191
+
192
+ ## Development
193
+
194
+ ```bash
195
+ bun install
196
+ bun run type-check
197
+ bun run lint
198
+ bun run build
199
+ ```
200
+
201
+ ## License
202
+
203
+ MIT
@@ -0,0 +1,34 @@
1
+ export type FetchUserInput = {
2
+ userId: string;
3
+ };
4
+ export declare const fetchUserProfile: import("@primero.ai/temporal-graph-tools").ConfiguredActivityReference<FetchUserInput, FetchUserOutput, "fetchUserProfile">;
5
+ export declare const sendWelcomeEmail: import("@primero.ai/temporal-graph-tools").ConfiguredActivityReference<FetchUserOutput, {
6
+ sent: boolean;
7
+ recipientSlug: string;
8
+ }, "sendWelcomeEmail">;
9
+ export type UserProfile = {
10
+ id: string;
11
+ name: string;
12
+ slug: string;
13
+ };
14
+ export type FetchUserOutput = {
15
+ profile: UserProfile;
16
+ };
17
+ export declare const syncCrmRecord: import("@primero.ai/temporal-graph-tools").ConfiguredActivityReference<FetchUserOutput, {
18
+ synced: boolean;
19
+ recordSlug: string;
20
+ }, "syncCrmRecord">;
21
+ export type ParallelResult = {
22
+ sendWelcomeEmail: {
23
+ sent: boolean;
24
+ recipientSlug: string;
25
+ };
26
+ syncCrmRecord: {
27
+ synced: boolean;
28
+ recordSlug: string;
29
+ };
30
+ };
31
+ export declare const logCompletion: import("@primero.ai/temporal-graph-tools").ConfiguredActivityReference<ParallelResult, void, "logCompletion">;
32
+ export declare const startGreet: import("@primero.ai/temporal-graph-tools").ConfiguredActivityReference<unknown, void, "startGreet">;
33
+ export declare const endGreet: import("@primero.ai/temporal-graph-tools").ConfiguredActivityReference<unknown, void, "endGreet">;
34
+ //# sourceMappingURL=activities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activities.d.ts","sourceRoot":"","sources":["../../../examples/bundle/activities.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/C,eAAO,MAAM,gBAAgB,6HAiB5B,CAAA;AAED,eAAO,MAAM,gBAAgB;UAC2B,OAAO;mBAAiB,MAAM;sBAOrF,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AACpE,MAAM,MAAM,eAAe,GAAG;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,CAAA;AAEtD,eAAO,MAAM,aAAa;YACgC,OAAO;gBAAc,MAAM;mBAOpF,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,gBAAgB,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D,aAAa,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAA;CACvD,CAAA;AAED,eAAO,MAAM,aAAa,+GASzB,CAAA;AACD,eAAO,MAAM,UAAU,qGAA0E,CAAA;AACjG,eAAO,MAAM,QAAQ,mGAAuE,CAAA"}
@@ -0,0 +1,35 @@
1
+ import kebabCase from 'lodash/kebabCase.js';
2
+ import { createActivity, } from '@primero.ai/temporal-graph-tools';
3
+ export const fetchUserProfile = createActivity(async (input) => {
4
+ const profileName = `User ${input.userId.slice(0, 6)}`;
5
+ const slug = kebabCase(profileName);
6
+ console.log(`[fetchUserProfile] fetching ${input.userId} as ${slug}`);
7
+ return {
8
+ profile: {
9
+ id: input.userId,
10
+ name: profileName,
11
+ slug,
12
+ },
13
+ };
14
+ }, {
15
+ id: 'fetchUserProfile',
16
+ });
17
+ export const sendWelcomeEmail = createActivity(async ({ profile }) => {
18
+ console.log(`[sendWelcomeEmail] sent email to ${profile.name} (${profile.slug})`);
19
+ return { sent: true, recipientSlug: profile.slug };
20
+ }, {
21
+ id: 'sendWelcomeEmail',
22
+ });
23
+ export const syncCrmRecord = createActivity(async ({ profile }) => {
24
+ console.log(`[syncCrmRecord] synced record ${profile.id} (${profile.slug})`);
25
+ return { synced: true, recordSlug: profile.slug };
26
+ }, {
27
+ id: 'syncCrmRecord',
28
+ });
29
+ export const logCompletion = createActivity(async (result) => {
30
+ console.log(`[logCompletion] email sent=${result.sendWelcomeEmail.sent} (slug=${result.sendWelcomeEmail.recipientSlug}), crm synced=${result.syncCrmRecord.synced}`);
31
+ }, {
32
+ id: 'logCompletion',
33
+ });
34
+ export const startGreet = createActivity(async () => console.log('Hello '), { id: 'startGreet' });
35
+ export const endGreet = createActivity(async () => console.log('World'), { id: 'endGreet' });
@@ -0,0 +1,3 @@
1
+ export declare const builderOnboarding: import("@primero.ai/temporal-graph-tools").WorkflowBuildResult;
2
+ export declare const builderHelloWorld: import("@primero.ai/temporal-graph-tools").WorkflowBuildResult;
3
+ //# sourceMappingURL=workflows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../../../examples/bundle/workflows.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,iBAAiB,gEAOnB,CAAA;AAEX,eAAO,MAAM,iBAAiB,gEAGnB,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { createWorkflowBuilder } from '@primero.ai/temporal-graph-tools';
2
+ import { endGreet, fetchUserProfile, logCompletion, sendWelcomeEmail, startGreet, syncCrmRecord, } from './activities';
3
+ export const builderOnboarding = createWorkflowBuilder({
4
+ workflowName: 'customerOnboardingWorkflow',
5
+ proxyOptions: { startToCloseTimeout: '2 minutes' },
6
+ })
7
+ .then(fetchUserProfile)
8
+ .parallel([sendWelcomeEmail, syncCrmRecord])
9
+ .then(logCompletion)
10
+ .commit();
11
+ export const builderHelloWorld = createWorkflowBuilder({ workflowName: 'greetWorkflow' })
12
+ .then(startGreet)
13
+ .then(endGreet)
14
+ .commit();
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=trigger-workflows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger-workflows.d.ts","sourceRoot":"","sources":["../../examples/trigger-workflows.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ import { Connection, WorkflowClient } from '@temporalio/client';
2
+ import { builderHelloWorld, builderOnboarding } from './bundle/workflows';
3
+ const DEFAULT_NAMESPACE = 'default';
4
+ const DEFAULT_TASK_QUEUE = 'default';
5
+ async function run() {
6
+ var _a, _b, _c, _d;
7
+ const connection = await Connection.connect({
8
+ address: (_a = process.env.TEMPORAL_ADDRESS) !== null && _a !== void 0 ? _a : 'localhost:7233',
9
+ tls: process.env.TEMPORAL_API_KEY ? {} : undefined,
10
+ apiKey: process.env.TEMPORAL_API_KEY,
11
+ });
12
+ const client = new WorkflowClient({
13
+ connection,
14
+ namespace: (_b = process.env.TEMPORAL_NAMESPACE) !== null && _b !== void 0 ? _b : DEFAULT_NAMESPACE,
15
+ });
16
+ const [onboardingHandle, greetHandle] = await Promise.all([
17
+ client.start(builderOnboarding.workflowName, {
18
+ taskQueue: (_c = process.env.TEMPORAL_TASK_QUEUE) !== null && _c !== void 0 ? _c : DEFAULT_TASK_QUEUE,
19
+ workflowId: `customer-onboarding-${Date.now()}`,
20
+ args: [{ userId: 'user-123' }],
21
+ }),
22
+ client.start(builderHelloWorld.workflowName, {
23
+ taskQueue: (_d = process.env.TEMPORAL_TASK_QUEUE) !== null && _d !== void 0 ? _d : DEFAULT_TASK_QUEUE,
24
+ workflowId: `greet-${Date.now()}`,
25
+ args: [{ userId: 'user-123' }],
26
+ }),
27
+ ]);
28
+ console.log('Onboarding Workflow started:', {
29
+ workflowId: onboardingHandle.workflowId,
30
+ runId: onboardingHandle.firstExecutionRunId,
31
+ });
32
+ console.log('Greet Workflow started:', {
33
+ workflowId: greetHandle.workflowId,
34
+ runId: greetHandle.firstExecutionRunId,
35
+ });
36
+ const result = await Promise.all([onboardingHandle.result(), greetHandle.result()]);
37
+ console.log('Workflows completed with result:', result);
38
+ await connection.close();
39
+ }
40
+ run().catch((error) => {
41
+ console.error('Failed to run workflow example:', error);
42
+ process.exitCode = 1;
43
+ });
@@ -0,0 +1,3 @@
1
+ import { NativeConnectionOptions } from '@temporalio/worker';
2
+ export declare function getConnectionOptions(): NativeConnectionOptions;
3
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../examples/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,uBAAuB,EAAU,MAAM,oBAAoB,CAAA;AAItF,wBAAgB,oBAAoB,IAAI,uBAAuB,CAM9D"}
@@ -0,0 +1,29 @@
1
+ import { NativeConnection, Worker } from '@temporalio/worker';
2
+ import { bundleWorkflows, loadActivitiesFromBundle } from '@primero.ai/temporal-graph-tools';
3
+ import { builderHelloWorld, builderOnboarding } from './bundle/workflows';
4
+ export function getConnectionOptions() {
5
+ return {
6
+ address: process.env.TEMPORAL_ADDRESS,
7
+ apiKey: process.env.TEMPORAL_API_KEY,
8
+ tls: {},
9
+ };
10
+ }
11
+ async function run() {
12
+ const { workflowBundle, activityBundle } = await bundleWorkflows([builderHelloWorld, builderOnboarding], {
13
+ activityBundle: {},
14
+ });
15
+ const activities = await loadActivitiesFromBundle(activityBundle);
16
+ const connection = await NativeConnection.connect(getConnectionOptions());
17
+ const worker = await Worker.create({
18
+ connection,
19
+ namespace: process.env.TEMPORAL_NAMESPACE,
20
+ taskQueue: process.env.TEMPORAL_TASK_QUEUE || 'default',
21
+ activities,
22
+ workflowBundle,
23
+ });
24
+ await worker.run();
25
+ }
26
+ run().catch((err) => {
27
+ console.error(err);
28
+ process.exit(1);
29
+ });
@@ -0,0 +1,25 @@
1
+ import type { WorkflowBuildResult } from './types.js';
2
+ import type { ActivityImplementations } from './workflow-bundler.js';
3
+ export type BundleWorkflowsOptions = {
4
+ filename?: string;
5
+ activityBundle?: BundleActivitiesOptions;
6
+ };
7
+ export type BundleWorkflowsResult = {
8
+ activityBundle: BundledActivitiesArtifact;
9
+ workflowBundle: {
10
+ code: string;
11
+ };
12
+ };
13
+ export type BundleActivitiesOptions = {
14
+ entrypoints?: readonly string[];
15
+ filename?: string;
16
+ externals?: readonly string[];
17
+ };
18
+ export type BundledActivitiesArtifact = {
19
+ filename: string;
20
+ code: string;
21
+ map?: string;
22
+ };
23
+ export declare function loadActivitiesFromBundle(bundle: BundledActivitiesArtifact): Promise<ActivityImplementations>;
24
+ export declare function bundleWorkflows(plans: readonly WorkflowBuildResult[], options?: BundleWorkflowsOptions): Promise<BundleWorkflowsResult>;
25
+ //# sourceMappingURL=bundler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/bundler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAkB,mBAAmB,EAA0B,MAAM,YAAY,CAAA;AAC7F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAIpE,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,uBAAuB,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,cAAc,EAAE,yBAAyB,CAAA;IACzC,cAAc,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAoCD,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,uBAAuB,CAAC,CAkClC;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CAqChC"}
@@ -0,0 +1,208 @@
1
+ import { isAbsolute, resolve, join } from 'node:path';
2
+ import { mkdtemp, writeFile } from 'node:fs/promises';
3
+ import { createRequire } from 'node:module';
4
+ import { buildWorkflowBundleCode } from './workflow-bundler.js';
5
+ import { collectWorkflowBuildResults } from './workflow/collection.js';
6
+ let cachedEsbuild;
7
+ async function loadEsbuild() {
8
+ if (cachedEsbuild) {
9
+ return cachedEsbuild;
10
+ }
11
+ const createLoadError = (error) => {
12
+ if (error instanceof Error) {
13
+ error.message = `Failed to load esbuild. Install it as a dependency before calling bundleWorkflows(). Original error: ${error.message}`;
14
+ return error;
15
+ }
16
+ return new Error(`Failed to load esbuild. Install it as a dependency before calling bundleWorkflows(). Original error: ${String(error)}`);
17
+ };
18
+ try {
19
+ const esbuildModule = await import('esbuild');
20
+ cachedEsbuild = esbuildModule;
21
+ return esbuildModule;
22
+ }
23
+ catch (error) {
24
+ throw createLoadError(error);
25
+ }
26
+ }
27
+ export async function loadActivitiesFromBundle(bundle) {
28
+ if (!bundle || typeof bundle.code !== 'string' || bundle.code.trim().length === 0) {
29
+ throw new Error('Activity bundle code must be a non-empty string.');
30
+ }
31
+ const source = ensureActivityInlineSourceMap(bundle.code, bundle.map);
32
+ const tempDir = await mkdtemp(join(process.cwd(), '.temporal-graph-tools-'));
33
+ const tempFile = join(tempDir, `activities-${Date.now()}-${Math.random().toString(16).slice(2)}.cjs`);
34
+ await writeFile(tempFile, source, 'utf8');
35
+ const require = createRequire(import.meta.url);
36
+ const moduleNamespace = require(tempFile);
37
+ const activitiesExport = moduleNamespace.activities;
38
+ if (isActivityImplementations(activitiesExport)) {
39
+ return activitiesExport;
40
+ }
41
+ const collected = {};
42
+ Object.entries(moduleNamespace).forEach(([key, value]) => {
43
+ if (typeof value === 'function') {
44
+ collected[key] = value;
45
+ }
46
+ });
47
+ if (Object.keys(collected).length > 0) {
48
+ return collected;
49
+ }
50
+ throw new Error('Activity bundle did not expose an activities object or callable exports.');
51
+ }
52
+ export async function bundleWorkflows(plans, options) {
53
+ var _a;
54
+ if (plans.length === 0) {
55
+ throw new Error('bundleWorkflows requires at least one workflow plan.');
56
+ }
57
+ const seenWorkflowNames = new Set();
58
+ plans.forEach((plan, index) => {
59
+ const workflowName = typeof plan.workflowName === 'string' ? plan.workflowName.trim() : '';
60
+ if (workflowName.length === 0) {
61
+ throw new Error(`Workflow plan at index ${index} is missing a valid workflowName. Provide workflowName when building workflows.`);
62
+ }
63
+ if (seenWorkflowNames.has(workflowName)) {
64
+ throw new Error(`Duplicate workflow name detected in bundle: '${workflowName}' at index ${index}. Workflow names must be unique.`);
65
+ }
66
+ seenWorkflowNames.add(workflowName);
67
+ });
68
+ const collection = collectWorkflowBuildResults(plans);
69
+ const activityBundle = await bundleActivitiesWithEsbuild(collection.activities, options === null || options === void 0 ? void 0 : options.activityBundle);
70
+ const filename = (_a = options === null || options === void 0 ? void 0 : options.filename) !== null && _a !== void 0 ? _a : createBundleFilename(collection.workflows);
71
+ const code = await buildWorkflowBundleCode(collection.workflows, filename);
72
+ return {
73
+ activityBundle,
74
+ workflowBundle: { code },
75
+ };
76
+ }
77
+ function createBundleFilename(workflows) {
78
+ const baseName = workflows
79
+ .map((workflow) => workflow.workflowName)
80
+ .filter((name) => typeof name === 'string' && name.trim().length > 0)
81
+ .join('-')
82
+ .trim();
83
+ const sanitizedBase = sanitizeFilenameBase(baseName.length > 0 ? baseName : 'workflow');
84
+ const randomSuffix = Math.random().toString(36).slice(2, 8);
85
+ return `${sanitizedBase}-${randomSuffix}.workflow.js`;
86
+ }
87
+ function sanitizeFilenameBase(candidate) {
88
+ const sanitized = candidate.replace(/[^A-Za-z0-9_.-]/g, '_');
89
+ if (sanitized.length === 0 || /^_+$/.test(sanitized)) {
90
+ return 'workflow';
91
+ }
92
+ return sanitized;
93
+ }
94
+ async function bundleActivitiesWithEsbuild(bundles, options = {}) {
95
+ var _a, _b, _c, _d, _e;
96
+ const entrypoints = normalizeActivityEntrypoints((_a = options.entrypoints) !== null && _a !== void 0 ? _a : deriveActivityEntrypoints(bundles));
97
+ if (entrypoints.length === 0) {
98
+ throw new Error('bundleActivities requires at least one entrypoint.');
99
+ }
100
+ const filename = ((_b = options.filename) !== null && _b !== void 0 ? _b : 'activities.bundle.cjs').trim();
101
+ const externals = new Set(['@primero.ai/temporal-graph-tools', '@swc/*']);
102
+ ((_c = options.externals) !== null && _c !== void 0 ? _c : []).forEach((name) => {
103
+ if (typeof name === 'string' && name.trim().length > 0) {
104
+ externals.add(name.trim());
105
+ }
106
+ });
107
+ const entrySource = createActivitiesEntrySource(entrypoints);
108
+ const esbuild = await loadEsbuild();
109
+ const buildResult = await esbuild.build({
110
+ stdin: {
111
+ contents: entrySource,
112
+ resolveDir: process.cwd(),
113
+ sourcefile: filename,
114
+ loader: 'ts',
115
+ },
116
+ bundle: true,
117
+ platform: 'node',
118
+ format: 'cjs',
119
+ target: ['node18'],
120
+ absWorkingDir: process.cwd(),
121
+ outfile: filename,
122
+ write: false,
123
+ sourcemap: 'inline',
124
+ external: Array.from(externals),
125
+ });
126
+ const jsFile = (_d = buildResult.outputFiles) === null || _d === void 0 ? void 0 : _d.find((file) => file.path.endsWith('.js') || file.path.endsWith('.mjs') || file.path.endsWith('.cjs'));
127
+ const mapFile = (_e = buildResult.outputFiles) === null || _e === void 0 ? void 0 : _e.find((file) => file.path.endsWith('.js.map') ||
128
+ file.path.endsWith('.mjs.map') ||
129
+ file.path.endsWith('.cjs.map'));
130
+ if (!jsFile) {
131
+ throw new Error('Failed to generate activities bundle.');
132
+ }
133
+ return {
134
+ filename,
135
+ code: jsFile.text,
136
+ ...(mapFile ? { map: mapFile.text } : {}),
137
+ };
138
+ }
139
+ function normalizeActivityEntrypoints(entrypoints) {
140
+ return entrypoints
141
+ .map((entry) => entry.trim())
142
+ .filter((entry) => entry.length > 0)
143
+ .map((entry) => (isAbsolute(entry) ? entry : resolve(process.cwd(), entry)));
144
+ }
145
+ function deriveActivityEntrypoints(bundles) {
146
+ const paths = new Set();
147
+ Object.values(bundles).forEach((bundle) => {
148
+ if (bundle === null || bundle === void 0 ? void 0 : bundle.sourceFile) {
149
+ paths.add(bundle.sourceFile);
150
+ }
151
+ });
152
+ if (paths.size === 0) {
153
+ throw new Error('Unable to derive activity sources automatically. Provide activityBundle.entrypoints.');
154
+ }
155
+ return Array.from(paths);
156
+ }
157
+ function createActivitiesEntrySource(entrypoints) {
158
+ const imports = entrypoints
159
+ .map((entry, index) => `import * as module${index} from ${JSON.stringify(entry)};`)
160
+ .join('\n');
161
+ const moduleRefs = entrypoints.map((_, index) => `module${index}`).join(', ');
162
+ return `
163
+ ${imports}
164
+
165
+ const merged = {} as Record<string, unknown>;
166
+ for (const mod of [${moduleRefs}]) {
167
+ if (!mod || typeof mod !== 'object') {
168
+ continue;
169
+ }
170
+ const candidate = (mod as { activities?: unknown }).activities;
171
+ if (candidate && typeof candidate === 'object') {
172
+ Object.assign(merged, candidate as Record<string, unknown>);
173
+ continue;
174
+ }
175
+ for (const [key, value] of Object.entries(mod)) {
176
+ if (key === 'default' || key === '__esModule') {
177
+ continue;
178
+ }
179
+ if (value && typeof value === 'object' && 'activity' in (value as { activity?: unknown })) {
180
+ const activity = (value as { activity?: unknown }).activity;
181
+ if (typeof activity === 'function') {
182
+ merged[key] = activity;
183
+ continue;
184
+ }
185
+ }
186
+ if (typeof value === 'function') {
187
+ merged[key] = value;
188
+ }
189
+ }
190
+ }
191
+
192
+ export const activities = merged;
193
+ `.trimStart();
194
+ }
195
+ function ensureActivityInlineSourceMap(code, map) {
196
+ const marker = '//# sourceMappingURL=data:application/json;base64,';
197
+ if (!map || code.includes(marker)) {
198
+ return code;
199
+ }
200
+ const base64 = Buffer.from(map, 'utf8').toString('base64');
201
+ return `${code}\n${marker}${base64}`;
202
+ }
203
+ function isActivityImplementations(value) {
204
+ if (typeof value !== 'object' || value === null) {
205
+ return false;
206
+ }
207
+ return Object.values(value).every((entry) => typeof entry === 'function');
208
+ }
@@ -0,0 +1,6 @@
1
+ export * from './types.js';
2
+ export { WorkflowBuilder, createWorkflowBuilder } from './workflow/builder.js';
3
+ export * from './workflow-bundler.js';
4
+ export { collectWorkflowBuildResults } from './workflow/collection.js';
5
+ export * from './bundler.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC9E,cAAc,uBAAuB,CAAA;AACrC,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAA;AACtE,cAAc,cAAc,CAAA"}
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export { WorkflowBuilder, createWorkflowBuilder } from './workflow/builder.js';
3
+ export * from './workflow-bundler.js';
4
+ export { collectWorkflowBuildResults } from './workflow/collection.js';
5
+ export * from './bundler.js';