@astami/temporal-functions 0.3.0 → 0.3.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/dist/observability/index.d.mts +20 -1
- package/dist/observability/index.d.ts +20 -1
- package/dist/observability/index.js +14 -3
- package/dist/observability/index.js.map +1 -1
- package/dist/observability/index.mjs +2 -3
- package/dist/observability/index.mjs.map +1 -1
- package/dist/worker/index.js +11 -4
- package/dist/worker/index.js.map +1 -1
- package/dist/worker/index.mjs +11 -4
- package/dist/worker/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { l as WorkerInterceptors } from '../types-DY4y7IE5.mjs';
|
|
2
2
|
export { OpenTelemetryActivityInboundInterceptor } from '@temporalio/interceptors-opentelemetry';
|
|
3
|
+
export { OpenTelemetryInboundInterceptor, OpenTelemetryInternalsInterceptor, OpenTelemetryOutboundInterceptor } from '@temporalio/interceptors-opentelemetry/lib/workflow';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Temporal Functions - Observability Package
|
|
@@ -23,7 +24,25 @@ interface WorkerInterceptorsConfig {
|
|
|
23
24
|
* This returns a WorkerInterceptors object that can be passed directly to
|
|
24
25
|
* the worker configuration. It includes:
|
|
25
26
|
* - Activity inbound interceptors for tracing and logging
|
|
26
|
-
*
|
|
27
|
+
*
|
|
28
|
+
* **IMPORTANT**: For end-to-end trace propagation from client -> workflow -> activities,
|
|
29
|
+
* your workflow module must also export an `interceptors` factory function:
|
|
30
|
+
*
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // In your workflows/index.ts
|
|
33
|
+
* import type { WorkflowInterceptorsFactory } from '@temporalio/workflow';
|
|
34
|
+
* import {
|
|
35
|
+
* OpenTelemetryInboundInterceptor,
|
|
36
|
+
* OpenTelemetryOutboundInterceptor,
|
|
37
|
+
* OpenTelemetryInternalsInterceptor,
|
|
38
|
+
* } from '@temporalio/interceptors-opentelemetry/lib/workflow';
|
|
39
|
+
*
|
|
40
|
+
* export const interceptors: WorkflowInterceptorsFactory = () => ({
|
|
41
|
+
* inbound: [new OpenTelemetryInboundInterceptor()],
|
|
42
|
+
* outbound: [new OpenTelemetryOutboundInterceptor()],
|
|
43
|
+
* internals: [new OpenTelemetryInternalsInterceptor()],
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
27
46
|
*
|
|
28
47
|
* @example
|
|
29
48
|
* ```typescript
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { l as WorkerInterceptors } from '../types-DY4y7IE5.js';
|
|
2
2
|
export { OpenTelemetryActivityInboundInterceptor } from '@temporalio/interceptors-opentelemetry';
|
|
3
|
+
export { OpenTelemetryInboundInterceptor, OpenTelemetryInternalsInterceptor, OpenTelemetryOutboundInterceptor } from '@temporalio/interceptors-opentelemetry/lib/workflow';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Temporal Functions - Observability Package
|
|
@@ -23,7 +24,25 @@ interface WorkerInterceptorsConfig {
|
|
|
23
24
|
* This returns a WorkerInterceptors object that can be passed directly to
|
|
24
25
|
* the worker configuration. It includes:
|
|
25
26
|
* - Activity inbound interceptors for tracing and logging
|
|
26
|
-
*
|
|
27
|
+
*
|
|
28
|
+
* **IMPORTANT**: For end-to-end trace propagation from client -> workflow -> activities,
|
|
29
|
+
* your workflow module must also export an `interceptors` factory function:
|
|
30
|
+
*
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // In your workflows/index.ts
|
|
33
|
+
* import type { WorkflowInterceptorsFactory } from '@temporalio/workflow';
|
|
34
|
+
* import {
|
|
35
|
+
* OpenTelemetryInboundInterceptor,
|
|
36
|
+
* OpenTelemetryOutboundInterceptor,
|
|
37
|
+
* OpenTelemetryInternalsInterceptor,
|
|
38
|
+
* } from '@temporalio/interceptors-opentelemetry/lib/workflow';
|
|
39
|
+
*
|
|
40
|
+
* export const interceptors: WorkflowInterceptorsFactory = () => ({
|
|
41
|
+
* inbound: [new OpenTelemetryInboundInterceptor()],
|
|
42
|
+
* outbound: [new OpenTelemetryOutboundInterceptor()],
|
|
43
|
+
* internals: [new OpenTelemetryInternalsInterceptor()],
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
27
46
|
*
|
|
28
47
|
* @example
|
|
29
48
|
* ```typescript
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var api = require('@opentelemetry/api');
|
|
4
4
|
var interceptorsOpentelemetry = require('@temporalio/interceptors-opentelemetry');
|
|
5
|
+
var workflow = require('@temporalio/interceptors-opentelemetry/lib/workflow');
|
|
5
6
|
|
|
6
7
|
// src/observability/index.ts
|
|
7
8
|
var TracingActivityInterceptor = class {
|
|
@@ -67,9 +68,7 @@ function createWorkerInterceptors(config) {
|
|
|
67
68
|
return {
|
|
68
69
|
activityInbound: [
|
|
69
70
|
(ctx) => new TracingActivityInterceptor(ctx, config)
|
|
70
|
-
]
|
|
71
|
-
// Include OTel workflow interceptors for end-to-end trace propagation
|
|
72
|
-
workflowModules: ["@temporalio/interceptors-opentelemetry/lib/workflow"]
|
|
71
|
+
]
|
|
73
72
|
};
|
|
74
73
|
}
|
|
75
74
|
|
|
@@ -77,6 +76,18 @@ Object.defineProperty(exports, "OpenTelemetryActivityInboundInterceptor", {
|
|
|
77
76
|
enumerable: true,
|
|
78
77
|
get: function () { return interceptorsOpentelemetry.OpenTelemetryActivityInboundInterceptor; }
|
|
79
78
|
});
|
|
79
|
+
Object.defineProperty(exports, "OpenTelemetryInboundInterceptor", {
|
|
80
|
+
enumerable: true,
|
|
81
|
+
get: function () { return workflow.OpenTelemetryInboundInterceptor; }
|
|
82
|
+
});
|
|
83
|
+
Object.defineProperty(exports, "OpenTelemetryInternalsInterceptor", {
|
|
84
|
+
enumerable: true,
|
|
85
|
+
get: function () { return workflow.OpenTelemetryInternalsInterceptor; }
|
|
86
|
+
});
|
|
87
|
+
Object.defineProperty(exports, "OpenTelemetryOutboundInterceptor", {
|
|
88
|
+
enumerable: true,
|
|
89
|
+
get: function () { return workflow.OpenTelemetryOutboundInterceptor; }
|
|
90
|
+
});
|
|
80
91
|
exports.createWorkerInterceptors = createWorkerInterceptors;
|
|
81
92
|
//# sourceMappingURL=index.js.map
|
|
82
93
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/observability/index.ts"],"names":["OpenTelemetryActivityInboundInterceptor","trace"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/observability/index.ts"],"names":["OpenTelemetryActivityInboundInterceptor","trace"],"mappings":";;;;;;;AA2CA,IAAM,6BAAN,MAA4E;AAAA,EACzD,eAAA;AAAA,EACA,MAAA;AAAA,EACA,GAAA;AAAA,EAEjB,WAAA,CAAY,KAAsB,MAAA,EAAkC;AAClE,IAAA,IAAA,CAAK,GAAA,GAAM,GAAA;AACX,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAEd,IAAA,IAAA,CAAK,eAAA,GAAkB,IAAIA,iEAAA,CAAwC,GAAG,CAAA;AAAA,EACxE;AAAA,EAEA,MAAM,OAAA,CACJ,KAAA,EACA,IAAA,EACkB;AAClB,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,YAAA;AACnC,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,iBAAA,CAAkB,UAAA;AACnD,IAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,IAAA,MAAM,MAAA,GAAS,OAAO,MAAA,IAAU,OAAA;AAGhC,IAAA,MAAM,WAAA,GAAgE,OACpE,SAAA,KACG;AAEH,MAAA,MAAM,WAAA,GAAcC,UAAM,aAAA,EAAc;AACxC,MAAA,MAAM,WAAA,GAAc,aAAa,WAAA,EAAY;AAE7C,MAAA,MAAA,CAAO,IAAA;AAAA,QACL;AAAA,UACE,QAAA,EAAU,YAAA;AAAA,UACV,UAAA;AAAA,UACA,WAAW,MAAA,CAAO,WAAA;AAAA,UAClB,UAAU,WAAA,EAAa,OAAA;AAAA,UACvB,SAAS,WAAA,EAAa;AAAA,SACxB;AAAA,QACA;AAAA,OACF;AAEA,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAS,CAAA;AAEnC,QAAA,MAAA,CAAO,IAAA;AAAA,UACL;AAAA,YACE,QAAA,EAAU,YAAA;AAAA,YACV,UAAA;AAAA,YACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,YACvB,UAAU,WAAA,EAAa,OAAA;AAAA,YACvB,SAAS,WAAA,EAAa;AAAA,WACxB;AAAA,UACA;AAAA,SACF;AAEA,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,KAAA,EAAO;AACd,QAAA,MAAA,CAAO,KAAA;AAAA,UACL;AAAA,YACE,QAAA,EAAU,YAAA;AAAA,YACV,UAAA;AAAA,YACA,KAAA;AAAA,YACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,YACvB,UAAU,WAAA,EAAa,OAAA;AAAA,YACvB,SAAS,WAAA,EAAa;AAAA,WACxB;AAAA,UACA;AAAA,SACF;AAEA,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF,CAAA;AAIA,IAAA,OAAO,IAAA,CAAK,eAAA,CAAgB,OAAA,CAAQ,KAAA,EAAO,WAAW,CAAA;AAAA,EACxD;AACF,CAAA;AAiDO,SAAS,yBAAyB,MAAA,EAAsD;AAC7F,EAAA,OAAO;AAAA,IACL,eAAA,EAAiB;AAAA,MACf,CAAC,GAAA,KAAyB,IAAI,0BAAA,CAA2B,KAAK,MAAM;AAAA;AACtE,GACF;AACF","file":"index.js","sourcesContent":["/**\n * Temporal Functions - Observability Package\n *\n * OpenTelemetry integration for Temporal workers with trace context propagation.\n * Import from '@astami/temporal-functions/observability'.\n */\n\nimport { trace } from '@opentelemetry/api';\nimport {\n OpenTelemetryActivityInboundInterceptor,\n} from '@temporalio/interceptors-opentelemetry';\nimport type { Context as ActivityContext } from '@temporalio/activity';\nimport type {\n ActivityInboundCallsInterceptor,\n ActivityExecuteInput,\n Next,\n} from '@temporalio/worker';\nimport type { WorkerInterceptors } from '../types.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface WorkerInterceptorsConfig {\n /** Service/processor name for logging */\n serviceName: string;\n /** Optional custom logger (defaults to console) */\n logger?: {\n info: (obj: Record<string, unknown>, msg: string) => void;\n error: (obj: Record<string, unknown>, msg: string) => void;\n };\n}\n\n// =============================================================================\n// Tracing Activity Interceptor\n// =============================================================================\n\n/**\n * Creates an activity interceptor that combines OpenTelemetry tracing with structured logging\n *\n * Uses Temporal's OpenTelemetryActivityInboundInterceptor for trace propagation,\n * and wraps it with additional structured logging that includes trace IDs.\n */\nclass TracingActivityInterceptor implements ActivityInboundCallsInterceptor {\n private readonly otelInterceptor: OpenTelemetryActivityInboundInterceptor;\n private readonly config: WorkerInterceptorsConfig;\n private readonly ctx: ActivityContext;\n\n constructor(ctx: ActivityContext, config: WorkerInterceptorsConfig) {\n this.ctx = ctx;\n this.config = config;\n // Use Temporal's built-in OTel interceptor for proper trace context propagation\n this.otelInterceptor = new OpenTelemetryActivityInboundInterceptor(ctx);\n }\n\n async execute(\n input: ActivityExecuteInput,\n next: Next<ActivityInboundCallsInterceptor, 'execute'>\n ): Promise<unknown> {\n const activityType = this.ctx.info.activityType;\n const workflowId = this.ctx.info.workflowExecution.workflowId;\n const startTime = Date.now();\n const config = this.config;\n const logger = config.logger ?? console;\n\n // Wrap the next function to add logging INSIDE the OTel span context\n const loggingNext: Next<ActivityInboundCallsInterceptor, 'execute'> = async (\n nextInput: ActivityExecuteInput\n ) => {\n // Now we're inside the OTel interceptor's span context\n const currentSpan = trace.getActiveSpan();\n const spanContext = currentSpan?.spanContext();\n\n logger.info(\n {\n activity: activityType,\n workflowId,\n processor: config.serviceName,\n trace_id: spanContext?.traceId,\n span_id: spanContext?.spanId,\n },\n 'Activity started'\n );\n\n try {\n const result = await next(nextInput);\n\n logger.info(\n {\n activity: activityType,\n workflowId,\n duration: Date.now() - startTime,\n trace_id: spanContext?.traceId,\n span_id: spanContext?.spanId,\n },\n 'Activity completed'\n );\n\n return result;\n } catch (error) {\n logger.error(\n {\n activity: activityType,\n workflowId,\n error,\n duration: Date.now() - startTime,\n trace_id: spanContext?.traceId,\n span_id: spanContext?.spanId,\n },\n 'Activity failed'\n );\n\n throw error;\n }\n };\n\n // Delegate to Temporal's OTel interceptor which sets up the span context,\n // then our loggingNext will execute with the proper trace context\n return this.otelInterceptor.execute(input, loggingNext);\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates worker interceptors configuration with OpenTelemetry trace propagation\n *\n * This returns a WorkerInterceptors object that can be passed directly to\n * the worker configuration. It includes:\n * - Activity inbound interceptors for tracing and logging\n *\n * **IMPORTANT**: For end-to-end trace propagation from client -> workflow -> activities,\n * your workflow module must also export an `interceptors` factory function:\n *\n * ```typescript\n * // In your workflows/index.ts\n * import type { WorkflowInterceptorsFactory } from '@temporalio/workflow';\n * import {\n * OpenTelemetryInboundInterceptor,\n * OpenTelemetryOutboundInterceptor,\n * OpenTelemetryInternalsInterceptor,\n * } from '@temporalio/interceptors-opentelemetry/lib/workflow';\n *\n * export const interceptors: WorkflowInterceptorsFactory = () => ({\n * inbound: [new OpenTelemetryInboundInterceptor()],\n * outbound: [new OpenTelemetryOutboundInterceptor()],\n * internals: [new OpenTelemetryInternalsInterceptor()],\n * });\n * ```\n *\n * @example\n * ```typescript\n * import { createWorker } from '@astami/temporal-functions/worker';\n * import { createWorkerInterceptors } from '@astami/temporal-functions/observability';\n * import * as activities from './activities';\n *\n * const worker = createWorker({\n * temporal: { address: 'localhost:7233', namespace: 'loop' },\n * taskQueue: 'stripe-payments',\n * workflowsPath: './dist/workflows/index.js',\n * activities,\n * interceptors: createWorkerInterceptors({ serviceName: 'stripe' }),\n * });\n *\n * await worker.start();\n * ```\n */\nexport function createWorkerInterceptors(config: WorkerInterceptorsConfig): WorkerInterceptors {\n return {\n activityInbound: [\n (ctx: ActivityContext) => new TracingActivityInterceptor(ctx, config),\n ],\n };\n}\n\n// =============================================================================\n// Re-exports from Temporal OTel package\n// =============================================================================\n\nexport {\n OpenTelemetryActivityInboundInterceptor,\n} from '@temporalio/interceptors-opentelemetry';\n\n// Re-export workflow interceptors for convenience\nexport {\n OpenTelemetryInboundInterceptor,\n OpenTelemetryOutboundInterceptor,\n OpenTelemetryInternalsInterceptor,\n} from '@temporalio/interceptors-opentelemetry/lib/workflow';\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { trace } from '@opentelemetry/api';
|
|
2
2
|
import { OpenTelemetryActivityInboundInterceptor } from '@temporalio/interceptors-opentelemetry';
|
|
3
3
|
export { OpenTelemetryActivityInboundInterceptor } from '@temporalio/interceptors-opentelemetry';
|
|
4
|
+
export { OpenTelemetryInboundInterceptor, OpenTelemetryInternalsInterceptor, OpenTelemetryOutboundInterceptor } from '@temporalio/interceptors-opentelemetry/lib/workflow';
|
|
4
5
|
|
|
5
6
|
// src/observability/index.ts
|
|
6
7
|
var TracingActivityInterceptor = class {
|
|
@@ -66,9 +67,7 @@ function createWorkerInterceptors(config) {
|
|
|
66
67
|
return {
|
|
67
68
|
activityInbound: [
|
|
68
69
|
(ctx) => new TracingActivityInterceptor(ctx, config)
|
|
69
|
-
]
|
|
70
|
-
// Include OTel workflow interceptors for end-to-end trace propagation
|
|
71
|
-
workflowModules: ["@temporalio/interceptors-opentelemetry/lib/workflow"]
|
|
70
|
+
]
|
|
72
71
|
};
|
|
73
72
|
}
|
|
74
73
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/observability/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/observability/index.ts"],"names":[],"mappings":";;;;;;AA2CA,IAAM,6BAAN,MAA4E;AAAA,EACzD,eAAA;AAAA,EACA,MAAA;AAAA,EACA,GAAA;AAAA,EAEjB,WAAA,CAAY,KAAsB,MAAA,EAAkC;AAClE,IAAA,IAAA,CAAK,GAAA,GAAM,GAAA;AACX,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAEd,IAAA,IAAA,CAAK,eAAA,GAAkB,IAAI,uCAAA,CAAwC,GAAG,CAAA;AAAA,EACxE;AAAA,EAEA,MAAM,OAAA,CACJ,KAAA,EACA,IAAA,EACkB;AAClB,IAAA,MAAM,YAAA,GAAe,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,YAAA;AACnC,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,iBAAA,CAAkB,UAAA;AACnD,IAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA;AACpB,IAAA,MAAM,MAAA,GAAS,OAAO,MAAA,IAAU,OAAA;AAGhC,IAAA,MAAM,WAAA,GAAgE,OACpE,SAAA,KACG;AAEH,MAAA,MAAM,WAAA,GAAc,MAAM,aAAA,EAAc;AACxC,MAAA,MAAM,WAAA,GAAc,aAAa,WAAA,EAAY;AAE7C,MAAA,MAAA,CAAO,IAAA;AAAA,QACL;AAAA,UACE,QAAA,EAAU,YAAA;AAAA,UACV,UAAA;AAAA,UACA,WAAW,MAAA,CAAO,WAAA;AAAA,UAClB,UAAU,WAAA,EAAa,OAAA;AAAA,UACvB,SAAS,WAAA,EAAa;AAAA,SACxB;AAAA,QACA;AAAA,OACF;AAEA,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,SAAS,CAAA;AAEnC,QAAA,MAAA,CAAO,IAAA;AAAA,UACL;AAAA,YACE,QAAA,EAAU,YAAA;AAAA,YACV,UAAA;AAAA,YACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,YACvB,UAAU,WAAA,EAAa,OAAA;AAAA,YACvB,SAAS,WAAA,EAAa;AAAA,WACxB;AAAA,UACA;AAAA,SACF;AAEA,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,KAAA,EAAO;AACd,QAAA,MAAA,CAAO,KAAA;AAAA,UACL;AAAA,YACE,QAAA,EAAU,YAAA;AAAA,YACV,UAAA;AAAA,YACA,KAAA;AAAA,YACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,YACvB,UAAU,WAAA,EAAa,OAAA;AAAA,YACvB,SAAS,WAAA,EAAa;AAAA,WACxB;AAAA,UACA;AAAA,SACF;AAEA,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF,CAAA;AAIA,IAAA,OAAO,IAAA,CAAK,eAAA,CAAgB,OAAA,CAAQ,KAAA,EAAO,WAAW,CAAA;AAAA,EACxD;AACF,CAAA;AAiDO,SAAS,yBAAyB,MAAA,EAAsD;AAC7F,EAAA,OAAO;AAAA,IACL,eAAA,EAAiB;AAAA,MACf,CAAC,GAAA,KAAyB,IAAI,0BAAA,CAA2B,KAAK,MAAM;AAAA;AACtE,GACF;AACF","file":"index.mjs","sourcesContent":["/**\n * Temporal Functions - Observability Package\n *\n * OpenTelemetry integration for Temporal workers with trace context propagation.\n * Import from '@astami/temporal-functions/observability'.\n */\n\nimport { trace } from '@opentelemetry/api';\nimport {\n OpenTelemetryActivityInboundInterceptor,\n} from '@temporalio/interceptors-opentelemetry';\nimport type { Context as ActivityContext } from '@temporalio/activity';\nimport type {\n ActivityInboundCallsInterceptor,\n ActivityExecuteInput,\n Next,\n} from '@temporalio/worker';\nimport type { WorkerInterceptors } from '../types.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface WorkerInterceptorsConfig {\n /** Service/processor name for logging */\n serviceName: string;\n /** Optional custom logger (defaults to console) */\n logger?: {\n info: (obj: Record<string, unknown>, msg: string) => void;\n error: (obj: Record<string, unknown>, msg: string) => void;\n };\n}\n\n// =============================================================================\n// Tracing Activity Interceptor\n// =============================================================================\n\n/**\n * Creates an activity interceptor that combines OpenTelemetry tracing with structured logging\n *\n * Uses Temporal's OpenTelemetryActivityInboundInterceptor for trace propagation,\n * and wraps it with additional structured logging that includes trace IDs.\n */\nclass TracingActivityInterceptor implements ActivityInboundCallsInterceptor {\n private readonly otelInterceptor: OpenTelemetryActivityInboundInterceptor;\n private readonly config: WorkerInterceptorsConfig;\n private readonly ctx: ActivityContext;\n\n constructor(ctx: ActivityContext, config: WorkerInterceptorsConfig) {\n this.ctx = ctx;\n this.config = config;\n // Use Temporal's built-in OTel interceptor for proper trace context propagation\n this.otelInterceptor = new OpenTelemetryActivityInboundInterceptor(ctx);\n }\n\n async execute(\n input: ActivityExecuteInput,\n next: Next<ActivityInboundCallsInterceptor, 'execute'>\n ): Promise<unknown> {\n const activityType = this.ctx.info.activityType;\n const workflowId = this.ctx.info.workflowExecution.workflowId;\n const startTime = Date.now();\n const config = this.config;\n const logger = config.logger ?? console;\n\n // Wrap the next function to add logging INSIDE the OTel span context\n const loggingNext: Next<ActivityInboundCallsInterceptor, 'execute'> = async (\n nextInput: ActivityExecuteInput\n ) => {\n // Now we're inside the OTel interceptor's span context\n const currentSpan = trace.getActiveSpan();\n const spanContext = currentSpan?.spanContext();\n\n logger.info(\n {\n activity: activityType,\n workflowId,\n processor: config.serviceName,\n trace_id: spanContext?.traceId,\n span_id: spanContext?.spanId,\n },\n 'Activity started'\n );\n\n try {\n const result = await next(nextInput);\n\n logger.info(\n {\n activity: activityType,\n workflowId,\n duration: Date.now() - startTime,\n trace_id: spanContext?.traceId,\n span_id: spanContext?.spanId,\n },\n 'Activity completed'\n );\n\n return result;\n } catch (error) {\n logger.error(\n {\n activity: activityType,\n workflowId,\n error,\n duration: Date.now() - startTime,\n trace_id: spanContext?.traceId,\n span_id: spanContext?.spanId,\n },\n 'Activity failed'\n );\n\n throw error;\n }\n };\n\n // Delegate to Temporal's OTel interceptor which sets up the span context,\n // then our loggingNext will execute with the proper trace context\n return this.otelInterceptor.execute(input, loggingNext);\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates worker interceptors configuration with OpenTelemetry trace propagation\n *\n * This returns a WorkerInterceptors object that can be passed directly to\n * the worker configuration. It includes:\n * - Activity inbound interceptors for tracing and logging\n *\n * **IMPORTANT**: For end-to-end trace propagation from client -> workflow -> activities,\n * your workflow module must also export an `interceptors` factory function:\n *\n * ```typescript\n * // In your workflows/index.ts\n * import type { WorkflowInterceptorsFactory } from '@temporalio/workflow';\n * import {\n * OpenTelemetryInboundInterceptor,\n * OpenTelemetryOutboundInterceptor,\n * OpenTelemetryInternalsInterceptor,\n * } from '@temporalio/interceptors-opentelemetry/lib/workflow';\n *\n * export const interceptors: WorkflowInterceptorsFactory = () => ({\n * inbound: [new OpenTelemetryInboundInterceptor()],\n * outbound: [new OpenTelemetryOutboundInterceptor()],\n * internals: [new OpenTelemetryInternalsInterceptor()],\n * });\n * ```\n *\n * @example\n * ```typescript\n * import { createWorker } from '@astami/temporal-functions/worker';\n * import { createWorkerInterceptors } from '@astami/temporal-functions/observability';\n * import * as activities from './activities';\n *\n * const worker = createWorker({\n * temporal: { address: 'localhost:7233', namespace: 'loop' },\n * taskQueue: 'stripe-payments',\n * workflowsPath: './dist/workflows/index.js',\n * activities,\n * interceptors: createWorkerInterceptors({ serviceName: 'stripe' }),\n * });\n *\n * await worker.start();\n * ```\n */\nexport function createWorkerInterceptors(config: WorkerInterceptorsConfig): WorkerInterceptors {\n return {\n activityInbound: [\n (ctx: ActivityContext) => new TracingActivityInterceptor(ctx, config),\n ],\n };\n}\n\n// =============================================================================\n// Re-exports from Temporal OTel package\n// =============================================================================\n\nexport {\n OpenTelemetryActivityInboundInterceptor,\n} from '@temporalio/interceptors-opentelemetry';\n\n// Re-export workflow interceptors for convenience\nexport {\n OpenTelemetryInboundInterceptor,\n OpenTelemetryOutboundInterceptor,\n OpenTelemetryInternalsInterceptor,\n} from '@temporalio/interceptors-opentelemetry/lib/workflow';\n"]}
|
package/dist/worker/index.js
CHANGED
|
@@ -243,13 +243,20 @@ var TemporalFunctionsWorker = class {
|
|
|
243
243
|
if (this.config.workflowsPath) {
|
|
244
244
|
workerOptions.workflowsPath = this.config.workflowsPath;
|
|
245
245
|
}
|
|
246
|
-
if (this.config.interceptors) {
|
|
246
|
+
if (this.config.interceptors || this.config.workflowsPath) {
|
|
247
247
|
workerOptions.interceptors = {};
|
|
248
|
-
if (this.config.interceptors
|
|
248
|
+
if (this.config.interceptors?.activityInbound) {
|
|
249
249
|
workerOptions.interceptors.activityInbound = this.config.interceptors.activityInbound;
|
|
250
250
|
}
|
|
251
|
-
|
|
252
|
-
|
|
251
|
+
const workflowModules = [];
|
|
252
|
+
if (this.config.interceptors?.workflowModules) {
|
|
253
|
+
workflowModules.push(...this.config.interceptors.workflowModules);
|
|
254
|
+
}
|
|
255
|
+
if (this.config.workflowsPath) {
|
|
256
|
+
workflowModules.push(this.config.workflowsPath);
|
|
257
|
+
}
|
|
258
|
+
if (workflowModules.length > 0) {
|
|
259
|
+
workerOptions.interceptors.workflowModules = workflowModules;
|
|
253
260
|
}
|
|
254
261
|
}
|
|
255
262
|
this.worker = await worker.Worker.create(workerOptions);
|
package/dist/worker/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/worker/index.ts"],"names":["NativeConnection","Worker"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Temporal Functions - Main Entry Point\n *\n * This module provides the core API for defining functions and workflows.\n * Import from 'temporal-functions' for function/workflow definitions.\n * Import from 'temporal-functions/client' for triggering workflows.\n * Import from 'temporal-functions/worker' for running workers.\n */\n\nimport type {\n FunctionDef,\n FunctionOptions,\n WorkflowDef,\n WorkflowOptions,\n WorkflowHandler,\n Registry,\n HttpTriggerOptions,\n CronTriggerOptions,\n} from './types.js';\n\n// =============================================================================\n// Global Registry\n// =============================================================================\n\n/**\n * Global registry for all defined functions and workflows\n */\nexport const registry: Registry = {\n functions: new Map(),\n workflows: new Map(),\n};\n\n// =============================================================================\n// Function Definition\n// =============================================================================\n\n/**\n * Define a function (maps to a Temporal Activity)\n *\n * @example\n * ```typescript\n * export const sendEmail = tfn.fn(\n * 'sendEmail',\n * async (params: EmailParams) => {\n * return await emailService.send(params);\n * },\n * { timeout: '30s', retries: 3 }\n * );\n * ```\n */\nfunction fn<TInput, TOutput>(\n name: string,\n handler: (input: TInput) => Promise<TOutput>,\n options: FunctionOptions = {}\n): FunctionDef<TInput, TOutput> {\n const def: FunctionDef<TInput, TOutput> = {\n name,\n handler,\n options: {\n startToCloseTimeout: '1m',\n ...options,\n },\n __type: 'function',\n };\n\n registry.functions.set(name, def as FunctionDef);\n return def;\n}\n\n// =============================================================================\n// Workflow Definition\n// =============================================================================\n\n/**\n * Define a workflow\n *\n * @example\n * ```typescript\n * export const processOrder = tfn.workflow(\n * 'processOrder',\n * async (ctx, order: Order) => {\n * const validated = await ctx.run(validateOrder, order);\n * const paid = await ctx.run(chargePayment, validated);\n * return { orderId: paid.id, status: 'complete' };\n * }\n * );\n * ```\n */\nfunction workflow<TInput, TOutput>(\n name: string,\n handler: WorkflowHandler<TInput, TOutput>,\n options: WorkflowOptions = {}\n): WorkflowDef<TInput, TOutput> {\n const def: WorkflowDef<TInput, TOutput> = {\n name,\n handler,\n options: {\n taskQueue: 'default',\n ...options,\n },\n __type: 'workflow',\n };\n\n registry.workflows.set(name, def as WorkflowDef);\n return def;\n}\n\n// =============================================================================\n// Trigger Definitions (Declarative)\n// =============================================================================\n\ninterface HttpTriggerDef {\n type: 'http';\n method: string;\n path: string;\n workflow: WorkflowDef;\n options: HttpTriggerOptions;\n}\n\ninterface CronTriggerDef {\n type: 'cron';\n schedule: string;\n workflow: WorkflowDef;\n options: CronTriggerOptions;\n}\n\ninterface SignalTriggerDef {\n type: 'signal';\n signalName: string;\n handler: (payload: unknown) => void | Promise<void>;\n}\n\ntype TriggerDef = HttpTriggerDef | CronTriggerDef | SignalTriggerDef;\n\n/**\n * Registry for trigger definitions\n */\nexport const triggers: TriggerDef[] = [];\n\n/**\n * Define an HTTP trigger for a workflow\n *\n * @example\n * ```typescript\n * tfn.http('POST', '/api/orders', processOrder);\n * ```\n */\nfunction http<TInput, TOutput>(\n method: string,\n path: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: HttpTriggerOptions = {}\n): void {\n triggers.push({\n type: 'http',\n method: method.toUpperCase(),\n path,\n workflow: workflow as WorkflowDef,\n options,\n });\n}\n\n/**\n * Define a cron/scheduled trigger for a workflow\n *\n * @example\n * ```typescript\n * tfn.cron('0 9 * * *', dailyReport); // Every day at 9am\n * ```\n */\nfunction cron<TInput, TOutput>(\n schedule: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: CronTriggerOptions = {}\n): void {\n triggers.push({\n type: 'cron',\n schedule,\n workflow: workflow as WorkflowDef,\n options,\n });\n}\n\n/**\n * Define an interval trigger (convenience wrapper around cron)\n *\n * @example\n * ```typescript\n * tfn.interval('5m', healthCheck); // Every 5 minutes\n * ```\n */\nfunction interval<TInput, TOutput>(\n duration: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: CronTriggerOptions = {}\n): void {\n // Convert duration to cron expression\n const cronSchedule = durationToCron(duration);\n cron(cronSchedule, workflow, options);\n}\n\n/**\n * Define a signal trigger\n *\n * @example\n * ```typescript\n * tfn.signal('order.cancel', handleCancellation);\n * ```\n */\nfunction signal(\n signalName: string,\n handler: (payload: unknown) => void | Promise<void>\n): void {\n triggers.push({\n type: 'signal',\n signalName,\n handler,\n });\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\n/**\n * Convert a duration string to a cron expression\n */\nfunction durationToCron(duration: string): string {\n const match = duration.match(/^(\\d+)(s|m|h|d)$/);\n if (!match) {\n throw new Error(`Invalid duration format: ${duration}. Use format like '5m', '1h', '30s'`);\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n\n switch (unit) {\n case 's':\n if (value < 60) {\n return `*/${value} * * * * *`; // Every N seconds (non-standard)\n }\n throw new Error('Seconds interval must be less than 60');\n case 'm':\n return `*/${value} * * * *`; // Every N minutes\n case 'h':\n return `0 */${value} * * *`; // Every N hours\n case 'd':\n return `0 0 */${value} * *`; // Every N days\n default:\n throw new Error(`Unknown duration unit: ${unit}`);\n }\n}\n\n/**\n * Check if a definition is a function\n */\nexport function isFunction(def: unknown): def is FunctionDef {\n return (def as FunctionDef)?.__type === 'function';\n}\n\n/**\n * Check if a definition is a workflow\n */\nexport function isWorkflow(def: unknown): def is WorkflowDef {\n return (def as WorkflowDef)?.__type === 'workflow';\n}\n\n// =============================================================================\n// Main API Export\n// =============================================================================\n\n/**\n * The main Temporal Functions API\n */\nexport const tfn = {\n /** Define a function (activity) */\n fn,\n /** Define a workflow */\n workflow,\n /** Define an HTTP trigger */\n http,\n /** Define a cron trigger */\n cron,\n /** Define an interval trigger */\n interval,\n /** Define a signal trigger */\n signal,\n};\n\n// Re-export types\nexport type {\n FunctionDef,\n FunctionOptions,\n WorkflowDef,\n WorkflowOptions,\n WorkflowContext,\n WorkflowHandler,\n WorkflowInfo,\n RetryPolicy,\n HttpTriggerOptions,\n CronTriggerOptions,\n TemporalConfig,\n ClientConfig,\n WorkerConfig,\n StartWorkflowOptions,\n WorkflowHandle,\n TFNClient,\n TFNWorker,\n} from './types.js';\n\nexport default tfn;\n","/**\n * Temporal Functions - Worker Package\n *\n * Full worker implementation for executing functions and workflows.\n * Import from 'temporal-functions/worker'.\n */\n\nimport { Worker, NativeConnection } from '@temporalio/worker';\nimport type {\n WorkerConfig,\n FunctionDef,\n WorkflowDef,\n TFNWorker,\n} from '../types.js';\nimport { isFunction, isWorkflow } from '../index.js';\n\n// =============================================================================\n// Worker Implementation\n// =============================================================================\n\nclass TemporalFunctionsWorker implements TFNWorker {\n private config: WorkerConfig;\n private functions: Map<string, FunctionDef> = new Map();\n private workflows: Map<string, WorkflowDef> = new Map();\n private worker: Worker | null = null;\n private shutdownRequested = false;\n\n constructor(config: WorkerConfig) {\n this.config = {\n ...config,\n temporal: {\n namespace: 'default',\n ...config.temporal,\n },\n maxConcurrentActivities: config.maxConcurrentActivities ?? 100,\n maxConcurrentWorkflows: config.maxConcurrentWorkflows ?? 50,\n };\n }\n\n /**\n * Register a function or workflow\n */\n register(def: FunctionDef | WorkflowDef): void {\n if (isFunction(def)) {\n this.functions.set(def.name, def);\n } else if (isWorkflow(def)) {\n this.workflows.set(def.name, def);\n } else {\n throw new Error('Invalid definition: must be a function or workflow created with tfn.fn() or tfn.workflow()');\n }\n }\n\n /**\n * Register all exported functions and workflows from a module\n */\n registerModule(module: Record<string, unknown>): void {\n for (const [, value] of Object.entries(module)) {\n if (isFunction(value) || isWorkflow(value)) {\n this.register(value);\n }\n }\n }\n\n /**\n * Build activities object from registered functions\n */\n private buildActivities(): Record<string, (...args: unknown[]) => Promise<unknown>> {\n const activities: Record<string, (...args: unknown[]) => Promise<unknown>> = {};\n\n for (const [name, def] of this.functions) {\n activities[name] = async (input: unknown) => {\n return def.handler(input);\n };\n }\n\n return activities;\n }\n\n /**\n * Generate workflow wrapper code for Temporal\n *\n * This creates the workflow module that Temporal requires,\n * wrapping our user-defined workflow handlers with the proper\n * Temporal workflow context.\n */\n private generateWorkflowBundle(): string {\n const workflowNames = Array.from(this.workflows.keys());\n\n // Generate the workflow module code\n const code = `\n import { proxyActivities, sleep, workflowInfo, setHandler, defineSignal, defineQuery, condition, CancellationScope } from '@temporalio/workflow';\n\n // Proxy all activities\n const activities = proxyActivities({\n startToCloseTimeout: '1 minute',\n });\n\n // Create workflow context\n function createContext() {\n const signalHandlers = new Map();\n const queryHandlers = new Map();\n const signalPayloads = new Map();\n const signalReceived = new Map();\n\n return {\n run: async (fn, input, options = {}) => {\n const activity = activities[fn.name];\n if (!activity) {\n throw new Error(\\`Function \\${fn.name} not registered\\`);\n }\n return activity(input);\n },\n sleep: async (duration) => {\n if (typeof duration === 'string') {\n const ms = parseDuration(duration);\n return sleep(ms);\n }\n return sleep(duration);\n },\n now: () => new Date(),\n startChild: async (workflow, input, options = {}) => {\n throw new Error('startChild not yet implemented');\n },\n continueAsNew: async (input) => {\n throw new Error('continueAsNew not yet implemented');\n },\n onSignal: (signalName, handler) => {\n signalHandlers.set(signalName, handler);\n const signal = defineSignal(signalName);\n setHandler(signal, handler);\n },\n waitForSignal: async (signalName, options = {}) => {\n // Register signal handler if not already registered\n if (!signalReceived.has(signalName)) {\n signalReceived.set(signalName, false);\n signalPayloads.set(signalName, undefined);\n const signal = defineSignal(signalName);\n setHandler(signal, (payload) => {\n signalPayloads.set(signalName, payload);\n signalReceived.set(signalName, true);\n });\n }\n\n // Wait for signal with optional timeout\n const timeoutMs = options.timeout ? parseDuration(options.timeout) : undefined;\n const received = await condition(() => signalReceived.get(signalName), timeoutMs);\n\n if (!received) {\n throw new Error(\\`Timeout waiting for signal: \\${signalName}\\`);\n }\n\n // Reset for potential reuse\n const payload = signalPayloads.get(signalName);\n signalReceived.set(signalName, false);\n signalPayloads.set(signalName, undefined);\n\n return payload;\n },\n onQuery: (queryName, handler) => {\n queryHandlers.set(queryName, handler);\n const query = defineQuery(queryName);\n setHandler(query, handler);\n },\n get info() {\n const info = workflowInfo();\n return {\n workflowId: info.workflowId,\n runId: info.runId,\n taskQueue: info.taskQueue,\n workflowType: info.workflowType,\n namespace: info.namespace,\n };\n },\n };\n }\n\n // Parse duration string to milliseconds\n function parseDuration(duration) {\n const match = duration.match(/^(\\\\d+(?:\\\\.\\\\d+)?)(ms|s|m|h|d)$/);\n if (!match) {\n throw new Error(\\`Invalid duration: \\${duration}\\`);\n }\n const value = parseFloat(match[1]);\n const unit = match[2];\n switch (unit) {\n case 'ms': return value;\n case 's': return value * 1000;\n case 'm': return value * 60 * 1000;\n case 'h': return value * 60 * 60 * 1000;\n case 'd': return value * 24 * 60 * 60 * 1000;\n default: throw new Error(\\`Unknown duration unit: \\${unit}\\`);\n }\n }\n\n // Export workflow functions\n ${workflowNames.map((name) => `\n export async function ${name}(input) {\n const ctx = createContext();\n const handler = __workflowHandlers__.get('${name}');\n if (!handler) {\n throw new Error('Workflow handler not found: ${name}');\n }\n return handler(ctx, input);\n }\n `).join('\\n')}\n `;\n\n return code;\n }\n\n /**\n * Start the worker\n */\n async start(): Promise<void> {\n // Allow starting with external workflowsPath and activities\n const hasRegisteredFunctions = this.functions.size > 0 || this.workflows.size > 0;\n const hasExternalConfig = this.config.workflowsPath || this.config.activities;\n\n if (!hasRegisteredFunctions && !hasExternalConfig) {\n throw new Error('No functions, workflows, or external config provided. Either call register() or provide workflowsPath/activities.');\n }\n\n console.log(`Starting Temporal Functions worker...`);\n console.log(` Temporal: ${this.config.temporal.address}`);\n console.log(` Namespace: ${this.config.temporal.namespace}`);\n console.log(` Task Queue: ${this.config.taskQueue}`);\n if (this.config.workflowsPath) {\n console.log(` Workflows Path: ${this.config.workflowsPath}`);\n }\n if (hasRegisteredFunctions) {\n console.log(` Registered Functions: ${this.functions.size}`);\n console.log(` Registered Workflows: ${this.workflows.size}`);\n }\n\n // Connect to Temporal\n const connection = await NativeConnection.connect({\n address: this.config.temporal.address,\n tls: this.config.temporal.tls\n ? {\n clientCertPair: this.config.temporal.tls.clientCertPath\n ? {\n crt: await import('fs').then((fs) =>\n fs.promises.readFile(this.config.temporal.tls!.clientCertPath!)\n ),\n key: await import('fs').then((fs) =>\n fs.promises.readFile(this.config.temporal.tls!.clientKeyPath!)\n ),\n }\n : undefined,\n }\n : undefined,\n });\n\n // Build activities - merge registered functions with external activities\n const registeredActivities = this.buildActivities();\n const activities = {\n ...registeredActivities,\n ...this.config.activities,\n };\n\n // Build worker options\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const workerOptions: any = {\n connection,\n namespace: this.config.temporal.namespace,\n taskQueue: this.config.taskQueue,\n activities: Object.keys(activities).length > 0 ? activities : undefined,\n maxConcurrentActivityTaskExecutions: this.config.maxConcurrentActivities,\n maxConcurrentWorkflowTaskExecutions: this.config.maxConcurrentWorkflows,\n };\n\n // Add workflowsPath if provided (for external workflow files)\n if (this.config.workflowsPath) {\n workerOptions.workflowsPath = this.config.workflowsPath;\n }\n\n // Add interceptors if provided\n if (this.config.interceptors) {\n workerOptions.interceptors = {};\n\n if (this.config.interceptors.activityInbound) {\n workerOptions.interceptors.activityInbound = this.config.interceptors.activityInbound;\n }\n\n if (this.config.interceptors.workflowModules) {\n workerOptions.interceptors.workflowModules = this.config.interceptors.workflowModules;\n }\n }\n\n // Create worker\n this.worker = await Worker.create(workerOptions);\n\n // Handle graceful shutdown\n const shutdown = async () => {\n if (this.shutdownRequested) return;\n this.shutdownRequested = true;\n console.log('\\nShutting down worker...');\n await this.shutdown();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n console.log('\\nWorker started. Press Ctrl+C to stop.\\n');\n\n // Run the worker (blocks until shutdown)\n await this.worker.run();\n }\n\n /**\n * Gracefully shutdown the worker\n */\n async shutdown(): Promise<void> {\n if (this.worker) {\n this.worker.shutdown();\n this.worker = null;\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create a Temporal Functions worker\n *\n * @example\n * ```typescript\n * // Option 1: Using registered functions (tfn pattern)\n * import { tfn } from 'temporal-functions/worker';\n * import { validateOrder, processOrder } from './functions';\n *\n * const worker = tfn.worker({\n * temporal: { address: 'localhost:7233', namespace: 'default' },\n * taskQueue: 'my-queue',\n * });\n *\n * worker.register(validateOrder);\n * worker.register(processOrder);\n * await worker.start();\n * ```\n *\n * @example\n * ```typescript\n * // Option 2: Using external workflowsPath and activities (processor pattern)\n * import { createWorker } from '@astami/temporal-functions/worker';\n * import { createWorkerInterceptors } from '@astami/temporal-functions/observability';\n * import * as activities from './activities';\n *\n * const worker = createWorker({\n * temporal: { address: 'localhost:7233', namespace: 'loop' },\n * taskQueue: 'stripe-payments',\n * workflowsPath: './dist/workflows/index.js',\n * activities,\n * interceptors: createWorkerInterceptors({ serviceName: 'stripe' }),\n * });\n *\n * await worker.start();\n * ```\n */\nfunction createWorker(config: WorkerConfig): TFNWorker {\n return new TemporalFunctionsWorker(config);\n}\n\n// =============================================================================\n// Export\n// =============================================================================\n\nexport const tfn = {\n worker: createWorker,\n};\n\nexport { createWorker };\nexport type { WorkerConfig, TFNWorker };\nexport default tfn;\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/worker/index.ts"],"names":["NativeConnection","Worker"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Temporal Functions - Main Entry Point\n *\n * This module provides the core API for defining functions and workflows.\n * Import from 'temporal-functions' for function/workflow definitions.\n * Import from 'temporal-functions/client' for triggering workflows.\n * Import from 'temporal-functions/worker' for running workers.\n */\n\nimport type {\n FunctionDef,\n FunctionOptions,\n WorkflowDef,\n WorkflowOptions,\n WorkflowHandler,\n Registry,\n HttpTriggerOptions,\n CronTriggerOptions,\n} from './types.js';\n\n// =============================================================================\n// Global Registry\n// =============================================================================\n\n/**\n * Global registry for all defined functions and workflows\n */\nexport const registry: Registry = {\n functions: new Map(),\n workflows: new Map(),\n};\n\n// =============================================================================\n// Function Definition\n// =============================================================================\n\n/**\n * Define a function (maps to a Temporal Activity)\n *\n * @example\n * ```typescript\n * export const sendEmail = tfn.fn(\n * 'sendEmail',\n * async (params: EmailParams) => {\n * return await emailService.send(params);\n * },\n * { timeout: '30s', retries: 3 }\n * );\n * ```\n */\nfunction fn<TInput, TOutput>(\n name: string,\n handler: (input: TInput) => Promise<TOutput>,\n options: FunctionOptions = {}\n): FunctionDef<TInput, TOutput> {\n const def: FunctionDef<TInput, TOutput> = {\n name,\n handler,\n options: {\n startToCloseTimeout: '1m',\n ...options,\n },\n __type: 'function',\n };\n\n registry.functions.set(name, def as FunctionDef);\n return def;\n}\n\n// =============================================================================\n// Workflow Definition\n// =============================================================================\n\n/**\n * Define a workflow\n *\n * @example\n * ```typescript\n * export const processOrder = tfn.workflow(\n * 'processOrder',\n * async (ctx, order: Order) => {\n * const validated = await ctx.run(validateOrder, order);\n * const paid = await ctx.run(chargePayment, validated);\n * return { orderId: paid.id, status: 'complete' };\n * }\n * );\n * ```\n */\nfunction workflow<TInput, TOutput>(\n name: string,\n handler: WorkflowHandler<TInput, TOutput>,\n options: WorkflowOptions = {}\n): WorkflowDef<TInput, TOutput> {\n const def: WorkflowDef<TInput, TOutput> = {\n name,\n handler,\n options: {\n taskQueue: 'default',\n ...options,\n },\n __type: 'workflow',\n };\n\n registry.workflows.set(name, def as WorkflowDef);\n return def;\n}\n\n// =============================================================================\n// Trigger Definitions (Declarative)\n// =============================================================================\n\ninterface HttpTriggerDef {\n type: 'http';\n method: string;\n path: string;\n workflow: WorkflowDef;\n options: HttpTriggerOptions;\n}\n\ninterface CronTriggerDef {\n type: 'cron';\n schedule: string;\n workflow: WorkflowDef;\n options: CronTriggerOptions;\n}\n\ninterface SignalTriggerDef {\n type: 'signal';\n signalName: string;\n handler: (payload: unknown) => void | Promise<void>;\n}\n\ntype TriggerDef = HttpTriggerDef | CronTriggerDef | SignalTriggerDef;\n\n/**\n * Registry for trigger definitions\n */\nexport const triggers: TriggerDef[] = [];\n\n/**\n * Define an HTTP trigger for a workflow\n *\n * @example\n * ```typescript\n * tfn.http('POST', '/api/orders', processOrder);\n * ```\n */\nfunction http<TInput, TOutput>(\n method: string,\n path: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: HttpTriggerOptions = {}\n): void {\n triggers.push({\n type: 'http',\n method: method.toUpperCase(),\n path,\n workflow: workflow as WorkflowDef,\n options,\n });\n}\n\n/**\n * Define a cron/scheduled trigger for a workflow\n *\n * @example\n * ```typescript\n * tfn.cron('0 9 * * *', dailyReport); // Every day at 9am\n * ```\n */\nfunction cron<TInput, TOutput>(\n schedule: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: CronTriggerOptions = {}\n): void {\n triggers.push({\n type: 'cron',\n schedule,\n workflow: workflow as WorkflowDef,\n options,\n });\n}\n\n/**\n * Define an interval trigger (convenience wrapper around cron)\n *\n * @example\n * ```typescript\n * tfn.interval('5m', healthCheck); // Every 5 minutes\n * ```\n */\nfunction interval<TInput, TOutput>(\n duration: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: CronTriggerOptions = {}\n): void {\n // Convert duration to cron expression\n const cronSchedule = durationToCron(duration);\n cron(cronSchedule, workflow, options);\n}\n\n/**\n * Define a signal trigger\n *\n * @example\n * ```typescript\n * tfn.signal('order.cancel', handleCancellation);\n * ```\n */\nfunction signal(\n signalName: string,\n handler: (payload: unknown) => void | Promise<void>\n): void {\n triggers.push({\n type: 'signal',\n signalName,\n handler,\n });\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\n/**\n * Convert a duration string to a cron expression\n */\nfunction durationToCron(duration: string): string {\n const match = duration.match(/^(\\d+)(s|m|h|d)$/);\n if (!match) {\n throw new Error(`Invalid duration format: ${duration}. Use format like '5m', '1h', '30s'`);\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n\n switch (unit) {\n case 's':\n if (value < 60) {\n return `*/${value} * * * * *`; // Every N seconds (non-standard)\n }\n throw new Error('Seconds interval must be less than 60');\n case 'm':\n return `*/${value} * * * *`; // Every N minutes\n case 'h':\n return `0 */${value} * * *`; // Every N hours\n case 'd':\n return `0 0 */${value} * *`; // Every N days\n default:\n throw new Error(`Unknown duration unit: ${unit}`);\n }\n}\n\n/**\n * Check if a definition is a function\n */\nexport function isFunction(def: unknown): def is FunctionDef {\n return (def as FunctionDef)?.__type === 'function';\n}\n\n/**\n * Check if a definition is a workflow\n */\nexport function isWorkflow(def: unknown): def is WorkflowDef {\n return (def as WorkflowDef)?.__type === 'workflow';\n}\n\n// =============================================================================\n// Main API Export\n// =============================================================================\n\n/**\n * The main Temporal Functions API\n */\nexport const tfn = {\n /** Define a function (activity) */\n fn,\n /** Define a workflow */\n workflow,\n /** Define an HTTP trigger */\n http,\n /** Define a cron trigger */\n cron,\n /** Define an interval trigger */\n interval,\n /** Define a signal trigger */\n signal,\n};\n\n// Re-export types\nexport type {\n FunctionDef,\n FunctionOptions,\n WorkflowDef,\n WorkflowOptions,\n WorkflowContext,\n WorkflowHandler,\n WorkflowInfo,\n RetryPolicy,\n HttpTriggerOptions,\n CronTriggerOptions,\n TemporalConfig,\n ClientConfig,\n WorkerConfig,\n StartWorkflowOptions,\n WorkflowHandle,\n TFNClient,\n TFNWorker,\n} from './types.js';\n\nexport default tfn;\n","/**\n * Temporal Functions - Worker Package\n *\n * Full worker implementation for executing functions and workflows.\n * Import from 'temporal-functions/worker'.\n */\n\nimport { Worker, NativeConnection } from '@temporalio/worker';\nimport type {\n WorkerConfig,\n FunctionDef,\n WorkflowDef,\n TFNWorker,\n} from '../types.js';\nimport { isFunction, isWorkflow } from '../index.js';\n\n// =============================================================================\n// Worker Implementation\n// =============================================================================\n\nclass TemporalFunctionsWorker implements TFNWorker {\n private config: WorkerConfig;\n private functions: Map<string, FunctionDef> = new Map();\n private workflows: Map<string, WorkflowDef> = new Map();\n private worker: Worker | null = null;\n private shutdownRequested = false;\n\n constructor(config: WorkerConfig) {\n this.config = {\n ...config,\n temporal: {\n namespace: 'default',\n ...config.temporal,\n },\n maxConcurrentActivities: config.maxConcurrentActivities ?? 100,\n maxConcurrentWorkflows: config.maxConcurrentWorkflows ?? 50,\n };\n }\n\n /**\n * Register a function or workflow\n */\n register(def: FunctionDef | WorkflowDef): void {\n if (isFunction(def)) {\n this.functions.set(def.name, def);\n } else if (isWorkflow(def)) {\n this.workflows.set(def.name, def);\n } else {\n throw new Error('Invalid definition: must be a function or workflow created with tfn.fn() or tfn.workflow()');\n }\n }\n\n /**\n * Register all exported functions and workflows from a module\n */\n registerModule(module: Record<string, unknown>): void {\n for (const [, value] of Object.entries(module)) {\n if (isFunction(value) || isWorkflow(value)) {\n this.register(value);\n }\n }\n }\n\n /**\n * Build activities object from registered functions\n */\n private buildActivities(): Record<string, (...args: unknown[]) => Promise<unknown>> {\n const activities: Record<string, (...args: unknown[]) => Promise<unknown>> = {};\n\n for (const [name, def] of this.functions) {\n activities[name] = async (input: unknown) => {\n return def.handler(input);\n };\n }\n\n return activities;\n }\n\n /**\n * Generate workflow wrapper code for Temporal\n *\n * This creates the workflow module that Temporal requires,\n * wrapping our user-defined workflow handlers with the proper\n * Temporal workflow context.\n */\n private generateWorkflowBundle(): string {\n const workflowNames = Array.from(this.workflows.keys());\n\n // Generate the workflow module code\n const code = `\n import { proxyActivities, sleep, workflowInfo, setHandler, defineSignal, defineQuery, condition, CancellationScope } from '@temporalio/workflow';\n\n // Proxy all activities\n const activities = proxyActivities({\n startToCloseTimeout: '1 minute',\n });\n\n // Create workflow context\n function createContext() {\n const signalHandlers = new Map();\n const queryHandlers = new Map();\n const signalPayloads = new Map();\n const signalReceived = new Map();\n\n return {\n run: async (fn, input, options = {}) => {\n const activity = activities[fn.name];\n if (!activity) {\n throw new Error(\\`Function \\${fn.name} not registered\\`);\n }\n return activity(input);\n },\n sleep: async (duration) => {\n if (typeof duration === 'string') {\n const ms = parseDuration(duration);\n return sleep(ms);\n }\n return sleep(duration);\n },\n now: () => new Date(),\n startChild: async (workflow, input, options = {}) => {\n throw new Error('startChild not yet implemented');\n },\n continueAsNew: async (input) => {\n throw new Error('continueAsNew not yet implemented');\n },\n onSignal: (signalName, handler) => {\n signalHandlers.set(signalName, handler);\n const signal = defineSignal(signalName);\n setHandler(signal, handler);\n },\n waitForSignal: async (signalName, options = {}) => {\n // Register signal handler if not already registered\n if (!signalReceived.has(signalName)) {\n signalReceived.set(signalName, false);\n signalPayloads.set(signalName, undefined);\n const signal = defineSignal(signalName);\n setHandler(signal, (payload) => {\n signalPayloads.set(signalName, payload);\n signalReceived.set(signalName, true);\n });\n }\n\n // Wait for signal with optional timeout\n const timeoutMs = options.timeout ? parseDuration(options.timeout) : undefined;\n const received = await condition(() => signalReceived.get(signalName), timeoutMs);\n\n if (!received) {\n throw new Error(\\`Timeout waiting for signal: \\${signalName}\\`);\n }\n\n // Reset for potential reuse\n const payload = signalPayloads.get(signalName);\n signalReceived.set(signalName, false);\n signalPayloads.set(signalName, undefined);\n\n return payload;\n },\n onQuery: (queryName, handler) => {\n queryHandlers.set(queryName, handler);\n const query = defineQuery(queryName);\n setHandler(query, handler);\n },\n get info() {\n const info = workflowInfo();\n return {\n workflowId: info.workflowId,\n runId: info.runId,\n taskQueue: info.taskQueue,\n workflowType: info.workflowType,\n namespace: info.namespace,\n };\n },\n };\n }\n\n // Parse duration string to milliseconds\n function parseDuration(duration) {\n const match = duration.match(/^(\\\\d+(?:\\\\.\\\\d+)?)(ms|s|m|h|d)$/);\n if (!match) {\n throw new Error(\\`Invalid duration: \\${duration}\\`);\n }\n const value = parseFloat(match[1]);\n const unit = match[2];\n switch (unit) {\n case 'ms': return value;\n case 's': return value * 1000;\n case 'm': return value * 60 * 1000;\n case 'h': return value * 60 * 60 * 1000;\n case 'd': return value * 24 * 60 * 60 * 1000;\n default: throw new Error(\\`Unknown duration unit: \\${unit}\\`);\n }\n }\n\n // Export workflow functions\n ${workflowNames.map((name) => `\n export async function ${name}(input) {\n const ctx = createContext();\n const handler = __workflowHandlers__.get('${name}');\n if (!handler) {\n throw new Error('Workflow handler not found: ${name}');\n }\n return handler(ctx, input);\n }\n `).join('\\n')}\n `;\n\n return code;\n }\n\n /**\n * Start the worker\n */\n async start(): Promise<void> {\n // Allow starting with external workflowsPath and activities\n const hasRegisteredFunctions = this.functions.size > 0 || this.workflows.size > 0;\n const hasExternalConfig = this.config.workflowsPath || this.config.activities;\n\n if (!hasRegisteredFunctions && !hasExternalConfig) {\n throw new Error('No functions, workflows, or external config provided. Either call register() or provide workflowsPath/activities.');\n }\n\n console.log(`Starting Temporal Functions worker...`);\n console.log(` Temporal: ${this.config.temporal.address}`);\n console.log(` Namespace: ${this.config.temporal.namespace}`);\n console.log(` Task Queue: ${this.config.taskQueue}`);\n if (this.config.workflowsPath) {\n console.log(` Workflows Path: ${this.config.workflowsPath}`);\n }\n if (hasRegisteredFunctions) {\n console.log(` Registered Functions: ${this.functions.size}`);\n console.log(` Registered Workflows: ${this.workflows.size}`);\n }\n\n // Connect to Temporal\n const connection = await NativeConnection.connect({\n address: this.config.temporal.address,\n tls: this.config.temporal.tls\n ? {\n clientCertPair: this.config.temporal.tls.clientCertPath\n ? {\n crt: await import('fs').then((fs) =>\n fs.promises.readFile(this.config.temporal.tls!.clientCertPath!)\n ),\n key: await import('fs').then((fs) =>\n fs.promises.readFile(this.config.temporal.tls!.clientKeyPath!)\n ),\n }\n : undefined,\n }\n : undefined,\n });\n\n // Build activities - merge registered functions with external activities\n const registeredActivities = this.buildActivities();\n const activities = {\n ...registeredActivities,\n ...this.config.activities,\n };\n\n // Build worker options\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const workerOptions: any = {\n connection,\n namespace: this.config.temporal.namespace,\n taskQueue: this.config.taskQueue,\n activities: Object.keys(activities).length > 0 ? activities : undefined,\n maxConcurrentActivityTaskExecutions: this.config.maxConcurrentActivities,\n maxConcurrentWorkflowTaskExecutions: this.config.maxConcurrentWorkflows,\n };\n\n // Add workflowsPath if provided (for external workflow files)\n if (this.config.workflowsPath) {\n workerOptions.workflowsPath = this.config.workflowsPath;\n }\n\n // Add interceptors if provided\n if (this.config.interceptors || this.config.workflowsPath) {\n workerOptions.interceptors = {};\n\n if (this.config.interceptors?.activityInbound) {\n workerOptions.interceptors.activityInbound = this.config.interceptors.activityInbound;\n }\n\n // Build workflow modules list:\n // 1. Include any explicitly configured workflowModules\n // 2. IMPORTANT: Also include the workflowsPath so its `interceptors` export is picked up\n // This enables OpenTelemetry trace propagation from the workflow module\n const workflowModules: string[] = [];\n\n if (this.config.interceptors?.workflowModules) {\n workflowModules.push(...this.config.interceptors.workflowModules);\n }\n\n // Auto-include the workflows module as an interceptor module\n // This allows the workflow module to export an `interceptors` factory function\n // that will be automatically invoked by the Temporal runtime\n if (this.config.workflowsPath) {\n workflowModules.push(this.config.workflowsPath);\n }\n\n if (workflowModules.length > 0) {\n workerOptions.interceptors.workflowModules = workflowModules;\n }\n }\n\n // Create worker\n this.worker = await Worker.create(workerOptions);\n\n // Handle graceful shutdown\n const shutdown = async () => {\n if (this.shutdownRequested) return;\n this.shutdownRequested = true;\n console.log('\\nShutting down worker...');\n await this.shutdown();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n console.log('\\nWorker started. Press Ctrl+C to stop.\\n');\n\n // Run the worker (blocks until shutdown)\n await this.worker.run();\n }\n\n /**\n * Gracefully shutdown the worker\n */\n async shutdown(): Promise<void> {\n if (this.worker) {\n this.worker.shutdown();\n this.worker = null;\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create a Temporal Functions worker\n *\n * @example\n * ```typescript\n * // Option 1: Using registered functions (tfn pattern)\n * import { tfn } from 'temporal-functions/worker';\n * import { validateOrder, processOrder } from './functions';\n *\n * const worker = tfn.worker({\n * temporal: { address: 'localhost:7233', namespace: 'default' },\n * taskQueue: 'my-queue',\n * });\n *\n * worker.register(validateOrder);\n * worker.register(processOrder);\n * await worker.start();\n * ```\n *\n * @example\n * ```typescript\n * // Option 2: Using external workflowsPath and activities (processor pattern)\n * import { createWorker } from '@astami/temporal-functions/worker';\n * import { createWorkerInterceptors } from '@astami/temporal-functions/observability';\n * import * as activities from './activities';\n *\n * const worker = createWorker({\n * temporal: { address: 'localhost:7233', namespace: 'loop' },\n * taskQueue: 'stripe-payments',\n * workflowsPath: './dist/workflows/index.js',\n * activities,\n * interceptors: createWorkerInterceptors({ serviceName: 'stripe' }),\n * });\n *\n * await worker.start();\n * ```\n */\nfunction createWorker(config: WorkerConfig): TFNWorker {\n return new TemporalFunctionsWorker(config);\n}\n\n// =============================================================================\n// Export\n// =============================================================================\n\nexport const tfn = {\n worker: createWorker,\n};\n\nexport { createWorker };\nexport type { WorkerConfig, TFNWorker };\nexport default tfn;\n"]}
|
package/dist/worker/index.mjs
CHANGED
|
@@ -239,13 +239,20 @@ var TemporalFunctionsWorker = class {
|
|
|
239
239
|
if (this.config.workflowsPath) {
|
|
240
240
|
workerOptions.workflowsPath = this.config.workflowsPath;
|
|
241
241
|
}
|
|
242
|
-
if (this.config.interceptors) {
|
|
242
|
+
if (this.config.interceptors || this.config.workflowsPath) {
|
|
243
243
|
workerOptions.interceptors = {};
|
|
244
|
-
if (this.config.interceptors
|
|
244
|
+
if (this.config.interceptors?.activityInbound) {
|
|
245
245
|
workerOptions.interceptors.activityInbound = this.config.interceptors.activityInbound;
|
|
246
246
|
}
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
const workflowModules = [];
|
|
248
|
+
if (this.config.interceptors?.workflowModules) {
|
|
249
|
+
workflowModules.push(...this.config.interceptors.workflowModules);
|
|
250
|
+
}
|
|
251
|
+
if (this.config.workflowsPath) {
|
|
252
|
+
workflowModules.push(this.config.workflowsPath);
|
|
253
|
+
}
|
|
254
|
+
if (workflowModules.length > 0) {
|
|
255
|
+
workerOptions.interceptors.workflowModules = workflowModules;
|
|
249
256
|
}
|
|
250
257
|
}
|
|
251
258
|
this.worker = await Worker.create(workerOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/worker/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Temporal Functions - Main Entry Point\n *\n * This module provides the core API for defining functions and workflows.\n * Import from 'temporal-functions' for function/workflow definitions.\n * Import from 'temporal-functions/client' for triggering workflows.\n * Import from 'temporal-functions/worker' for running workers.\n */\n\nimport type {\n FunctionDef,\n FunctionOptions,\n WorkflowDef,\n WorkflowOptions,\n WorkflowHandler,\n Registry,\n HttpTriggerOptions,\n CronTriggerOptions,\n} from './types.js';\n\n// =============================================================================\n// Global Registry\n// =============================================================================\n\n/**\n * Global registry for all defined functions and workflows\n */\nexport const registry: Registry = {\n functions: new Map(),\n workflows: new Map(),\n};\n\n// =============================================================================\n// Function Definition\n// =============================================================================\n\n/**\n * Define a function (maps to a Temporal Activity)\n *\n * @example\n * ```typescript\n * export const sendEmail = tfn.fn(\n * 'sendEmail',\n * async (params: EmailParams) => {\n * return await emailService.send(params);\n * },\n * { timeout: '30s', retries: 3 }\n * );\n * ```\n */\nfunction fn<TInput, TOutput>(\n name: string,\n handler: (input: TInput) => Promise<TOutput>,\n options: FunctionOptions = {}\n): FunctionDef<TInput, TOutput> {\n const def: FunctionDef<TInput, TOutput> = {\n name,\n handler,\n options: {\n startToCloseTimeout: '1m',\n ...options,\n },\n __type: 'function',\n };\n\n registry.functions.set(name, def as FunctionDef);\n return def;\n}\n\n// =============================================================================\n// Workflow Definition\n// =============================================================================\n\n/**\n * Define a workflow\n *\n * @example\n * ```typescript\n * export const processOrder = tfn.workflow(\n * 'processOrder',\n * async (ctx, order: Order) => {\n * const validated = await ctx.run(validateOrder, order);\n * const paid = await ctx.run(chargePayment, validated);\n * return { orderId: paid.id, status: 'complete' };\n * }\n * );\n * ```\n */\nfunction workflow<TInput, TOutput>(\n name: string,\n handler: WorkflowHandler<TInput, TOutput>,\n options: WorkflowOptions = {}\n): WorkflowDef<TInput, TOutput> {\n const def: WorkflowDef<TInput, TOutput> = {\n name,\n handler,\n options: {\n taskQueue: 'default',\n ...options,\n },\n __type: 'workflow',\n };\n\n registry.workflows.set(name, def as WorkflowDef);\n return def;\n}\n\n// =============================================================================\n// Trigger Definitions (Declarative)\n// =============================================================================\n\ninterface HttpTriggerDef {\n type: 'http';\n method: string;\n path: string;\n workflow: WorkflowDef;\n options: HttpTriggerOptions;\n}\n\ninterface CronTriggerDef {\n type: 'cron';\n schedule: string;\n workflow: WorkflowDef;\n options: CronTriggerOptions;\n}\n\ninterface SignalTriggerDef {\n type: 'signal';\n signalName: string;\n handler: (payload: unknown) => void | Promise<void>;\n}\n\ntype TriggerDef = HttpTriggerDef | CronTriggerDef | SignalTriggerDef;\n\n/**\n * Registry for trigger definitions\n */\nexport const triggers: TriggerDef[] = [];\n\n/**\n * Define an HTTP trigger for a workflow\n *\n * @example\n * ```typescript\n * tfn.http('POST', '/api/orders', processOrder);\n * ```\n */\nfunction http<TInput, TOutput>(\n method: string,\n path: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: HttpTriggerOptions = {}\n): void {\n triggers.push({\n type: 'http',\n method: method.toUpperCase(),\n path,\n workflow: workflow as WorkflowDef,\n options,\n });\n}\n\n/**\n * Define a cron/scheduled trigger for a workflow\n *\n * @example\n * ```typescript\n * tfn.cron('0 9 * * *', dailyReport); // Every day at 9am\n * ```\n */\nfunction cron<TInput, TOutput>(\n schedule: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: CronTriggerOptions = {}\n): void {\n triggers.push({\n type: 'cron',\n schedule,\n workflow: workflow as WorkflowDef,\n options,\n });\n}\n\n/**\n * Define an interval trigger (convenience wrapper around cron)\n *\n * @example\n * ```typescript\n * tfn.interval('5m', healthCheck); // Every 5 minutes\n * ```\n */\nfunction interval<TInput, TOutput>(\n duration: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: CronTriggerOptions = {}\n): void {\n // Convert duration to cron expression\n const cronSchedule = durationToCron(duration);\n cron(cronSchedule, workflow, options);\n}\n\n/**\n * Define a signal trigger\n *\n * @example\n * ```typescript\n * tfn.signal('order.cancel', handleCancellation);\n * ```\n */\nfunction signal(\n signalName: string,\n handler: (payload: unknown) => void | Promise<void>\n): void {\n triggers.push({\n type: 'signal',\n signalName,\n handler,\n });\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\n/**\n * Convert a duration string to a cron expression\n */\nfunction durationToCron(duration: string): string {\n const match = duration.match(/^(\\d+)(s|m|h|d)$/);\n if (!match) {\n throw new Error(`Invalid duration format: ${duration}. Use format like '5m', '1h', '30s'`);\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n\n switch (unit) {\n case 's':\n if (value < 60) {\n return `*/${value} * * * * *`; // Every N seconds (non-standard)\n }\n throw new Error('Seconds interval must be less than 60');\n case 'm':\n return `*/${value} * * * *`; // Every N minutes\n case 'h':\n return `0 */${value} * * *`; // Every N hours\n case 'd':\n return `0 0 */${value} * *`; // Every N days\n default:\n throw new Error(`Unknown duration unit: ${unit}`);\n }\n}\n\n/**\n * Check if a definition is a function\n */\nexport function isFunction(def: unknown): def is FunctionDef {\n return (def as FunctionDef)?.__type === 'function';\n}\n\n/**\n * Check if a definition is a workflow\n */\nexport function isWorkflow(def: unknown): def is WorkflowDef {\n return (def as WorkflowDef)?.__type === 'workflow';\n}\n\n// =============================================================================\n// Main API Export\n// =============================================================================\n\n/**\n * The main Temporal Functions API\n */\nexport const tfn = {\n /** Define a function (activity) */\n fn,\n /** Define a workflow */\n workflow,\n /** Define an HTTP trigger */\n http,\n /** Define a cron trigger */\n cron,\n /** Define an interval trigger */\n interval,\n /** Define a signal trigger */\n signal,\n};\n\n// Re-export types\nexport type {\n FunctionDef,\n FunctionOptions,\n WorkflowDef,\n WorkflowOptions,\n WorkflowContext,\n WorkflowHandler,\n WorkflowInfo,\n RetryPolicy,\n HttpTriggerOptions,\n CronTriggerOptions,\n TemporalConfig,\n ClientConfig,\n WorkerConfig,\n StartWorkflowOptions,\n WorkflowHandle,\n TFNClient,\n TFNWorker,\n} from './types.js';\n\nexport default tfn;\n","/**\n * Temporal Functions - Worker Package\n *\n * Full worker implementation for executing functions and workflows.\n * Import from 'temporal-functions/worker'.\n */\n\nimport { Worker, NativeConnection } from '@temporalio/worker';\nimport type {\n WorkerConfig,\n FunctionDef,\n WorkflowDef,\n TFNWorker,\n} from '../types.js';\nimport { isFunction, isWorkflow } from '../index.js';\n\n// =============================================================================\n// Worker Implementation\n// =============================================================================\n\nclass TemporalFunctionsWorker implements TFNWorker {\n private config: WorkerConfig;\n private functions: Map<string, FunctionDef> = new Map();\n private workflows: Map<string, WorkflowDef> = new Map();\n private worker: Worker | null = null;\n private shutdownRequested = false;\n\n constructor(config: WorkerConfig) {\n this.config = {\n ...config,\n temporal: {\n namespace: 'default',\n ...config.temporal,\n },\n maxConcurrentActivities: config.maxConcurrentActivities ?? 100,\n maxConcurrentWorkflows: config.maxConcurrentWorkflows ?? 50,\n };\n }\n\n /**\n * Register a function or workflow\n */\n register(def: FunctionDef | WorkflowDef): void {\n if (isFunction(def)) {\n this.functions.set(def.name, def);\n } else if (isWorkflow(def)) {\n this.workflows.set(def.name, def);\n } else {\n throw new Error('Invalid definition: must be a function or workflow created with tfn.fn() or tfn.workflow()');\n }\n }\n\n /**\n * Register all exported functions and workflows from a module\n */\n registerModule(module: Record<string, unknown>): void {\n for (const [, value] of Object.entries(module)) {\n if (isFunction(value) || isWorkflow(value)) {\n this.register(value);\n }\n }\n }\n\n /**\n * Build activities object from registered functions\n */\n private buildActivities(): Record<string, (...args: unknown[]) => Promise<unknown>> {\n const activities: Record<string, (...args: unknown[]) => Promise<unknown>> = {};\n\n for (const [name, def] of this.functions) {\n activities[name] = async (input: unknown) => {\n return def.handler(input);\n };\n }\n\n return activities;\n }\n\n /**\n * Generate workflow wrapper code for Temporal\n *\n * This creates the workflow module that Temporal requires,\n * wrapping our user-defined workflow handlers with the proper\n * Temporal workflow context.\n */\n private generateWorkflowBundle(): string {\n const workflowNames = Array.from(this.workflows.keys());\n\n // Generate the workflow module code\n const code = `\n import { proxyActivities, sleep, workflowInfo, setHandler, defineSignal, defineQuery, condition, CancellationScope } from '@temporalio/workflow';\n\n // Proxy all activities\n const activities = proxyActivities({\n startToCloseTimeout: '1 minute',\n });\n\n // Create workflow context\n function createContext() {\n const signalHandlers = new Map();\n const queryHandlers = new Map();\n const signalPayloads = new Map();\n const signalReceived = new Map();\n\n return {\n run: async (fn, input, options = {}) => {\n const activity = activities[fn.name];\n if (!activity) {\n throw new Error(\\`Function \\${fn.name} not registered\\`);\n }\n return activity(input);\n },\n sleep: async (duration) => {\n if (typeof duration === 'string') {\n const ms = parseDuration(duration);\n return sleep(ms);\n }\n return sleep(duration);\n },\n now: () => new Date(),\n startChild: async (workflow, input, options = {}) => {\n throw new Error('startChild not yet implemented');\n },\n continueAsNew: async (input) => {\n throw new Error('continueAsNew not yet implemented');\n },\n onSignal: (signalName, handler) => {\n signalHandlers.set(signalName, handler);\n const signal = defineSignal(signalName);\n setHandler(signal, handler);\n },\n waitForSignal: async (signalName, options = {}) => {\n // Register signal handler if not already registered\n if (!signalReceived.has(signalName)) {\n signalReceived.set(signalName, false);\n signalPayloads.set(signalName, undefined);\n const signal = defineSignal(signalName);\n setHandler(signal, (payload) => {\n signalPayloads.set(signalName, payload);\n signalReceived.set(signalName, true);\n });\n }\n\n // Wait for signal with optional timeout\n const timeoutMs = options.timeout ? parseDuration(options.timeout) : undefined;\n const received = await condition(() => signalReceived.get(signalName), timeoutMs);\n\n if (!received) {\n throw new Error(\\`Timeout waiting for signal: \\${signalName}\\`);\n }\n\n // Reset for potential reuse\n const payload = signalPayloads.get(signalName);\n signalReceived.set(signalName, false);\n signalPayloads.set(signalName, undefined);\n\n return payload;\n },\n onQuery: (queryName, handler) => {\n queryHandlers.set(queryName, handler);\n const query = defineQuery(queryName);\n setHandler(query, handler);\n },\n get info() {\n const info = workflowInfo();\n return {\n workflowId: info.workflowId,\n runId: info.runId,\n taskQueue: info.taskQueue,\n workflowType: info.workflowType,\n namespace: info.namespace,\n };\n },\n };\n }\n\n // Parse duration string to milliseconds\n function parseDuration(duration) {\n const match = duration.match(/^(\\\\d+(?:\\\\.\\\\d+)?)(ms|s|m|h|d)$/);\n if (!match) {\n throw new Error(\\`Invalid duration: \\${duration}\\`);\n }\n const value = parseFloat(match[1]);\n const unit = match[2];\n switch (unit) {\n case 'ms': return value;\n case 's': return value * 1000;\n case 'm': return value * 60 * 1000;\n case 'h': return value * 60 * 60 * 1000;\n case 'd': return value * 24 * 60 * 60 * 1000;\n default: throw new Error(\\`Unknown duration unit: \\${unit}\\`);\n }\n }\n\n // Export workflow functions\n ${workflowNames.map((name) => `\n export async function ${name}(input) {\n const ctx = createContext();\n const handler = __workflowHandlers__.get('${name}');\n if (!handler) {\n throw new Error('Workflow handler not found: ${name}');\n }\n return handler(ctx, input);\n }\n `).join('\\n')}\n `;\n\n return code;\n }\n\n /**\n * Start the worker\n */\n async start(): Promise<void> {\n // Allow starting with external workflowsPath and activities\n const hasRegisteredFunctions = this.functions.size > 0 || this.workflows.size > 0;\n const hasExternalConfig = this.config.workflowsPath || this.config.activities;\n\n if (!hasRegisteredFunctions && !hasExternalConfig) {\n throw new Error('No functions, workflows, or external config provided. Either call register() or provide workflowsPath/activities.');\n }\n\n console.log(`Starting Temporal Functions worker...`);\n console.log(` Temporal: ${this.config.temporal.address}`);\n console.log(` Namespace: ${this.config.temporal.namespace}`);\n console.log(` Task Queue: ${this.config.taskQueue}`);\n if (this.config.workflowsPath) {\n console.log(` Workflows Path: ${this.config.workflowsPath}`);\n }\n if (hasRegisteredFunctions) {\n console.log(` Registered Functions: ${this.functions.size}`);\n console.log(` Registered Workflows: ${this.workflows.size}`);\n }\n\n // Connect to Temporal\n const connection = await NativeConnection.connect({\n address: this.config.temporal.address,\n tls: this.config.temporal.tls\n ? {\n clientCertPair: this.config.temporal.tls.clientCertPath\n ? {\n crt: await import('fs').then((fs) =>\n fs.promises.readFile(this.config.temporal.tls!.clientCertPath!)\n ),\n key: await import('fs').then((fs) =>\n fs.promises.readFile(this.config.temporal.tls!.clientKeyPath!)\n ),\n }\n : undefined,\n }\n : undefined,\n });\n\n // Build activities - merge registered functions with external activities\n const registeredActivities = this.buildActivities();\n const activities = {\n ...registeredActivities,\n ...this.config.activities,\n };\n\n // Build worker options\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const workerOptions: any = {\n connection,\n namespace: this.config.temporal.namespace,\n taskQueue: this.config.taskQueue,\n activities: Object.keys(activities).length > 0 ? activities : undefined,\n maxConcurrentActivityTaskExecutions: this.config.maxConcurrentActivities,\n maxConcurrentWorkflowTaskExecutions: this.config.maxConcurrentWorkflows,\n };\n\n // Add workflowsPath if provided (for external workflow files)\n if (this.config.workflowsPath) {\n workerOptions.workflowsPath = this.config.workflowsPath;\n }\n\n // Add interceptors if provided\n if (this.config.interceptors) {\n workerOptions.interceptors = {};\n\n if (this.config.interceptors.activityInbound) {\n workerOptions.interceptors.activityInbound = this.config.interceptors.activityInbound;\n }\n\n if (this.config.interceptors.workflowModules) {\n workerOptions.interceptors.workflowModules = this.config.interceptors.workflowModules;\n }\n }\n\n // Create worker\n this.worker = await Worker.create(workerOptions);\n\n // Handle graceful shutdown\n const shutdown = async () => {\n if (this.shutdownRequested) return;\n this.shutdownRequested = true;\n console.log('\\nShutting down worker...');\n await this.shutdown();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n console.log('\\nWorker started. Press Ctrl+C to stop.\\n');\n\n // Run the worker (blocks until shutdown)\n await this.worker.run();\n }\n\n /**\n * Gracefully shutdown the worker\n */\n async shutdown(): Promise<void> {\n if (this.worker) {\n this.worker.shutdown();\n this.worker = null;\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create a Temporal Functions worker\n *\n * @example\n * ```typescript\n * // Option 1: Using registered functions (tfn pattern)\n * import { tfn } from 'temporal-functions/worker';\n * import { validateOrder, processOrder } from './functions';\n *\n * const worker = tfn.worker({\n * temporal: { address: 'localhost:7233', namespace: 'default' },\n * taskQueue: 'my-queue',\n * });\n *\n * worker.register(validateOrder);\n * worker.register(processOrder);\n * await worker.start();\n * ```\n *\n * @example\n * ```typescript\n * // Option 2: Using external workflowsPath and activities (processor pattern)\n * import { createWorker } from '@astami/temporal-functions/worker';\n * import { createWorkerInterceptors } from '@astami/temporal-functions/observability';\n * import * as activities from './activities';\n *\n * const worker = createWorker({\n * temporal: { address: 'localhost:7233', namespace: 'loop' },\n * taskQueue: 'stripe-payments',\n * workflowsPath: './dist/workflows/index.js',\n * activities,\n * interceptors: createWorkerInterceptors({ serviceName: 'stripe' }),\n * });\n *\n * await worker.start();\n * ```\n */\nfunction createWorker(config: WorkerConfig): TFNWorker {\n return new TemporalFunctionsWorker(config);\n}\n\n// =============================================================================\n// Export\n// =============================================================================\n\nexport const tfn = {\n worker: createWorker,\n};\n\nexport { createWorker };\nexport type { WorkerConfig, TFNWorker };\nexport default tfn;\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/worker/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Temporal Functions - Main Entry Point\n *\n * This module provides the core API for defining functions and workflows.\n * Import from 'temporal-functions' for function/workflow definitions.\n * Import from 'temporal-functions/client' for triggering workflows.\n * Import from 'temporal-functions/worker' for running workers.\n */\n\nimport type {\n FunctionDef,\n FunctionOptions,\n WorkflowDef,\n WorkflowOptions,\n WorkflowHandler,\n Registry,\n HttpTriggerOptions,\n CronTriggerOptions,\n} from './types.js';\n\n// =============================================================================\n// Global Registry\n// =============================================================================\n\n/**\n * Global registry for all defined functions and workflows\n */\nexport const registry: Registry = {\n functions: new Map(),\n workflows: new Map(),\n};\n\n// =============================================================================\n// Function Definition\n// =============================================================================\n\n/**\n * Define a function (maps to a Temporal Activity)\n *\n * @example\n * ```typescript\n * export const sendEmail = tfn.fn(\n * 'sendEmail',\n * async (params: EmailParams) => {\n * return await emailService.send(params);\n * },\n * { timeout: '30s', retries: 3 }\n * );\n * ```\n */\nfunction fn<TInput, TOutput>(\n name: string,\n handler: (input: TInput) => Promise<TOutput>,\n options: FunctionOptions = {}\n): FunctionDef<TInput, TOutput> {\n const def: FunctionDef<TInput, TOutput> = {\n name,\n handler,\n options: {\n startToCloseTimeout: '1m',\n ...options,\n },\n __type: 'function',\n };\n\n registry.functions.set(name, def as FunctionDef);\n return def;\n}\n\n// =============================================================================\n// Workflow Definition\n// =============================================================================\n\n/**\n * Define a workflow\n *\n * @example\n * ```typescript\n * export const processOrder = tfn.workflow(\n * 'processOrder',\n * async (ctx, order: Order) => {\n * const validated = await ctx.run(validateOrder, order);\n * const paid = await ctx.run(chargePayment, validated);\n * return { orderId: paid.id, status: 'complete' };\n * }\n * );\n * ```\n */\nfunction workflow<TInput, TOutput>(\n name: string,\n handler: WorkflowHandler<TInput, TOutput>,\n options: WorkflowOptions = {}\n): WorkflowDef<TInput, TOutput> {\n const def: WorkflowDef<TInput, TOutput> = {\n name,\n handler,\n options: {\n taskQueue: 'default',\n ...options,\n },\n __type: 'workflow',\n };\n\n registry.workflows.set(name, def as WorkflowDef);\n return def;\n}\n\n// =============================================================================\n// Trigger Definitions (Declarative)\n// =============================================================================\n\ninterface HttpTriggerDef {\n type: 'http';\n method: string;\n path: string;\n workflow: WorkflowDef;\n options: HttpTriggerOptions;\n}\n\ninterface CronTriggerDef {\n type: 'cron';\n schedule: string;\n workflow: WorkflowDef;\n options: CronTriggerOptions;\n}\n\ninterface SignalTriggerDef {\n type: 'signal';\n signalName: string;\n handler: (payload: unknown) => void | Promise<void>;\n}\n\ntype TriggerDef = HttpTriggerDef | CronTriggerDef | SignalTriggerDef;\n\n/**\n * Registry for trigger definitions\n */\nexport const triggers: TriggerDef[] = [];\n\n/**\n * Define an HTTP trigger for a workflow\n *\n * @example\n * ```typescript\n * tfn.http('POST', '/api/orders', processOrder);\n * ```\n */\nfunction http<TInput, TOutput>(\n method: string,\n path: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: HttpTriggerOptions = {}\n): void {\n triggers.push({\n type: 'http',\n method: method.toUpperCase(),\n path,\n workflow: workflow as WorkflowDef,\n options,\n });\n}\n\n/**\n * Define a cron/scheduled trigger for a workflow\n *\n * @example\n * ```typescript\n * tfn.cron('0 9 * * *', dailyReport); // Every day at 9am\n * ```\n */\nfunction cron<TInput, TOutput>(\n schedule: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: CronTriggerOptions = {}\n): void {\n triggers.push({\n type: 'cron',\n schedule,\n workflow: workflow as WorkflowDef,\n options,\n });\n}\n\n/**\n * Define an interval trigger (convenience wrapper around cron)\n *\n * @example\n * ```typescript\n * tfn.interval('5m', healthCheck); // Every 5 minutes\n * ```\n */\nfunction interval<TInput, TOutput>(\n duration: string,\n workflow: WorkflowDef<TInput, TOutput>,\n options: CronTriggerOptions = {}\n): void {\n // Convert duration to cron expression\n const cronSchedule = durationToCron(duration);\n cron(cronSchedule, workflow, options);\n}\n\n/**\n * Define a signal trigger\n *\n * @example\n * ```typescript\n * tfn.signal('order.cancel', handleCancellation);\n * ```\n */\nfunction signal(\n signalName: string,\n handler: (payload: unknown) => void | Promise<void>\n): void {\n triggers.push({\n type: 'signal',\n signalName,\n handler,\n });\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\n/**\n * Convert a duration string to a cron expression\n */\nfunction durationToCron(duration: string): string {\n const match = duration.match(/^(\\d+)(s|m|h|d)$/);\n if (!match) {\n throw new Error(`Invalid duration format: ${duration}. Use format like '5m', '1h', '30s'`);\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2];\n\n switch (unit) {\n case 's':\n if (value < 60) {\n return `*/${value} * * * * *`; // Every N seconds (non-standard)\n }\n throw new Error('Seconds interval must be less than 60');\n case 'm':\n return `*/${value} * * * *`; // Every N minutes\n case 'h':\n return `0 */${value} * * *`; // Every N hours\n case 'd':\n return `0 0 */${value} * *`; // Every N days\n default:\n throw new Error(`Unknown duration unit: ${unit}`);\n }\n}\n\n/**\n * Check if a definition is a function\n */\nexport function isFunction(def: unknown): def is FunctionDef {\n return (def as FunctionDef)?.__type === 'function';\n}\n\n/**\n * Check if a definition is a workflow\n */\nexport function isWorkflow(def: unknown): def is WorkflowDef {\n return (def as WorkflowDef)?.__type === 'workflow';\n}\n\n// =============================================================================\n// Main API Export\n// =============================================================================\n\n/**\n * The main Temporal Functions API\n */\nexport const tfn = {\n /** Define a function (activity) */\n fn,\n /** Define a workflow */\n workflow,\n /** Define an HTTP trigger */\n http,\n /** Define a cron trigger */\n cron,\n /** Define an interval trigger */\n interval,\n /** Define a signal trigger */\n signal,\n};\n\n// Re-export types\nexport type {\n FunctionDef,\n FunctionOptions,\n WorkflowDef,\n WorkflowOptions,\n WorkflowContext,\n WorkflowHandler,\n WorkflowInfo,\n RetryPolicy,\n HttpTriggerOptions,\n CronTriggerOptions,\n TemporalConfig,\n ClientConfig,\n WorkerConfig,\n StartWorkflowOptions,\n WorkflowHandle,\n TFNClient,\n TFNWorker,\n} from './types.js';\n\nexport default tfn;\n","/**\n * Temporal Functions - Worker Package\n *\n * Full worker implementation for executing functions and workflows.\n * Import from 'temporal-functions/worker'.\n */\n\nimport { Worker, NativeConnection } from '@temporalio/worker';\nimport type {\n WorkerConfig,\n FunctionDef,\n WorkflowDef,\n TFNWorker,\n} from '../types.js';\nimport { isFunction, isWorkflow } from '../index.js';\n\n// =============================================================================\n// Worker Implementation\n// =============================================================================\n\nclass TemporalFunctionsWorker implements TFNWorker {\n private config: WorkerConfig;\n private functions: Map<string, FunctionDef> = new Map();\n private workflows: Map<string, WorkflowDef> = new Map();\n private worker: Worker | null = null;\n private shutdownRequested = false;\n\n constructor(config: WorkerConfig) {\n this.config = {\n ...config,\n temporal: {\n namespace: 'default',\n ...config.temporal,\n },\n maxConcurrentActivities: config.maxConcurrentActivities ?? 100,\n maxConcurrentWorkflows: config.maxConcurrentWorkflows ?? 50,\n };\n }\n\n /**\n * Register a function or workflow\n */\n register(def: FunctionDef | WorkflowDef): void {\n if (isFunction(def)) {\n this.functions.set(def.name, def);\n } else if (isWorkflow(def)) {\n this.workflows.set(def.name, def);\n } else {\n throw new Error('Invalid definition: must be a function or workflow created with tfn.fn() or tfn.workflow()');\n }\n }\n\n /**\n * Register all exported functions and workflows from a module\n */\n registerModule(module: Record<string, unknown>): void {\n for (const [, value] of Object.entries(module)) {\n if (isFunction(value) || isWorkflow(value)) {\n this.register(value);\n }\n }\n }\n\n /**\n * Build activities object from registered functions\n */\n private buildActivities(): Record<string, (...args: unknown[]) => Promise<unknown>> {\n const activities: Record<string, (...args: unknown[]) => Promise<unknown>> = {};\n\n for (const [name, def] of this.functions) {\n activities[name] = async (input: unknown) => {\n return def.handler(input);\n };\n }\n\n return activities;\n }\n\n /**\n * Generate workflow wrapper code for Temporal\n *\n * This creates the workflow module that Temporal requires,\n * wrapping our user-defined workflow handlers with the proper\n * Temporal workflow context.\n */\n private generateWorkflowBundle(): string {\n const workflowNames = Array.from(this.workflows.keys());\n\n // Generate the workflow module code\n const code = `\n import { proxyActivities, sleep, workflowInfo, setHandler, defineSignal, defineQuery, condition, CancellationScope } from '@temporalio/workflow';\n\n // Proxy all activities\n const activities = proxyActivities({\n startToCloseTimeout: '1 minute',\n });\n\n // Create workflow context\n function createContext() {\n const signalHandlers = new Map();\n const queryHandlers = new Map();\n const signalPayloads = new Map();\n const signalReceived = new Map();\n\n return {\n run: async (fn, input, options = {}) => {\n const activity = activities[fn.name];\n if (!activity) {\n throw new Error(\\`Function \\${fn.name} not registered\\`);\n }\n return activity(input);\n },\n sleep: async (duration) => {\n if (typeof duration === 'string') {\n const ms = parseDuration(duration);\n return sleep(ms);\n }\n return sleep(duration);\n },\n now: () => new Date(),\n startChild: async (workflow, input, options = {}) => {\n throw new Error('startChild not yet implemented');\n },\n continueAsNew: async (input) => {\n throw new Error('continueAsNew not yet implemented');\n },\n onSignal: (signalName, handler) => {\n signalHandlers.set(signalName, handler);\n const signal = defineSignal(signalName);\n setHandler(signal, handler);\n },\n waitForSignal: async (signalName, options = {}) => {\n // Register signal handler if not already registered\n if (!signalReceived.has(signalName)) {\n signalReceived.set(signalName, false);\n signalPayloads.set(signalName, undefined);\n const signal = defineSignal(signalName);\n setHandler(signal, (payload) => {\n signalPayloads.set(signalName, payload);\n signalReceived.set(signalName, true);\n });\n }\n\n // Wait for signal with optional timeout\n const timeoutMs = options.timeout ? parseDuration(options.timeout) : undefined;\n const received = await condition(() => signalReceived.get(signalName), timeoutMs);\n\n if (!received) {\n throw new Error(\\`Timeout waiting for signal: \\${signalName}\\`);\n }\n\n // Reset for potential reuse\n const payload = signalPayloads.get(signalName);\n signalReceived.set(signalName, false);\n signalPayloads.set(signalName, undefined);\n\n return payload;\n },\n onQuery: (queryName, handler) => {\n queryHandlers.set(queryName, handler);\n const query = defineQuery(queryName);\n setHandler(query, handler);\n },\n get info() {\n const info = workflowInfo();\n return {\n workflowId: info.workflowId,\n runId: info.runId,\n taskQueue: info.taskQueue,\n workflowType: info.workflowType,\n namespace: info.namespace,\n };\n },\n };\n }\n\n // Parse duration string to milliseconds\n function parseDuration(duration) {\n const match = duration.match(/^(\\\\d+(?:\\\\.\\\\d+)?)(ms|s|m|h|d)$/);\n if (!match) {\n throw new Error(\\`Invalid duration: \\${duration}\\`);\n }\n const value = parseFloat(match[1]);\n const unit = match[2];\n switch (unit) {\n case 'ms': return value;\n case 's': return value * 1000;\n case 'm': return value * 60 * 1000;\n case 'h': return value * 60 * 60 * 1000;\n case 'd': return value * 24 * 60 * 60 * 1000;\n default: throw new Error(\\`Unknown duration unit: \\${unit}\\`);\n }\n }\n\n // Export workflow functions\n ${workflowNames.map((name) => `\n export async function ${name}(input) {\n const ctx = createContext();\n const handler = __workflowHandlers__.get('${name}');\n if (!handler) {\n throw new Error('Workflow handler not found: ${name}');\n }\n return handler(ctx, input);\n }\n `).join('\\n')}\n `;\n\n return code;\n }\n\n /**\n * Start the worker\n */\n async start(): Promise<void> {\n // Allow starting with external workflowsPath and activities\n const hasRegisteredFunctions = this.functions.size > 0 || this.workflows.size > 0;\n const hasExternalConfig = this.config.workflowsPath || this.config.activities;\n\n if (!hasRegisteredFunctions && !hasExternalConfig) {\n throw new Error('No functions, workflows, or external config provided. Either call register() or provide workflowsPath/activities.');\n }\n\n console.log(`Starting Temporal Functions worker...`);\n console.log(` Temporal: ${this.config.temporal.address}`);\n console.log(` Namespace: ${this.config.temporal.namespace}`);\n console.log(` Task Queue: ${this.config.taskQueue}`);\n if (this.config.workflowsPath) {\n console.log(` Workflows Path: ${this.config.workflowsPath}`);\n }\n if (hasRegisteredFunctions) {\n console.log(` Registered Functions: ${this.functions.size}`);\n console.log(` Registered Workflows: ${this.workflows.size}`);\n }\n\n // Connect to Temporal\n const connection = await NativeConnection.connect({\n address: this.config.temporal.address,\n tls: this.config.temporal.tls\n ? {\n clientCertPair: this.config.temporal.tls.clientCertPath\n ? {\n crt: await import('fs').then((fs) =>\n fs.promises.readFile(this.config.temporal.tls!.clientCertPath!)\n ),\n key: await import('fs').then((fs) =>\n fs.promises.readFile(this.config.temporal.tls!.clientKeyPath!)\n ),\n }\n : undefined,\n }\n : undefined,\n });\n\n // Build activities - merge registered functions with external activities\n const registeredActivities = this.buildActivities();\n const activities = {\n ...registeredActivities,\n ...this.config.activities,\n };\n\n // Build worker options\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const workerOptions: any = {\n connection,\n namespace: this.config.temporal.namespace,\n taskQueue: this.config.taskQueue,\n activities: Object.keys(activities).length > 0 ? activities : undefined,\n maxConcurrentActivityTaskExecutions: this.config.maxConcurrentActivities,\n maxConcurrentWorkflowTaskExecutions: this.config.maxConcurrentWorkflows,\n };\n\n // Add workflowsPath if provided (for external workflow files)\n if (this.config.workflowsPath) {\n workerOptions.workflowsPath = this.config.workflowsPath;\n }\n\n // Add interceptors if provided\n if (this.config.interceptors || this.config.workflowsPath) {\n workerOptions.interceptors = {};\n\n if (this.config.interceptors?.activityInbound) {\n workerOptions.interceptors.activityInbound = this.config.interceptors.activityInbound;\n }\n\n // Build workflow modules list:\n // 1. Include any explicitly configured workflowModules\n // 2. IMPORTANT: Also include the workflowsPath so its `interceptors` export is picked up\n // This enables OpenTelemetry trace propagation from the workflow module\n const workflowModules: string[] = [];\n\n if (this.config.interceptors?.workflowModules) {\n workflowModules.push(...this.config.interceptors.workflowModules);\n }\n\n // Auto-include the workflows module as an interceptor module\n // This allows the workflow module to export an `interceptors` factory function\n // that will be automatically invoked by the Temporal runtime\n if (this.config.workflowsPath) {\n workflowModules.push(this.config.workflowsPath);\n }\n\n if (workflowModules.length > 0) {\n workerOptions.interceptors.workflowModules = workflowModules;\n }\n }\n\n // Create worker\n this.worker = await Worker.create(workerOptions);\n\n // Handle graceful shutdown\n const shutdown = async () => {\n if (this.shutdownRequested) return;\n this.shutdownRequested = true;\n console.log('\\nShutting down worker...');\n await this.shutdown();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n console.log('\\nWorker started. Press Ctrl+C to stop.\\n');\n\n // Run the worker (blocks until shutdown)\n await this.worker.run();\n }\n\n /**\n * Gracefully shutdown the worker\n */\n async shutdown(): Promise<void> {\n if (this.worker) {\n this.worker.shutdown();\n this.worker = null;\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create a Temporal Functions worker\n *\n * @example\n * ```typescript\n * // Option 1: Using registered functions (tfn pattern)\n * import { tfn } from 'temporal-functions/worker';\n * import { validateOrder, processOrder } from './functions';\n *\n * const worker = tfn.worker({\n * temporal: { address: 'localhost:7233', namespace: 'default' },\n * taskQueue: 'my-queue',\n * });\n *\n * worker.register(validateOrder);\n * worker.register(processOrder);\n * await worker.start();\n * ```\n *\n * @example\n * ```typescript\n * // Option 2: Using external workflowsPath and activities (processor pattern)\n * import { createWorker } from '@astami/temporal-functions/worker';\n * import { createWorkerInterceptors } from '@astami/temporal-functions/observability';\n * import * as activities from './activities';\n *\n * const worker = createWorker({\n * temporal: { address: 'localhost:7233', namespace: 'loop' },\n * taskQueue: 'stripe-payments',\n * workflowsPath: './dist/workflows/index.js',\n * activities,\n * interceptors: createWorkerInterceptors({ serviceName: 'stripe' }),\n * });\n *\n * await worker.start();\n * ```\n */\nfunction createWorker(config: WorkerConfig): TFNWorker {\n return new TemporalFunctionsWorker(config);\n}\n\n// =============================================================================\n// Export\n// =============================================================================\n\nexport const tfn = {\n worker: createWorker,\n};\n\nexport { createWorker };\nexport type { WorkerConfig, TFNWorker };\nexport default tfn;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astami/temporal-functions",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A lightweight TypeScript framework providing lambda-like DX on top of Temporal's durable execution engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|