@superdurable/dex 0.1.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/LICENSE +7 -0
- package/README.md +145 -0
- package/dist/src/blob-cache.d.ts +14 -0
- package/dist/src/blob-cache.js +22 -0
- package/dist/src/blob-cache.js.map +1 -0
- package/dist/src/client.d.ts +36 -0
- package/dist/src/client.js +441 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/codec.d.ts +38 -0
- package/dist/src/codec.js +86 -0
- package/dist/src/codec.js.map +1 -0
- package/dist/src/context.d.ts +23 -0
- package/dist/src/context.js +9 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/errors.d.ts +44 -0
- package/dist/src/errors.js +73 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/flow.d.ts +31 -0
- package/dist/src/flow.js +155 -0
- package/dist/src/flow.js.map +1 -0
- package/dist/src/gen/dex.d.ts +1529 -0
- package/dist/src/gen/dex.js +10661 -0
- package/dist/src/gen/dex.js.map +1 -0
- package/dist/src/gen/google/protobuf/duration.d.ts +103 -0
- package/dist/src/gen/google/protobuf/duration.js +64 -0
- package/dist/src/gen/google/protobuf/duration.js.map +1 -0
- package/dist/src/gen/google/protobuf/empty.d.ts +37 -0
- package/dist/src/gen/google/protobuf/empty.js +39 -0
- package/dist/src/gen/google/protobuf/empty.js.map +1 -0
- package/dist/src/gen/google/protobuf/struct.d.ts +140 -0
- package/dist/src/gen/google/protobuf/struct.js +359 -0
- package/dist/src/gen/google/protobuf/struct.js.map +1 -0
- package/dist/src/gen/google/protobuf/timestamp.d.ts +133 -0
- package/dist/src/gen/google/protobuf/timestamp.js +64 -0
- package/dist/src/gen/google/protobuf/timestamp.js.map +1 -0
- package/dist/src/grpc-status.d.ts +4 -0
- package/dist/src/grpc-status.js +105 -0
- package/dist/src/grpc-status.js.map +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +20 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/invocation-context.d.ts +44 -0
- package/dist/src/invocation-context.js +203 -0
- package/dist/src/invocation-context.js.map +1 -0
- package/dist/src/options.d.ts +106 -0
- package/dist/src/options.js +55 -0
- package/dist/src/options.js.map +1 -0
- package/dist/src/persistence.d.ts +45 -0
- package/dist/src/persistence.js +65 -0
- package/dist/src/persistence.js.map +1 -0
- package/dist/src/rpc.d.ts +39 -0
- package/dist/src/rpc.js +50 -0
- package/dist/src/rpc.js.map +1 -0
- package/dist/src/step.d.ts +92 -0
- package/dist/src/step.js +62 -0
- package/dist/src/step.js.map +1 -0
- package/dist/src/validation.d.ts +3 -0
- package/dist/src/validation.js +33 -0
- package/dist/src/validation.js.map +1 -0
- package/dist/src/value-mapper.d.ts +14 -0
- package/dist/src/value-mapper.js +199 -0
- package/dist/src/value-mapper.js.map +1 -0
- package/dist/src/wait.d.ts +57 -0
- package/dist/src/wait.js +124 -0
- package/dist/src/wait.js.map +1 -0
- package/dist/src/worker-dispatcher.d.ts +14 -0
- package/dist/src/worker-dispatcher.js +374 -0
- package/dist/src/worker-dispatcher.js.map +1 -0
- package/dist/src/worker.d.ts +18 -0
- package/dist/src/worker.js +139 -0
- package/dist/src/worker.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Dex SDK for TypeScript
|
|
2
|
+
|
|
3
|
+
This package targets Node.js 22 and 24. It provides strongly typed workflow
|
|
4
|
+
contracts and a Promise-based gRPC Client. The Client and Worker runtime use
|
|
5
|
+
`@grpc/grpc-js`. The native BlobCache binding remains a separate runtime phase.
|
|
6
|
+
|
|
7
|
+
Application values use `Codec<T>`. Flow, Step, RPC, Attribute, and Channel
|
|
8
|
+
definitions retain their input and output types. Client methods return Promise
|
|
9
|
+
because Node network I/O is asynchronous.
|
|
10
|
+
|
|
11
|
+
The Client uses `@grpc/grpc-js` directly. Rust is only the implementation
|
|
12
|
+
boundary for the shared BlobCache; TypeScript callbacks and network transport
|
|
13
|
+
stay in Node.
|
|
14
|
+
|
|
15
|
+
Step input codecs and RPC input/output codecs remain explicit because
|
|
16
|
+
TypeScript erases generic types at runtime. They are serialization metadata,
|
|
17
|
+
not builder arguments.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
class ApproveOrder implements Step<string> {
|
|
21
|
+
readonly inputCodec = stringCodec;
|
|
22
|
+
|
|
23
|
+
getStepType(): string {
|
|
24
|
+
return "ApproveOrder";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
waitFor(_context: Context, _orderId: string): Wait {
|
|
28
|
+
return Wait.allOf(Timer.byDuration(1_000));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
execute(_context: Context, orderId: string): StepDecision {
|
|
32
|
+
return gracefulComplete(orderId);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class Orders implements Flow<string> {
|
|
37
|
+
readonly approve = new ApproveOrder();
|
|
38
|
+
|
|
39
|
+
getFlowType(): string {
|
|
40
|
+
return "Orders";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getSteps() {
|
|
44
|
+
return StepList.startStep(this.approve);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const orders = new Orders();
|
|
49
|
+
const registry = new Registry([orders]);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Flows return all Steps once. Start with `StepList.startStep(step)` and append
|
|
53
|
+
heterogeneous Steps with `.otherSteps(...)`. Use
|
|
54
|
+
`StepList.withoutStartStep<void>(...)` for RPC-triggered Steps, or
|
|
55
|
+
`StepList.empty()` when the Flow has no Steps.
|
|
56
|
+
`Flow<StartInput>` only types the starting Step and `Client.startFlow()` input;
|
|
57
|
+
`StepList<StartInput>` enforces that relationship during type checking.
|
|
58
|
+
Non-starting Steps may use unrelated input types. `Flow` defaults to `void` for
|
|
59
|
+
Flows without a start input.
|
|
60
|
+
|
|
61
|
+
`StepOptions.waitForMethodTimeoutMs` and `executeMethodTimeoutMs` bound the two
|
|
62
|
+
handler calls. Timer and channel conditions determine how long a Step waits.
|
|
63
|
+
|
|
64
|
+
Every TypeScript Flow and Step must return an explicit durable name from
|
|
65
|
+
`getFlowType()` or `getStepType()`. Class names are never used as fallbacks
|
|
66
|
+
because bundlers and minifiers may rename them.
|
|
67
|
+
|
|
68
|
+
## Source layout
|
|
69
|
+
|
|
70
|
+
Public contracts are grouped by domain under `src/`. The root `src/index.ts`
|
|
71
|
+
is a barrel that re-exports the supported package API; applications should
|
|
72
|
+
continue importing only from `@superdurable/dex`.
|
|
73
|
+
|
|
74
|
+
- `codec.ts`: wire values and codecs
|
|
75
|
+
- `persistence.ts`: attributes, indexes, locks, and schemas
|
|
76
|
+
- `wait.ts`: channels, timers, conditions, and waits
|
|
77
|
+
- `step.ts`: Steps, movements, options, and decisions
|
|
78
|
+
- `rpc.ts`: typed RPC contracts and decorators
|
|
79
|
+
- `flow.ts`: Flows, registration, and validation
|
|
80
|
+
- `client.ts`: Promise-based FlowService Client
|
|
81
|
+
- `worker.ts`: Worker gRPC service and lifecycle
|
|
82
|
+
- `worker-dispatcher.ts`: typed callback dispatch and response mapping
|
|
83
|
+
- `invocation-context.ts`: invocation-scoped persistence and condition state
|
|
84
|
+
- `blob-cache.ts`: injectable cache contract and future N-API binding
|
|
85
|
+
- `gen/`: checked-in protobuf and grpc-js bindings
|
|
86
|
+
|
|
87
|
+
Run `npm test` for runtime contracts and `npm run typecheck` for strict static
|
|
88
|
+
contracts. Run `./run-integration-tests.sh` for all 58 IWF compatibility
|
|
89
|
+
scenarios against an isolated `dexcli dev` environment. Run
|
|
90
|
+
`npm run generate:proto` after changing `protos/dex.proto`; `protoc` and its
|
|
91
|
+
standard protobuf includes must be installed.
|
|
92
|
+
|
|
93
|
+
## Integration coverage
|
|
94
|
+
|
|
95
|
+
Run the complete integration suite with TypeScript source coverage:
|
|
96
|
+
|
|
97
|
+
```shell
|
|
98
|
+
npm run coverage:integration
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The terminal report lists coverage per SDK source file and every uncovered
|
|
102
|
+
line. Open `coverage/index.html` for annotated source, or inspect
|
|
103
|
+
`coverage/coverage-summary.json` programmatically. `coverage/lcov.info` is the
|
|
104
|
+
report uploaded by CI. Generated protobuf code under `src/gen/` is excluded.
|
|
105
|
+
|
|
106
|
+
CI uploads the LCOV report to Codecov with GitHub OIDC, so no upload secret is
|
|
107
|
+
stored in this repository. The report uses the `sdk-typescript-integration`
|
|
108
|
+
flag and contributes to the TypeScript SDK component defined in the root
|
|
109
|
+
`codecov.yml`. After the first successful `main` upload, Codecov displays
|
|
110
|
+
project and patch coverage in its dashboard, GitHub checks, and PR comments.
|
|
111
|
+
The Actions run also publishes the complete HTML report as
|
|
112
|
+
`sdk-typescript-integration-coverage`.
|
|
113
|
+
|
|
114
|
+
The complete legacy IWF integration inventory lives under
|
|
115
|
+
[`test/integ`](test/integ/README.md). Its Flow fixtures retain the Java
|
|
116
|
+
suite's workflow behavior and its 58 assertions run against a real Dex server.
|
|
117
|
+
|
|
118
|
+
## Releases
|
|
119
|
+
|
|
120
|
+
The npm package is published as [`@superdurable/dex`](https://www.npmjs.com/package/@superdurable/dex).
|
|
121
|
+
Update `package.json` and `package-lock.json` to the same version, merge the
|
|
122
|
+
change, then publish a GitHub Release tagged `sdk-typescript/vX.Y.Z`. The
|
|
123
|
+
release workflow verifies that the tag matches `package.json`, runs type checks
|
|
124
|
+
and tests, inspects the tarball, and publishes through npm Trusted Publishing.
|
|
125
|
+
Prerelease versions use the `next` npm dist-tag; stable versions use `latest`.
|
|
126
|
+
|
|
127
|
+
Trusted Publishing can only be configured after the package exists. Bootstrap
|
|
128
|
+
the first version from a maintainer workstation with 2FA:
|
|
129
|
+
|
|
130
|
+
```shell
|
|
131
|
+
cd sdk-typescript
|
|
132
|
+
npm ci
|
|
133
|
+
npm run typecheck
|
|
134
|
+
npm test
|
|
135
|
+
npm pack --dry-run
|
|
136
|
+
npm login
|
|
137
|
+
npm publish --access public
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Then open the package settings on npmjs.com and add a GitHub Actions trusted
|
|
141
|
+
publisher with organization `superdurable`, repository `dex`, workflow
|
|
142
|
+
`sdk-typescript-publish.yml`, no environment, and `npm publish` permission.
|
|
143
|
+
Future releases use short-lived OIDC credentials and require no `NPM_TOKEN`.
|
|
144
|
+
After verifying the first OIDC release, configure npm publishing access to
|
|
145
|
+
require 2FA and disallow token-based publication.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface BlobCacheConfig {
|
|
2
|
+
readonly directory: string;
|
|
3
|
+
readonly maxBytes: number;
|
|
4
|
+
readonly frequencyCounters?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface BlobCache {
|
|
7
|
+
readonly config: BlobCacheConfig;
|
|
8
|
+
get(blobId: string): Uint8Array | undefined;
|
|
9
|
+
put(blobId: string, payload: Uint8Array): boolean;
|
|
10
|
+
delete(blobId: string): void;
|
|
11
|
+
deleteAll(): void;
|
|
12
|
+
close(): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function openBlobCache(config: BlobCacheConfig): BlobCache;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright (c) 2026 Super Durable, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Super Durable Source License 1.0.
|
|
4
|
+
// You may not use this file except in compliance with the License.
|
|
5
|
+
// See the LICENSE file in the repository root.
|
|
6
|
+
//
|
|
7
|
+
// SPDX-License-Identifier: LicenseRef-Super-Durable-1.0
|
|
8
|
+
import { laterPhase } from "./errors.js";
|
|
9
|
+
export function openBlobCache(config) {
|
|
10
|
+
if (config.directory.length === 0) {
|
|
11
|
+
throw new TypeError("blob cache directory is required");
|
|
12
|
+
}
|
|
13
|
+
if (!Number.isSafeInteger(config.maxBytes) || config.maxBytes <= 0) {
|
|
14
|
+
throw new RangeError("blob cache maxBytes must be a positive safe integer");
|
|
15
|
+
}
|
|
16
|
+
if (config.frequencyCounters !== undefined &&
|
|
17
|
+
(!Number.isSafeInteger(config.frequencyCounters) || config.frequencyCounters < 0)) {
|
|
18
|
+
throw new RangeError("blob cache frequencyCounters must be a non-negative safe integer");
|
|
19
|
+
}
|
|
20
|
+
throw laterPhase("BlobCache bridge");
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=blob-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-cache.js","sourceRoot":"","sources":["../../src/blob-cache.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,uDAAuD;AACvD,mEAAmE;AACnE,+CAA+C;AAC/C,EAAE;AACF,wDAAwD;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiBzC,MAAM,UAAU,aAAa,CAAC,MAAuB;IACnD,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,UAAU,CAAC,qDAAqD,CAAC,CAAC;IAC9E,CAAC;IACD,IACE,MAAM,CAAC,iBAAiB,KAAK,SAAS;QACtC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,iBAAiB,GAAG,CAAC,CAAC,EACjF,CAAC;QACD,MAAM,IAAI,UAAU,CAAC,kEAAkE,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,UAAU,CAAC,kBAAkB,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { BlobCache } from "./blob-cache.js";
|
|
2
|
+
import type { Codec } from "./codec.js";
|
|
3
|
+
import type { Context } from "./context.js";
|
|
4
|
+
import { type Flow, type Registry } from "./flow.js";
|
|
5
|
+
import { type ClientOptions, type FlowConfig, type FlowInfo, type ResetFlowOptions, type StartFlowOptions, type StepExecutionId, type StopFlowOptions, type TimerId } from "./options.js";
|
|
6
|
+
import { AttributeMap, type Attribute } from "./persistence.js";
|
|
7
|
+
import type { RPCResult } from "./rpc.js";
|
|
8
|
+
import { ChannelMap, type Channel } from "./wait.js";
|
|
9
|
+
export declare class Client {
|
|
10
|
+
readonly registry: Registry;
|
|
11
|
+
readonly blobCache: BlobCache;
|
|
12
|
+
readonly options: ClientOptions;
|
|
13
|
+
private readonly service;
|
|
14
|
+
private readonly hydrator;
|
|
15
|
+
constructor(registry: Registry, blobCache: BlobCache, options?: ClientOptions);
|
|
16
|
+
startFlow<StartInput>(flow: Flow<StartInput>, flowId: string, input: StartInput, options?: StartFlowOptions): Promise<string>;
|
|
17
|
+
invokeRPC<Input, Output>(rpcMethod: (context: Context, input: Input) => RPCResult<Output>, flowId: string, input: Input, runId?: string): Promise<Output>;
|
|
18
|
+
invokeRPC<Output>(rpcMethod: (context: Context) => RPCResult<Output>, flowId: string, runId?: string): Promise<Output>;
|
|
19
|
+
invokeRPC<Input>(rpcMethod: (context: Context, input: Input) => void, flowId: string, input: Input, runId?: string): Promise<void>;
|
|
20
|
+
invokeRPC(rpcMethod: (context: Context) => void, flowId: string, runId?: string): Promise<void>;
|
|
21
|
+
getAttribute<T>(flowId: string, attribute: Attribute<T>, runId?: string): Promise<T | undefined>;
|
|
22
|
+
getAttribute<T>(flowId: string, attribute: AttributeMap<T>, instance: string, runId?: string): Promise<T | undefined>;
|
|
23
|
+
setAttribute<T>(flowId: string, attribute: Attribute<T>, value: T, runId?: string): Promise<void>;
|
|
24
|
+
setAttribute<T>(flowId: string, attribute: AttributeMap<T>, instance: string, value: T, runId?: string): Promise<void>;
|
|
25
|
+
publish<T>(flowId: string, channel: Channel<T>, ...values: readonly T[]): Promise<void>;
|
|
26
|
+
publish<T>(flowId: string, channel: ChannelMap<T>, instance: string, ...values: readonly T[]): Promise<void>;
|
|
27
|
+
waitForFlow(flowId: string): Promise<void>;
|
|
28
|
+
waitForFlow<Output>(flowId: string, outputCodec: Codec<Output>, timeoutMs?: number): Promise<Output>;
|
|
29
|
+
stopFlow(flowId: string, options?: StopFlowOptions): Promise<void>;
|
|
30
|
+
describeFlow(flowId: string): Promise<FlowInfo>;
|
|
31
|
+
resetFlow(flowId: string, options: ResetFlowOptions): Promise<string>;
|
|
32
|
+
skipTimer(flowId: string, stepExecutionId: StepExecutionId, timerId: TimerId): Promise<void>;
|
|
33
|
+
waitForStepCompletion(flowId: string, stepExecutionId: StepExecutionId, timeoutMs: number): Promise<void>;
|
|
34
|
+
updateFlowConfig(flowId: string, config: FlowConfig): Promise<void>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
// Copyright (c) 2026 Super Durable, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Super Durable Source License 1.0.
|
|
4
|
+
// You may not use this file except in compliance with the License.
|
|
5
|
+
// See the LICENSE file in the repository root.
|
|
6
|
+
//
|
|
7
|
+
// SPDX-License-Identifier: LicenseRef-Super-Durable-1.0
|
|
8
|
+
import { credentials, status as GrpcStatus } from "@grpc/grpc-js";
|
|
9
|
+
import { ActiveStepSearchMode as ProtoActiveStepSearchMode, FlowErrorType as ProtoFlowErrorType, FlowResetType as ProtoFlowResetType, FlowServiceClient, FlowStatus as ProtoFlowStatus, IdReusePolicy as ProtoIdReusePolicy, IndexType as ProtoIndexType, ExecuteMethodFailurePolicy, StartFlowRequest, StepDurability as ProtoStepDurability, StepOptions as ProtoStepOptions, StopType as ProtoStopType, WaitForMethodFailurePolicy, } from "./gen/dex.js";
|
|
10
|
+
import { DexError, ErrorSubStatus, FlowErrorType, FlowUncompletedError, LongPollTimeoutError, } from "./errors.js";
|
|
11
|
+
import { registeredFlow, registeredRPC, } from "./flow.js";
|
|
12
|
+
import { translateServiceError } from "./grpc-status.js";
|
|
13
|
+
import { ActiveStepSearchMode, IdReusePolicy, ResetType, StopType, } from "./options.js";
|
|
14
|
+
import { AttributeMap, IndexType } from "./persistence.js";
|
|
15
|
+
import { requireName } from "./validation.js";
|
|
16
|
+
import { decodeValue, encodeValue, ValueHydrator } from "./value-mapper.js";
|
|
17
|
+
import { ChannelMap } from "./wait.js";
|
|
18
|
+
const defaultServerAddress = "localhost:8801";
|
|
19
|
+
export class Client {
|
|
20
|
+
registry;
|
|
21
|
+
blobCache;
|
|
22
|
+
options;
|
|
23
|
+
service;
|
|
24
|
+
hydrator;
|
|
25
|
+
constructor(registry, blobCache, options = {}) {
|
|
26
|
+
this.registry = registry;
|
|
27
|
+
this.blobCache = blobCache;
|
|
28
|
+
this.options = options;
|
|
29
|
+
this.service = new FlowServiceClient(options.serverAddress ?? defaultServerAddress, credentials.createInsecure());
|
|
30
|
+
this.hydrator = new ValueHydrator(this.service, blobCache);
|
|
31
|
+
}
|
|
32
|
+
async startFlow(flow, flowId, input, options = {}) {
|
|
33
|
+
const registered = registeredFlow(this.registry, flow);
|
|
34
|
+
const request = StartFlowRequest.create({
|
|
35
|
+
flowId: requireName(flowId),
|
|
36
|
+
flowType: registered.name,
|
|
37
|
+
flowTimeoutSeconds: seconds(options.timeoutMs),
|
|
38
|
+
requestId: options.requestId ?? crypto.randomUUID(),
|
|
39
|
+
flowStartOptions: {
|
|
40
|
+
idReusePolicy: mapIdReusePolicy(options.idReusePolicy),
|
|
41
|
+
cronSchedule: options.cronSchedule ?? "",
|
|
42
|
+
flowStartDelaySeconds: seconds(options.startDelayMs),
|
|
43
|
+
retryPolicy: mapFlowRetryPolicy(options.retryPolicy),
|
|
44
|
+
attributes: (options.attributes ?? []).map((initial) => ({
|
|
45
|
+
key: physicalName(initial.attribute.name, initial.instance),
|
|
46
|
+
value: encodeValue(initial.attribute.codec, initial.value),
|
|
47
|
+
indexConfig: mapIndex(initial.attribute.index),
|
|
48
|
+
})),
|
|
49
|
+
flowConfigOverride: mapFlowConfig(options.configOverride, this.options),
|
|
50
|
+
flowAlreadyStartedOptions: {
|
|
51
|
+
ignoreAlreadyStartedError: options.ignoreAlreadyStarted ?? false,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
if (registered.startStep === undefined) {
|
|
56
|
+
if (input !== undefined) {
|
|
57
|
+
throw new TypeError("Flow without a start Step requires undefined input");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
request.startStepType = registered.startStep.name;
|
|
62
|
+
request.stepInput = encodeValue(registered.startStep.step.inputCodec, input);
|
|
63
|
+
request.stepOptions = mapStepOptions(registered.startStep.step.getStepOptions?.(), registered.startStep.step.waitFor === undefined, registered);
|
|
64
|
+
}
|
|
65
|
+
return (await unary((callback) => this.service.startFlow(request, callback)))
|
|
66
|
+
.runId;
|
|
67
|
+
}
|
|
68
|
+
async invokeRPC(rpcMethod, flowId, inputOrRunId, runId = "") {
|
|
69
|
+
const rpc = registeredRPC(this.registry, rpcMethod);
|
|
70
|
+
const hasInput = rpc.options.inputCodec !== undefined;
|
|
71
|
+
const response = await unary((callback) => this.service.invokeRpc({
|
|
72
|
+
flowId: requireName(flowId),
|
|
73
|
+
runId: hasInput ? runId : inputOrRunId ?? "",
|
|
74
|
+
rpcName: rpc.name,
|
|
75
|
+
input: hasInput
|
|
76
|
+
? encodeValue(rpc.options.inputCodec, inputOrRunId)
|
|
77
|
+
: undefined,
|
|
78
|
+
timeoutSeconds: seconds(rpc.options.timeoutMs),
|
|
79
|
+
lockAttributeKeys: (rpc.options.lockAttributes ?? []).map((lock) => physicalName(lock.attribute.name, lock.instance)),
|
|
80
|
+
requestId: crypto.randomUUID(),
|
|
81
|
+
}, callback));
|
|
82
|
+
if (rpc.options.outputCodec === undefined) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
return decodeValue(rpc.options.outputCodec, await this.hydrator.hydrate(response.output));
|
|
86
|
+
}
|
|
87
|
+
async getAttribute(flowId, attribute, instanceOrRunId = "", runId = "") {
|
|
88
|
+
const isMap = attribute instanceof AttributeMap;
|
|
89
|
+
const response = await unary((callback) => this.service.getAttributes({
|
|
90
|
+
flowId: requireName(flowId),
|
|
91
|
+
runId: isMap ? runId : instanceOrRunId,
|
|
92
|
+
keys: [physicalName(attribute.name, isMap ? instanceOrRunId : undefined)],
|
|
93
|
+
allKeys: false,
|
|
94
|
+
}, callback));
|
|
95
|
+
const value = response.attributes[0]?.value;
|
|
96
|
+
if (value === undefined) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return decodeValue(attribute.codec, await this.hydrator.hydrate(value));
|
|
100
|
+
}
|
|
101
|
+
async setAttribute(flowId, attribute, instanceOrValue, valueOrRunId, runId = "") {
|
|
102
|
+
const isMap = attribute instanceof AttributeMap;
|
|
103
|
+
const instance = isMap ? String(instanceOrValue) : undefined;
|
|
104
|
+
const value = isMap ? valueOrRunId : instanceOrValue;
|
|
105
|
+
await unary((callback) => this.service.setAttributes({
|
|
106
|
+
flowId: requireName(flowId),
|
|
107
|
+
runId: isMap ? runId : typeof valueOrRunId === "string" ? valueOrRunId : "",
|
|
108
|
+
attributes: [
|
|
109
|
+
{
|
|
110
|
+
key: physicalName(attribute.name, instance),
|
|
111
|
+
value: encodeValue(attribute.codec, value),
|
|
112
|
+
indexConfig: mapIndex(attribute.index),
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
requestId: crypto.randomUUID(),
|
|
116
|
+
}, callback));
|
|
117
|
+
}
|
|
118
|
+
async publish(flowId, channel, ...instanceAndValues) {
|
|
119
|
+
const isMap = channel instanceof ChannelMap;
|
|
120
|
+
const instance = isMap ? String(instanceAndValues[0]) : undefined;
|
|
121
|
+
const values = isMap ? instanceAndValues.slice(1) : instanceAndValues;
|
|
122
|
+
await unary((callback) => this.service.publishToChannel({
|
|
123
|
+
flowId: requireName(flowId),
|
|
124
|
+
runId: "",
|
|
125
|
+
messages: values.map((value) => ({
|
|
126
|
+
channelName: physicalName(channel.name, instance),
|
|
127
|
+
value: encodeValue(channel.codec, value),
|
|
128
|
+
})),
|
|
129
|
+
}, callback));
|
|
130
|
+
}
|
|
131
|
+
async waitForFlow(flowId, outputCodec, timeoutMs) {
|
|
132
|
+
let response;
|
|
133
|
+
try {
|
|
134
|
+
response = await unary((callback) => this.service.waitForFlow({
|
|
135
|
+
flowId: requireName(flowId),
|
|
136
|
+
runId: "",
|
|
137
|
+
needsResults: outputCodec !== undefined,
|
|
138
|
+
waitTimeSeconds: seconds(timeoutMs),
|
|
139
|
+
}, callback));
|
|
140
|
+
}
|
|
141
|
+
catch (failure) {
|
|
142
|
+
if (failure instanceof DexError &&
|
|
143
|
+
(failure.code === GrpcStatus.DEADLINE_EXCEEDED ||
|
|
144
|
+
failure.subStatus === ErrorSubStatus.LONG_POLL_TIMEOUT)) {
|
|
145
|
+
throw new LongPollTimeoutError(flowId, { cause: failure });
|
|
146
|
+
}
|
|
147
|
+
throw failure;
|
|
148
|
+
}
|
|
149
|
+
if (response.flowStatus !== ProtoFlowStatus.FLOW_STATUS_COMPLETED) {
|
|
150
|
+
const summary = await this.describeFlow(flowId);
|
|
151
|
+
const results = await this.hydrator.hydrateAll(response.results.map((result) => result.completedStepOutput));
|
|
152
|
+
throw new FlowUncompletedError(summary.runId, mapFlowStatus(response.flowStatus), mapFlowErrorType(response.errorType), response.errorMessage || undefined, results);
|
|
153
|
+
}
|
|
154
|
+
if (outputCodec === undefined) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
for (let index = response.results.length - 1; index >= 0; index -= 1) {
|
|
158
|
+
const value = response.results[index]?.completedStepOutput;
|
|
159
|
+
if (value !== undefined) {
|
|
160
|
+
return decodeValue(outputCodec, await this.hydrator.hydrate(value));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
throw new TypeError(`Flow ${flowId} completed without an output`);
|
|
164
|
+
}
|
|
165
|
+
async stopFlow(flowId, options = {}) {
|
|
166
|
+
await unary((callback) => this.service.stopFlow({
|
|
167
|
+
flowId: requireName(flowId),
|
|
168
|
+
runId: "",
|
|
169
|
+
reason: options.reason ?? "",
|
|
170
|
+
stopType: mapStopType(options.type),
|
|
171
|
+
}, callback));
|
|
172
|
+
}
|
|
173
|
+
async describeFlow(flowId) {
|
|
174
|
+
const response = await unary((callback) => this.service.getFlowSummary({ flowId: requireName(flowId), runId: "" }, callback));
|
|
175
|
+
if (response.flowExecutionId === undefined || response.startTime === undefined) {
|
|
176
|
+
throw new TypeError(`Dex returned an incomplete summary for Flow ${flowId}`);
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
flowId: response.flowExecutionId.flowId,
|
|
180
|
+
runId: response.flowExecutionId.runId,
|
|
181
|
+
flowType: response.flowType,
|
|
182
|
+
status: mapFlowStatus(response.flowStatus),
|
|
183
|
+
startedAt: response.startTime,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
async resetFlow(flowId, options) {
|
|
187
|
+
const response = await unary((callback) => this.service.resetFlow({
|
|
188
|
+
flowId: requireName(flowId),
|
|
189
|
+
runId: "",
|
|
190
|
+
resetType: mapResetType(options.type),
|
|
191
|
+
historyEventId: number64(options.historyEventId),
|
|
192
|
+
reason: options.reason ?? "",
|
|
193
|
+
historyEventTime: options.historyEventTime?.toISOString() ?? "",
|
|
194
|
+
stepType: options.stepType ?? "",
|
|
195
|
+
stepExecutionId: options.stepExecutionId ?? "",
|
|
196
|
+
skipChannelMessagesReapply: options.skipChannelMessagesReapply ?? false,
|
|
197
|
+
skipLockingRpcReapply: options.skipLockingRpcReapply ?? false,
|
|
198
|
+
}, callback));
|
|
199
|
+
return response.runId;
|
|
200
|
+
}
|
|
201
|
+
async skipTimer(flowId, stepExecutionId, timerId) {
|
|
202
|
+
await unary((callback) => this.service.skipTimer({
|
|
203
|
+
flowId: requireName(flowId),
|
|
204
|
+
runId: "",
|
|
205
|
+
stepExecutionId: `${stepExecutionId.stepType}-${stepExecutionId.number ?? 1}`,
|
|
206
|
+
timerConditionId: timerId.conditionId ?? "",
|
|
207
|
+
timerConditionIndex: timerId.conditionIndex,
|
|
208
|
+
}, callback));
|
|
209
|
+
}
|
|
210
|
+
async waitForStepCompletion(flowId, stepExecutionId, timeoutMs) {
|
|
211
|
+
await unary((callback) => this.service.waitForStepCompletion({
|
|
212
|
+
flowId: requireName(flowId),
|
|
213
|
+
stepType: stepExecutionId.stepType,
|
|
214
|
+
stepExecutionNumber: String(stepExecutionId.number ?? 1),
|
|
215
|
+
waitTimeSeconds: seconds(timeoutMs),
|
|
216
|
+
requestId: crypto.randomUUID(),
|
|
217
|
+
}, callback));
|
|
218
|
+
}
|
|
219
|
+
async updateFlowConfig(flowId, config) {
|
|
220
|
+
await unary((callback) => this.service.updateFlowConfig({
|
|
221
|
+
flowId: requireName(flowId),
|
|
222
|
+
runId: "",
|
|
223
|
+
flowConfig: mapFlowConfig(config, this.options),
|
|
224
|
+
}, callback));
|
|
225
|
+
}
|
|
226
|
+
async close() {
|
|
227
|
+
this.service.close();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function mapStepOptions(options, skipWaitFor, flow) {
|
|
231
|
+
const executeFailureStep = options?.executeFailure?.step;
|
|
232
|
+
const executeFailureDefinition = executeFailureStep === undefined
|
|
233
|
+
? undefined
|
|
234
|
+
: flow.steps.find((definition) => definition.step === executeFailureStep);
|
|
235
|
+
if (executeFailureStep !== undefined && executeFailureDefinition === undefined) {
|
|
236
|
+
throw new TypeError("execute failure Step must belong to the Flow");
|
|
237
|
+
}
|
|
238
|
+
const executeFailureOptions = options?.executeFailure?.options ?? executeFailureDefinition?.step.getStepOptions?.();
|
|
239
|
+
return ProtoStepOptions.create({
|
|
240
|
+
waitForTimeoutSeconds: seconds(options?.waitForMethodTimeoutMs),
|
|
241
|
+
executeTimeoutSeconds: seconds(options?.executeMethodTimeoutMs),
|
|
242
|
+
waitForRetryPolicy: mapRetryPolicy(options?.waitForRetry),
|
|
243
|
+
executeRetryPolicy: mapRetryPolicy(options?.executeRetry),
|
|
244
|
+
waitForFailurePolicy: options?.waitForFailure === "proceed"
|
|
245
|
+
? WaitForMethodFailurePolicy.WAIT_FOR_METHOD_FAILURE_POLICY_PROCEED_ON_FAILURE
|
|
246
|
+
: options?.waitForFailure === "failFlow"
|
|
247
|
+
? WaitForMethodFailurePolicy.WAIT_FOR_METHOD_FAILURE_POLICY_FAIL_FLOW_ON_FAILURE
|
|
248
|
+
: WaitForMethodFailurePolicy.WAIT_FOR_METHOD_FAILURE_POLICY_UNSPECIFIED,
|
|
249
|
+
executeFailurePolicy: executeFailureDefinition === undefined
|
|
250
|
+
? ExecuteMethodFailurePolicy.EXECUTE_METHOD_FAILURE_POLICY_UNSPECIFIED
|
|
251
|
+
: ExecuteMethodFailurePolicy.EXECUTE_METHOD_FAILURE_POLICY_PROCEED_TO_CONFIGURED_STEP,
|
|
252
|
+
executeFailureProceedStepType: executeFailureDefinition?.name ?? "",
|
|
253
|
+
executeFailureProceedStepOptions: executeFailureDefinition === undefined
|
|
254
|
+
? undefined
|
|
255
|
+
: mapStepOptions(executeFailureOptions, executeFailureDefinition.step.waitFor === undefined, flow),
|
|
256
|
+
skipWaitFor,
|
|
257
|
+
waitForDurabilityOverride: mapDurability(options?.waitForDurability),
|
|
258
|
+
executeDurabilityOverride: mapDurability(options?.executeDurability),
|
|
259
|
+
waitForLockAttributeKeys: (options?.waitForLockAttributes ?? []).map((lock) => physicalName(lock.attribute.name, lock.instance)),
|
|
260
|
+
executeLockAttributeKeys: (options?.executeLockAttributes ?? []).map((lock) => physicalName(lock.attribute.name, lock.instance)),
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function mapRetryPolicy(retry) {
|
|
264
|
+
if (retry === undefined) {
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
initialIntervalSeconds: seconds(retry.initialIntervalMs),
|
|
269
|
+
backoffCoefficient: retry.backoffCoefficient ?? 0,
|
|
270
|
+
maximumIntervalSeconds: seconds(retry.maximumIntervalMs),
|
|
271
|
+
maximumAttempts: retry.maximumAttempts ?? 0,
|
|
272
|
+
totalDurationSeconds: seconds(retry.totalDurationMs),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function mapFlowRetryPolicy(retry) {
|
|
276
|
+
if (retry === undefined) {
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
initialIntervalSeconds: seconds(retry.initialIntervalMs),
|
|
281
|
+
backoffCoefficient: retry.backoffCoefficient ?? 0,
|
|
282
|
+
maximumIntervalSeconds: seconds(retry.maximumIntervalMs),
|
|
283
|
+
maximumAttempts: retry.maximumAttempts ?? 0,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function mapFlowConfig(config, clientOptions) {
|
|
287
|
+
const workerTarget = config?.workerTarget ?? clientOptions.workerTarget;
|
|
288
|
+
if (config === undefined && workerTarget === undefined) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
activeStepSearchMode: config?.activeStepSearchMode === undefined
|
|
293
|
+
? undefined
|
|
294
|
+
: config.activeStepSearchMode === ActiveStepSearchMode.ALL
|
|
295
|
+
? ProtoActiveStepSearchMode.ACTIVE_STEP_SEARCH_MODE_ENABLED_FOR_ALL
|
|
296
|
+
: ProtoActiveStepSearchMode.ACTIVE_STEP_SEARCH_MODE_UNSPECIFIED,
|
|
297
|
+
continueAsNewThreshold: config?.continueAsNewThreshold,
|
|
298
|
+
continueAsNewPageSizeInBytes: config?.continueAsNewPageSizeBytes,
|
|
299
|
+
stepDurability: config?.stepDurability === undefined ? undefined : mapDurability(config.stepDurability),
|
|
300
|
+
workerTarget: workerTarget === undefined
|
|
301
|
+
? undefined
|
|
302
|
+
: { address: workerTarget.address, isHeadlessAddress: workerTarget.headless ?? false },
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function mapIndex(index) {
|
|
306
|
+
if (index === undefined) {
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
const types = {
|
|
310
|
+
[IndexType.KEYWORD]: ProtoIndexType.INDEX_TYPE_KEYWORD,
|
|
311
|
+
[IndexType.FULL_TEXT]: ProtoIndexType.INDEX_TYPE_TEXT,
|
|
312
|
+
[IndexType.KEYWORD_ARRAY]: ProtoIndexType.INDEX_TYPE_KEYWORD_ARRAY,
|
|
313
|
+
[IndexType.INT]: ProtoIndexType.INDEX_TYPE_INT,
|
|
314
|
+
[IndexType.DOUBLE]: ProtoIndexType.INDEX_TYPE_DOUBLE,
|
|
315
|
+
[IndexType.BOOL]: ProtoIndexType.INDEX_TYPE_BOOL,
|
|
316
|
+
[IndexType.DATETIME]: ProtoIndexType.INDEX_TYPE_DATETIME,
|
|
317
|
+
};
|
|
318
|
+
return {
|
|
319
|
+
enable: true,
|
|
320
|
+
type: types[index.type],
|
|
321
|
+
indexKey: index.indexKey ?? "",
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function mapIdReusePolicy(policy) {
|
|
325
|
+
switch (policy) {
|
|
326
|
+
case IdReusePolicy.ALLOW_IF_PREVIOUS_FAILED:
|
|
327
|
+
return ProtoIdReusePolicy.ID_REUSE_POLICY_ALLOW_IF_PREVIOUS_EXISTS_ABNORMALLY;
|
|
328
|
+
case IdReusePolicy.ALLOW_IF_NOT_RUNNING:
|
|
329
|
+
return ProtoIdReusePolicy.ID_REUSE_POLICY_ALLOW_IF_NO_RUNNING;
|
|
330
|
+
case IdReusePolicy.ALLOW_TERMINATE_IF_RUNNING:
|
|
331
|
+
return ProtoIdReusePolicy.ID_REUSE_POLICY_ALLOW_TERMINATE_IF_RUNNING;
|
|
332
|
+
case IdReusePolicy.DISALLOW:
|
|
333
|
+
return ProtoIdReusePolicy.ID_REUSE_POLICY_DISALLOW_REUSE;
|
|
334
|
+
default:
|
|
335
|
+
return ProtoIdReusePolicy.ID_REUSE_POLICY_UNSPECIFIED;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function mapStopType(type) {
|
|
339
|
+
switch (type) {
|
|
340
|
+
case StopType.TERMINATE:
|
|
341
|
+
return ProtoStopType.STOP_TYPE_TERMINATE;
|
|
342
|
+
case StopType.FAIL:
|
|
343
|
+
return ProtoStopType.STOP_TYPE_FAIL;
|
|
344
|
+
default:
|
|
345
|
+
return ProtoStopType.STOP_TYPE_CANCEL;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function mapResetType(type) {
|
|
349
|
+
const types = {
|
|
350
|
+
[ResetType.BEGINNING]: ProtoFlowResetType.FLOW_RESET_TYPE_BEGINNING,
|
|
351
|
+
[ResetType.HISTORY_EVENT_ID]: ProtoFlowResetType.FLOW_RESET_TYPE_HISTORY_EVENT_ID,
|
|
352
|
+
[ResetType.HISTORY_EVENT_TIME]: ProtoFlowResetType.FLOW_RESET_TYPE_HISTORY_EVENT_TIME,
|
|
353
|
+
[ResetType.STEP_TYPE]: ProtoFlowResetType.FLOW_RESET_TYPE_STEP_TYPE,
|
|
354
|
+
[ResetType.STEP_EXECUTION_ID]: ProtoFlowResetType.FLOW_RESET_TYPE_STEP_EXECUTION_ID,
|
|
355
|
+
};
|
|
356
|
+
return types[type];
|
|
357
|
+
}
|
|
358
|
+
function mapDurability(value) {
|
|
359
|
+
if (value === "sync") {
|
|
360
|
+
return ProtoStepDurability.STEP_DURABILITY_SYNC;
|
|
361
|
+
}
|
|
362
|
+
if (value === "async") {
|
|
363
|
+
return ProtoStepDurability.STEP_DURABILITY_ASYNC;
|
|
364
|
+
}
|
|
365
|
+
return ProtoStepDurability.STEP_DURABILITY_UNSPECIFIED;
|
|
366
|
+
}
|
|
367
|
+
function mapFlowStatus(status) {
|
|
368
|
+
switch (status) {
|
|
369
|
+
case ProtoFlowStatus.FLOW_STATUS_RUNNING:
|
|
370
|
+
return "running";
|
|
371
|
+
case ProtoFlowStatus.FLOW_STATUS_COMPLETED:
|
|
372
|
+
return "completed";
|
|
373
|
+
case ProtoFlowStatus.FLOW_STATUS_FAILED:
|
|
374
|
+
return "failed";
|
|
375
|
+
case ProtoFlowStatus.FLOW_STATUS_TIMEOUT:
|
|
376
|
+
return "timedOut";
|
|
377
|
+
case ProtoFlowStatus.FLOW_STATUS_TERMINATED:
|
|
378
|
+
return "terminated";
|
|
379
|
+
case ProtoFlowStatus.FLOW_STATUS_CANCELED:
|
|
380
|
+
return "cancelled";
|
|
381
|
+
case ProtoFlowStatus.FLOW_STATUS_CONTINUED_AS_NEW:
|
|
382
|
+
return "continuedAsNew";
|
|
383
|
+
default:
|
|
384
|
+
throw new TypeError(`unsupported Flow status ${status}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function mapFlowErrorType(type) {
|
|
388
|
+
switch (type) {
|
|
389
|
+
case ProtoFlowErrorType.FLOW_ERROR_TYPE_STEP_DECISION_FAILING_FLOW:
|
|
390
|
+
return FlowErrorType.STEP_DECISION_FAILED;
|
|
391
|
+
case ProtoFlowErrorType.FLOW_ERROR_TYPE_CLIENT_API_FAILING_FLOW:
|
|
392
|
+
return FlowErrorType.CLIENT_API_FAILED;
|
|
393
|
+
case ProtoFlowErrorType.FLOW_ERROR_TYPE_WORKER_API_FAIL:
|
|
394
|
+
return FlowErrorType.WORKER_API_FAILED;
|
|
395
|
+
case ProtoFlowErrorType.FLOW_ERROR_TYPE_INVALID_USER_FLOW_CODE:
|
|
396
|
+
return FlowErrorType.INVALID_USER_FLOW_CODE;
|
|
397
|
+
case ProtoFlowErrorType.FLOW_ERROR_TYPE_INTERNAL:
|
|
398
|
+
return FlowErrorType.INTERNAL;
|
|
399
|
+
default:
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
function physicalName(name, instance) {
|
|
404
|
+
if (instance === undefined) {
|
|
405
|
+
return name;
|
|
406
|
+
}
|
|
407
|
+
requireName(instance);
|
|
408
|
+
const encoded = encodeURIComponent(instance).replace(/[!'()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
409
|
+
return `${name}/${encoded}`;
|
|
410
|
+
}
|
|
411
|
+
function seconds(milliseconds) {
|
|
412
|
+
if (milliseconds === undefined) {
|
|
413
|
+
return 0;
|
|
414
|
+
}
|
|
415
|
+
if (!Number.isSafeInteger(milliseconds) || milliseconds < 0 || milliseconds % 1_000 !== 0) {
|
|
416
|
+
throw new RangeError("duration must be a non-negative whole number of seconds");
|
|
417
|
+
}
|
|
418
|
+
return milliseconds / 1_000;
|
|
419
|
+
}
|
|
420
|
+
function number64(value) {
|
|
421
|
+
if (value === undefined) {
|
|
422
|
+
return 0;
|
|
423
|
+
}
|
|
424
|
+
const number = Number(value);
|
|
425
|
+
if (!Number.isSafeInteger(number)) {
|
|
426
|
+
throw new RangeError("history event ID exceeds JavaScript's safe integer range");
|
|
427
|
+
}
|
|
428
|
+
return number;
|
|
429
|
+
}
|
|
430
|
+
function unary(invoke) {
|
|
431
|
+
return new Promise((resolve, reject) => {
|
|
432
|
+
invoke((error, response) => {
|
|
433
|
+
if (error !== null) {
|
|
434
|
+
reject(translateServiceError(error));
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
resolve(response);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
//# sourceMappingURL=client.js.map
|