@temporalio/nexus 1.13.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.md +23 -0
- package/README.md +8 -0
- package/lib/context.d.ts +48 -0
- package/lib/context.js +86 -0
- package/lib/context.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -0
- package/lib/link-converter.d.ts +6 -0
- package/lib/link-converter.js +134 -0
- package/lib/link-converter.js.map +1 -0
- package/lib/token.d.ts +45 -0
- package/lib/token.js +72 -0
- package/lib/token.js.map +1 -0
- package/lib/workflow-helpers.d.ts +65 -0
- package/lib/workflow-helpers.js +122 -0
- package/lib/workflow-helpers.js.map +1 -0
- package/package.json +42 -0
- package/src/context.ts +103 -0
- package/src/index.ts +14 -0
- package/src/link-converter.ts +152 -0
- package/src/token.ts +107 -0
- package/src/workflow-helpers.ts +151 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Temporal TypeScript SDK
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2021 Temporal Technologies Inc. All Rights Reserved
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# `@temporalio/nexus`
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@temporalio/nexus)
|
|
4
|
+
|
|
5
|
+
Part of [Temporal](https://temporal.io)'s [TypeScript SDK](https://docs.temporal.io/typescript/introduction/).
|
|
6
|
+
|
|
7
|
+
- [API reference](https://typescript.temporal.io/api/namespaces/nexus)
|
|
8
|
+
- [Sample projects](https://github.com/temporalio/samples-typescript)
|
package/lib/context.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { Logger, MetricMeter } from '@temporalio/common';
|
|
3
|
+
import { Client } from '@temporalio/client';
|
|
4
|
+
export declare const asyncLocalStorage: AsyncLocalStorage<HandlerContext>;
|
|
5
|
+
export declare function getHandlerContext(): HandlerContext;
|
|
6
|
+
/**
|
|
7
|
+
* Context used internally in the SDK to propagate information from the worker to the Temporal Nexus helpers.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
* @hidden
|
|
11
|
+
*/
|
|
12
|
+
export interface HandlerContext {
|
|
13
|
+
log: Logger;
|
|
14
|
+
metrics: MetricMeter;
|
|
15
|
+
client: Client;
|
|
16
|
+
namespace: string;
|
|
17
|
+
taskQueue: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A logger for use in Nexus Handler scope.
|
|
21
|
+
*
|
|
22
|
+
* This defaults to the `Runtime`'s Logger (see {@link Runtime.logger}). Attributes from the
|
|
23
|
+
* current Nexus handler context are automatically included as metadata on every log entries. An
|
|
24
|
+
* extra `sdkComponent` metadata attribute is also added, with value `nexus`; this can be used
|
|
25
|
+
* for fine-grained filtering of log entries further downstream.
|
|
26
|
+
*
|
|
27
|
+
* To customize log attributes, register a {@link nexus.NexusOutboundCallsInterceptor} that
|
|
28
|
+
* intercepts the `getLogAttributes()` method.
|
|
29
|
+
*
|
|
30
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
31
|
+
*/
|
|
32
|
+
export declare const log: Logger;
|
|
33
|
+
/**
|
|
34
|
+
* A metric meter for use in Nexus handler scope, with Nexus handler-specific tags.
|
|
35
|
+
*
|
|
36
|
+
* To add custom tags, register a {@link nexus.NexusOutboundCallsInterceptor} that
|
|
37
|
+
* intercepts the `getMetricTags()` method.
|
|
38
|
+
*
|
|
39
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
40
|
+
*/
|
|
41
|
+
export declare const metricMeter: MetricMeter;
|
|
42
|
+
/**
|
|
43
|
+
* Returns a client to be used in a Nexus Operation's context, this Client is powered by the same
|
|
44
|
+
* NativeConnection that the worker was created with.
|
|
45
|
+
*
|
|
46
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
47
|
+
*/
|
|
48
|
+
export declare function getClient(): Client;
|
package/lib/context.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.metricMeter = exports.log = exports.asyncLocalStorage = void 0;
|
|
4
|
+
exports.getHandlerContext = getHandlerContext;
|
|
5
|
+
exports.getClient = getClient;
|
|
6
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
7
|
+
// Context Storage /////////////////////////////////////////////////////////////////////////////////
|
|
8
|
+
// Make it safe to use @temporalio/nexus with multiple versions installed.
|
|
9
|
+
const asyncLocalStorageSymbol = Symbol.for('__temporal_nexus_context_storage__');
|
|
10
|
+
if (!globalThis[asyncLocalStorageSymbol]) {
|
|
11
|
+
globalThis[asyncLocalStorageSymbol] = new node_async_hooks_1.AsyncLocalStorage();
|
|
12
|
+
}
|
|
13
|
+
exports.asyncLocalStorage = globalThis[asyncLocalStorageSymbol];
|
|
14
|
+
function getHandlerContext() {
|
|
15
|
+
const ctx = exports.asyncLocalStorage.getStore();
|
|
16
|
+
if (ctx == null) {
|
|
17
|
+
throw new ReferenceError('Not in a Nexus handler context');
|
|
18
|
+
}
|
|
19
|
+
return ctx;
|
|
20
|
+
}
|
|
21
|
+
// Basic APIs //////////////////////////////////////////////////////////////////////////////////////
|
|
22
|
+
/**
|
|
23
|
+
* A logger for use in Nexus Handler scope.
|
|
24
|
+
*
|
|
25
|
+
* This defaults to the `Runtime`'s Logger (see {@link Runtime.logger}). Attributes from the
|
|
26
|
+
* current Nexus handler context are automatically included as metadata on every log entries. An
|
|
27
|
+
* extra `sdkComponent` metadata attribute is also added, with value `nexus`; this can be used
|
|
28
|
+
* for fine-grained filtering of log entries further downstream.
|
|
29
|
+
*
|
|
30
|
+
* To customize log attributes, register a {@link nexus.NexusOutboundCallsInterceptor} that
|
|
31
|
+
* intercepts the `getLogAttributes()` method.
|
|
32
|
+
*
|
|
33
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
34
|
+
*/
|
|
35
|
+
exports.log = {
|
|
36
|
+
log(level, message, meta) {
|
|
37
|
+
return getHandlerContext().log.log(level, message, meta);
|
|
38
|
+
},
|
|
39
|
+
trace(message, meta) {
|
|
40
|
+
return getHandlerContext().log.trace(message, meta);
|
|
41
|
+
},
|
|
42
|
+
debug(message, meta) {
|
|
43
|
+
return getHandlerContext().log.debug(message, meta);
|
|
44
|
+
},
|
|
45
|
+
info(message, meta) {
|
|
46
|
+
return getHandlerContext().log.info(message, meta);
|
|
47
|
+
},
|
|
48
|
+
warn(message, meta) {
|
|
49
|
+
return getHandlerContext().log.warn(message, meta);
|
|
50
|
+
},
|
|
51
|
+
error(message, meta) {
|
|
52
|
+
return getHandlerContext().log.error(message, meta);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* A metric meter for use in Nexus handler scope, with Nexus handler-specific tags.
|
|
57
|
+
*
|
|
58
|
+
* To add custom tags, register a {@link nexus.NexusOutboundCallsInterceptor} that
|
|
59
|
+
* intercepts the `getMetricTags()` method.
|
|
60
|
+
*
|
|
61
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
62
|
+
*/
|
|
63
|
+
exports.metricMeter = {
|
|
64
|
+
createCounter(name, unit, description) {
|
|
65
|
+
return getHandlerContext().metrics.createCounter(name, unit, description);
|
|
66
|
+
},
|
|
67
|
+
createHistogram(name, valueType = 'int', unit, description) {
|
|
68
|
+
return getHandlerContext().metrics.createHistogram(name, valueType, unit, description);
|
|
69
|
+
},
|
|
70
|
+
createGauge(name, valueType = 'int', unit, description) {
|
|
71
|
+
return getHandlerContext().metrics.createGauge(name, valueType, unit, description);
|
|
72
|
+
},
|
|
73
|
+
withTags(tags) {
|
|
74
|
+
return getHandlerContext().metrics.withTags(tags);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Returns a client to be used in a Nexus Operation's context, this Client is powered by the same
|
|
79
|
+
* NativeConnection that the worker was created with.
|
|
80
|
+
*
|
|
81
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
82
|
+
*/
|
|
83
|
+
function getClient() {
|
|
84
|
+
return getHandlerContext().client;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":";;;AAaA,8CAMC;AAiFD,8BAEC;AAtGD,uDAAqD;AAIrD,oGAAoG;AAEpG,0EAA0E;AAC1E,MAAM,uBAAuB,GAAG,MAAM,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;AACjF,IAAI,CAAE,UAAkB,CAAC,uBAAuB,CAAC,EAAE,CAAC;IACjD,UAAkB,CAAC,uBAAuB,CAAC,GAAG,IAAI,oCAAiB,EAAkB,CAAC;AACzF,CAAC;AACY,QAAA,iBAAiB,GAAuC,UAAkB,CAAC,uBAAuB,CAAC,CAAC;AAEjH,SAAgB,iBAAiB;IAC/B,MAAM,GAAG,GAAG,yBAAiB,CAAC,QAAQ,EAAE,CAAC;IACzC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,cAAc,CAAC,gCAAgC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAgBD,oGAAoG;AAEpG;;;;;;;;;;;;GAYG;AACU,QAAA,GAAG,GAAW;IACzB,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAAkB;QACtD,OAAO,iBAAiB,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAC3D,CAAC;IACD,KAAK,CAAC,OAAe,EAAE,IAAkB;QACvC,OAAO,iBAAiB,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACtD,CAAC;IACD,KAAK,CAAC,OAAe,EAAE,IAAkB;QACvC,OAAO,iBAAiB,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,CAAC,OAAe,EAAE,IAAkB;QACtC,OAAO,iBAAiB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,OAAe,EAAE,IAAkB;QACtC,OAAO,iBAAiB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC;IACD,KAAK,CAAC,OAAe,EAAE,IAAkB;QACvC,OAAO,iBAAiB,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACtD,CAAC;CACF,CAAC;AAEF;;;;;;;GAOG;AACU,QAAA,WAAW,GAAgB;IACtC,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW;QACnC,OAAO,iBAAiB,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IAC5E,CAAC;IACD,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,KAAK,EAAE,IAAI,EAAE,WAAW;QACxD,OAAO,iBAAiB,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IACzF,CAAC;IACD,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,KAAK,EAAE,IAAI,EAAE,WAAW;QACpD,OAAO,iBAAiB,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IACrF,CAAC;IACD,QAAQ,CAAC,IAAI;QACX,OAAO,iBAAiB,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC;CACF,CAAC;AAEF;;;;;GAKG;AACH,SAAgB,SAAS;IACvB,OAAO,iBAAiB,EAAE,CAAC,MAAM,CAAC;AACpC,CAAC"}
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkflowRunOperationHandler = exports.startWorkflow = exports.metricMeter = exports.getClient = exports.log = void 0;
|
|
4
|
+
var context_1 = require("./context");
|
|
5
|
+
//
|
|
6
|
+
Object.defineProperty(exports, "log", { enumerable: true, get: function () { return context_1.log; } });
|
|
7
|
+
Object.defineProperty(exports, "getClient", { enumerable: true, get: function () { return context_1.getClient; } });
|
|
8
|
+
Object.defineProperty(exports, "metricMeter", { enumerable: true, get: function () { return context_1.metricMeter; } });
|
|
9
|
+
var workflow_helpers_1 = require("./workflow-helpers");
|
|
10
|
+
Object.defineProperty(exports, "startWorkflow", { enumerable: true, get: function () { return workflow_helpers_1.startWorkflow; } });
|
|
11
|
+
Object.defineProperty(exports, "WorkflowRunOperationHandler", { enumerable: true, get: function () { return workflow_helpers_1.WorkflowRunOperationHandler; } });
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qCAKmB;AAJjB,EAAE;AACF,8FAAA,GAAG,OAAA;AACH,oGAAA,SAAS,OAAA;AACT,sGAAA,WAAW,OAAA;AAGb,uDAM4B;AAL1B,iHAAA,aAAa,OAAA;AAEb,+HAAA,2BAA2B,OAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Link as NexusLink } from 'nexus-rpc';
|
|
2
|
+
import { temporal } from '@temporalio/proto';
|
|
3
|
+
type WorkflowEventLink = temporal.api.common.v1.Link.IWorkflowEvent;
|
|
4
|
+
export declare function convertWorkflowEventLinkToNexusLink(we: WorkflowEventLink): NexusLink;
|
|
5
|
+
export declare function convertNexusLinkToWorkflowEventLink(link: NexusLink): WorkflowEventLink;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.convertWorkflowEventLinkToNexusLink = convertWorkflowEventLinkToNexusLink;
|
|
7
|
+
exports.convertNexusLinkToWorkflowEventLink = convertNexusLinkToWorkflowEventLink;
|
|
8
|
+
const long_1 = __importDefault(require("long"));
|
|
9
|
+
const proto_1 = require("@temporalio/proto");
|
|
10
|
+
const { EventType } = proto_1.temporal.api.enums.v1;
|
|
11
|
+
const LINK_EVENT_ID_PARAM = 'eventID';
|
|
12
|
+
const LINK_EVENT_TYPE_PARAM = 'eventType';
|
|
13
|
+
const LINK_REQUEST_ID_PARAM = 'requestID';
|
|
14
|
+
const LINK_REFERENCE_TYPE_KEY = 'referenceType';
|
|
15
|
+
const EVENT_REFERENCE_TYPE = 'EventReference';
|
|
16
|
+
const REQUEST_ID_REFERENCE_TYPE = 'RequestIdReference';
|
|
17
|
+
// fullName isn't part of the generated typed unfortunately.
|
|
18
|
+
const WORKFLOW_EVENT_TYPE = proto_1.temporal.api.common.v1.Link.WorkflowEvent.fullName.slice(1);
|
|
19
|
+
function convertWorkflowEventLinkToNexusLink(we) {
|
|
20
|
+
if (!we.namespace || !we.workflowId || !we.runId) {
|
|
21
|
+
throw new TypeError('Missing required fields: namespace, workflowId, or runId');
|
|
22
|
+
}
|
|
23
|
+
const url = new URL(`temporal:///namespaces/${encodeURIComponent(we.namespace)}/workflows/${encodeURIComponent(we.workflowId)}/${encodeURIComponent(we.runId)}/history`);
|
|
24
|
+
if (we.eventRef != null) {
|
|
25
|
+
url.search = convertLinkWorkflowEventEventReferenceToURLQuery(we.eventRef);
|
|
26
|
+
}
|
|
27
|
+
else if (we.requestIdRef != null) {
|
|
28
|
+
url.search = convertLinkWorkflowEventRequestIdReferenceToURLQuery(we.requestIdRef);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
url,
|
|
32
|
+
type: WORKFLOW_EVENT_TYPE,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function convertNexusLinkToWorkflowEventLink(link) {
|
|
36
|
+
if (link.url.protocol !== 'temporal:') {
|
|
37
|
+
throw new TypeError(`Invalid URL scheme: ${link.url}, expected 'temporal:', got '${link.url.protocol}'`);
|
|
38
|
+
}
|
|
39
|
+
// /namespaces/:namespace/workflows/:workflowId/:runId/history
|
|
40
|
+
const parts = link.url.pathname.split('/');
|
|
41
|
+
if (parts.length !== 7 || parts[1] !== 'namespaces' || parts[3] !== 'workflows' || parts[6] !== 'history') {
|
|
42
|
+
throw new TypeError(`Invalid URL path: ${link.url}`);
|
|
43
|
+
}
|
|
44
|
+
const namespace = decodeURIComponent(parts[2]);
|
|
45
|
+
const workflowId = decodeURIComponent(parts[4]);
|
|
46
|
+
const runId = decodeURIComponent(parts[5]);
|
|
47
|
+
const query = link.url.searchParams;
|
|
48
|
+
const refType = query.get(LINK_REFERENCE_TYPE_KEY);
|
|
49
|
+
const workflowEventLink = {
|
|
50
|
+
namespace,
|
|
51
|
+
workflowId,
|
|
52
|
+
runId,
|
|
53
|
+
};
|
|
54
|
+
switch (refType) {
|
|
55
|
+
case EVENT_REFERENCE_TYPE:
|
|
56
|
+
workflowEventLink.eventRef = convertURLQueryToLinkWorkflowEventEventReference(query);
|
|
57
|
+
break;
|
|
58
|
+
case REQUEST_ID_REFERENCE_TYPE:
|
|
59
|
+
workflowEventLink.requestIdRef = convertURLQueryToLinkWorkflowEventRequestIdReference(query);
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
throw new TypeError(`Unknown reference type: ${refType}`);
|
|
63
|
+
}
|
|
64
|
+
return workflowEventLink;
|
|
65
|
+
}
|
|
66
|
+
function convertLinkWorkflowEventEventReferenceToURLQuery(eventRef) {
|
|
67
|
+
const params = new URLSearchParams();
|
|
68
|
+
params.set(LINK_REFERENCE_TYPE_KEY, EVENT_REFERENCE_TYPE);
|
|
69
|
+
if (eventRef.eventId != null) {
|
|
70
|
+
const eventId = eventRef.eventId.toNumber();
|
|
71
|
+
if (eventId > 0) {
|
|
72
|
+
params.set(LINK_EVENT_ID_PARAM, `${eventId}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (eventRef.eventType != null) {
|
|
76
|
+
const eventType = constantCaseToPascalCase(EventType[eventRef.eventType].replace('EVENT_TYPE_', ''));
|
|
77
|
+
params.set(LINK_EVENT_TYPE_PARAM, eventType);
|
|
78
|
+
}
|
|
79
|
+
return params.toString();
|
|
80
|
+
}
|
|
81
|
+
function convertURLQueryToLinkWorkflowEventEventReference(query) {
|
|
82
|
+
let eventId = 0;
|
|
83
|
+
const eventIdParam = query.get(LINK_EVENT_ID_PARAM);
|
|
84
|
+
if (eventIdParam && /^\d+$/.test(eventIdParam)) {
|
|
85
|
+
eventId = parseInt(eventIdParam, 10);
|
|
86
|
+
}
|
|
87
|
+
const eventTypeParam = query.get(LINK_EVENT_TYPE_PARAM);
|
|
88
|
+
if (!eventTypeParam) {
|
|
89
|
+
throw new TypeError(`Missing eventType parameter`);
|
|
90
|
+
}
|
|
91
|
+
const eventType = EventType[normalizeEnumValue(eventTypeParam, 'EVENT_TYPE')];
|
|
92
|
+
if (eventType == null) {
|
|
93
|
+
throw new TypeError(`Unknown eventType parameter: ${eventTypeParam}`);
|
|
94
|
+
}
|
|
95
|
+
return { eventId: long_1.default.fromNumber(eventId), eventType };
|
|
96
|
+
}
|
|
97
|
+
function convertLinkWorkflowEventRequestIdReferenceToURLQuery(requestIdRef) {
|
|
98
|
+
const params = new URLSearchParams();
|
|
99
|
+
params.set(LINK_REFERENCE_TYPE_KEY, REQUEST_ID_REFERENCE_TYPE);
|
|
100
|
+
if (requestIdRef.requestId != null) {
|
|
101
|
+
params.set(LINK_REQUEST_ID_PARAM, requestIdRef.requestId);
|
|
102
|
+
}
|
|
103
|
+
if (requestIdRef.eventType != null) {
|
|
104
|
+
const eventType = constantCaseToPascalCase(EventType[requestIdRef.eventType].replace('EVENT_TYPE_', ''));
|
|
105
|
+
params.set(LINK_EVENT_TYPE_PARAM, eventType);
|
|
106
|
+
}
|
|
107
|
+
return params.toString();
|
|
108
|
+
}
|
|
109
|
+
function convertURLQueryToLinkWorkflowEventRequestIdReference(query) {
|
|
110
|
+
const requestId = query.get(LINK_REQUEST_ID_PARAM);
|
|
111
|
+
const eventTypeParam = query.get(LINK_EVENT_TYPE_PARAM);
|
|
112
|
+
if (!eventTypeParam) {
|
|
113
|
+
throw new TypeError(`Missing eventType parameter`);
|
|
114
|
+
}
|
|
115
|
+
const eventType = EventType[normalizeEnumValue(eventTypeParam, 'EVENT_TYPE')];
|
|
116
|
+
if (eventType == null) {
|
|
117
|
+
throw new TypeError(`Unknown eventType parameter: ${eventTypeParam}`);
|
|
118
|
+
}
|
|
119
|
+
return { requestId, eventType };
|
|
120
|
+
}
|
|
121
|
+
function normalizeEnumValue(value, prefix) {
|
|
122
|
+
value = pascalCaseToConstantCase(value);
|
|
123
|
+
if (!value.startsWith(prefix)) {
|
|
124
|
+
value = `${prefix}_${value}`;
|
|
125
|
+
}
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
function pascalCaseToConstantCase(s) {
|
|
129
|
+
return s.replace(/[^\b][A-Z]/g, (m) => `${m[0]}_${m[1]}`).toUpperCase();
|
|
130
|
+
}
|
|
131
|
+
function constantCaseToPascalCase(s) {
|
|
132
|
+
return s.replace(/[A-Z]+_?/g, (m) => `${m[0]}${m.slice(1).toLocaleLowerCase()}`.replace(/_/, ''));
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=link-converter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-converter.js","sourceRoot":"","sources":["../src/link-converter.ts"],"names":[],"mappings":"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"}
|
package/lib/token.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
* @hidden
|
|
4
|
+
*/
|
|
5
|
+
export interface WorkflowRunOperationToken {
|
|
6
|
+
/**
|
|
7
|
+
* Version of the token, by default we assume we're on version 1, this field is not emitted as part of the output,
|
|
8
|
+
* it's only used to reject newer token versions on load.
|
|
9
|
+
*/
|
|
10
|
+
v?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Type of the Operation. Must be OPERATION_TOKEN_TYPE_WORKFLOW_RUN.
|
|
13
|
+
*/
|
|
14
|
+
t: OperationTokenType;
|
|
15
|
+
/**
|
|
16
|
+
* Namespace of the workflow.
|
|
17
|
+
*/
|
|
18
|
+
ns: string;
|
|
19
|
+
/**
|
|
20
|
+
* ID of the workflow.
|
|
21
|
+
*/
|
|
22
|
+
wid: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* OperationTokenType is used to identify the type of Operation token.
|
|
26
|
+
* Currently, we only have one type of Operation token: WorkflowRun.
|
|
27
|
+
*/
|
|
28
|
+
type OperationTokenType = (typeof OperationTokenType)[keyof typeof OperationTokenType];
|
|
29
|
+
/**
|
|
30
|
+
* @internal
|
|
31
|
+
* @hidden
|
|
32
|
+
*/
|
|
33
|
+
declare const OperationTokenType: {
|
|
34
|
+
readonly WORKFLOW_RUN: 1;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Generate a workflow run Operation token.
|
|
38
|
+
*/
|
|
39
|
+
export declare function generateWorkflowRunOperationToken(namespace: string, workflowId: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Load and validate a workflow run Operation token.
|
|
42
|
+
*/
|
|
43
|
+
export declare function loadWorkflowRunOperationToken(data: string): WorkflowRunOperationToken;
|
|
44
|
+
export declare function base64URLEncodeNoPadding(str: string): string;
|
|
45
|
+
export {};
|
package/lib/token.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateWorkflowRunOperationToken = generateWorkflowRunOperationToken;
|
|
4
|
+
exports.loadWorkflowRunOperationToken = loadWorkflowRunOperationToken;
|
|
5
|
+
exports.base64URLEncodeNoPadding = base64URLEncodeNoPadding;
|
|
6
|
+
/**
|
|
7
|
+
* @internal
|
|
8
|
+
* @hidden
|
|
9
|
+
*/
|
|
10
|
+
const OperationTokenType = {
|
|
11
|
+
WORKFLOW_RUN: 1,
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Generate a workflow run Operation token.
|
|
15
|
+
*/
|
|
16
|
+
function generateWorkflowRunOperationToken(namespace, workflowId) {
|
|
17
|
+
const token = {
|
|
18
|
+
t: OperationTokenType.WORKFLOW_RUN,
|
|
19
|
+
ns: namespace,
|
|
20
|
+
wid: workflowId,
|
|
21
|
+
};
|
|
22
|
+
return base64URLEncodeNoPadding(JSON.stringify(token));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Load and validate a workflow run Operation token.
|
|
26
|
+
*/
|
|
27
|
+
function loadWorkflowRunOperationToken(data) {
|
|
28
|
+
if (!data) {
|
|
29
|
+
throw new TypeError('invalid workflow run token: token is empty');
|
|
30
|
+
}
|
|
31
|
+
let decoded;
|
|
32
|
+
try {
|
|
33
|
+
decoded = base64URLDecodeNoPadding(data);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
throw new TypeError('failed to decode token', { cause: err });
|
|
37
|
+
}
|
|
38
|
+
let token;
|
|
39
|
+
try {
|
|
40
|
+
token = JSON.parse(decoded);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
throw new TypeError('failed to unmarshal workflow run Operation token', { cause: err });
|
|
44
|
+
}
|
|
45
|
+
if (token.t !== OperationTokenType.WORKFLOW_RUN) {
|
|
46
|
+
throw new TypeError(`invalid workflow token type: ${token.t}, expected: ${OperationTokenType.WORKFLOW_RUN}`);
|
|
47
|
+
}
|
|
48
|
+
if (token.v !== undefined && token.v !== 0) {
|
|
49
|
+
throw new TypeError('invalid workflow run token: "v" field should not be present');
|
|
50
|
+
}
|
|
51
|
+
if (!token.wid) {
|
|
52
|
+
throw new TypeError('invalid workflow run token: missing workflow ID (wid)');
|
|
53
|
+
}
|
|
54
|
+
return token;
|
|
55
|
+
}
|
|
56
|
+
// Exported for use in tests.
|
|
57
|
+
function base64URLEncodeNoPadding(str) {
|
|
58
|
+
const base64 = Buffer.from(str).toString('base64url');
|
|
59
|
+
return base64.replace(/[=]+$/, '');
|
|
60
|
+
}
|
|
61
|
+
function base64URLDecodeNoPadding(str) {
|
|
62
|
+
// Validate the string contains only valid base64URL characters
|
|
63
|
+
if (!/^[A-Za-z0-9_-]*$/.test(str)) {
|
|
64
|
+
throw new TypeError('invalid base64URL encoded string: contains invalid characters');
|
|
65
|
+
}
|
|
66
|
+
const paddingLength = str.length % 4;
|
|
67
|
+
if (paddingLength > 0) {
|
|
68
|
+
str += '='.repeat(4 - paddingLength);
|
|
69
|
+
}
|
|
70
|
+
return Buffer.from(str, 'base64url').toString('utf-8');
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=token.js.map
|
package/lib/token.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../src/token.ts"],"names":[],"mappings":";;AA4CA,8EAOC;AAKD,sEA8BC;AAGD,4DAGC;AA3DD;;;GAGG;AACH,MAAM,kBAAkB,GAAG;IACzB,YAAY,EAAE,CAAC;CACP,CAAC;AAEX;;GAEG;AACH,SAAgB,iCAAiC,CAAC,SAAiB,EAAE,UAAkB;IACrF,MAAM,KAAK,GAA8B;QACvC,CAAC,EAAE,kBAAkB,CAAC,YAAY;QAClC,EAAE,EAAE,SAAS;QACb,GAAG,EAAE,UAAU;KAChB,CAAC;IACF,OAAO,wBAAwB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,SAAgB,6BAA6B,CAAC,IAAY;IACxD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,SAAS,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,KAAgC,CAAC;IACrC,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,SAAS,CAAC,kDAAkD,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,IAAI,KAAK,CAAC,CAAC,KAAK,kBAAkB,CAAC,YAAY,EAAE,CAAC;QAChD,MAAM,IAAI,SAAS,CAAC,gCAAgC,KAAK,CAAC,CAAC,eAAe,kBAAkB,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/G,CAAC;IACD,IAAI,KAAK,CAAC,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,SAAS,CAAC,6DAA6D,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAC;IAC/E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,6BAA6B;AAC7B,SAAgB,wBAAwB,CAAC,GAAW;IAClD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,wBAAwB,CAAC,GAAW;IAC3C,+DAA+D;IAC/D,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,SAAS,CAAC,+DAA+D,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;IACrC,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as nexus from 'nexus-rpc';
|
|
2
|
+
import { Workflow } from '@temporalio/common';
|
|
3
|
+
import { Replace } from '@temporalio/common/lib/type-helpers';
|
|
4
|
+
import { WorkflowStartOptions as ClientWorkflowStartOptions } from '@temporalio/client';
|
|
5
|
+
declare const isNexusWorkflowHandle: unique symbol;
|
|
6
|
+
/**
|
|
7
|
+
* A handle to a running workflow that is returned by the {@link startWorkflow} helper.
|
|
8
|
+
* This handle should be returned by {@link WorkflowRunOperationStartHandler} implementations.
|
|
9
|
+
*
|
|
10
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
11
|
+
*/
|
|
12
|
+
export interface WorkflowHandle<_T> {
|
|
13
|
+
readonly workflowId: string;
|
|
14
|
+
readonly runId: string;
|
|
15
|
+
/**
|
|
16
|
+
* Virtual type brand to maintain a distinction between {@link WorkflowHandle} provided by the
|
|
17
|
+
* {@link startWorkflow} helper (which will have attached links, request ID, completion URL, etc)
|
|
18
|
+
* and the `WorkflowHandle` type returned by the {@link WorkflowClient.start}.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
* @hidden
|
|
22
|
+
*
|
|
23
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
24
|
+
*/
|
|
25
|
+
readonly [isNexusWorkflowHandle]: typeof isNexusWorkflowHandle;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Options for starting a workflow using {@link startWorkflow}, this type is identical to the
|
|
29
|
+
* client's `WorkflowStartOptions` with the exception that `taskQueue` is optional and defaults
|
|
30
|
+
* to the current worker's task queue.
|
|
31
|
+
*
|
|
32
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
33
|
+
*/
|
|
34
|
+
export type WorkflowStartOptions<T extends Workflow> = Replace<ClientWorkflowStartOptions<T>, {
|
|
35
|
+
taskQueue?: string;
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Starts a workflow run for a {@link WorkflowRunOperationStartHandler}, linking the execution chain
|
|
39
|
+
* to a Nexus Operation (subsequent runs started from continue-as-new and retries). Automatically
|
|
40
|
+
* propagates the callback, request ID, and back and forward links from the Nexus options to the
|
|
41
|
+
* Workflow.
|
|
42
|
+
*
|
|
43
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
44
|
+
*/
|
|
45
|
+
export declare function startWorkflow<T extends Workflow>(ctx: nexus.StartOperationContext, workflowTypeOrFunc: string | T, workflowOptions: WorkflowStartOptions<T>): Promise<WorkflowHandle<T>>;
|
|
46
|
+
/**
|
|
47
|
+
* A handler function for the {@link WorkflowRunOperationHandler} constructor.
|
|
48
|
+
*
|
|
49
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
50
|
+
*/
|
|
51
|
+
export type WorkflowRunOperationStartHandler<I, O> = (ctx: nexus.StartOperationContext, input: I) => Promise<WorkflowHandle<O>>;
|
|
52
|
+
/**
|
|
53
|
+
* A Nexus Operation implementation that is backed by a Workflow run.
|
|
54
|
+
*
|
|
55
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
56
|
+
*/
|
|
57
|
+
export declare class WorkflowRunOperationHandler<I, O> implements nexus.OperationHandler<I, O> {
|
|
58
|
+
readonly handler: WorkflowRunOperationStartHandler<I, O>;
|
|
59
|
+
constructor(handler: WorkflowRunOperationStartHandler<I, O>);
|
|
60
|
+
start(ctx: nexus.StartOperationContext, input: I): Promise<nexus.HandlerStartOperationResult<O>>;
|
|
61
|
+
getInfo(_ctx: nexus.GetOperationInfoContext, _token: string): Promise<nexus.OperationInfo>;
|
|
62
|
+
getResult(_ctx: nexus.GetOperationResultContext, _token: string): Promise<O>;
|
|
63
|
+
cancel(_ctx: nexus.CancelOperationContext, token: string): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.WorkflowRunOperationHandler = void 0;
|
|
27
|
+
exports.startWorkflow = startWorkflow;
|
|
28
|
+
const nexus = __importStar(require("nexus-rpc"));
|
|
29
|
+
const internal_1 = require("@temporalio/client/lib/internal");
|
|
30
|
+
const token_1 = require("./token");
|
|
31
|
+
const link_converter_1 = require("./link-converter");
|
|
32
|
+
const context_1 = require("./context");
|
|
33
|
+
/**
|
|
34
|
+
* Starts a workflow run for a {@link WorkflowRunOperationStartHandler}, linking the execution chain
|
|
35
|
+
* to a Nexus Operation (subsequent runs started from continue-as-new and retries). Automatically
|
|
36
|
+
* propagates the callback, request ID, and back and forward links from the Nexus options to the
|
|
37
|
+
* Workflow.
|
|
38
|
+
*
|
|
39
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
40
|
+
*/
|
|
41
|
+
async function startWorkflow(ctx, workflowTypeOrFunc, workflowOptions) {
|
|
42
|
+
const { client, taskQueue } = (0, context_1.getHandlerContext)();
|
|
43
|
+
const links = Array();
|
|
44
|
+
if (ctx.inboundLinks?.length > 0) {
|
|
45
|
+
for (const l of ctx.inboundLinks) {
|
|
46
|
+
try {
|
|
47
|
+
links.push({
|
|
48
|
+
workflowEvent: (0, link_converter_1.convertNexusLinkToWorkflowEventLink)(l),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
context_1.log.warn('failed to convert Nexus link to Workflow event link', { error });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const internalOptions = {
|
|
57
|
+
links,
|
|
58
|
+
requestId: ctx.requestId,
|
|
59
|
+
};
|
|
60
|
+
internalOptions.onConflictOptions = {
|
|
61
|
+
attachLinks: true,
|
|
62
|
+
attachCompletionCallbacks: true,
|
|
63
|
+
attachRequestId: true,
|
|
64
|
+
};
|
|
65
|
+
if (ctx.callbackUrl) {
|
|
66
|
+
internalOptions.completionCallbacks = [
|
|
67
|
+
{
|
|
68
|
+
nexus: { url: ctx.callbackUrl, header: ctx.callbackHeaders },
|
|
69
|
+
links, // pass in links here as well for older servers, newer servers dedupe them.
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
const { taskQueue: userSpecifiedTaskQueue, ...rest } = workflowOptions;
|
|
74
|
+
const startOptions = {
|
|
75
|
+
...rest,
|
|
76
|
+
taskQueue: userSpecifiedTaskQueue || taskQueue,
|
|
77
|
+
[internal_1.InternalWorkflowStartOptionsSymbol]: internalOptions,
|
|
78
|
+
};
|
|
79
|
+
const handle = await client.workflow.start(workflowTypeOrFunc, startOptions);
|
|
80
|
+
if (internalOptions.backLink?.workflowEvent != null) {
|
|
81
|
+
try {
|
|
82
|
+
ctx.outboundLinks.push((0, link_converter_1.convertWorkflowEventLinkToNexusLink)(internalOptions.backLink.workflowEvent));
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
context_1.log.warn('failed to convert Workflow event link to Nexus link', { error });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
workflowId: handle.workflowId,
|
|
90
|
+
runId: handle.firstExecutionRunId,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A Nexus Operation implementation that is backed by a Workflow run.
|
|
95
|
+
*
|
|
96
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
97
|
+
*/
|
|
98
|
+
class WorkflowRunOperationHandler {
|
|
99
|
+
handler;
|
|
100
|
+
constructor(handler) {
|
|
101
|
+
this.handler = handler;
|
|
102
|
+
}
|
|
103
|
+
async start(ctx, input) {
|
|
104
|
+
const { namespace } = (0, context_1.getHandlerContext)();
|
|
105
|
+
const handle = await this.handler(ctx, input);
|
|
106
|
+
return nexus.HandlerStartOperationResult.async((0, token_1.generateWorkflowRunOperationToken)(namespace, handle.workflowId));
|
|
107
|
+
}
|
|
108
|
+
getInfo(_ctx, _token) {
|
|
109
|
+
// Not implemented in Temporal yet.
|
|
110
|
+
throw new nexus.HandlerError('NOT_IMPLEMENTED', 'Method not implemented');
|
|
111
|
+
}
|
|
112
|
+
getResult(_ctx, _token) {
|
|
113
|
+
// Not implemented in Temporal yet.
|
|
114
|
+
throw new nexus.HandlerError('NOT_IMPLEMENTED', 'Method not implemented');
|
|
115
|
+
}
|
|
116
|
+
async cancel(_ctx, token) {
|
|
117
|
+
const decoded = (0, token_1.loadWorkflowRunOperationToken)(token);
|
|
118
|
+
await (0, context_1.getClient)().workflow.getHandle(decoded.wid).cancel();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.WorkflowRunOperationHandler = WorkflowRunOperationHandler;
|
|
122
|
+
//# sourceMappingURL=workflow-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-helpers.js","sourceRoot":"","sources":["../src/workflow-helpers.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@temporalio/nexus",
|
|
3
|
+
"version": "1.13.0",
|
|
4
|
+
"description": "Temporal.io SDK Nexus sub-package",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "./lib/index.d.ts",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"temporal",
|
|
9
|
+
"workflow",
|
|
10
|
+
"worker",
|
|
11
|
+
"nexus"
|
|
12
|
+
],
|
|
13
|
+
"author": "Temporal Technologies Inc. <sdk@temporal.io>",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@temporalio/client": "1.13.0",
|
|
17
|
+
"@temporalio/common": "1.13.0",
|
|
18
|
+
"@temporalio/proto": "1.13.0",
|
|
19
|
+
"long": "^5.2.3",
|
|
20
|
+
"nexus-rpc": "^0.0.1"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/temporalio/sdk-typescript/issues"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/temporalio/sdk-typescript.git",
|
|
28
|
+
"directory": "packages/nexus"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/nexus",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">= 18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"src",
|
|
39
|
+
"lib"
|
|
40
|
+
],
|
|
41
|
+
"gitHead": "cf7c1e7d70f0c775f1382fd335b8bd721495f8cc"
|
|
42
|
+
}
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { Logger, LogLevel, LogMetadata, MetricMeter } from '@temporalio/common';
|
|
3
|
+
import { Client } from '@temporalio/client';
|
|
4
|
+
|
|
5
|
+
// Context Storage /////////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
|
|
7
|
+
// Make it safe to use @temporalio/nexus with multiple versions installed.
|
|
8
|
+
const asyncLocalStorageSymbol = Symbol.for('__temporal_nexus_context_storage__');
|
|
9
|
+
if (!(globalThis as any)[asyncLocalStorageSymbol]) {
|
|
10
|
+
(globalThis as any)[asyncLocalStorageSymbol] = new AsyncLocalStorage<HandlerContext>();
|
|
11
|
+
}
|
|
12
|
+
export const asyncLocalStorage: AsyncLocalStorage<HandlerContext> = (globalThis as any)[asyncLocalStorageSymbol];
|
|
13
|
+
|
|
14
|
+
export function getHandlerContext(): HandlerContext {
|
|
15
|
+
const ctx = asyncLocalStorage.getStore();
|
|
16
|
+
if (ctx == null) {
|
|
17
|
+
throw new ReferenceError('Not in a Nexus handler context');
|
|
18
|
+
}
|
|
19
|
+
return ctx;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Context used internally in the SDK to propagate information from the worker to the Temporal Nexus helpers.
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
26
|
+
* @hidden
|
|
27
|
+
*/
|
|
28
|
+
export interface HandlerContext {
|
|
29
|
+
log: Logger;
|
|
30
|
+
metrics: MetricMeter;
|
|
31
|
+
client: Client;
|
|
32
|
+
namespace: string;
|
|
33
|
+
taskQueue: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Basic APIs //////////////////////////////////////////////////////////////////////////////////////
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A logger for use in Nexus Handler scope.
|
|
40
|
+
*
|
|
41
|
+
* This defaults to the `Runtime`'s Logger (see {@link Runtime.logger}). Attributes from the
|
|
42
|
+
* current Nexus handler context are automatically included as metadata on every log entries. An
|
|
43
|
+
* extra `sdkComponent` metadata attribute is also added, with value `nexus`; this can be used
|
|
44
|
+
* for fine-grained filtering of log entries further downstream.
|
|
45
|
+
*
|
|
46
|
+
* To customize log attributes, register a {@link nexus.NexusOutboundCallsInterceptor} that
|
|
47
|
+
* intercepts the `getLogAttributes()` method.
|
|
48
|
+
*
|
|
49
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
50
|
+
*/
|
|
51
|
+
export const log: Logger = {
|
|
52
|
+
log(level: LogLevel, message: string, meta?: LogMetadata): any {
|
|
53
|
+
return getHandlerContext().log.log(level, message, meta);
|
|
54
|
+
},
|
|
55
|
+
trace(message: string, meta?: LogMetadata): any {
|
|
56
|
+
return getHandlerContext().log.trace(message, meta);
|
|
57
|
+
},
|
|
58
|
+
debug(message: string, meta?: LogMetadata): any {
|
|
59
|
+
return getHandlerContext().log.debug(message, meta);
|
|
60
|
+
},
|
|
61
|
+
info(message: string, meta?: LogMetadata): any {
|
|
62
|
+
return getHandlerContext().log.info(message, meta);
|
|
63
|
+
},
|
|
64
|
+
warn(message: string, meta?: LogMetadata): any {
|
|
65
|
+
return getHandlerContext().log.warn(message, meta);
|
|
66
|
+
},
|
|
67
|
+
error(message: string, meta?: LogMetadata): any {
|
|
68
|
+
return getHandlerContext().log.error(message, meta);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A metric meter for use in Nexus handler scope, with Nexus handler-specific tags.
|
|
74
|
+
*
|
|
75
|
+
* To add custom tags, register a {@link nexus.NexusOutboundCallsInterceptor} that
|
|
76
|
+
* intercepts the `getMetricTags()` method.
|
|
77
|
+
*
|
|
78
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
79
|
+
*/
|
|
80
|
+
export const metricMeter: MetricMeter = {
|
|
81
|
+
createCounter(name, unit, description) {
|
|
82
|
+
return getHandlerContext().metrics.createCounter(name, unit, description);
|
|
83
|
+
},
|
|
84
|
+
createHistogram(name, valueType = 'int', unit, description) {
|
|
85
|
+
return getHandlerContext().metrics.createHistogram(name, valueType, unit, description);
|
|
86
|
+
},
|
|
87
|
+
createGauge(name, valueType = 'int', unit, description) {
|
|
88
|
+
return getHandlerContext().metrics.createGauge(name, valueType, unit, description);
|
|
89
|
+
},
|
|
90
|
+
withTags(tags) {
|
|
91
|
+
return getHandlerContext().metrics.withTags(tags);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Returns a client to be used in a Nexus Operation's context, this Client is powered by the same
|
|
97
|
+
* NativeConnection that the worker was created with.
|
|
98
|
+
*
|
|
99
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
100
|
+
*/
|
|
101
|
+
export function getClient(): Client {
|
|
102
|
+
return getHandlerContext().client;
|
|
103
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import Long from 'long';
|
|
2
|
+
import { Link as NexusLink } from 'nexus-rpc';
|
|
3
|
+
import { temporal } from '@temporalio/proto';
|
|
4
|
+
|
|
5
|
+
const { EventType } = temporal.api.enums.v1;
|
|
6
|
+
type WorkflowEventLink = temporal.api.common.v1.Link.IWorkflowEvent;
|
|
7
|
+
type EventReference = temporal.api.common.v1.Link.WorkflowEvent.IEventReference;
|
|
8
|
+
type RequestIdReference = temporal.api.common.v1.Link.WorkflowEvent.IRequestIdReference;
|
|
9
|
+
|
|
10
|
+
const LINK_EVENT_ID_PARAM = 'eventID';
|
|
11
|
+
const LINK_EVENT_TYPE_PARAM = 'eventType';
|
|
12
|
+
const LINK_REQUEST_ID_PARAM = 'requestID';
|
|
13
|
+
const LINK_REFERENCE_TYPE_KEY = 'referenceType';
|
|
14
|
+
|
|
15
|
+
const EVENT_REFERENCE_TYPE = 'EventReference';
|
|
16
|
+
const REQUEST_ID_REFERENCE_TYPE = 'RequestIdReference';
|
|
17
|
+
|
|
18
|
+
// fullName isn't part of the generated typed unfortunately.
|
|
19
|
+
const WORKFLOW_EVENT_TYPE: string = (temporal.api.common.v1.Link.WorkflowEvent as any).fullName.slice(1);
|
|
20
|
+
|
|
21
|
+
export function convertWorkflowEventLinkToNexusLink(we: WorkflowEventLink): NexusLink {
|
|
22
|
+
if (!we.namespace || !we.workflowId || !we.runId) {
|
|
23
|
+
throw new TypeError('Missing required fields: namespace, workflowId, or runId');
|
|
24
|
+
}
|
|
25
|
+
const url = new URL(
|
|
26
|
+
`temporal:///namespaces/${encodeURIComponent(we.namespace)}/workflows/${encodeURIComponent(
|
|
27
|
+
we.workflowId
|
|
28
|
+
)}/${encodeURIComponent(we.runId)}/history`
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (we.eventRef != null) {
|
|
32
|
+
url.search = convertLinkWorkflowEventEventReferenceToURLQuery(we.eventRef);
|
|
33
|
+
} else if (we.requestIdRef != null) {
|
|
34
|
+
url.search = convertLinkWorkflowEventRequestIdReferenceToURLQuery(we.requestIdRef);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
url,
|
|
39
|
+
type: WORKFLOW_EVENT_TYPE,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function convertNexusLinkToWorkflowEventLink(link: NexusLink): WorkflowEventLink {
|
|
44
|
+
if (link.url.protocol !== 'temporal:') {
|
|
45
|
+
throw new TypeError(`Invalid URL scheme: ${link.url}, expected 'temporal:', got '${link.url.protocol}'`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// /namespaces/:namespace/workflows/:workflowId/:runId/history
|
|
49
|
+
const parts = link.url.pathname.split('/');
|
|
50
|
+
if (parts.length !== 7 || parts[1] !== 'namespaces' || parts[3] !== 'workflows' || parts[6] !== 'history') {
|
|
51
|
+
throw new TypeError(`Invalid URL path: ${link.url}`);
|
|
52
|
+
}
|
|
53
|
+
const namespace = decodeURIComponent(parts[2]);
|
|
54
|
+
const workflowId = decodeURIComponent(parts[4]);
|
|
55
|
+
const runId = decodeURIComponent(parts[5]);
|
|
56
|
+
|
|
57
|
+
const query = link.url.searchParams;
|
|
58
|
+
const refType = query.get(LINK_REFERENCE_TYPE_KEY);
|
|
59
|
+
|
|
60
|
+
const workflowEventLink: WorkflowEventLink = {
|
|
61
|
+
namespace,
|
|
62
|
+
workflowId,
|
|
63
|
+
runId,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
switch (refType) {
|
|
67
|
+
case EVENT_REFERENCE_TYPE:
|
|
68
|
+
workflowEventLink.eventRef = convertURLQueryToLinkWorkflowEventEventReference(query);
|
|
69
|
+
break;
|
|
70
|
+
case REQUEST_ID_REFERENCE_TYPE:
|
|
71
|
+
workflowEventLink.requestIdRef = convertURLQueryToLinkWorkflowEventRequestIdReference(query);
|
|
72
|
+
break;
|
|
73
|
+
default:
|
|
74
|
+
throw new TypeError(`Unknown reference type: ${refType}`);
|
|
75
|
+
}
|
|
76
|
+
return workflowEventLink;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function convertLinkWorkflowEventEventReferenceToURLQuery(eventRef: EventReference): string {
|
|
80
|
+
const params = new URLSearchParams();
|
|
81
|
+
params.set(LINK_REFERENCE_TYPE_KEY, EVENT_REFERENCE_TYPE);
|
|
82
|
+
if (eventRef.eventId != null) {
|
|
83
|
+
const eventId = eventRef.eventId.toNumber();
|
|
84
|
+
if (eventId > 0) {
|
|
85
|
+
params.set(LINK_EVENT_ID_PARAM, `${eventId}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (eventRef.eventType != null) {
|
|
89
|
+
const eventType = constantCaseToPascalCase(EventType[eventRef.eventType].replace('EVENT_TYPE_', ''));
|
|
90
|
+
params.set(LINK_EVENT_TYPE_PARAM, eventType);
|
|
91
|
+
}
|
|
92
|
+
return params.toString();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function convertURLQueryToLinkWorkflowEventEventReference(query: URLSearchParams): EventReference {
|
|
96
|
+
let eventId = 0;
|
|
97
|
+
const eventIdParam = query.get(LINK_EVENT_ID_PARAM);
|
|
98
|
+
if (eventIdParam && /^\d+$/.test(eventIdParam)) {
|
|
99
|
+
eventId = parseInt(eventIdParam, 10);
|
|
100
|
+
}
|
|
101
|
+
const eventTypeParam = query.get(LINK_EVENT_TYPE_PARAM);
|
|
102
|
+
if (!eventTypeParam) {
|
|
103
|
+
throw new TypeError(`Missing eventType parameter`);
|
|
104
|
+
}
|
|
105
|
+
const eventType = EventType[normalizeEnumValue(eventTypeParam, 'EVENT_TYPE') as keyof typeof EventType];
|
|
106
|
+
if (eventType == null) {
|
|
107
|
+
throw new TypeError(`Unknown eventType parameter: ${eventTypeParam}`);
|
|
108
|
+
}
|
|
109
|
+
return { eventId: Long.fromNumber(eventId), eventType };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function convertLinkWorkflowEventRequestIdReferenceToURLQuery(requestIdRef: RequestIdReference): string {
|
|
113
|
+
const params = new URLSearchParams();
|
|
114
|
+
params.set(LINK_REFERENCE_TYPE_KEY, REQUEST_ID_REFERENCE_TYPE);
|
|
115
|
+
if (requestIdRef.requestId != null) {
|
|
116
|
+
params.set(LINK_REQUEST_ID_PARAM, requestIdRef.requestId);
|
|
117
|
+
}
|
|
118
|
+
if (requestIdRef.eventType != null) {
|
|
119
|
+
const eventType = constantCaseToPascalCase(EventType[requestIdRef.eventType].replace('EVENT_TYPE_', ''));
|
|
120
|
+
params.set(LINK_EVENT_TYPE_PARAM, eventType);
|
|
121
|
+
}
|
|
122
|
+
return params.toString();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function convertURLQueryToLinkWorkflowEventRequestIdReference(query: URLSearchParams): RequestIdReference {
|
|
126
|
+
const requestId = query.get(LINK_REQUEST_ID_PARAM);
|
|
127
|
+
const eventTypeParam = query.get(LINK_EVENT_TYPE_PARAM);
|
|
128
|
+
if (!eventTypeParam) {
|
|
129
|
+
throw new TypeError(`Missing eventType parameter`);
|
|
130
|
+
}
|
|
131
|
+
const eventType = EventType[normalizeEnumValue(eventTypeParam, 'EVENT_TYPE') as keyof typeof EventType];
|
|
132
|
+
if (eventType == null) {
|
|
133
|
+
throw new TypeError(`Unknown eventType parameter: ${eventTypeParam}`);
|
|
134
|
+
}
|
|
135
|
+
return { requestId, eventType };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function normalizeEnumValue(value: string, prefix: string) {
|
|
139
|
+
value = pascalCaseToConstantCase(value);
|
|
140
|
+
if (!value.startsWith(prefix)) {
|
|
141
|
+
value = `${prefix}_${value}`;
|
|
142
|
+
}
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function pascalCaseToConstantCase(s: string) {
|
|
147
|
+
return s.replace(/[^\b][A-Z]/g, (m) => `${m[0]}_${m[1]}`).toUpperCase();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function constantCaseToPascalCase(s: string) {
|
|
151
|
+
return s.replace(/[A-Z]+_?/g, (m) => `${m[0]}${m.slice(1).toLocaleLowerCase()}`.replace(/_/, ''));
|
|
152
|
+
}
|
package/src/token.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
* @hidden
|
|
4
|
+
*/
|
|
5
|
+
export interface WorkflowRunOperationToken {
|
|
6
|
+
/**
|
|
7
|
+
* Version of the token, by default we assume we're on version 1, this field is not emitted as part of the output,
|
|
8
|
+
* it's only used to reject newer token versions on load.
|
|
9
|
+
*/
|
|
10
|
+
v?: number;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Type of the Operation. Must be OPERATION_TOKEN_TYPE_WORKFLOW_RUN.
|
|
14
|
+
*/
|
|
15
|
+
t: OperationTokenType;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Namespace of the workflow.
|
|
19
|
+
*/
|
|
20
|
+
ns: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* ID of the workflow.
|
|
24
|
+
*/
|
|
25
|
+
wid: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* OperationTokenType is used to identify the type of Operation token.
|
|
30
|
+
* Currently, we only have one type of Operation token: WorkflowRun.
|
|
31
|
+
*/
|
|
32
|
+
type OperationTokenType = (typeof OperationTokenType)[keyof typeof OperationTokenType];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @internal
|
|
36
|
+
* @hidden
|
|
37
|
+
*/
|
|
38
|
+
const OperationTokenType = {
|
|
39
|
+
WORKFLOW_RUN: 1,
|
|
40
|
+
} as const;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate a workflow run Operation token.
|
|
44
|
+
*/
|
|
45
|
+
export function generateWorkflowRunOperationToken(namespace: string, workflowId: string): string {
|
|
46
|
+
const token: WorkflowRunOperationToken = {
|
|
47
|
+
t: OperationTokenType.WORKFLOW_RUN,
|
|
48
|
+
ns: namespace,
|
|
49
|
+
wid: workflowId,
|
|
50
|
+
};
|
|
51
|
+
return base64URLEncodeNoPadding(JSON.stringify(token));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Load and validate a workflow run Operation token.
|
|
56
|
+
*/
|
|
57
|
+
export function loadWorkflowRunOperationToken(data: string): WorkflowRunOperationToken {
|
|
58
|
+
if (!data) {
|
|
59
|
+
throw new TypeError('invalid workflow run token: token is empty');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let decoded: string;
|
|
63
|
+
try {
|
|
64
|
+
decoded = base64URLDecodeNoPadding(data);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
throw new TypeError('failed to decode token', { cause: err });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let token: WorkflowRunOperationToken;
|
|
70
|
+
try {
|
|
71
|
+
token = JSON.parse(decoded);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
throw new TypeError('failed to unmarshal workflow run Operation token', { cause: err });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (token.t !== OperationTokenType.WORKFLOW_RUN) {
|
|
77
|
+
throw new TypeError(`invalid workflow token type: ${token.t}, expected: ${OperationTokenType.WORKFLOW_RUN}`);
|
|
78
|
+
}
|
|
79
|
+
if (token.v !== undefined && token.v !== 0) {
|
|
80
|
+
throw new TypeError('invalid workflow run token: "v" field should not be present');
|
|
81
|
+
}
|
|
82
|
+
if (!token.wid) {
|
|
83
|
+
throw new TypeError('invalid workflow run token: missing workflow ID (wid)');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return token;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Exported for use in tests.
|
|
90
|
+
export function base64URLEncodeNoPadding(str: string): string {
|
|
91
|
+
const base64 = Buffer.from(str).toString('base64url');
|
|
92
|
+
return base64.replace(/[=]+$/, '');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function base64URLDecodeNoPadding(str: string): string {
|
|
96
|
+
// Validate the string contains only valid base64URL characters
|
|
97
|
+
if (!/^[A-Za-z0-9_-]*$/.test(str)) {
|
|
98
|
+
throw new TypeError('invalid base64URL encoded string: contains invalid characters');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const paddingLength = str.length % 4;
|
|
102
|
+
if (paddingLength > 0) {
|
|
103
|
+
str += '='.repeat(4 - paddingLength);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return Buffer.from(str, 'base64url').toString('utf-8');
|
|
107
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as nexus from 'nexus-rpc';
|
|
2
|
+
import { Workflow } from '@temporalio/common';
|
|
3
|
+
import { Replace } from '@temporalio/common/lib/type-helpers';
|
|
4
|
+
import { WorkflowStartOptions as ClientWorkflowStartOptions } from '@temporalio/client';
|
|
5
|
+
import { type temporal } from '@temporalio/proto';
|
|
6
|
+
import { InternalWorkflowStartOptionsSymbol, InternalWorkflowStartOptions } from '@temporalio/client/lib/internal';
|
|
7
|
+
import { generateWorkflowRunOperationToken, loadWorkflowRunOperationToken } from './token';
|
|
8
|
+
import { convertNexusLinkToWorkflowEventLink, convertWorkflowEventLinkToNexusLink } from './link-converter';
|
|
9
|
+
import { getClient, getHandlerContext, log } from './context';
|
|
10
|
+
|
|
11
|
+
declare const isNexusWorkflowHandle: unique symbol;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A handle to a running workflow that is returned by the {@link startWorkflow} helper.
|
|
15
|
+
* This handle should be returned by {@link WorkflowRunOperationStartHandler} implementations.
|
|
16
|
+
*
|
|
17
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
18
|
+
*/
|
|
19
|
+
export interface WorkflowHandle<_T> {
|
|
20
|
+
readonly workflowId: string;
|
|
21
|
+
readonly runId: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Virtual type brand to maintain a distinction between {@link WorkflowHandle} provided by the
|
|
25
|
+
* {@link startWorkflow} helper (which will have attached links, request ID, completion URL, etc)
|
|
26
|
+
* and the `WorkflowHandle` type returned by the {@link WorkflowClient.start}.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
* @hidden
|
|
30
|
+
*
|
|
31
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
32
|
+
*/
|
|
33
|
+
readonly [isNexusWorkflowHandle]: typeof isNexusWorkflowHandle;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options for starting a workflow using {@link startWorkflow}, this type is identical to the
|
|
38
|
+
* client's `WorkflowStartOptions` with the exception that `taskQueue` is optional and defaults
|
|
39
|
+
* to the current worker's task queue.
|
|
40
|
+
*
|
|
41
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
42
|
+
*/
|
|
43
|
+
export type WorkflowStartOptions<T extends Workflow> = Replace<ClientWorkflowStartOptions<T>, { taskQueue?: string }>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Starts a workflow run for a {@link WorkflowRunOperationStartHandler}, linking the execution chain
|
|
47
|
+
* to a Nexus Operation (subsequent runs started from continue-as-new and retries). Automatically
|
|
48
|
+
* propagates the callback, request ID, and back and forward links from the Nexus options to the
|
|
49
|
+
* Workflow.
|
|
50
|
+
*
|
|
51
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
52
|
+
*/
|
|
53
|
+
export async function startWorkflow<T extends Workflow>(
|
|
54
|
+
ctx: nexus.StartOperationContext,
|
|
55
|
+
workflowTypeOrFunc: string | T,
|
|
56
|
+
workflowOptions: WorkflowStartOptions<T>
|
|
57
|
+
): Promise<WorkflowHandle<T>> {
|
|
58
|
+
const { client, taskQueue } = getHandlerContext();
|
|
59
|
+
const links = Array<temporal.api.common.v1.ILink>();
|
|
60
|
+
if (ctx.inboundLinks?.length > 0) {
|
|
61
|
+
for (const l of ctx.inboundLinks) {
|
|
62
|
+
try {
|
|
63
|
+
links.push({
|
|
64
|
+
workflowEvent: convertNexusLinkToWorkflowEventLink(l),
|
|
65
|
+
});
|
|
66
|
+
} catch (error) {
|
|
67
|
+
log.warn('failed to convert Nexus link to Workflow event link', { error });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const internalOptions: InternalWorkflowStartOptions[typeof InternalWorkflowStartOptionsSymbol] = {
|
|
72
|
+
links,
|
|
73
|
+
requestId: ctx.requestId,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
internalOptions.onConflictOptions = {
|
|
77
|
+
attachLinks: true,
|
|
78
|
+
attachCompletionCallbacks: true,
|
|
79
|
+
attachRequestId: true,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (ctx.callbackUrl) {
|
|
83
|
+
internalOptions.completionCallbacks = [
|
|
84
|
+
{
|
|
85
|
+
nexus: { url: ctx.callbackUrl, header: ctx.callbackHeaders },
|
|
86
|
+
links, // pass in links here as well for older servers, newer servers dedupe them.
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const { taskQueue: userSpecifiedTaskQueue, ...rest } = workflowOptions;
|
|
92
|
+
const startOptions: ClientWorkflowStartOptions = {
|
|
93
|
+
...rest,
|
|
94
|
+
taskQueue: userSpecifiedTaskQueue || taskQueue,
|
|
95
|
+
[InternalWorkflowStartOptionsSymbol]: internalOptions,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handle = await client.workflow.start(workflowTypeOrFunc, startOptions);
|
|
99
|
+
if (internalOptions.backLink?.workflowEvent != null) {
|
|
100
|
+
try {
|
|
101
|
+
ctx.outboundLinks.push(convertWorkflowEventLinkToNexusLink(internalOptions.backLink.workflowEvent));
|
|
102
|
+
} catch (error) {
|
|
103
|
+
log.warn('failed to convert Workflow event link to Nexus link', { error });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
workflowId: handle.workflowId,
|
|
109
|
+
runId: handle.firstExecutionRunId,
|
|
110
|
+
} as WorkflowHandle<T>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* A handler function for the {@link WorkflowRunOperationHandler} constructor.
|
|
115
|
+
*
|
|
116
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
117
|
+
*/
|
|
118
|
+
export type WorkflowRunOperationStartHandler<I, O> = (
|
|
119
|
+
ctx: nexus.StartOperationContext,
|
|
120
|
+
input: I
|
|
121
|
+
) => Promise<WorkflowHandle<O>>;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A Nexus Operation implementation that is backed by a Workflow run.
|
|
125
|
+
*
|
|
126
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
127
|
+
*/
|
|
128
|
+
export class WorkflowRunOperationHandler<I, O> implements nexus.OperationHandler<I, O> {
|
|
129
|
+
constructor(readonly handler: WorkflowRunOperationStartHandler<I, O>) {}
|
|
130
|
+
|
|
131
|
+
async start(ctx: nexus.StartOperationContext, input: I): Promise<nexus.HandlerStartOperationResult<O>> {
|
|
132
|
+
const { namespace } = getHandlerContext();
|
|
133
|
+
const handle = await this.handler(ctx, input);
|
|
134
|
+
return nexus.HandlerStartOperationResult.async(generateWorkflowRunOperationToken(namespace, handle.workflowId));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
getInfo(_ctx: nexus.GetOperationInfoContext, _token: string): Promise<nexus.OperationInfo> {
|
|
138
|
+
// Not implemented in Temporal yet.
|
|
139
|
+
throw new nexus.HandlerError('NOT_IMPLEMENTED', 'Method not implemented');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
getResult(_ctx: nexus.GetOperationResultContext, _token: string): Promise<O> {
|
|
143
|
+
// Not implemented in Temporal yet.
|
|
144
|
+
throw new nexus.HandlerError('NOT_IMPLEMENTED', 'Method not implemented');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async cancel(_ctx: nexus.CancelOperationContext, token: string): Promise<void> {
|
|
148
|
+
const decoded = loadWorkflowRunOperationToken(token);
|
|
149
|
+
await getClient().workflow.getHandle(decoded.wid).cancel();
|
|
150
|
+
}
|
|
151
|
+
}
|