@temporalio/client
Advanced tools
Comparing version 0.17.2 to 0.18.0
@@ -11,2 +11,3 @@ "use strict"; | ||
const connection_1 = require("./connection"); | ||
const errors_1 = require("./errors"); | ||
/** | ||
@@ -45,9 +46,2 @@ * Thrown by {@link AsyncCompletionClient} when trying to complete or heartbeat | ||
exports.ActivityCancelledError = ActivityCancelledError; | ||
/** | ||
* Type assertion helper, assertion is mostly empty because any additional | ||
* properties are optional. | ||
*/ | ||
function isServerErrorResponse(err) { | ||
return err instanceof Error; | ||
} | ||
function defaultAsyncCompletionClientOptions() { | ||
@@ -73,3 +67,3 @@ return { | ||
handleError(err) { | ||
if (isServerErrorResponse(err)) { | ||
if ((0, errors_1.isServerErrorResponse)(err)) { | ||
if (err.code === constants_1.Status.NOT_FOUND) { | ||
@@ -76,0 +70,0 @@ throw new ActivityNotFoundError('Not found'); |
@@ -52,3 +52,3 @@ import * as grpc from '@grpc/grpc-js'; | ||
export declare class Connection { | ||
static readonly Client: import("@grpc/grpc-js/build/src/make-client").ServiceClientConstructor; | ||
static readonly Client: grpc.ServiceClientConstructor; | ||
readonly options: ConnectionOptionsWithDefaults; | ||
@@ -55,0 +55,0 @@ readonly client: grpc.Client; |
@@ -0,3 +1,15 @@ | ||
import { ServerErrorResponse } from '@grpc/grpc-js'; | ||
import { RetryState, TemporalFailure } from '@temporalio/common'; | ||
export { WorkflowExecutionAlreadyStartedError } from '@temporalio/common'; | ||
/** | ||
* Generic Error class for errors coming from the service | ||
*/ | ||
export declare class ServiceError extends Error { | ||
readonly name: string; | ||
readonly cause?: Error; | ||
constructor(message: string, opts?: { | ||
cause: Error; | ||
}); | ||
} | ||
/** | ||
* Thrown by the client while waiting on Workflow execution result if execution | ||
@@ -28,1 +40,6 @@ * completes with failure. | ||
} | ||
/** | ||
* Type assertion helper, assertion is mostly empty because any additional | ||
* properties are optional. | ||
*/ | ||
export declare function isServerErrorResponse(err: unknown): err is ServerErrorResponse; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.WorkflowContinuedAsNewError = exports.WorkflowFailedError = void 0; | ||
exports.isServerErrorResponse = exports.WorkflowContinuedAsNewError = exports.WorkflowFailedError = exports.ServiceError = exports.WorkflowExecutionAlreadyStartedError = void 0; | ||
var common_1 = require("@temporalio/common"); | ||
Object.defineProperty(exports, "WorkflowExecutionAlreadyStartedError", { enumerable: true, get: function () { return common_1.WorkflowExecutionAlreadyStartedError; } }); | ||
/** | ||
* Generic Error class for errors coming from the service | ||
*/ | ||
class ServiceError extends Error { | ||
constructor(message, opts) { | ||
super(message); | ||
this.name = 'ServiceError'; | ||
this.cause = opts?.cause; | ||
} | ||
} | ||
exports.ServiceError = ServiceError; | ||
/** | ||
* Thrown by the client while waiting on Workflow execution result if execution | ||
@@ -36,2 +49,10 @@ * completes with failure. | ||
exports.WorkflowContinuedAsNewError = WorkflowContinuedAsNewError; | ||
/** | ||
* Type assertion helper, assertion is mostly empty because any additional | ||
* properties are optional. | ||
*/ | ||
function isServerErrorResponse(err) { | ||
return err instanceof Error; | ||
} | ||
exports.isServerErrorResponse = isServerErrorResponse; | ||
//# sourceMappingURL=errors.js.map |
@@ -44,2 +44,3 @@ /** | ||
readonly details?: unknown[]; | ||
readonly firstExecutionRunId?: string; | ||
} | ||
@@ -49,2 +50,3 @@ /** Input for WorkflowClientCallsInterceptor.cancel */ | ||
readonly workflowExecution: WorkflowExecution; | ||
readonly firstExecutionRunId?: string; | ||
} | ||
@@ -51,0 +53,0 @@ /** |
import { temporal } from '@temporalio/proto'; | ||
export declare type WorkflowExecution = temporal.api.common.v1.IWorkflowExecution; | ||
export interface WorkflowExecution { | ||
workflowId: string; | ||
runId?: string; | ||
} | ||
export declare type StartWorkflowExecutionRequest = temporal.api.workflowservice.v1.IStartWorkflowExecutionRequest; | ||
@@ -4,0 +7,0 @@ export declare type GetWorkflowExecutionHistoryRequest = temporal.api.workflowservice.v1.IGetWorkflowExecutionHistoryRequest; |
@@ -6,3 +6,3 @@ import { temporal } from '@temporalio/proto'; | ||
import { WorkflowCancelInput, WorkflowClientCallsInterceptor, WorkflowQueryInput, WorkflowSignalInput, WorkflowSignalWithStartInput, WorkflowStartInput, WorkflowTerminateInput } from './interceptors'; | ||
import { DescribeWorkflowExecutionResponse, TerminateWorkflowExecutionResponse, RequestCancelWorkflowExecutionResponse } from './types'; | ||
import { DescribeWorkflowExecutionResponse, TerminateWorkflowExecutionResponse, RequestCancelWorkflowExecutionResponse, WorkflowExecution } from './types'; | ||
import { WorkflowService } from './connection'; | ||
@@ -119,4 +119,27 @@ /** | ||
*/ | ||
followRuns: boolean; | ||
followRuns?: boolean; | ||
} | ||
export interface GetWorkflowHandleOptions extends WorkflowResultOptions { | ||
/** | ||
* ID of the first execution in the Workflow execution chain. | ||
* | ||
* When getting a handle with no `runId`, pass this option to ensure some | ||
* {@link WorkflowHandle} methods (e.g. `terminate` and `cancel`) don't | ||
* affect executions from another chain. | ||
*/ | ||
firstExecutionRunId?: string; | ||
} | ||
interface WorkflowHandleOptions extends GetWorkflowHandleOptions { | ||
workflowId: string; | ||
runId?: string; | ||
interceptors: WorkflowClientCallsInterceptor[]; | ||
/** | ||
* A runId to use for getting the workflow's result. | ||
* | ||
* - When creating a handle using `getHandle`, uses the provided runId or firstExecutionRunId | ||
* - When creating a handle using `start`, uses the returned runId (first in the chain) | ||
* - When creating a handle using `signalWithStart`, uses the the returned runId | ||
*/ | ||
runIdForResult?: string; | ||
} | ||
/** | ||
@@ -171,2 +194,3 @@ * Options for starting a Workflow | ||
result<T extends Workflow>(workflowId: string, runId?: string, opts?: WorkflowResultOptions): Promise<WorkflowResultType<T>>; | ||
protected rethrowGrpcError(err: unknown, workflowExecution: WorkflowExecution, fallbackMessage: string): never; | ||
/** | ||
@@ -211,3 +235,3 @@ * Uses given input to make a queryWorkflow call to the service | ||
*/ | ||
protected _createWorkflowHandle<T extends Workflow>(workflowId: string, runId: string | undefined, interceptors: WorkflowClientCallsInterceptor[], resultOptions: WorkflowResultOptions): WorkflowHandle<T>; | ||
protected _createWorkflowHandle<T extends Workflow>({ workflowId, runId, firstExecutionRunId, interceptors, runIdForResult, ...resultOptions }: WorkflowHandleOptions): WorkflowHandle<T>; | ||
/** | ||
@@ -219,3 +243,3 @@ * Creates a Workflow handle for existing Workflow using `workflowId` and optional `runId`. | ||
*/ | ||
getHandle<T extends Workflow>(workflowId: string, runId?: string, options?: WorkflowResultOptions): WorkflowHandle<T>; | ||
getHandle<T extends Workflow>(workflowId: string, runId?: string, options?: GetWorkflowHandleOptions): WorkflowHandle<T>; | ||
} | ||
@@ -227,1 +251,2 @@ export declare class QueryRejectedError extends Error { | ||
} | ||
export {}; |
@@ -8,2 +8,3 @@ "use strict"; | ||
const os_1 = __importDefault(require("os")); | ||
const grpc_js_1 = require("@grpc/grpc-js"); | ||
const proto_1 = require("@temporalio/proto"); | ||
@@ -92,3 +93,10 @@ const uuid_1 = require("uuid"); | ||
const runId = await this._start(workflowTypeOrFunc, { ...options, workflowId }, interceptors); | ||
const handle = this._createWorkflowHandle(workflowId, runId, interceptors, { | ||
// runId is not used in handles created with `start*` calls because these | ||
// handles should allow interacting with the workflow if it continues as new. | ||
const handle = this._createWorkflowHandle({ | ||
workflowId, | ||
runId: undefined, | ||
firstExecutionRunId: runId, | ||
runIdForResult: runId, | ||
interceptors, | ||
followRuns: options.followRuns ?? true, | ||
@@ -109,3 +117,10 @@ }); // Cast is safe because we know we add the originalRunId below | ||
const runId = await this._signalWithStart(workflowTypeOrFunc, options, interceptors); | ||
const handle = this._createWorkflowHandle(workflowId, runId, interceptors, { | ||
// runId is not used in handles created with `start*` calls because these | ||
// handles should allow interacting with the workflow if it continues as new. | ||
const handle = this._createWorkflowHandle({ | ||
workflowId, | ||
runId: undefined, | ||
firstExecutionRunId: undefined, | ||
runIdForResult: runId, | ||
interceptors, | ||
followRuns: options.followRuns ?? true, | ||
@@ -146,3 +161,9 @@ }); // Cast is safe because we know we add the originalRunId below | ||
for (;;) { | ||
const res = await this.service.getWorkflowExecutionHistory(req); | ||
let res; | ||
try { | ||
res = await this.service.getWorkflowExecutionHistory(req); | ||
} | ||
catch (err) { | ||
this.rethrowGrpcError(err, { workflowId, runId }, 'Failed to get Workflow execution history'); | ||
} | ||
if (!res.history) { | ||
@@ -180,4 +201,4 @@ throw new Error('No history returned by service'); | ||
} | ||
const { failure } = ev.workflowExecutionFailedEventAttributes; | ||
throw new errors_1.WorkflowFailedError('Workflow execution failed', await (0, common_1.optionalFailureToOptionalError)(failure, this.options.dataConverter), common_1.RetryState.RETRY_STATE_NON_RETRYABLE_FAILURE); | ||
const { failure, retryState } = ev.workflowExecutionFailedEventAttributes; | ||
throw new errors_1.WorkflowFailedError('Workflow execution failed', await (0, common_1.optionalFailureToOptionalError)(failure, this.options.dataConverter), retryState ?? common_1.RetryState.RETRY_STATE_UNSPECIFIED); | ||
} | ||
@@ -195,3 +216,3 @@ else if (ev.workflowExecutionCanceledEventAttributes) { | ||
else if (ev.workflowExecutionTimedOutEventAttributes) { | ||
if (ev.workflowExecutionTimedOutEventAttributes.newExecutionRunId) { | ||
if (followRuns && ev.workflowExecutionTimedOutEventAttributes.newExecutionRunId) { | ||
execution.runId = ev.workflowExecutionTimedOutEventAttributes.newExecutionRunId; | ||
@@ -219,2 +240,11 @@ req.nextPageToken = undefined; | ||
} | ||
rethrowGrpcError(err, workflowExecution, fallbackMessage) { | ||
if ((0, errors_1.isServerErrorResponse)(err)) { | ||
if (err.code === grpc_js_1.status.NOT_FOUND) { | ||
throw new common_1.WorkflowNotFoundError(err.details ?? 'Workflow not found', workflowExecution.workflowId, workflowExecution.runId); | ||
} | ||
throw new errors_1.ServiceError(fallbackMessage, { cause: err }); | ||
} | ||
throw new errors_1.ServiceError('Unexpeced error while making gRPC request'); | ||
} | ||
/** | ||
@@ -226,11 +256,17 @@ * Uses given input to make a queryWorkflow call to the service | ||
async _queryWorkflowHandler(input) { | ||
const response = await this.service.queryWorkflow({ | ||
queryRejectCondition: input.queryRejectCondition, | ||
namespace: this.options.namespace, | ||
execution: input.workflowExecution, | ||
query: { | ||
queryType: input.queryType, | ||
queryArgs: { payloads: await this.options.dataConverter.toPayloads(...input.args) }, | ||
}, | ||
}); | ||
let response; | ||
try { | ||
response = await this.service.queryWorkflow({ | ||
queryRejectCondition: input.queryRejectCondition, | ||
namespace: this.options.namespace, | ||
execution: input.workflowExecution, | ||
query: { | ||
queryType: input.queryType, | ||
queryArgs: { payloads: await this.options.dataConverter.toPayloads(...input.args) }, | ||
}, | ||
}); | ||
} | ||
catch (err) { | ||
this.rethrowGrpcError(err, input.workflowExecution, 'Failed to query Workflow'); | ||
} | ||
if (response.queryRejected) { | ||
@@ -254,11 +290,16 @@ if (response.queryRejected.status === undefined || response.queryRejected.status === null) { | ||
async _signalWorkflowHandler(input) { | ||
await this.service.signalWorkflowExecution({ | ||
identity: this.options.identity, | ||
namespace: this.options.namespace, | ||
workflowExecution: input.workflowExecution, | ||
requestId: (0, uuid_1.v4)(), | ||
// control is unused, | ||
signalName: input.signalName, | ||
input: { payloads: await this.options.dataConverter.toPayloads(...input.args) }, | ||
}); | ||
try { | ||
await this.service.signalWorkflowExecution({ | ||
identity: this.options.identity, | ||
namespace: this.options.namespace, | ||
workflowExecution: input.workflowExecution, | ||
requestId: (0, uuid_1.v4)(), | ||
// control is unused, | ||
signalName: input.signalName, | ||
input: { payloads: await this.options.dataConverter.toPayloads(...input.args) }, | ||
}); | ||
} | ||
catch (err) { | ||
this.rethrowGrpcError(err, input.workflowExecution, 'Failed to signal Workflow'); | ||
} | ||
} | ||
@@ -273,30 +314,35 @@ /** | ||
const { options, workflowType, signalName, signalArgs, headers } = input; | ||
const { runId } = await this.service.signalWithStartWorkflowExecution({ | ||
namespace: this.options.namespace, | ||
identity, | ||
requestId: (0, uuid_1.v4)(), | ||
workflowId: options.workflowId, | ||
workflowIdReusePolicy: options.workflowIdReusePolicy, | ||
workflowType: { name: workflowType }, | ||
input: { payloads: await dataConverter.toPayloads(...options.args) }, | ||
signalName, | ||
signalInput: { payloads: await dataConverter.toPayloads(...signalArgs) }, | ||
taskQueue: { | ||
kind: proto_1.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED, | ||
name: options.taskQueue, | ||
}, | ||
workflowExecutionTimeout: options.workflowExecutionTimeout, | ||
workflowRunTimeout: options.workflowRunTimeout, | ||
workflowTaskTimeout: options.workflowTaskTimeout, | ||
retryPolicy: options.retry ? (0, common_1.compileRetryPolicy)(options.retry) : undefined, | ||
memo: options.memo ? { fields: await (0, common_1.mapToPayloads)(dataConverter, options.memo) } : undefined, | ||
searchAttributes: options.searchAttributes | ||
? { | ||
indexedFields: await (0, common_1.mapToPayloads)(dataConverter, options.searchAttributes), | ||
} | ||
: undefined, | ||
cronSchedule: options.cronSchedule, | ||
header: { fields: headers }, | ||
}); | ||
return runId; | ||
try { | ||
const { runId } = await this.service.signalWithStartWorkflowExecution({ | ||
namespace: this.options.namespace, | ||
identity, | ||
requestId: (0, uuid_1.v4)(), | ||
workflowId: options.workflowId, | ||
workflowIdReusePolicy: options.workflowIdReusePolicy, | ||
workflowType: { name: workflowType }, | ||
input: { payloads: await dataConverter.toPayloads(...options.args) }, | ||
signalName, | ||
signalInput: { payloads: await dataConverter.toPayloads(...signalArgs) }, | ||
taskQueue: { | ||
kind: proto_1.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED, | ||
name: options.taskQueue, | ||
}, | ||
workflowExecutionTimeout: options.workflowExecutionTimeout, | ||
workflowRunTimeout: options.workflowRunTimeout, | ||
workflowTaskTimeout: options.workflowTaskTimeout, | ||
retryPolicy: options.retry ? (0, common_1.compileRetryPolicy)(options.retry) : undefined, | ||
memo: options.memo ? { fields: await (0, common_1.mapToPayloads)(dataConverter, options.memo) } : undefined, | ||
searchAttributes: options.searchAttributes | ||
? { | ||
indexedFields: await (0, common_1.mapToPayloads)(dataConverter, options.searchAttributes), | ||
} | ||
: undefined, | ||
cronSchedule: options.cronSchedule, | ||
header: { fields: headers }, | ||
}); | ||
return runId; | ||
} | ||
catch (err) { | ||
this.rethrowGrpcError(err, { workflowId: options.workflowId }, 'Failed to signalWithStart Workflow'); | ||
} | ||
} | ||
@@ -309,3 +355,3 @@ /** | ||
async _startWorkflowHandler(input) { | ||
const { options: opts, workflowType: name, headers } = input; | ||
const { options: opts, workflowType, headers } = input; | ||
const { identity, dataConverter } = this.options; | ||
@@ -318,3 +364,3 @@ const req = { | ||
workflowIdReusePolicy: opts.workflowIdReusePolicy, | ||
workflowType: { name }, | ||
workflowType: { name: workflowType }, | ||
input: { payloads: await dataConverter.toPayloads(...opts.args) }, | ||
@@ -338,4 +384,12 @@ taskQueue: { | ||
}; | ||
const res = await this.service.startWorkflowExecution(req); | ||
return res.runId; | ||
try { | ||
const res = await this.service.startWorkflowExecution(req); | ||
return res.runId; | ||
} | ||
catch (err) { | ||
if (err.code === grpc_js_1.status.ALREADY_EXISTS) { | ||
throw new errors_1.WorkflowExecutionAlreadyStartedError('Workflow execution already started', opts.workflowId, workflowType); | ||
} | ||
this.rethrowGrpcError(err, { workflowId: opts.workflowId }, 'Failed to start Workflow'); | ||
} | ||
} | ||
@@ -348,8 +402,14 @@ /** | ||
async _terminateWorkflowHandler(input) { | ||
return await this.service.terminateWorkflowExecution({ | ||
namespace: this.options.namespace, | ||
identity: this.options.identity, | ||
...input, | ||
details: { payloads: await this.options.dataConverter.toPayloads(input.details) }, | ||
}); | ||
try { | ||
return await this.service.terminateWorkflowExecution({ | ||
namespace: this.options.namespace, | ||
identity: this.options.identity, | ||
...input, | ||
details: { payloads: await this.options.dataConverter.toPayloads(input.details) }, | ||
firstExecutionRunId: input.firstExecutionRunId, | ||
}); | ||
} | ||
catch (err) { | ||
this.rethrowGrpcError(err, input.workflowExecution, 'Failed to terminate Workflow'); | ||
} | ||
} | ||
@@ -362,8 +422,14 @@ /** | ||
async _cancelWorkflowHandler(input) { | ||
return await this.service.requestCancelWorkflowExecution({ | ||
namespace: this.options.namespace, | ||
identity: this.options.identity, | ||
requestId: (0, uuid_1.v4)(), | ||
workflowExecution: input.workflowExecution, | ||
}); | ||
try { | ||
return await this.service.requestCancelWorkflowExecution({ | ||
namespace: this.options.namespace, | ||
identity: this.options.identity, | ||
requestId: (0, uuid_1.v4)(), | ||
workflowExecution: input.workflowExecution, | ||
firstExecutionRunId: input.firstExecutionRunId, | ||
}); | ||
} | ||
catch (err) { | ||
this.rethrowGrpcError(err, input.workflowExecution, 'Failed to cancel workflow'); | ||
} | ||
} | ||
@@ -373,3 +439,3 @@ /** | ||
*/ | ||
_createWorkflowHandle(workflowId, runId, interceptors, resultOptions) { | ||
_createWorkflowHandle({ workflowId, runId, firstExecutionRunId, interceptors, runIdForResult, ...resultOptions }) { | ||
const namespace = this.options.namespace; | ||
@@ -380,3 +446,3 @@ return { | ||
async result() { | ||
return this.client.result(workflowId, runId, resultOptions); | ||
return this.client.result(workflowId, runIdForResult, resultOptions); | ||
}, | ||
@@ -389,2 +455,3 @@ async terminate(reason) { | ||
reason, | ||
firstExecutionRunId, | ||
}); | ||
@@ -397,2 +464,3 @@ }, | ||
workflowExecution: { workflowId, runId }, | ||
firstExecutionRunId, | ||
}); | ||
@@ -438,3 +506,8 @@ }, | ||
const interceptors = (this.options.interceptors.calls ?? []).map((ctor) => ctor({ workflowId, runId })); | ||
return this._createWorkflowHandle(workflowId, runId, interceptors, { | ||
return this._createWorkflowHandle({ | ||
workflowId, | ||
runId, | ||
firstExecutionRunId: options?.firstExecutionRunId, | ||
runIdForResult: runId ?? options?.firstExecutionRunId, | ||
interceptors, | ||
followRuns: options?.followRuns ?? true, | ||
@@ -441,0 +514,0 @@ }); |
{ | ||
"name": "@temporalio/client", | ||
"version": "0.17.2", | ||
"version": "0.18.0", | ||
"description": "Temporal.io SDK Client sub-package", | ||
@@ -17,4 +17,4 @@ "main": "lib/index.js", | ||
"@grpc/grpc-js": "^1.3.7", | ||
"@temporalio/common": "^0.17.2", | ||
"@temporalio/proto": "^0.17.2", | ||
"@temporalio/common": "^0.18.0", | ||
"@temporalio/proto": "^0.18.0", | ||
"ms": "^2.1.3", | ||
@@ -34,3 +34,3 @@ "protobufjs": "^6.11.2", | ||
}, | ||
"gitHead": "2232465a4f9b0cade28f0c21c2d7856053678728" | ||
"gitHead": "1f8030e0e003fac70969bee9bb816d9520910d02" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
102511
1691
1
+ Added@temporalio/common@0.18.0(transitive)
+ Added@temporalio/proto@0.18.0(transitive)
- Removed@temporalio/common@0.17.2(transitive)
- Removed@temporalio/proto@0.17.2(transitive)
Updated@temporalio/common@^0.18.0
Updated@temporalio/proto@^0.18.0