@proompteng/temporal-bun-sdk 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -10
- package/dist/src/bin/replay-command.d.ts.map +1 -1
- package/dist/src/bin/replay-command.js +6 -2
- package/dist/src/bin/replay-command.js.map +1 -1
- package/dist/src/bin/temporal-bun.d.ts +1 -1
- package/dist/src/bin/temporal-bun.d.ts.map +1 -1
- package/dist/src/bin/temporal-bun.js +74 -0
- package/dist/src/bin/temporal-bun.js.map +1 -1
- package/dist/src/client/layer.d.ts +2 -2
- package/dist/src/client/layer.d.ts.map +1 -1
- package/dist/src/client/retries.d.ts.map +1 -1
- package/dist/src/client/retries.js +27 -3
- package/dist/src/client/retries.js.map +1 -1
- package/dist/src/client/serialization.d.ts +34 -2
- package/dist/src/client/serialization.d.ts.map +1 -1
- package/dist/src/client/serialization.js +78 -5
- package/dist/src/client/serialization.js.map +1 -1
- package/dist/src/client/types.d.ts +26 -0
- package/dist/src/client/types.d.ts.map +1 -1
- package/dist/src/client.d.ts +22 -6
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +488 -39
- package/dist/src/client.js.map +1 -1
- package/dist/src/common/payloads/codecs.d.ts +38 -0
- package/dist/src/common/payloads/codecs.d.ts.map +1 -0
- package/dist/src/common/payloads/codecs.js +174 -0
- package/dist/src/common/payloads/codecs.js.map +1 -0
- package/dist/src/common/payloads/converter.d.ts +52 -3
- package/dist/src/common/payloads/converter.d.ts.map +1 -1
- package/dist/src/common/payloads/converter.js +340 -2
- package/dist/src/common/payloads/converter.js.map +1 -1
- package/dist/src/common/payloads/failure.d.ts +5 -6
- package/dist/src/common/payloads/failure.d.ts.map +1 -1
- package/dist/src/common/payloads/failure.js +3 -52
- package/dist/src/common/payloads/failure.js.map +1 -1
- package/dist/src/common/payloads/index.d.ts +1 -0
- package/dist/src/common/payloads/index.d.ts.map +1 -1
- package/dist/src/common/payloads/index.js +1 -0
- package/dist/src/common/payloads/index.js.map +1 -1
- package/dist/src/config.d.ts +9 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +62 -1
- package/dist/src/config.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/interceptors/client.d.ts +28 -0
- package/dist/src/interceptors/client.d.ts.map +1 -0
- package/dist/src/interceptors/client.js +169 -0
- package/dist/src/interceptors/client.js.map +1 -0
- package/dist/src/interceptors/types.d.ts +33 -0
- package/dist/src/interceptors/types.d.ts.map +1 -0
- package/dist/src/interceptors/types.js +44 -0
- package/dist/src/interceptors/types.js.map +1 -0
- package/dist/src/interceptors/worker.d.ts +22 -0
- package/dist/src/interceptors/worker.d.ts.map +1 -0
- package/dist/src/interceptors/worker.js +156 -0
- package/dist/src/interceptors/worker.js.map +1 -0
- package/dist/src/runtime/cli-layer.d.ts +4 -3
- package/dist/src/runtime/cli-layer.d.ts.map +1 -1
- package/dist/src/runtime/cli-layer.js +5 -2
- package/dist/src/runtime/cli-layer.js.map +1 -1
- package/dist/src/runtime/effect-layers.d.ts +9 -0
- package/dist/src/runtime/effect-layers.d.ts.map +1 -1
- package/dist/src/runtime/effect-layers.js +15 -0
- package/dist/src/runtime/effect-layers.js.map +1 -1
- package/dist/src/worker/concurrency.d.ts +8 -0
- package/dist/src/worker/concurrency.d.ts.map +1 -1
- package/dist/src/worker/concurrency.js +5 -9
- package/dist/src/worker/concurrency.js.map +1 -1
- package/dist/src/worker/runtime.d.ts +5 -0
- package/dist/src/worker/runtime.d.ts.map +1 -1
- package/dist/src/worker/runtime.js +509 -40
- package/dist/src/worker/runtime.js.map +1 -1
- package/dist/src/worker/sticky-cache.d.ts +5 -0
- package/dist/src/worker/sticky-cache.d.ts.map +1 -1
- package/dist/src/worker/sticky-cache.js +26 -0
- package/dist/src/worker/sticky-cache.js.map +1 -1
- package/dist/src/worker/update-protocol.d.ts +33 -0
- package/dist/src/worker/update-protocol.d.ts.map +1 -0
- package/dist/src/worker/update-protocol.js +243 -0
- package/dist/src/worker/update-protocol.js.map +1 -0
- package/dist/src/worker.js +1 -0
- package/dist/src/worker.js.map +1 -1
- package/dist/src/workflow/commands.d.ts +38 -2
- package/dist/src/workflow/commands.d.ts.map +1 -1
- package/dist/src/workflow/commands.js +153 -1
- package/dist/src/workflow/commands.js.map +1 -1
- package/dist/src/workflow/context.d.ts +111 -3
- package/dist/src/workflow/context.d.ts.map +1 -1
- package/dist/src/workflow/context.js +526 -6
- package/dist/src/workflow/context.js.map +1 -1
- package/dist/src/workflow/definition.d.ts +29 -3
- package/dist/src/workflow/definition.d.ts.map +1 -1
- package/dist/src/workflow/definition.js +30 -4
- package/dist/src/workflow/definition.js.map +1 -1
- package/dist/src/workflow/determinism.d.ts +64 -0
- package/dist/src/workflow/determinism.d.ts.map +1 -1
- package/dist/src/workflow/determinism.js +120 -3
- package/dist/src/workflow/determinism.js.map +1 -1
- package/dist/src/workflow/errors.d.ts +6 -0
- package/dist/src/workflow/errors.d.ts.map +1 -1
- package/dist/src/workflow/errors.js +12 -0
- package/dist/src/workflow/errors.js.map +1 -1
- package/dist/src/workflow/executor.d.ts +56 -0
- package/dist/src/workflow/executor.d.ts.map +1 -1
- package/dist/src/workflow/executor.js +300 -9
- package/dist/src/workflow/executor.js.map +1 -1
- package/dist/src/workflow/inbound.d.ts +84 -0
- package/dist/src/workflow/inbound.d.ts.map +1 -0
- package/dist/src/workflow/inbound.js +65 -0
- package/dist/src/workflow/inbound.js.map +1 -0
- package/dist/src/workflow/index.d.ts +1 -0
- package/dist/src/workflow/index.d.ts.map +1 -1
- package/dist/src/workflow/index.js +1 -0
- package/dist/src/workflow/index.js.map +1 -1
- package/dist/src/workflow/replay.d.ts +24 -2
- package/dist/src/workflow/replay.d.ts.map +1 -1
- package/dist/src/workflow/replay.js +679 -15
- package/dist/src/workflow/replay.js.map +1 -1
- package/dist/src/workflows/index.d.ts +1 -1
- package/dist/src/workflows/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/src/client.js
CHANGED
|
@@ -5,16 +5,19 @@ import { Context, Effect } from 'effect';
|
|
|
5
5
|
import * as Option from 'effect/Option';
|
|
6
6
|
import { createDefaultHeaders, mergeHeaders, normalizeMetadataHeaders } from './client/headers';
|
|
7
7
|
import { makeDefaultInterceptorBuilder } from './client/interceptors';
|
|
8
|
-
import {
|
|
9
|
-
import { buildCancelRequest, buildQueryRequest, buildSignalRequest, buildSignalWithStartRequest, buildStartWorkflowRequest, buildTerminateRequest, computeSignalRequestId, createSignalRequestEntropy, decodeMemoAttributes, decodeSearchAttributes, encodeMemoAttributes, encodeSearchAttributes, } from './client/serialization';
|
|
8
|
+
import { buildCancelRequest, buildPollWorkflowUpdateRequest, buildQueryRequest, buildSignalRequest, buildSignalWithStartRequest, buildStartWorkflowRequest, buildTerminateRequest, buildUpdateWorkflowRequest, computeSignalRequestId, createSignalRequestEntropy, createUpdateRequestId, decodeMemoAttributes, decodeSearchAttributes, decodeUpdateOutcome, encodeMemoAttributes, encodeSearchAttributes, } from './client/serialization';
|
|
10
9
|
import { buildTransportOptions, normalizeTemporalAddress } from './client/transport';
|
|
11
10
|
import { createWorkflowHandle, } from './client/types';
|
|
12
|
-
import { createDefaultDataConverter, decodePayloadsToValues } from './common/payloads';
|
|
11
|
+
import { buildCodecsFromConfig, createDefaultDataConverter, decodePayloadsToValues, } from './common/payloads';
|
|
13
12
|
import { loadTemporalConfig } from './config';
|
|
13
|
+
import { makeDefaultClientInterceptors, runClientInterceptors, } from './interceptors/client';
|
|
14
14
|
import { createObservabilityServices } from './observability';
|
|
15
|
-
import {
|
|
15
|
+
import { WorkflowExecutionSchema, } from './proto/temporal/api/common/v1/message_pb';
|
|
16
|
+
import { UpdateWorkflowExecutionLifecycleStage } from './proto/temporal/api/enums/v1/update_pb';
|
|
17
|
+
import { HistoryEventFilterType } from './proto/temporal/api/enums/v1/workflow_pb';
|
|
18
|
+
import { DescribeNamespaceRequestSchema, DescribeNamespaceResponseSchema, GetWorkflowExecutionHistoryRequestSchema, } from './proto/temporal/api/workflowservice/v1/request_response_pb';
|
|
16
19
|
import { WorkflowService } from './proto/temporal/api/workflowservice/v1/service_pb';
|
|
17
|
-
import { LoggerService, MetricsExporterService, MetricsService, TemporalConfigService, WorkflowServiceClientService, } from './runtime/effect-layers';
|
|
20
|
+
import { DataConverterService, LoggerService, MetricsExporterService, MetricsService, TemporalConfigService, WorkflowServiceClientService, } from './runtime/effect-layers';
|
|
18
21
|
export const temporalCallOptions = (options) => {
|
|
19
22
|
const copy = { ...options };
|
|
20
23
|
Object.defineProperty(copy, CALL_OPTIONS_MARKER, {
|
|
@@ -53,6 +56,38 @@ const describeError = (error) => {
|
|
|
53
56
|
};
|
|
54
57
|
const TLS_ERROR_CODE_PREFIXES = ['ERR_TLS_', 'ERR_SSL_'];
|
|
55
58
|
const TLS_ERROR_MESSAGE_HINTS = [/handshake/i, /certificate/i, /secure tls/i, /ssl/i];
|
|
59
|
+
const isAbortLikeError = (error) => (error instanceof Error && error.name === 'AbortError') ||
|
|
60
|
+
(error instanceof ConnectError && error.code === Code.Canceled);
|
|
61
|
+
const normalizeUnknownError = (error) => {
|
|
62
|
+
const unwrap = (value) => {
|
|
63
|
+
if (!value || typeof value !== 'object') {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
if (value instanceof TemporalTlsHandshakeError || value instanceof ConnectError) {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
const candidate = value;
|
|
70
|
+
if (candidate.cause !== undefined) {
|
|
71
|
+
return unwrap(candidate.cause);
|
|
72
|
+
}
|
|
73
|
+
if (candidate.error !== undefined) {
|
|
74
|
+
return unwrap(candidate.error);
|
|
75
|
+
}
|
|
76
|
+
if (candidate._tag === 'UnknownException') {
|
|
77
|
+
return unwrap(candidate.cause ?? candidate.error ?? value);
|
|
78
|
+
}
|
|
79
|
+
const symbols = Object.getOwnPropertySymbols(value);
|
|
80
|
+
for (const symbol of symbols) {
|
|
81
|
+
const inner = value[symbol];
|
|
82
|
+
const unwrapped = unwrap(inner);
|
|
83
|
+
if (unwrapped !== inner) {
|
|
84
|
+
return unwrapped;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
};
|
|
89
|
+
return unwrap(error);
|
|
90
|
+
};
|
|
56
91
|
const isCallOptionsCandidate = (value) => {
|
|
57
92
|
if (!value || typeof value !== 'object') {
|
|
58
93
|
return false;
|
|
@@ -131,6 +166,12 @@ export const createTemporalClient = async (options = {}) => {
|
|
|
131
166
|
if (!workflowService) {
|
|
132
167
|
throw new Error('Temporal workflow service is not available');
|
|
133
168
|
}
|
|
169
|
+
const dataConverter = options.dataConverter ??
|
|
170
|
+
createDefaultDataConverter({
|
|
171
|
+
payloadCodecs: buildCodecsFromConfig(config.payloadCodecs),
|
|
172
|
+
logger,
|
|
173
|
+
metricsRegistry,
|
|
174
|
+
});
|
|
134
175
|
const effect = makeTemporalClientEffect({
|
|
135
176
|
...options,
|
|
136
177
|
config,
|
|
@@ -139,9 +180,10 @@ export const createTemporalClient = async (options = {}) => {
|
|
|
139
180
|
metricsExporter,
|
|
140
181
|
workflowService,
|
|
141
182
|
transport,
|
|
183
|
+
dataConverter,
|
|
142
184
|
});
|
|
143
185
|
try {
|
|
144
|
-
return await Effect.runPromise(effect.pipe(Effect.provideService(TemporalConfigService, config), Effect.provideService(LoggerService, logger), Effect.provideService(MetricsService, metricsRegistry), Effect.provideService(MetricsExporterService, metricsExporter), Effect.provideService(WorkflowServiceClientService, workflowService)));
|
|
186
|
+
return await Effect.runPromise(effect.pipe(Effect.provideService(TemporalConfigService, config), Effect.provideService(LoggerService, logger), Effect.provideService(MetricsService, metricsRegistry), Effect.provideService(MetricsExporterService, metricsExporter), Effect.provideService(WorkflowServiceClientService, workflowService), Effect.provideService(DataConverterService, dataConverter)));
|
|
145
187
|
}
|
|
146
188
|
catch (error) {
|
|
147
189
|
await createdTransport?.close?.();
|
|
@@ -153,11 +195,33 @@ export const makeTemporalClientEffect = (options = {}) => Effect.gen(function* (
|
|
|
153
195
|
const namespace = options.namespace ?? config.namespace;
|
|
154
196
|
const identity = options.identity ?? config.workerIdentity;
|
|
155
197
|
const taskQueue = options.taskQueue ?? config.taskQueue;
|
|
156
|
-
const dataConverter = options.dataConverter ?? createDefaultDataConverter();
|
|
157
|
-
const initialHeaders = createDefaultHeaders(config.apiKey);
|
|
158
198
|
const logger = options.logger ?? (yield* LoggerService);
|
|
159
199
|
const metricsRegistry = options.metrics ?? (yield* MetricsService);
|
|
160
200
|
const metricsExporter = options.metricsExporter ?? (yield* MetricsExporterService);
|
|
201
|
+
const contextualDataConverter = yield* Effect.contextWith((context) => Context.getOption(context, DataConverterService));
|
|
202
|
+
const dataConverter = options.dataConverter ??
|
|
203
|
+
Option.getOrUndefined(contextualDataConverter) ??
|
|
204
|
+
createDefaultDataConverter({
|
|
205
|
+
payloadCodecs: buildCodecsFromConfig(config.payloadCodecs),
|
|
206
|
+
logger,
|
|
207
|
+
metricsRegistry,
|
|
208
|
+
});
|
|
209
|
+
const initialHeaders = createDefaultHeaders(config.apiKey);
|
|
210
|
+
const tracingEnabled = options.tracingEnabled ?? config.tracingInterceptorsEnabled ?? false;
|
|
211
|
+
const clientInterceptorBuilder = options.clientInterceptorBuilder ?? {
|
|
212
|
+
build: (input) => makeDefaultClientInterceptors(input),
|
|
213
|
+
};
|
|
214
|
+
const defaultClientInterceptors = yield* clientInterceptorBuilder.build({
|
|
215
|
+
namespace,
|
|
216
|
+
taskQueue,
|
|
217
|
+
identity,
|
|
218
|
+
logger,
|
|
219
|
+
metricsRegistry,
|
|
220
|
+
metricsExporter,
|
|
221
|
+
retryPolicy: config.rpcRetryPolicy,
|
|
222
|
+
tracingEnabled,
|
|
223
|
+
});
|
|
224
|
+
const clientInterceptors = [...defaultClientInterceptors, ...(options.clientInterceptors ?? [])];
|
|
161
225
|
const workflowServiceFromContext = yield* Effect.contextWith((context) => Context.getOption(context, WorkflowServiceClientService));
|
|
162
226
|
let workflowService = options.workflowService ?? Option.getOrUndefined(workflowServiceFromContext);
|
|
163
227
|
let transport = options.transport;
|
|
@@ -198,6 +262,7 @@ export const makeTemporalClientEffect = (options = {}) => Effect.gen(function* (
|
|
|
198
262
|
logger,
|
|
199
263
|
metrics: clientMetrics,
|
|
200
264
|
metricsExporter,
|
|
265
|
+
clientInterceptors,
|
|
201
266
|
});
|
|
202
267
|
return { client, config };
|
|
203
268
|
});
|
|
@@ -215,6 +280,9 @@ class TemporalClientImpl {
|
|
|
215
280
|
#logger;
|
|
216
281
|
#clientMetrics;
|
|
217
282
|
#metricsExporter;
|
|
283
|
+
#clientInterceptors;
|
|
284
|
+
#pendingUpdateControllers = new Map();
|
|
285
|
+
#abortedUpdates = new Set();
|
|
218
286
|
closed = false;
|
|
219
287
|
headers;
|
|
220
288
|
static async initMetrics(registry) {
|
|
@@ -238,6 +306,7 @@ class TemporalClientImpl {
|
|
|
238
306
|
this.#logger = handles.logger;
|
|
239
307
|
this.#clientMetrics = handles.metrics;
|
|
240
308
|
this.#metricsExporter = handles.metricsExporter;
|
|
309
|
+
this.#clientInterceptors = handles.clientInterceptors;
|
|
241
310
|
this.memo = {
|
|
242
311
|
encode: (input) => encodeMemoAttributes(this.dataConverter, input),
|
|
243
312
|
decode: (memo) => decodeMemoAttributes(this.dataConverter, memo),
|
|
@@ -253,12 +322,17 @@ class TemporalClientImpl {
|
|
|
253
322
|
terminate: (handle, options, callOptions) => this.terminateWorkflow(handle, options, callOptions),
|
|
254
323
|
cancel: (handle, callOptions) => this.cancelWorkflow(handle, callOptions),
|
|
255
324
|
signalWithStart: (options, callOptions) => this.signalWithStart(options, callOptions),
|
|
325
|
+
update: (workflowHandle, updateOptions, callOptions) => this.updateWorkflow(workflowHandle, updateOptions, callOptions),
|
|
326
|
+
awaitUpdate: (updateHandle, options, callOptions) => this.awaitWorkflowUpdate(updateHandle, options, callOptions),
|
|
327
|
+
cancelUpdate: (updateHandle) => this.cancelWorkflowUpdate(updateHandle),
|
|
328
|
+
getUpdateHandle: (workflowHandle, updateId, firstExecutionRunId) => this.getWorkflowUpdateHandle(workflowHandle, updateId, firstExecutionRunId),
|
|
329
|
+
result: (handle, callOptions) => this.getWorkflowResult(handle, callOptions),
|
|
256
330
|
};
|
|
257
331
|
}
|
|
258
332
|
async startWorkflow(options, callOptions) {
|
|
259
|
-
|
|
333
|
+
const parsedOptions = sanitizeStartWorkflowOptions(options);
|
|
334
|
+
return this.#instrumentOperation('workflow.start', async () => {
|
|
260
335
|
this.ensureOpen();
|
|
261
|
-
const parsedOptions = sanitizeStartWorkflowOptions(options);
|
|
262
336
|
const request = await buildStartWorkflowRequest({
|
|
263
337
|
options: parsedOptions,
|
|
264
338
|
defaults: {
|
|
@@ -273,12 +347,15 @@ class TemporalClientImpl {
|
|
|
273
347
|
namespace: request.namespace,
|
|
274
348
|
firstExecutionRunId: response.started ? response.runId : undefined,
|
|
275
349
|
});
|
|
350
|
+
}, {
|
|
351
|
+
workflowId: parsedOptions.workflowId,
|
|
352
|
+
taskQueue: options.taskQueue ?? this.defaultTaskQueue,
|
|
276
353
|
});
|
|
277
354
|
}
|
|
278
355
|
async signalWorkflow(handle, signalName, ...rawArgs) {
|
|
279
|
-
|
|
356
|
+
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
357
|
+
return this.#instrumentOperation('workflow.signal', async () => {
|
|
280
358
|
this.ensureOpen();
|
|
281
|
-
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
282
359
|
const normalizedSignalName = ensureNonEmptyString(signalName, 'signalName');
|
|
283
360
|
const { values, callOptions } = this.#splitArgsAndOptions(rawArgs);
|
|
284
361
|
const identity = this.defaultIdentity;
|
|
@@ -300,39 +377,101 @@ class TemporalClientImpl {
|
|
|
300
377
|
requestId,
|
|
301
378
|
}, this.dataConverter);
|
|
302
379
|
await this.executeRpc('signalWorkflow', (rpcOptions) => this.workflowService.signalWorkflowExecution(request, rpcOptions), callOptions);
|
|
303
|
-
});
|
|
380
|
+
}, { workflowId: resolvedHandle.workflowId, runId: resolvedHandle.runId, taskQueue: this.defaultTaskQueue });
|
|
304
381
|
}
|
|
305
382
|
async queryWorkflow(handle, queryName, ...rawArgs) {
|
|
306
|
-
|
|
383
|
+
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
384
|
+
return this.#instrumentOperation('workflow.query', async () => {
|
|
307
385
|
this.ensureOpen();
|
|
308
|
-
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
309
386
|
const { values, callOptions } = this.#splitArgsAndOptions(rawArgs);
|
|
310
|
-
const request = await buildQueryRequest(resolvedHandle, queryName, values, this.dataConverter
|
|
387
|
+
const request = await buildQueryRequest(resolvedHandle, queryName, values, this.dataConverter, {
|
|
388
|
+
rejectCondition: callOptions?.queryRejectCondition,
|
|
389
|
+
});
|
|
311
390
|
const response = await this.executeRpc('queryWorkflow', (rpcOptions) => this.workflowService.queryWorkflow(request, rpcOptions), callOptions);
|
|
312
391
|
return this.parseQueryResult(response);
|
|
313
|
-
});
|
|
392
|
+
}, { workflowId: resolvedHandle.workflowId, runId: resolvedHandle.runId });
|
|
314
393
|
}
|
|
315
394
|
async terminateWorkflow(handle, options = {}, callOptions) {
|
|
316
|
-
return this.#instrumentOperation('
|
|
395
|
+
return this.#instrumentOperation('workflow.terminate', async () => {
|
|
317
396
|
this.ensureOpen();
|
|
318
397
|
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
319
398
|
const parsedOptions = sanitizeTerminateWorkflowOptions(options);
|
|
320
399
|
const request = await buildTerminateRequest(resolvedHandle, parsedOptions, this.dataConverter, this.defaultIdentity);
|
|
321
400
|
await this.executeRpc('terminateWorkflow', (rpcOptions) => this.workflowService.terminateWorkflowExecution(request, rpcOptions), callOptions);
|
|
322
|
-
});
|
|
401
|
+
}, { workflowId: handle.workflowId, runId: handle.runId });
|
|
323
402
|
}
|
|
324
403
|
async cancelWorkflow(handle, callOptions) {
|
|
325
|
-
|
|
404
|
+
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
405
|
+
return this.#instrumentOperation('workflow.cancel', async () => {
|
|
326
406
|
this.ensureOpen();
|
|
327
|
-
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
328
407
|
const request = buildCancelRequest(resolvedHandle, this.defaultIdentity);
|
|
329
408
|
await this.executeRpc('cancelWorkflow', (rpcOptions) => this.workflowService.requestCancelWorkflowExecution(request, rpcOptions), callOptions);
|
|
330
|
-
});
|
|
409
|
+
}, { workflowId: resolvedHandle.workflowId, runId: resolvedHandle.runId });
|
|
410
|
+
}
|
|
411
|
+
async getWorkflowResult(handle, callOptions) {
|
|
412
|
+
let resolvedHandle = resolveHandle(this.namespace, handle);
|
|
413
|
+
return this.#instrumentOperation('workflow.result', async () => {
|
|
414
|
+
this.ensureOpen();
|
|
415
|
+
while (true) {
|
|
416
|
+
const execution = create(WorkflowExecutionSchema, {
|
|
417
|
+
workflowId: resolvedHandle.workflowId,
|
|
418
|
+
...(resolvedHandle.runId ? { runId: resolvedHandle.runId } : {}),
|
|
419
|
+
});
|
|
420
|
+
const request = create(GetWorkflowExecutionHistoryRequestSchema, {
|
|
421
|
+
namespace: resolvedHandle.namespace ?? this.namespace,
|
|
422
|
+
execution,
|
|
423
|
+
maximumPageSize: 1,
|
|
424
|
+
historyEventFilterType: HistoryEventFilterType.CLOSE_EVENT,
|
|
425
|
+
waitNewEvent: true,
|
|
426
|
+
skipArchival: true,
|
|
427
|
+
});
|
|
428
|
+
const response = await this.executeRpc('getWorkflowExecutionHistory', (rpcOptions) => this.workflowService.getWorkflowExecutionHistory(request, rpcOptions), callOptions);
|
|
429
|
+
const closeEvent = this.#extractCloseEvent(response);
|
|
430
|
+
if (!closeEvent || !closeEvent.attributes) {
|
|
431
|
+
throw new Error('Workflow close event not found');
|
|
432
|
+
}
|
|
433
|
+
const attributes = closeEvent.attributes;
|
|
434
|
+
switch (attributes.case) {
|
|
435
|
+
case 'workflowExecutionContinuedAsNewEventAttributes': {
|
|
436
|
+
const nextRunId = attributes.value.newExecutionRunId;
|
|
437
|
+
if (!nextRunId) {
|
|
438
|
+
throw new Error('Continue-as-new event missing newExecutionRunId');
|
|
439
|
+
}
|
|
440
|
+
resolvedHandle = { ...resolvedHandle, runId: nextRunId };
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
case 'workflowExecutionCompletedEventAttributes': {
|
|
444
|
+
const payloads = attributes.value.result?.payloads ?? [];
|
|
445
|
+
const decoded = await this.dataConverter.fromPayloads(payloads);
|
|
446
|
+
return (decoded.length <= 1 ? decoded[0] : decoded) ?? undefined;
|
|
447
|
+
}
|
|
448
|
+
case 'workflowExecutionFailedEventAttributes': {
|
|
449
|
+
const failure = await this.dataConverter.decodeFailurePayloads(attributes.value.failure);
|
|
450
|
+
const error = await this.dataConverter.failureToError(failure);
|
|
451
|
+
throw error ?? new Error('Workflow failed');
|
|
452
|
+
}
|
|
453
|
+
case 'workflowExecutionTimedOutEventAttributes':
|
|
454
|
+
throw new Error('Workflow timed out');
|
|
455
|
+
case 'workflowExecutionCanceledEventAttributes': {
|
|
456
|
+
const details = attributes.value.details?.payloads ?? [];
|
|
457
|
+
const decoded = await this.dataConverter.fromPayloads(details);
|
|
458
|
+
const detail = decoded.length <= 1 ? decoded[0] : decoded;
|
|
459
|
+
throw new Error(detail ? `Workflow canceled: ${JSON.stringify(detail)}` : 'Workflow canceled without details');
|
|
460
|
+
}
|
|
461
|
+
case 'workflowExecutionTerminatedEventAttributes': {
|
|
462
|
+
const reason = attributes.value.reason;
|
|
463
|
+
throw new Error(reason ? `Workflow terminated: ${reason}` : 'Workflow terminated');
|
|
464
|
+
}
|
|
465
|
+
default:
|
|
466
|
+
throw new Error(`Unsupported workflow close event type: ${attributes.case}`);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}, { workflowId: resolvedHandle.workflowId, runId: resolvedHandle.runId });
|
|
331
470
|
}
|
|
332
471
|
async signalWithStart(options, callOptions) {
|
|
333
|
-
|
|
472
|
+
const startOptions = sanitizeStartWorkflowOptions(options);
|
|
473
|
+
return this.#instrumentOperation('workflow.signalWithStart', async () => {
|
|
334
474
|
this.ensureOpen();
|
|
335
|
-
const startOptions = sanitizeStartWorkflowOptions(options);
|
|
336
475
|
const signalName = ensureNonEmptyString(options.signalName, 'signalName');
|
|
337
476
|
const signalArgs = options.signalArgs ?? [];
|
|
338
477
|
if (!Array.isArray(signalArgs)) {
|
|
@@ -356,17 +495,144 @@ class TemporalClientImpl {
|
|
|
356
495
|
namespace: request.namespace,
|
|
357
496
|
firstExecutionRunId: response.started ? response.runId : undefined,
|
|
358
497
|
});
|
|
498
|
+
}, { workflowId: startOptions.workflowId });
|
|
499
|
+
}
|
|
500
|
+
async updateWorkflow(handle, options, callOptions) {
|
|
501
|
+
this.ensureOpen();
|
|
502
|
+
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
503
|
+
const parsedOptions = sanitizeWorkflowUpdateOptions(options);
|
|
504
|
+
const updateId = parsedOptions.updateId ?? createUpdateRequestId();
|
|
505
|
+
const waitStage = this.#stageToProto(parsedOptions.waitForStage);
|
|
506
|
+
return this.#instrumentOperation('workflow.update', async () => {
|
|
507
|
+
const updateKey = this.#makeUpdateKey(resolvedHandle, updateId);
|
|
508
|
+
const { options: mergedCallOptions, controller, cleanup, } = this.#prepareUpdateCallOptions(updateKey, callOptions);
|
|
509
|
+
try {
|
|
510
|
+
const request = await buildUpdateWorkflowRequest({
|
|
511
|
+
handle: resolvedHandle,
|
|
512
|
+
namespace: this.namespace,
|
|
513
|
+
identity: this.defaultIdentity,
|
|
514
|
+
updateId,
|
|
515
|
+
updateName: parsedOptions.updateName,
|
|
516
|
+
args: parsedOptions.args,
|
|
517
|
+
headers: parsedOptions.headers,
|
|
518
|
+
waitStage,
|
|
519
|
+
firstExecutionRunId: parsedOptions.firstExecutionRunId,
|
|
520
|
+
}, this.dataConverter);
|
|
521
|
+
let response = await this.executeRpc('updateWorkflowExecution', (rpcOptions) => this.workflowService.updateWorkflowExecution(request, rpcOptions), mergedCallOptions);
|
|
522
|
+
while ((response.stage ?? UpdateWorkflowExecutionLifecycleStage.UNSPECIFIED) < waitStage) {
|
|
523
|
+
response = await this.executeRpc('updateWorkflowExecution', (rpcOptions) => this.workflowService.updateWorkflowExecution(request, rpcOptions), mergedCallOptions);
|
|
524
|
+
}
|
|
525
|
+
const runId = response.updateRef?.workflowExecution?.runId || resolvedHandle.runId;
|
|
526
|
+
const updateHandle = this.#createWorkflowUpdateHandle(resolvedHandle, {
|
|
527
|
+
updateId,
|
|
528
|
+
runId,
|
|
529
|
+
firstExecutionRunId: parsedOptions.firstExecutionRunId,
|
|
530
|
+
});
|
|
531
|
+
const stage = this.#stageFromProto(response.stage);
|
|
532
|
+
const outcome = await decodeUpdateOutcome(this.dataConverter, response.outcome);
|
|
533
|
+
return {
|
|
534
|
+
handle: updateHandle,
|
|
535
|
+
stage,
|
|
536
|
+
outcome,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
finally {
|
|
540
|
+
this.#releaseUpdateController(updateKey, controller);
|
|
541
|
+
cleanup?.();
|
|
542
|
+
}
|
|
543
|
+
}, { workflowId: resolvedHandle.workflowId, runId: resolvedHandle.runId, updateId });
|
|
544
|
+
}
|
|
545
|
+
async awaitWorkflowUpdate(handle, options = {}, callOptions) {
|
|
546
|
+
this.ensureOpen();
|
|
547
|
+
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
548
|
+
const updateId = ensureNonEmptyString(handle.updateId, 'updateId');
|
|
549
|
+
const waitStage = this.#stageToProto(options.waitForStage ?? 'completed');
|
|
550
|
+
const firstExecutionRunIdOverride = ensureOptionalTrimmedString(options.firstExecutionRunId, 'firstExecutionRunId', 1);
|
|
551
|
+
const pollHandle = {
|
|
552
|
+
workflowId: resolvedHandle.workflowId,
|
|
553
|
+
namespace: resolvedHandle.namespace,
|
|
554
|
+
runId: resolvedHandle.runId,
|
|
555
|
+
firstExecutionRunId: firstExecutionRunIdOverride ?? resolvedHandle.firstExecutionRunId,
|
|
556
|
+
};
|
|
557
|
+
return this.#instrumentOperation('workflow.awaitUpdate', async () => {
|
|
558
|
+
const request = buildPollWorkflowUpdateRequest({
|
|
559
|
+
handle: pollHandle,
|
|
560
|
+
namespace: this.namespace,
|
|
561
|
+
identity: this.defaultIdentity,
|
|
562
|
+
updateId,
|
|
563
|
+
waitStage,
|
|
564
|
+
});
|
|
565
|
+
const updateKey = this.#makeUpdateKey(resolvedHandle, updateId);
|
|
566
|
+
const { options: mergedCallOptions, controller, cleanup, } = this.#prepareUpdateCallOptions(updateKey, callOptions);
|
|
567
|
+
const throwIfAborted = () => {
|
|
568
|
+
if (controller.signal.aborted || this.#abortedUpdates.has(updateKey)) {
|
|
569
|
+
this.#abortedUpdates.delete(updateKey);
|
|
570
|
+
const abortError = new Error('Workflow update polling aborted');
|
|
571
|
+
abortError.name = 'AbortError';
|
|
572
|
+
throw abortError;
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
throwIfAborted();
|
|
576
|
+
try {
|
|
577
|
+
const response = await this.executeRpc('pollWorkflowExecutionUpdate', (rpcOptions) => this.workflowService.pollWorkflowExecutionUpdate(request, rpcOptions), mergedCallOptions);
|
|
578
|
+
const stage = this.#stageFromProto(response.stage);
|
|
579
|
+
const runId = response.updateRef?.workflowExecution?.runId || resolvedHandle.runId;
|
|
580
|
+
const updateHandle = this.#createWorkflowUpdateHandle(resolvedHandle, {
|
|
581
|
+
updateId,
|
|
582
|
+
runId,
|
|
583
|
+
firstExecutionRunId: pollHandle.firstExecutionRunId,
|
|
584
|
+
});
|
|
585
|
+
const outcome = await decodeUpdateOutcome(this.dataConverter, response.outcome);
|
|
586
|
+
return {
|
|
587
|
+
handle: updateHandle,
|
|
588
|
+
stage,
|
|
589
|
+
outcome,
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
catch (error) {
|
|
593
|
+
throwIfAborted();
|
|
594
|
+
throw error;
|
|
595
|
+
}
|
|
596
|
+
finally {
|
|
597
|
+
this.#abortedUpdates.delete(updateKey);
|
|
598
|
+
this.#releaseUpdateController(updateKey, controller);
|
|
599
|
+
cleanup?.();
|
|
600
|
+
}
|
|
601
|
+
}, { workflowId: resolvedHandle.workflowId, runId: resolvedHandle.runId, updateId });
|
|
602
|
+
}
|
|
603
|
+
getWorkflowUpdateHandle(handle, updateId, firstExecutionRunId) {
|
|
604
|
+
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
605
|
+
const normalizedUpdateId = ensureNonEmptyString(updateId, 'updateId');
|
|
606
|
+
const normalizedFirstExecution = ensureOptionalTrimmedString(firstExecutionRunId, 'firstExecutionRunId', 1);
|
|
607
|
+
return this.#createWorkflowUpdateHandle(resolvedHandle, {
|
|
608
|
+
updateId: normalizedUpdateId,
|
|
609
|
+
firstExecutionRunId: normalizedFirstExecution,
|
|
359
610
|
});
|
|
360
611
|
}
|
|
612
|
+
async cancelWorkflowUpdate(handle) {
|
|
613
|
+
return this.#instrumentOperation('workflow.update', async () => {
|
|
614
|
+
this.ensureOpen();
|
|
615
|
+
const resolvedHandle = resolveHandle(this.namespace, handle);
|
|
616
|
+
const updateId = ensureNonEmptyString(handle.updateId, 'updateId');
|
|
617
|
+
const updateKey = this.#makeUpdateKey(resolvedHandle, updateId);
|
|
618
|
+
const aborted = this.#abortUpdateControllers(updateKey);
|
|
619
|
+
if (!aborted) {
|
|
620
|
+
this.#log('debug', 'no pending workflow update operation to cancel', {
|
|
621
|
+
workflowId: resolvedHandle.workflowId,
|
|
622
|
+
updateId,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
}, { workflowId: handle.workflowId, runId: handle.runId, updateId: handle.updateId });
|
|
626
|
+
}
|
|
361
627
|
async describeNamespace(targetNamespace, callOptions) {
|
|
362
|
-
return this.#instrumentOperation('
|
|
628
|
+
return this.#instrumentOperation('workflow.describe', async () => {
|
|
363
629
|
this.ensureOpen();
|
|
364
630
|
const request = create(DescribeNamespaceRequestSchema, {
|
|
365
631
|
namespace: targetNamespace ?? this.namespace,
|
|
366
632
|
});
|
|
367
633
|
const response = await this.executeRpc('describeNamespace', (rpcOptions) => this.workflowService.describeNamespace(request, rpcOptions), callOptions);
|
|
368
634
|
return toBinary(DescribeNamespaceResponseSchema, response);
|
|
369
|
-
});
|
|
635
|
+
}, { namespace: targetNamespace ?? this.namespace });
|
|
370
636
|
}
|
|
371
637
|
async updateHeaders(headers) {
|
|
372
638
|
if (this.closed) {
|
|
@@ -404,24 +670,66 @@ class TemporalClientImpl {
|
|
|
404
670
|
void Effect.runPromise(this.#clientMetrics.operationErrors.inc());
|
|
405
671
|
}
|
|
406
672
|
}
|
|
407
|
-
async #instrumentOperation(operation, action) {
|
|
673
|
+
async #instrumentOperation(operation, action, metadata = {}) {
|
|
674
|
+
const context = {
|
|
675
|
+
kind: operation,
|
|
676
|
+
namespace: this.namespace,
|
|
677
|
+
taskQueue: metadata.taskQueue ?? this.defaultTaskQueue,
|
|
678
|
+
identity: this.defaultIdentity,
|
|
679
|
+
workflowId: metadata.workflowId,
|
|
680
|
+
runId: metadata.runId,
|
|
681
|
+
updateId: metadata.updateId,
|
|
682
|
+
metadata,
|
|
683
|
+
};
|
|
408
684
|
const start = Date.now();
|
|
685
|
+
const effect = runClientInterceptors(this.#clientInterceptors, context, () => Effect.tryPromise(action));
|
|
409
686
|
try {
|
|
410
|
-
const result = await
|
|
687
|
+
const result = await Effect.runPromise(effect);
|
|
411
688
|
this.#log('debug', `temporal client ${operation} succeeded`, {
|
|
412
689
|
operation,
|
|
413
690
|
namespace: this.namespace,
|
|
691
|
+
...metadata,
|
|
414
692
|
});
|
|
415
693
|
this.#recordMetrics(Date.now() - start, false);
|
|
416
694
|
return result;
|
|
417
695
|
}
|
|
418
696
|
catch (error) {
|
|
697
|
+
const normalized = normalizeUnknownError(error);
|
|
698
|
+
const finalError = normalized instanceof Error &&
|
|
699
|
+
normalized.message.toLowerCase().includes('unknown error occurred in effect.trypromise')
|
|
700
|
+
? (normalized.cause ??
|
|
701
|
+
normalized.error ??
|
|
702
|
+
normalized)
|
|
703
|
+
: normalized;
|
|
704
|
+
const message = finalError instanceof Error ? finalError.message.toLowerCase() : '';
|
|
705
|
+
const isUnknownPollAbort = operation === 'workflow.awaitUpdate' && message.includes('unknown error occurred in effect.trypromise');
|
|
706
|
+
if (isAbortLikeError(normalized)) {
|
|
707
|
+
this.#log('warn', `temporal client ${operation} aborted`, {
|
|
708
|
+
operation,
|
|
709
|
+
error: describeError(normalized),
|
|
710
|
+
...metadata,
|
|
711
|
+
});
|
|
712
|
+
this.#recordMetrics(Date.now() - start, true);
|
|
713
|
+
throw finalError;
|
|
714
|
+
}
|
|
715
|
+
if (isUnknownPollAbort) {
|
|
716
|
+
const abortError = new Error('Workflow update polling aborted');
|
|
717
|
+
abortError.name = 'AbortError';
|
|
718
|
+
this.#log('warn', `temporal client ${operation} aborted`, {
|
|
719
|
+
operation,
|
|
720
|
+
error: describeError(abortError),
|
|
721
|
+
...metadata,
|
|
722
|
+
});
|
|
723
|
+
this.#recordMetrics(Date.now() - start, true);
|
|
724
|
+
throw abortError;
|
|
725
|
+
}
|
|
419
726
|
this.#log('error', `temporal client ${operation} failed`, {
|
|
420
727
|
operation,
|
|
421
|
-
error: describeError(
|
|
728
|
+
error: describeError(finalError),
|
|
729
|
+
...metadata,
|
|
422
730
|
});
|
|
423
731
|
this.#recordMetrics(Date.now() - start, true);
|
|
424
|
-
throw
|
|
732
|
+
throw finalError;
|
|
425
733
|
}
|
|
426
734
|
}
|
|
427
735
|
#log(level, message, fields) {
|
|
@@ -447,6 +755,89 @@ class TemporalClientImpl {
|
|
|
447
755
|
}
|
|
448
756
|
return { values: args };
|
|
449
757
|
}
|
|
758
|
+
#prepareUpdateCallOptions(key, callOptions) {
|
|
759
|
+
const controller = new AbortController();
|
|
760
|
+
let cleanup;
|
|
761
|
+
if (callOptions?.signal) {
|
|
762
|
+
if (callOptions.signal.aborted) {
|
|
763
|
+
controller.abort();
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
const onAbort = () => controller.abort();
|
|
767
|
+
callOptions.signal.addEventListener('abort', onAbort, { once: true });
|
|
768
|
+
cleanup = () => callOptions.signal?.removeEventListener('abort', onAbort);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
this.#registerUpdateController(key, controller);
|
|
772
|
+
const overrides = {
|
|
773
|
+
...(callOptions ? { ...callOptions } : {}),
|
|
774
|
+
signal: controller.signal,
|
|
775
|
+
};
|
|
776
|
+
return { options: overrides, controller, cleanup };
|
|
777
|
+
}
|
|
778
|
+
#registerUpdateController(key, controller) {
|
|
779
|
+
this.#abortedUpdates.delete(key);
|
|
780
|
+
const current = this.#pendingUpdateControllers.get(key);
|
|
781
|
+
if (current) {
|
|
782
|
+
current.add(controller);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
this.#pendingUpdateControllers.set(key, new Set([controller]));
|
|
786
|
+
}
|
|
787
|
+
#releaseUpdateController(key, controller) {
|
|
788
|
+
const current = this.#pendingUpdateControllers.get(key);
|
|
789
|
+
if (!current) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
current.delete(controller);
|
|
793
|
+
if (current.size === 0) {
|
|
794
|
+
this.#pendingUpdateControllers.delete(key);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
#abortUpdateControllers(key) {
|
|
798
|
+
const current = this.#pendingUpdateControllers.get(key);
|
|
799
|
+
if (!current || current.size === 0) {
|
|
800
|
+
this.#abortedUpdates.add(key);
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
for (const controller of current) {
|
|
804
|
+
controller.abort();
|
|
805
|
+
}
|
|
806
|
+
this.#pendingUpdateControllers.delete(key);
|
|
807
|
+
this.#abortedUpdates.add(key);
|
|
808
|
+
return true;
|
|
809
|
+
}
|
|
810
|
+
#makeUpdateKey(handle, updateId) {
|
|
811
|
+
const namespace = handle.namespace ?? this.namespace;
|
|
812
|
+
const runId = handle.runId ?? '';
|
|
813
|
+
return `${namespace}::${handle.workflowId}::${runId}::${updateId}`;
|
|
814
|
+
}
|
|
815
|
+
#createWorkflowUpdateHandle(handle, overrides) {
|
|
816
|
+
return {
|
|
817
|
+
workflowId: handle.workflowId,
|
|
818
|
+
namespace: handle.namespace,
|
|
819
|
+
runId: overrides.runId ?? handle.runId,
|
|
820
|
+
firstExecutionRunId: overrides.firstExecutionRunId ?? handle.firstExecutionRunId,
|
|
821
|
+
updateId: overrides.updateId,
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
#stageToProto(stage, minimum = UpdateWorkflowExecutionLifecycleStage.ADMITTED) {
|
|
825
|
+
const normalized = ensureWorkflowUpdateStage(stage);
|
|
826
|
+
const protoStage = WORKFLOW_UPDATE_STAGE_TO_PROTO[normalized];
|
|
827
|
+
return protoStage >= minimum ? protoStage : minimum;
|
|
828
|
+
}
|
|
829
|
+
#stageFromProto(stage) {
|
|
830
|
+
if (stage === UpdateWorkflowExecutionLifecycleStage.ADMITTED) {
|
|
831
|
+
return 'admitted';
|
|
832
|
+
}
|
|
833
|
+
if (stage === UpdateWorkflowExecutionLifecycleStage.ACCEPTED) {
|
|
834
|
+
return 'accepted';
|
|
835
|
+
}
|
|
836
|
+
if (stage === UpdateWorkflowExecutionLifecycleStage.COMPLETED) {
|
|
837
|
+
return 'completed';
|
|
838
|
+
}
|
|
839
|
+
return 'unspecified';
|
|
840
|
+
}
|
|
450
841
|
#mergeRetryPolicy(overrides) {
|
|
451
842
|
const base = this.config.rpcRetryPolicy;
|
|
452
843
|
if (!overrides) {
|
|
@@ -490,6 +881,13 @@ class TemporalClientImpl {
|
|
|
490
881
|
retryableStatusCodes,
|
|
491
882
|
};
|
|
492
883
|
}
|
|
884
|
+
#extractCloseEvent(response) {
|
|
885
|
+
const events = response?.history?.events ?? [];
|
|
886
|
+
if (events.length === 0) {
|
|
887
|
+
return undefined;
|
|
888
|
+
}
|
|
889
|
+
return events[events.length - 1];
|
|
890
|
+
}
|
|
493
891
|
#buildCallContext(overrides) {
|
|
494
892
|
const userHeaders = overrides?.headers ? normalizeMetadataHeaders(overrides.headers) : undefined;
|
|
495
893
|
const mergedHeaders = userHeaders ? mergeHeaders(this.headers, userHeaders) : { ...this.headers };
|
|
@@ -502,29 +900,37 @@ class TemporalClientImpl {
|
|
|
502
900
|
signal,
|
|
503
901
|
}),
|
|
504
902
|
retryPolicy: this.#mergeRetryPolicy(overrides?.retryPolicy),
|
|
903
|
+
headers: mergedHeaders,
|
|
505
904
|
};
|
|
506
905
|
}
|
|
507
906
|
async executeRpc(operation, rpc, overrides) {
|
|
508
|
-
const { create, retryPolicy } = this.#buildCallContext(overrides);
|
|
509
|
-
|
|
510
|
-
|
|
907
|
+
const { create, retryPolicy, headers } = this.#buildCallContext(overrides);
|
|
908
|
+
const interceptorContext = {
|
|
909
|
+
kind: 'rpc',
|
|
910
|
+
namespace: this.namespace,
|
|
911
|
+
taskQueue: this.defaultTaskQueue,
|
|
912
|
+
identity: this.defaultIdentity,
|
|
913
|
+
headers,
|
|
914
|
+
metadata: { retryPolicy },
|
|
915
|
+
};
|
|
916
|
+
const baseEffect = () => Effect.tryPromise({
|
|
511
917
|
try: () => {
|
|
512
|
-
attempt
|
|
918
|
+
interceptorContext.attempt = (interceptorContext.attempt ?? 0) + 1;
|
|
513
919
|
return rpc(create());
|
|
514
920
|
},
|
|
515
921
|
catch: (error) => wrapRpcError(error),
|
|
516
922
|
}).pipe(Effect.tapError((error) => Effect.sync(() => {
|
|
517
923
|
this.#log('warn', `temporal rpc ${operation} attempt failed`, {
|
|
518
924
|
operation,
|
|
519
|
-
attempt,
|
|
925
|
+
attempt: interceptorContext.attempt,
|
|
520
926
|
error: describeError(error),
|
|
521
927
|
});
|
|
522
928
|
})));
|
|
523
|
-
const result = await Effect.runPromise(
|
|
524
|
-
if (attempt > 1) {
|
|
525
|
-
this.#log('info', `temporal rpc ${operation} succeeded after ${attempt} attempts`, {
|
|
929
|
+
const result = await Effect.runPromise(runClientInterceptors(this.#clientInterceptors, interceptorContext, baseEffect));
|
|
930
|
+
if ((interceptorContext.attempt ?? 1) > 1) {
|
|
931
|
+
this.#log('info', `temporal rpc ${operation} succeeded after ${interceptorContext.attempt} attempts`, {
|
|
526
932
|
operation,
|
|
527
|
-
attempts: attempt,
|
|
933
|
+
attempts: interceptorContext.attempt,
|
|
528
934
|
});
|
|
529
935
|
}
|
|
530
936
|
return result;
|
|
@@ -650,6 +1056,49 @@ const sanitizeTerminateWorkflowOptions = (options = {}) => {
|
|
|
650
1056
|
firstExecutionRunId: ensureOptionalTrimmedString(options.firstExecutionRunId, 'firstExecutionRunId', 1),
|
|
651
1057
|
};
|
|
652
1058
|
};
|
|
1059
|
+
const sanitizeWorkflowUpdateOptions = (options) => {
|
|
1060
|
+
if (!options || typeof options !== 'object') {
|
|
1061
|
+
throw new Error('Workflow update options must be provided');
|
|
1062
|
+
}
|
|
1063
|
+
const updateName = ensureNonEmptyString(options.updateName, 'updateName');
|
|
1064
|
+
const args = options.args ?? [];
|
|
1065
|
+
if (!Array.isArray(args)) {
|
|
1066
|
+
throw new Error('update args must be an array when provided');
|
|
1067
|
+
}
|
|
1068
|
+
const waitForStage = ensureWorkflowUpdateStage(options.waitForStage);
|
|
1069
|
+
const updateId = ensureOptionalTrimmedString(options.updateId, 'updateId', 1);
|
|
1070
|
+
const firstExecutionRunId = ensureOptionalTrimmedString(options.firstExecutionRunId, 'firstExecutionRunId', 1);
|
|
1071
|
+
return {
|
|
1072
|
+
updateName,
|
|
1073
|
+
args,
|
|
1074
|
+
headers: options.headers,
|
|
1075
|
+
updateId,
|
|
1076
|
+
waitForStage,
|
|
1077
|
+
firstExecutionRunId,
|
|
1078
|
+
};
|
|
1079
|
+
};
|
|
1080
|
+
const WORKFLOW_UPDATE_STAGE_VALUES = new Set([
|
|
1081
|
+
'unspecified',
|
|
1082
|
+
'admitted',
|
|
1083
|
+
'accepted',
|
|
1084
|
+
'completed',
|
|
1085
|
+
]);
|
|
1086
|
+
const DEFAULT_WORKFLOW_UPDATE_STAGE = 'accepted';
|
|
1087
|
+
const WORKFLOW_UPDATE_STAGE_TO_PROTO = {
|
|
1088
|
+
unspecified: UpdateWorkflowExecutionLifecycleStage.UNSPECIFIED,
|
|
1089
|
+
admitted: UpdateWorkflowExecutionLifecycleStage.ADMITTED,
|
|
1090
|
+
accepted: UpdateWorkflowExecutionLifecycleStage.ACCEPTED,
|
|
1091
|
+
completed: UpdateWorkflowExecutionLifecycleStage.COMPLETED,
|
|
1092
|
+
};
|
|
1093
|
+
const ensureWorkflowUpdateStage = (stage) => {
|
|
1094
|
+
if (!stage) {
|
|
1095
|
+
return DEFAULT_WORKFLOW_UPDATE_STAGE;
|
|
1096
|
+
}
|
|
1097
|
+
if (!WORKFLOW_UPDATE_STAGE_VALUES.has(stage)) {
|
|
1098
|
+
throw new Error('waitForStage must be one of unspecified, admitted, accepted, or completed');
|
|
1099
|
+
}
|
|
1100
|
+
return stage;
|
|
1101
|
+
};
|
|
653
1102
|
const resolveHandle = (defaultNamespace, handle) => {
|
|
654
1103
|
const workflowId = ensureNonEmptyString(handle.workflowId, 'workflowId');
|
|
655
1104
|
const namespace = ensureOptionalTrimmedString(handle.namespace, 'namespace', 1) ?? defaultNamespace;
|