@diia-inhouse/workflow 1.17.11 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/activities/index.d.ts +1 -0
  2. package/dist/activities/index.js +2 -18
  3. package/dist/activities/proxy.d.ts +34 -0
  4. package/dist/activities/proxy.js +16 -24
  5. package/dist/activity.d.ts +2 -0
  6. package/dist/activity.js +2 -15
  7. package/dist/cli/checkWorkflowDeterminism.js +249 -275
  8. package/dist/cli/determinism/errorClassifier.js +56 -60
  9. package/dist/cli/determinism/historyFiles.js +68 -97
  10. package/dist/cli/determinism/index.js +7 -19
  11. package/dist/cli/determinism/replayExecutor.js +114 -133
  12. package/dist/cli/determinism/replayOptions.js +13 -22
  13. package/dist/cli/determinism/report.js +55 -45
  14. package/dist/cli/determinism/reportPrinter.js +101 -138
  15. package/dist/cli/index.d.ts +1 -0
  16. package/dist/cli/index.js +79 -119
  17. package/dist/cli/syncTemporalSchedules.js +74 -91
  18. package/dist/cli/updateTemporalSchedule.js +43 -53
  19. package/dist/client.d.ts +3 -0
  20. package/dist/client.js +3 -19
  21. package/dist/common.d.ts +2 -0
  22. package/dist/common.js +2 -13
  23. package/dist/encryption/crypto.d.ts +7 -0
  24. package/dist/encryption/crypto.js +20 -22
  25. package/dist/encryption/dataConverter.d.ts +7 -0
  26. package/dist/encryption/dataConverter.js +15 -22
  27. package/dist/encryption/encryptionCodec.d.ts +31 -0
  28. package/dist/encryption/encryptionCodec.js +108 -124
  29. package/dist/encryption/index.d.ts +3 -0
  30. package/dist/encryption/index.js +4 -20
  31. package/dist/index.d.ts +7 -0
  32. package/dist/index.js +6 -42
  33. package/dist/instrumentation.js +6 -10
  34. package/dist/interceptors/asyncLocalStorageBridge.js +29 -66
  35. package/dist/interceptors/traceLogAttributes.d.ts +6 -0
  36. package/dist/interceptors/traceLogAttributes.js +16 -54
  37. package/dist/interceptors.d.ts +6 -0
  38. package/dist/interceptors.js +6 -8
  39. package/dist/interfaces/config.d.ts +58 -0
  40. package/dist/interfaces/index.d.ts +1 -0
  41. package/dist/interfaces/services/schedulesExporter.d.ts +96 -0
  42. package/dist/interfaces/services/worker.d.ts +60 -0
  43. package/dist/operations.d.ts +9 -0
  44. package/dist/operations.js +11 -75
  45. package/dist/services/client.d.ts +24 -0
  46. package/dist/services/client.js +89 -96
  47. package/dist/services/schedulesExporter.d.ts +101 -0
  48. package/dist/services/schedulesExporter.js +456 -0
  49. package/dist/services/worker/identity.d.ts +4 -0
  50. package/dist/services/worker/identity.js +6 -9
  51. package/dist/services/worker.d.ts +124 -0
  52. package/dist/services/worker.js +324 -304
  53. package/dist/services/workerHealth.d.ts +15 -0
  54. package/dist/services/workerHealth.js +26 -35
  55. package/dist/testing.d.ts +42 -0
  56. package/dist/testing.js +43 -54
  57. package/dist/worker.d.ts +9 -0
  58. package/dist/worker.js +7 -25
  59. package/package.json +40 -37
  60. package/dist/activities/index.js.map +0 -1
  61. package/dist/activities/proxy.js.map +0 -1
  62. package/dist/activity.js.map +0 -1
  63. package/dist/cli/checkWorkflowDeterminism.js.map +0 -1
  64. package/dist/cli/determinism/errorClassifier.js.map +0 -1
  65. package/dist/cli/determinism/historyFiles.js.map +0 -1
  66. package/dist/cli/determinism/index.js.map +0 -1
  67. package/dist/cli/determinism/replayExecutor.js.map +0 -1
  68. package/dist/cli/determinism/replayOptions.js.map +0 -1
  69. package/dist/cli/determinism/report.js.map +0 -1
  70. package/dist/cli/determinism/reportPrinter.js.map +0 -1
  71. package/dist/cli/determinism/types.js +0 -3
  72. package/dist/cli/determinism/types.js.map +0 -1
  73. package/dist/cli/index.js.map +0 -1
  74. package/dist/cli/syncTemporalSchedules.js.map +0 -1
  75. package/dist/cli/updateTemporalSchedule.js.map +0 -1
  76. package/dist/client.js.map +0 -1
  77. package/dist/common.js.map +0 -1
  78. package/dist/encryption/crypto.js.map +0 -1
  79. package/dist/encryption/dataConverter.js.map +0 -1
  80. package/dist/encryption/encryptionCodec.js.map +0 -1
  81. package/dist/encryption/index.js.map +0 -1
  82. package/dist/index.js.map +0 -1
  83. package/dist/instrumentation.js.map +0 -1
  84. package/dist/interceptors/asyncLocalStorageBridge.js.map +0 -1
  85. package/dist/interceptors/index.js +0 -8
  86. package/dist/interceptors/index.js.map +0 -1
  87. package/dist/interceptors/traceLogAttributes.js.map +0 -1
  88. package/dist/interceptors.js.map +0 -1
  89. package/dist/interfaces/config.js +0 -3
  90. package/dist/interfaces/config.js.map +0 -1
  91. package/dist/interfaces/index.js +0 -18
  92. package/dist/interfaces/index.js.map +0 -1
  93. package/dist/interfaces/services/worker.js +0 -3
  94. package/dist/interfaces/services/worker.js.map +0 -1
  95. package/dist/operations.js.map +0 -1
  96. package/dist/services/client.js.map +0 -1
  97. package/dist/services/index.js +0 -19
  98. package/dist/services/index.js.map +0 -1
  99. package/dist/services/worker/identity.js.map +0 -1
  100. package/dist/services/worker/index.js +0 -18
  101. package/dist/services/worker/index.js.map +0 -1
  102. package/dist/services/worker.js.map +0 -1
  103. package/dist/services/workerHealth.js.map +0 -1
  104. package/dist/testing.js.map +0 -1
  105. package/dist/types/activities/index.d.ts +0 -1
  106. package/dist/types/activities/proxy.d.ts +0 -35
  107. package/dist/types/activity.d.ts +0 -1
  108. package/dist/types/cli/checkWorkflowDeterminism.d.ts +0 -19
  109. package/dist/types/cli/determinism/errorClassifier.d.ts +0 -15
  110. package/dist/types/cli/determinism/historyFiles.d.ts +0 -18
  111. package/dist/types/cli/determinism/index.d.ts +0 -10
  112. package/dist/types/cli/determinism/replayExecutor.d.ts +0 -9
  113. package/dist/types/cli/determinism/replayOptions.d.ts +0 -7
  114. package/dist/types/cli/determinism/report.d.ts +0 -16
  115. package/dist/types/cli/determinism/reportPrinter.d.ts +0 -5
  116. package/dist/types/cli/determinism/types.d.ts +0 -44
  117. package/dist/types/cli/index.d.ts +0 -2
  118. package/dist/types/cli/syncTemporalSchedules.d.ts +0 -12
  119. package/dist/types/cli/updateTemporalSchedule.d.ts +0 -9
  120. package/dist/types/client.d.ts +0 -2
  121. package/dist/types/common.d.ts +0 -1
  122. package/dist/types/encryption/crypto.d.ts +0 -3
  123. package/dist/types/encryption/dataConverter.d.ts +0 -3
  124. package/dist/types/encryption/encryptionCodec.d.ts +0 -27
  125. package/dist/types/encryption/index.d.ts +0 -3
  126. package/dist/types/index.d.ts +0 -3
  127. package/dist/types/instrumentation.d.ts +0 -2
  128. package/dist/types/interceptors/asyncLocalStorageBridge.d.ts +0 -21
  129. package/dist/types/interceptors/index.d.ts +0 -2
  130. package/dist/types/interceptors/traceLogAttributes.d.ts +0 -2
  131. package/dist/types/interceptors.d.ts +0 -2
  132. package/dist/types/interfaces/config.d.ts +0 -38
  133. package/dist/types/interfaces/index.d.ts +0 -1
  134. package/dist/types/interfaces/services/worker.d.ts +0 -37
  135. package/dist/types/operations.d.ts +0 -5
  136. package/dist/types/services/client.d.ts +0 -20
  137. package/dist/types/services/index.d.ts +0 -2
  138. package/dist/types/services/worker/identity.d.ts +0 -1
  139. package/dist/types/services/worker/index.d.ts +0 -1
  140. package/dist/types/services/worker.d.ts +0 -113
  141. package/dist/types/services/workerHealth.d.ts +0 -11
  142. package/dist/types/testing.d.ts +0 -42
  143. package/dist/types/worker.d.ts +0 -3
  144. package/dist/worker.js.map +0 -1
@@ -1,333 +1,353 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.WorkerHealthService = exports.buildWorkerIdentity = void 0;
7
- exports.applyServiceProcessConfig = applyServiceProcessConfig;
8
- exports.applyWorkerProcessConfig = applyWorkerProcessConfig;
9
- exports.instantiateActivities = instantiateActivities;
10
- exports.initTemporalWorker = initTemporalWorker;
11
- exports.bootstrapWorker = bootstrapWorker;
12
- exports.initWorker = initWorker;
13
- const node_path_1 = __importDefault(require("node:path"));
14
- const resources_1 = require("@opentelemetry/resources");
15
- const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
16
- const worker_1 = require("@temporalio/interceptors-opentelemetry/lib/worker");
17
- const worker_2 = require("@temporalio/worker");
18
- const env_1 = require("@diia-inhouse/env");
19
- const encryption_1 = require("../encryption");
20
- const instrumentation_1 = require("../instrumentation");
21
- const asyncLocalStorageBridge_1 = require("../interceptors/asyncLocalStorageBridge");
22
- const identity_1 = require("./worker/identity");
23
- const workerHealth_1 = require("./workerHealth");
24
- var identity_2 = require("./worker/identity");
25
- Object.defineProperty(exports, "buildWorkerIdentity", { enumerable: true, get: function () { return identity_2.buildWorkerIdentity; } });
26
- var workerHealth_2 = require("./workerHealth");
27
- Object.defineProperty(exports, "WorkerHealthService", { enumerable: true, get: function () { return workerHealth_2.WorkerHealthService; } });
1
+ import { getDataConverter } from "../encryption/dataConverter.js";
2
+ import "../encryption/index.js";
3
+ import { traceExporter } from "../instrumentation.js";
4
+ import { AsyncLocalStorageBridgeInterceptor } from "../interceptors/asyncLocalStorageBridge.js";
5
+ import { SchedulesExporter } from "./schedulesExporter.js";
6
+ import { buildWorkerIdentity } from "./worker/identity.js";
7
+ import { WorkerHealthService } from "./workerHealth.js";
8
+ import { EnvService } from "@diia-inhouse/env";
9
+ import { NativeConnection, Runtime, Worker } from "@temporalio/worker";
10
+ import path from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import { Resource } from "@opentelemetry/resources";
13
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
14
+ import { OpenTelemetryActivityInboundInterceptor, OpenTelemetryActivityOutboundInterceptor, makeWorkflowExporter } from "@temporalio/interceptors-opentelemetry/lib/worker/index.js";
15
+ import * as promClient from "prom-client";
16
+ //#region src/services/worker.ts
28
17
  /**
29
- * Applies service process configuration overrides when the worker runs separately.
30
- *
31
- * When `temporal.workerInProcess` is `false`, disables `temporal` and `temporal-worker` scrapers
32
- * so the main service does not scrape metrics that the worker process handles.
33
- *
34
- * Mutates the config object in place. Safe to call when scrapers are absent.
35
- */
18
+ * Applies service process configuration overrides when the worker runs separately.
19
+ *
20
+ * When `temporal.workerInProcess` is `false`, disables `temporal` and `temporal-worker` scrapers
21
+ * so the main service does not scrape metrics that the worker process handles.
22
+ *
23
+ * Mutates the config object in place. Safe to call when scrapers are absent.
24
+ */
36
25
  function applyServiceProcessConfig(config) {
37
- const { workerInProcess = true } = config.temporal;
38
- if (workerInProcess) {
39
- return;
40
- }
41
- for (const scraper of config.metrics.custom.scrapers ?? []) {
42
- if (scraper.name === 'temporal' || scraper.name === 'temporal-worker') {
43
- scraper.disabled = true;
44
- }
45
- }
26
+ const { workerInProcess = true } = config.temporal;
27
+ if (workerInProcess) return;
28
+ for (const scraper of config.metrics.custom.scrapers ?? []) if (scraper.name === "temporal" || scraper.name === "temporal-worker") scraper.disabled = true;
46
29
  }
47
30
  /**
48
- * Applies worker process configuration overrides.
49
- *
50
- * - Disables queue consumers on all rabbit connections (unless `temporal.disableQueueConsumers` is `false`)
51
- * - Overrides `metrics.custom.port` with the `'temporal-worker'` scraper port and disables that scraper to prevent self-scraping
52
- *
53
- * Mutates the config object in place. Safe to call when queue config is absent.
54
- */
31
+ * Applies worker process configuration overrides.
32
+ *
33
+ * - Disables queue consumers on all rabbit connections (unless `temporal.disableQueueConsumers` is `false`)
34
+ * - Overrides `metrics.custom.port` with the `'temporal-worker'` scraper port and disables that scraper to prevent self-scraping
35
+ *
36
+ * Mutates the config object in place. Safe to call when queue config is absent.
37
+ */
55
38
  function applyWorkerProcessConfig(config) {
56
- const { disableQueueConsumers = true } = config.temporal;
57
- if (disableQueueConsumers && config.rabbit) {
58
- for (const value of Object.values(config.rabbit)) {
59
- if (value && typeof value === 'object' && 'consumerEnabled' in value) {
60
- value.consumerEnabled = false;
61
- }
62
- }
63
- }
64
- const workerScraper = config.metrics.custom.scrapers?.find((s) => s.name === 'temporal-worker');
65
- if (workerScraper?.port !== undefined) {
66
- config.metrics.custom.port = workerScraper.port;
67
- workerScraper.disabled = true;
68
- }
39
+ const { disableQueueConsumers = true } = config.temporal;
40
+ if (disableQueueConsumers && config.rabbit) {
41
+ for (const value of Object.values(config.rabbit)) if (value && typeof value === "object" && "consumerEnabled" in value) value.consumerEnabled = false;
42
+ }
43
+ const workerScraper = config.metrics.custom.scrapers?.find((s) => s.name === "temporal-worker");
44
+ if (workerScraper?.port !== void 0) {
45
+ config.metrics.custom.port = workerScraper.port;
46
+ workerScraper.disabled = true;
47
+ }
69
48
  }
70
49
  /**
71
- * Builds worker interceptors with OpenTelemetry and AsyncLocalStorage support.
72
- * OpenTelemetry creates span first, then AsyncLocalStorage bridge extracts traceId.
73
- */
74
- const traceLogAttributesModulePath = node_path_1.default.resolve(__dirname, '../interceptors/traceLogAttributes');
50
+ * Accepts either a filesystem path or a `file://` URL (e.g. from `import.meta.resolve`)
51
+ * and returns a filesystem path suitable for Temporal's worker.
52
+ */
53
+ function toWorkflowsPath(input) {
54
+ return input.startsWith("file://") ? fileURLToPath(input) : input;
55
+ }
56
+ /**
57
+ * Builds worker interceptors with OpenTelemetry and AsyncLocalStorage support.
58
+ * OpenTelemetry creates span first, then AsyncLocalStorage bridge extracts traceId.
59
+ */
60
+ const traceLogAttributesModulePath = path.resolve(import.meta.dirname, "../interceptors/traceLogAttributes");
75
61
  function buildWorkerInterceptors(tracingEnabled, asyncLocalStorage, logger, workflowsPath) {
76
- if (tracingEnabled) {
77
- const workflowModules = [traceLogAttributesModulePath];
78
- if (workflowsPath) {
79
- workflowModules.unshift(workflowsPath);
80
- }
81
- return {
82
- activity: [
83
- (ctx) => ({
84
- inbound: new worker_1.OpenTelemetryActivityInboundInterceptor(ctx),
85
- outbound: new worker_1.OpenTelemetryActivityOutboundInterceptor(ctx),
86
- }),
87
- ...(asyncLocalStorage && logger
88
- ? [
89
- (ctx) => ({
90
- inbound: new asyncLocalStorageBridge_1.AsyncLocalStorageBridgeInterceptor(ctx, asyncLocalStorage, logger),
91
- }),
92
- ]
93
- : []),
94
- ],
95
- workflowModules,
96
- };
97
- }
98
- if (asyncLocalStorage && logger) {
99
- return {
100
- activity: [
101
- (ctx) => ({
102
- inbound: new asyncLocalStorageBridge_1.AsyncLocalStorageBridgeInterceptor(ctx, asyncLocalStorage, logger),
103
- outbound: new worker_1.OpenTelemetryActivityOutboundInterceptor(ctx),
104
- }),
105
- ],
106
- workflowModules: workflowsPath ? [workflowsPath] : [],
107
- };
108
- }
109
- return undefined;
62
+ if (tracingEnabled) {
63
+ const workflowModules = [traceLogAttributesModulePath];
64
+ if (workflowsPath) workflowModules.unshift(workflowsPath);
65
+ return {
66
+ activity: [(ctx) => ({
67
+ inbound: new OpenTelemetryActivityInboundInterceptor(ctx),
68
+ outbound: new OpenTelemetryActivityOutboundInterceptor(ctx)
69
+ }), ...asyncLocalStorage && logger ? [(ctx) => ({ inbound: new AsyncLocalStorageBridgeInterceptor(ctx, asyncLocalStorage, logger) })] : []],
70
+ workflowModules
71
+ };
72
+ }
73
+ if (asyncLocalStorage && logger) return {
74
+ activity: [(ctx) => ({
75
+ inbound: new AsyncLocalStorageBridgeInterceptor(ctx, asyncLocalStorage, logger),
76
+ outbound: new OpenTelemetryActivityOutboundInterceptor(ctx)
77
+ })],
78
+ workflowModules: workflowsPath ? [workflowsPath] : []
79
+ };
110
80
  }
111
81
  /**
112
- * Merges built-in interceptors with custom interceptors from options.
113
- */
82
+ * Merges built-in interceptors with custom interceptors from options.
83
+ */
114
84
  function mergeInterceptors(builtIn, custom) {
115
- if (!builtIn && !custom) {
116
- return undefined;
117
- }
118
- return {
119
- activity: [...(builtIn?.activity || []), ...(custom?.activity || [])],
120
- workflowModules: [...(builtIn?.workflowModules || []), ...(custom?.workflowModules || [])],
121
- };
85
+ if (!builtIn && !custom) return;
86
+ return {
87
+ activity: [...builtIn?.activity || [], ...custom?.activity || []],
88
+ workflowModules: [...builtIn?.workflowModules || [], ...custom?.workflowModules || []]
89
+ };
122
90
  }
123
91
  function buildActivities(app, activities) {
124
- const instances = {};
125
- for (const [key, value] of Object.entries(activities)) {
126
- instances[key] = app.container.build(value);
127
- }
128
- return instances;
92
+ const instances = {};
93
+ for (const [key, value] of Object.entries(activities)) instances[key] = app.container.build(value);
94
+ return instances;
129
95
  }
130
96
  function bindActivities(key, instance) {
131
- const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(instance)).filter((name) => name !== 'constructor');
132
- const boundActivities = {};
133
- for (const method of methods) {
134
- const fn = instance[method];
135
- const token = `${key}.${String(method)}`;
136
- boundActivities[token] = fn.bind(instance);
137
- }
138
- return boundActivities;
97
+ const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(instance)).filter((name) => name !== "constructor");
98
+ const boundActivities = {};
99
+ for (const method of methods) {
100
+ const fn = instance[method];
101
+ const token = `${key}.${String(method)}`;
102
+ boundActivities[token] = fn.bind(instance);
103
+ }
104
+ return boundActivities;
139
105
  }
140
106
  function instantiateActivities(app, workerActivities) {
141
- const activitiesInstances = buildActivities(app, workerActivities);
142
- const activities = {};
143
- for (const [key, value] of Object.entries(activitiesInstances)) {
144
- Object.assign(activities, bindActivities(key, value));
145
- }
146
- return activities;
107
+ const activitiesInstances = buildActivities(app, workerActivities);
108
+ const activities = {};
109
+ for (const [key, value] of Object.entries(activitiesInstances)) Object.assign(activities, bindActivities(key, value));
110
+ return activities;
147
111
  }
148
112
  /**
149
- * Initializes and starts Temporal worker with full dependency injection support.
150
- *
151
- * This is the recommended way to initialize Temporal workers. It handles:
152
- * - Automatic dependency injection for activities
153
- * - AsyncLocalStorage setup for distributed tracing
154
- * - OpenTelemetry integration
155
- * - Activity instantiation and binding
156
- *
157
- * @param app - App instance for DI container and config access
158
- * @param options - Worker configuration options
159
- * @param options.nodeTracerProvider - OpenTelemetry tracer provider
160
- * @param options.workflowsPath - Path to workflows module
161
- * @param options.activities - Activity classes to instantiate
162
- *
163
- * @example
164
- * ```typescript
165
- * // Define your activities
166
- * const workerActivities = {
167
- * userActivity: UserActivity,
168
- * notificationActivity: NotificationActivity,
169
- * }
170
- *
171
- * // Initialize and start the worker
172
- * await initTemporalWorker(app, {
173
- * nodeTracerProvider,
174
- * workflowsPath: require.resolve('./worker/workflows'),
175
- * activities: workerActivities,
176
- * })
177
- * ```
178
- */
113
+ * Initializes and starts Temporal worker with full dependency injection support.
114
+ *
115
+ * This is the recommended way to initialize Temporal workers. It handles:
116
+ * - Automatic dependency injection for activities
117
+ * - AsyncLocalStorage setup for distributed tracing
118
+ * - OpenTelemetry integration
119
+ * - Activity instantiation and binding
120
+ *
121
+ * @param app - App instance for DI container and config access
122
+ * @param options - Worker configuration options
123
+ * @param options.nodeTracerProvider - OpenTelemetry tracer provider
124
+ * @param options.workflowsPath - Path to workflows module
125
+ * @param options.activities - Activity classes to instantiate
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * // Define your activities
130
+ * const workerActivities = {
131
+ * userActivity: UserActivity,
132
+ * notificationActivity: NotificationActivity,
133
+ * }
134
+ *
135
+ * // Initialize and start the worker
136
+ * await initTemporalWorker(app, {
137
+ * nodeTracerProvider,
138
+ * workflowsPath: import.meta.resolve('./worker/workflows/index.js'),
139
+ * activities: workerActivities,
140
+ * })
141
+ * ```
142
+ */
179
143
  async function initTemporalWorker(app, options) {
180
- const config = app.getConfig?.();
181
- const { nodeTracerProvider, workflowsPath, activities, ...workerOptions } = options;
182
- const envService = app.container.resolve('envService');
183
- const logger = app.container.resolve('logger');
184
- const asyncLocalStorage = app.container.resolve('asyncLocalStorage');
185
- const instantiatedActivities = instantiateActivities(app, activities);
186
- const worker = await initWorker(config, { ...workerOptions, workflowsPath, activities: instantiatedActivities }, envService, logger, nodeTracerProvider, asyncLocalStorage);
187
- await worker.run();
144
+ const config = app.getConfig?.();
145
+ const { nodeTracerProvider, workflowsPath: workflowsPathInput, activities, ...workerOptions } = options;
146
+ const workflowsPath = toWorkflowsPath(workflowsPathInput);
147
+ const envService = app.container.resolve("envService");
148
+ const logger = app.container.resolve("logger");
149
+ const asyncLocalStorage = app.container.resolve("asyncLocalStorage");
150
+ const instantiatedActivities = instantiateActivities(app, activities);
151
+ const worker = await initWorker(config, {
152
+ ...workerOptions,
153
+ workflowsPath,
154
+ activities: instantiatedActivities
155
+ }, envService, logger, nodeTracerProvider, asyncLocalStorage);
156
+ const schedulesExporter = await startSchedulesExporter(app, config, logger);
157
+ try {
158
+ await worker.run();
159
+ } finally {
160
+ await schedulesExporter?.onDestroy().catch((err) => logger.error("SchedulesExporter shutdown failed", { err }));
161
+ }
188
162
  }
189
163
  /**
190
- * Bootstraps and runs Temporal worker with graceful shutdown.
191
- *
192
- * Handles both in-process and separate-process worker topologies:
193
- *
194
- * - **In-process** (`workerInProcess` is `true` or unset): initializes and runs the worker.
195
- * - **Separate process** (called from a dedicated worker entry with `configFactory`/`deps`):
196
- * manages the full application lifecycle: setConfig → apply worker overrides → setDeps →
197
- * initialize → start → run worker.
198
- * - **Service-only** (`workerInProcess` is `false`, no `configFactory`): disables temporal
199
- * scrapers on the main service (worker handles them separately) and returns immediately.
200
- *
201
- * Automatically integrates worker health with the app's centralized health check
202
- * system via `HealthCheck.addHealthCheckable()`.
203
- *
204
- * @param app - App instance for DI container and config access
205
- * @param options - Worker bootstrap options
206
- *
207
- * @example
208
- * ```typescript
209
- * // Separate worker process with full lifecycle management
210
- * const app = new Application(serviceName, nodeTracerProvider, loggerConfig)
211
- *
212
- * await bootstrapWorker(app, {
213
- * configFactory,
214
- * deps,
215
- * workflowsPath: require.resolve('./worker/workflows'),
216
- * activities: workerActivities,
217
- * nodeTracerProvider,
218
- * })
219
- * ```
220
- */
164
+ * Bootstraps and runs Temporal worker with graceful shutdown.
165
+ *
166
+ * Handles both in-process and separate-process worker topologies:
167
+ *
168
+ * - **In-process** (`workerInProcess` is `true` or unset): initializes and runs the worker.
169
+ * - **Separate process** (called from a dedicated worker entry with `configFactory`/`deps`):
170
+ * manages the full application lifecycle: setConfig → apply worker overrides → setDeps →
171
+ * initialize → start → run worker.
172
+ * - **Service-only** (`workerInProcess` is `false`, no `configFactory`): disables temporal
173
+ * scrapers on the main service (worker handles them separately) and returns immediately.
174
+ *
175
+ * Automatically integrates worker health with the app's centralized health check
176
+ * system via `HealthCheck.addHealthCheckable()`.
177
+ *
178
+ * @param app - App instance for DI container and config access
179
+ * @param options - Worker bootstrap options
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * // Separate worker process with full lifecycle management
184
+ * const app = new Application(serviceName, nodeTracerProvider, loggerConfig)
185
+ *
186
+ * await bootstrapWorker(app, {
187
+ * configFactory,
188
+ * deps,
189
+ * workflowsPath: import.meta.resolve('./worker/workflows/index.js'),
190
+ * activities: workerActivities,
191
+ * nodeTracerProvider,
192
+ * })
193
+ * ```
194
+ */
221
195
  async function bootstrapWorker(app, options) {
222
- const { configFactory, deps, workflowsPath, activities, nodeTracerProvider, shutdownSignals = ['SIGTERM', 'SIGINT'], ...workerOptions } = options;
223
- if (configFactory && deps) {
224
- await app.setConfig(configFactory);
225
- applyWorkerProcessConfig(app.getConfig());
226
- await app.setDeps(deps);
227
- const appOperator = await app.initialize();
228
- await appOperator.start();
229
- }
230
- const config = app.getConfig?.();
231
- if (!configFactory && config.temporal.workerInProcess === false) {
232
- applyServiceProcessConfig(config);
233
- return;
234
- }
235
- const envService = app.container.resolve('envService');
236
- const logger = app.container.resolve('logger');
237
- const asyncLocalStorage = app.container.resolve('asyncLocalStorage');
238
- const taskQueue = config.temporal.taskQueue;
239
- const identity = (0, identity_1.buildWorkerIdentity)(taskQueue, workerOptions.identity);
240
- const instantiatedActivities = instantiateActivities(app, activities);
241
- const worker = await initWorker(config, {
242
- ...workerOptions,
243
- workflowsPath,
244
- activities: instantiatedActivities,
245
- identity,
246
- }, envService, logger, nodeTracerProvider, asyncLocalStorage);
247
- logger.info('Starting Temporal worker', { taskQueue, identity });
248
- const healthCheck = tryResolve(app.container, 'healthCheck');
249
- if (healthCheck) {
250
- const workerHealthService = new workerHealth_1.WorkerHealthService();
251
- workerHealthService.setStatusProvider(() => worker.getStatus());
252
- healthCheck.addHealthCheckable(workerHealthService);
253
- }
254
- const signalHandler = () => {
255
- worker.shutdown();
256
- };
257
- for (const signal of shutdownSignals) {
258
- process.once(signal, signalHandler);
259
- }
260
- try {
261
- await worker.run();
262
- }
263
- finally {
264
- for (const signal of shutdownSignals) {
265
- process.off(signal, signalHandler);
266
- }
267
- }
196
+ const { configFactory, deps, workflowsPath: workflowsPathInput, activities, nodeTracerProvider, shutdownSignals = ["SIGTERM", "SIGINT"], ...workerOptions } = options;
197
+ const workflowsPath = toWorkflowsPath(workflowsPathInput);
198
+ if (configFactory && deps) {
199
+ await app.setConfig(configFactory);
200
+ applyWorkerProcessConfig(app.getConfig());
201
+ await app.setDeps(deps);
202
+ await (await app.initialize()).start();
203
+ }
204
+ const config = app.getConfig?.();
205
+ if (!configFactory && config.temporal.workerInProcess === false) {
206
+ applyServiceProcessConfig(config);
207
+ return;
208
+ }
209
+ const envService = app.container.resolve("envService");
210
+ const logger = app.container.resolve("logger");
211
+ const asyncLocalStorage = app.container.resolve("asyncLocalStorage");
212
+ const taskQueue = config.temporal.taskQueue;
213
+ const identity = buildWorkerIdentity(taskQueue, workerOptions.identity);
214
+ const instantiatedActivities = instantiateActivities(app, activities);
215
+ const worker = await initWorker(config, {
216
+ ...workerOptions,
217
+ workflowsPath,
218
+ activities: instantiatedActivities,
219
+ identity
220
+ }, envService, logger, nodeTracerProvider, asyncLocalStorage);
221
+ const schedulesExporter = await startSchedulesExporter(app, config, logger);
222
+ logger.info("Starting Temporal worker", {
223
+ taskQueue,
224
+ identity
225
+ });
226
+ const healthCheck = tryResolve(app.container, "healthCheck");
227
+ if (healthCheck) {
228
+ const workerHealthService = new WorkerHealthService();
229
+ workerHealthService.setStatusProvider(() => worker.getStatus());
230
+ healthCheck.addHealthCheckable(workerHealthService);
231
+ }
232
+ const signalHandler = () => {
233
+ worker.shutdown();
234
+ };
235
+ for (const signal of shutdownSignals) process.once(signal, signalHandler);
236
+ try {
237
+ await worker.run();
238
+ } finally {
239
+ for (const signal of shutdownSignals) process.off(signal, signalHandler);
240
+ await schedulesExporter?.onDestroy().catch((err) => logger.error("SchedulesExporter shutdown failed", { err }));
241
+ }
268
242
  }
269
243
  function tryResolve(container, key) {
270
- try {
271
- return container.resolve(key);
272
- }
273
- catch {
274
- return undefined;
275
- }
244
+ try {
245
+ return container.resolve(key);
246
+ } catch {
247
+ return;
248
+ }
249
+ }
250
+ /**
251
+ * Wires the SchedulesExporter to the in-process worker. Auto-enabled; opt out by setting
252
+ * `config.temporal.schedulesExporter` to `false`. Returns the exporter so callers can stop
253
+ * it on shutdown.
254
+ */
255
+ async function startSchedulesExporter(app, config, logger) {
256
+ const exporterConfig = config.temporal.schedulesExporter;
257
+ if (exporterConfig === false) return;
258
+ const temporalClient = tryResolve(app.container, "temporalClient");
259
+ if (!temporalClient) {
260
+ logger.warn("SchedulesExporter not started: temporalClient is not registered in the DI container");
261
+ return;
262
+ }
263
+ const exporter = new SchedulesExporter({
264
+ client: temporalClient,
265
+ taskQueue: config.temporal.taskQueue,
266
+ logger
267
+ }, exporterConfig ?? {});
268
+ await exporter.onInit();
269
+ return exporter;
276
270
  }
277
271
  /**
278
- * Initializes Temporal worker.
279
- *
280
- * @param config - Application configuration
281
- * @param options - Worker options including workflows path
282
- * @param envService - Environment service instance
283
- * @param logger - Logger instance (optional)
284
- * @param nodeTracerProvider - OpenTelemetry tracer provider (optional)
285
- * @param asyncLocalStorage - AsyncLocalStorage instance for tracing context (optional)
286
- * @returns Configured Temporal worker
287
- */
272
+ * Initializes Temporal worker.
273
+ *
274
+ * @param config - Application configuration
275
+ * @param options - Worker options including workflows path
276
+ * @param envService - Environment service instance
277
+ * @param logger - Logger instance (optional)
278
+ * @param nodeTracerProvider - OpenTelemetry tracer provider (optional)
279
+ * @param asyncLocalStorage - AsyncLocalStorage instance for tracing context (optional)
280
+ * @returns Configured Temporal worker
281
+ */
288
282
  async function initWorker({ temporal: temporalConfig, metrics: { custom: metricsConfig } }, options, envService, logger, nodeTracerProvider, asyncLocalStorage) {
289
- const { encryptionEnabled, encryptionKeyId, encryptionKeyRefreshInterval, namespace = 'default', address, taskQueue } = temporalConfig;
290
- const runtimeParams = {};
291
- if (logger) {
292
- runtimeParams.logger = logger.child({ taskQueue });
293
- }
294
- const temporalMetrics = metricsConfig.scrapers?.find((s) => s.name === 'temporal');
295
- if (temporalMetrics && !temporalMetrics.disabled) {
296
- runtimeParams.telemetryOptions = {
297
- metrics: {
298
- prometheus: {
299
- bindAddress: `0.0.0.0:${temporalMetrics.port}`,
300
- useSecondsForDurations: true,
301
- countersTotalSuffix: true,
302
- unitSuffix: true,
303
- },
304
- },
305
- };
306
- }
307
- worker_2.Runtime.install(runtimeParams);
308
- const resource = new resources_1.Resource({
309
- [semantic_conventions_1.ATTR_SERVICE_NAME]: nodeTracerProvider?.resource.attributes[semantic_conventions_1.ATTR_SERVICE_NAME],
310
- });
311
- const tracingEnabled = env_1.EnvService.getVar('TRACING_ENABLED', 'boolean', false);
312
- const builtInInterceptors = buildWorkerInterceptors(tracingEnabled, asyncLocalStorage, logger, options.workflowsPath);
313
- const mergedInterceptors = mergeInterceptors(builtInInterceptors, options.interceptors);
314
- try {
315
- const worker = await worker_2.Worker.create({
316
- namespace,
317
- taskQueue,
318
- connection: await worker_2.NativeConnection.connect({ address }),
319
- dataConverter: encryptionEnabled
320
- ? await (0, encryption_1.getDataConverter)(encryptionKeyId, envService, encryptionKeyRefreshInterval)
321
- : undefined,
322
- ...options,
323
- sinks: tracingEnabled ? { exporter: (0, worker_1.makeWorkflowExporter)(instrumentation_1.traceExporter, resource) } : undefined,
324
- interceptors: mergedInterceptors,
325
- });
326
- return worker;
327
- }
328
- catch (err) {
329
- logger?.error('Failed to create Temporal worker', { err });
330
- throw new Error('Failed to create Temporal worker', { cause: err });
331
- }
283
+ const { encryptionEnabled, encryptionKeyId, encryptionKeyRefreshInterval, namespace = "default", address, taskQueue } = temporalConfig;
284
+ const runtimeParams = {};
285
+ if (logger) runtimeParams.logger = logger.child({ taskQueue });
286
+ const temporalMetrics = metricsConfig.scrapers?.find((s) => s.name === "temporal");
287
+ if (temporalMetrics && !temporalMetrics.disabled) runtimeParams.telemetryOptions = { metrics: { prometheus: {
288
+ bindAddress: `0.0.0.0:${temporalMetrics.port}`,
289
+ useSecondsForDurations: true,
290
+ countersTotalSuffix: true,
291
+ unitSuffix: true
292
+ } } };
293
+ Runtime.install(runtimeParams);
294
+ const providerWithPrivateResource = nodeTracerProvider;
295
+ const tracerResource = nodeTracerProvider?.resource ?? providerWithPrivateResource?.["_resource"];
296
+ const resource = new Resource({ [ATTR_SERVICE_NAME]: tracerResource?.attributes?.[ATTR_SERVICE_NAME] });
297
+ const tracingEnabled = EnvService.getVar("TRACING_ENABLED", "boolean", false);
298
+ const workflowsPath = options.workflowsPath ? toWorkflowsPath(options.workflowsPath) : void 0;
299
+ const mergedInterceptors = mergeInterceptors(buildWorkerInterceptors(tracingEnabled, asyncLocalStorage, logger, workflowsPath), options.interceptors);
300
+ try {
301
+ const worker = await Worker.create({
302
+ namespace,
303
+ taskQueue,
304
+ connection: await NativeConnection.connect({ address }),
305
+ dataConverter: encryptionEnabled ? await getDataConverter(encryptionKeyId, envService, encryptionKeyRefreshInterval) : void 0,
306
+ ...options,
307
+ workflowsPath,
308
+ sinks: tracingEnabled ? { exporter: makeWorkflowExporter(traceExporter, resource) } : void 0,
309
+ interceptors: mergedInterceptors
310
+ });
311
+ if (workflowsPath && taskQueue) await emitRegisteredWorkflowTypes(workflowsPath, taskQueue, logger);
312
+ return worker;
313
+ } catch (err) {
314
+ logger?.error("Failed to create Temporal worker", { err });
315
+ throw new Error("Failed to create Temporal worker", { cause: err });
316
+ }
317
+ }
318
+ /**
319
+ * Dynamic-imports the workflows entrypoint module and emits
320
+ * `diia_workflow_registered_type_info{workflow_type, task_queue}=1` for every exported
321
+ * function. Pairs with `diia_schedule_*` series so a dashboard can detect zombie schedules
322
+ * (schedules whose workflow type is no longer registered on the same task queue).
323
+ *
324
+ * Failures are logged and swallowed this is observability, not load-bearing.
325
+ */
326
+ async function emitRegisteredWorkflowTypes(workflowsPath, taskQueue, logger) {
327
+ try {
328
+ const mod = await (workflowsPath.startsWith("file://") ? import(workflowsPath) : import(`file://${workflowsPath}`));
329
+ const existing = promClient.register.getSingleMetric("diia_workflow_registered_type_info");
330
+ const gauge = existing instanceof promClient.Gauge ? existing : new promClient.Gauge({
331
+ name: "diia_workflow_registered_type_info",
332
+ help: "1 if the workflow type is registered on this task queue (function exported from the worker entrypoint module)",
333
+ labelNames: ["workflow_type", "task_queue"]
334
+ });
335
+ const registered = [];
336
+ for (const [name, value] of Object.entries(mod)) if (typeof value === "function") {
337
+ gauge.set({
338
+ workflow_type: name,
339
+ task_queue: taskQueue
340
+ }, 1);
341
+ registered.push(name);
342
+ }
343
+ logger?.info("Registered workflow types emitted to Prometheus", {
344
+ taskQueue,
345
+ count: registered.length,
346
+ types: registered
347
+ });
348
+ } catch (err) {
349
+ logger?.warn("Failed to enumerate registered workflow types", { err });
350
+ }
332
351
  }
333
- //# sourceMappingURL=worker.js.map
352
+ //#endregion
353
+ export { applyServiceProcessConfig, applyWorkerProcessConfig, bootstrapWorker, initTemporalWorker, initWorker, instantiateActivities, toWorkflowsPath };