From fd9978f75dea98e0e5c842ff5c3d0617d7dcf8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Sat, 22 Jan 2022 00:48:04 -0500 Subject: [PATCH 01/33] Revert "Revert "feat: Add Protobuf binary and JSON data converter and WorkerOptions.dataConverterPath (#430)"" This reverts commit 139550dd5a0d920fe692357f641421669197854b. --- .prettierignore | 2 + docs/protobuf-libraries.md | 109 ++ package-lock.json | 1365 +++++++++++------ package.json | 1 + packages/common/package.json | 6 +- .../common/src/converter/data-converter.ts | 35 +- packages/common/src/converter/helpers.ts | 38 + .../common/src/converter/payload-converter.ts | 159 +- packages/common/src/converter/types.ts | 2 + packages/common/src/errors.ts | 18 + packages/common/src/index.ts | 1 + packages/common/src/type-helpers.ts | 18 + packages/proto/scripts/compile-proto.js | 2 +- packages/test/.gitignore | 2 + packages/test/package.json | 9 +- packages/test/protos/messages.proto | 14 + .../test/protos/namespaced-messages.proto | 9 + packages/test/protos/root.js | 3 + packages/test/protos/uppercase.proto | 8 + packages/test/scripts/compile-proto.js | 61 + packages/test/src/activities/index.ts | 5 + .../data-converter-bad-export.ts | 1 + .../data-converter-no-export.ts | 1 + .../src/data-converters/data-converter.ts | 6 + packages/test/src/mock-native-worker.ts | 12 +- .../test/src/test-custom-data-converter.ts | 139 ++ packages/test/src/test-data-converter.ts | 142 +- packages/test/src/test-patch-root.ts | 33 + packages/test/src/test-worker-activities.ts | 2 +- packages/test/src/test-worker-lifecycle.ts | 4 +- packages/test/src/test-workflows.ts | 2 +- packages/test/src/workflows/index.ts | 1 + packages/test/src/workflows/protobufs.ts | 5 + packages/worker/src/worker-options.ts | 8 +- packages/worker/src/worker.ts | 53 +- packages/worker/src/workflow/bundler.ts | 2 +- packages/worker/src/workflow/threaded-vm.ts | 11 +- packages/worker/src/workflow/vm.ts | 2 +- .../src/workflow/workflow-worker-thread.ts | 2 +- .../workflow/workflow-worker-thread/input.ts | 2 +- packages/workflow/src/internals.ts | 1 - packages/workflow/src/worker-interface.ts | 6 +- 42 files changed, 1787 insertions(+), 515 deletions(-) create mode 100644 docs/protobuf-libraries.md create mode 100644 packages/common/src/converter/helpers.ts create mode 100644 packages/test/.gitignore create mode 100644 packages/test/protos/messages.proto create mode 100644 packages/test/protos/namespaced-messages.proto create mode 100644 packages/test/protos/root.js create mode 100644 packages/test/protos/uppercase.proto create mode 100644 packages/test/scripts/compile-proto.js create mode 100644 packages/test/src/data-converters/data-converter-bad-export.ts create mode 100644 packages/test/src/data-converters/data-converter-no-export.ts create mode 100644 packages/test/src/data-converters/data-converter.ts create mode 100644 packages/test/src/test-custom-data-converter.ts create mode 100644 packages/test/src/test-patch-root.ts create mode 100644 packages/test/src/workflows/protobufs.ts diff --git a/.prettierignore b/.prettierignore index 3279afe0d..3f3bdf2e6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,6 +11,8 @@ packages/docs/docs/api typedoc-sidebar.js lerna.json README.md +packages/test/protos/root.d.ts +packages/test/protos/json-module.js # Ignored since it fails Windows CI packages/create-project/cli.js \ No newline at end of file diff --git a/docs/protobuf-libraries.md b/docs/protobuf-libraries.md new file mode 100644 index 000000000..dd2d0525f --- /dev/null +++ b/docs/protobuf-libraries.md @@ -0,0 +1,109 @@ +Loren & Roey surveyed the available protobuf libraries in Dec '21 for use with our `ProtobufBinaryDataConverter` and `ProtobufJsonDataConverter`. The main criteria was: + +- A. TypeScript types for messages +- B. Being able to check at runtime whether an object passed to the SDK as input or returned to the SDK from a workflow/query/activity is meant to be protobuf-serialized, without adding annotations to the functions. +- C. Spec-compliant [proto3 JSON encoding](https://developers.google.com/protocol-buffers/docs/proto3#json) so that the TS SDK is interoperable with the other SDKs + +## Options + +### protobufjs + +A and B, but not C. + +- Most popular lib (5M downloads/wk) +- Fairly inactive maintainers (infrequent updates, many open PRs & issues) +- Non-standard JSON serialization +- Message classes with generated types and runtime-checkable instances + +### proto3-json-serializer + +C + +- Adds spec-compliant JSON encoding to protobufjs +- Maintained by responsive Googlers, 900k downloads/wk +- Requires runtime-loaded messages (not compatible with generated classes) + +### google-protobuf + +B + +- Official Google lib, 800k downloads/wk +- No types or JSON encoding +- Compiler installed separately (not on npm) + +### ts-proto + +A and some of C + +- Generates TS interfaces and encoding functions +- Designed for POJOs (no instances of message classes), so can't do B +- JSON encoding is probably [not yet fully spec compliant](https://github.com/stephenh/ts-proto/pull/448#issuecomment-998166664) + +### protoc-gen-ts + +A and B + +- Plugin for Google's `protoc` compiler +- Generated classes extend `google-protobuf`'s Message, but doesn't add JSON +- Maintainer [seems interested in JSON encoding](https://github.com/protocolbuffers/protobuf/issues/4540#issuecomment-915609405), but isn't there yet (only has `to/fromObject` methods—need eg a fromJSON that converts the below base64 to a bytearray, and a toJSON that converts a bytearray to base64) + +## Current solution + +- Use `protobufjs` with `proto3-json-serializer` +- Have users use runtime-loaded messages (not generated classes) and `Class.create` (not `new Class()`) +- Patch `json-module` output (which adds `nested` attributes to lowercase namespaces [which causes TS error](https://github.com/protobufjs/protobuf.js/issues/1014)) + +```ts +// json-module.js generated with: +// pbjs -t json-module -w commonjs -o json-module.js *.proto + +// protos/root.js +const { patchProtobufRoot } = require('@temporalio/common'); +const unpatchedRoot = require('./json-module'); +module.exports = patchProtobufRoot(unpatchedRoot); + +// root.d.ts generated with: +// pbjs -t static-module *.proto | pbts -o root.d.ts - + +// src/client.ts +import { foo } from '../protos/root'; + +await client.start(protoWorkflow, { + args: [foo.bar.ProtoInput.create({ name: 'Proto', age: 1 })], + taskQueue: 'tutorial', + workflowId: 'my-business-id', +}); + +// src/workflows.ts +import { foo } from '../protos/root'; + +export async function protoWorkflow(input: foo.bar.ProtoInput): Promise { + return foo.bar.ProtoResult.create({ sentence: `Name is ${input.name}` }); +} +``` + +We originally were thinking of this, but the namespaces in `json-module.js` get lost through `patchProtobufRoot()`: + +``` +import * as generatedRoot from '../protos/json-module'; + +const patchProtobufRoot = (x: T): T => x; +const root = patchProtobufRoot(generatedRoot); + +function myWorkflowError(input: root.foo.bar.ProtoActivityInput) { + return input.name; +} +``` + +On root in `root.foo.bar.ProtoActivityInput`, TS errors: `Cannot find namespace 'root'.` + +## Future work + +If we can get changes merged into `protobufjs` (or want to fork), we can do one or both of the below: + +1. Change the `json-module` output to not have `nested` attributes so we don't have to patch +2. Add to the generated classes: + +- spec-compliant `to/fromJSON` methods +- `typename` field that includes the namespace (eg `"foo.bar.MyMessage"`) +- "this is a generated file" comment @ top diff --git a/package-lock.json b/package-lock.json index 5f62ee221..986ef0d86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,6 +45,7 @@ "json5": "^2.2.0", "lerna": "^4.0.0", "long": "^4.0.0", + "npm-run-all": "^4.1.5", "pidusage": "^2.0.21", "prettier": "^2.3.2", "tail": "^2.2.3", @@ -440,9 +441,9 @@ "dev": true }, "node_modules/@grpc/grpc-js": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.4.tgz", - "integrity": "sha512-a6222b7Dl6fIlMgzVl7e+NiRoLiZFbpcwvBH2Oli56Bn7W4/3Ld+86hK4ffPn5rx2DlDidmIcvIJiOQXyhv9gA==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.5.tgz", + "integrity": "sha512-A6cOzSu7dqXZ7rzvh/9JZf+Jg/MOpLEMP0IdT8pT8hrWJZ6TB4ydN/MRuqOtAugInJe/VQ9F8BPricUpYZSaZA==", "dependencies": { "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" @@ -1801,13 +1802,16 @@ "dev": true }, "node_modules/@npmcli/fs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz", - "integrity": "sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz", + "integrity": "sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==", "dev": true, "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" } }, "node_modules/@npmcli/git": { @@ -2600,18 +2604,18 @@ "dev": true }, "node_modules/@types/eslint": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.0.tgz", - "integrity": "sha512-74hbvsnc+7TEDa1z5YLSe4/q8hGYB3USNvCuzHUJrjPV6hXaq8IXcngCrHkuvFt0+8rFz7xYXrHgNayIX0UZvQ==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz", + "integrity": "sha512-UP9rzNn/XyGwb5RQ2fok+DzcIRIYwc16qTXse5+Smsy8MOIccCChT15KAwnsgQx4PzJkaMq4myFyZ4CL5TjhIQ==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "node_modules/@types/eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.2.tgz", + "integrity": "sha512-TzgYCWoPiTeRg6RQYgtuW7iODtVoKu3RVL72k3WohqhjfaOLK5Mg2T4Tg1o2bSfu0vPkoI48wdQFv5b/Xe04wQ==", "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -2703,9 +2707,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" + "version": "16.11.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.14.tgz", + "integrity": "sha512-mK6BKLpL0bG6v2CxHbm0ed6RcZrAtTHBTd/ZpnlVPVa3HkumsqLE4BC4u6TQ8D7pnrRbOU0am6epuALs+Ncnzw==" }, "node_modules/@types/node-fetch": { "version": "2.5.12", @@ -2756,9 +2760,9 @@ "dev": true }, "node_modules/@types/ramda": { - "version": "0.27.56", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.56.tgz", - "integrity": "sha512-fMloxwXvaL/nsLpYAkDKdoo4U8d6Gm2MSLz6Csv38Pst19MSCVs4tLf0HjsXETkBMj/wZmFZ0kUwoq0kluvi8w==", + "version": "0.27.60", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.60.tgz", + "integrity": "sha512-6ie74xhtl2ducVG8cC8mnYwpvIUWHBoLHbmvEsl5qPqJkqVP9ce5yZW10WgheKd2ua1yPIwd0miMWBsG19UmiQ==", "dev": true, "dependencies": { "ts-toolbelt": "^6.15.1" @@ -2871,24 +2875,6 @@ "eslint": "*" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, "node_modules/@typescript-eslint/parser": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", @@ -3636,9 +3622,9 @@ "dev": true }, "node_modules/bignumber.js": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==", "dev": true, "engines": { "node": "*" @@ -3725,12 +3711,12 @@ } }, "node_modules/browserslist": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", - "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "dependencies": { - "caniuse-lite": "^1.0.30001280", - "electron-to-chromium": "^1.3.896", + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", "escalade": "^3.1.1", "node-releases": "^2.0.1", "picocolors": "^1.0.0" @@ -3891,7 +3877,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -3936,9 +3921,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001283", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz", - "integrity": "sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==", + "version": "1.0.30001289", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001289.tgz", + "integrity": "sha512-hV6x4IfrYViN8cJbGFVbjD7KCrhS/O7wfDgvevYRanJ/IN+hhxpTcXXqaxy3CzPNFe5rlqdimdEB/k7H0YzxHg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/browserslist" @@ -4906,7 +4891,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "dependencies": { "object-keys": "^1.0.12" }, @@ -5052,9 +5036,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.5.tgz", - "integrity": "sha512-YKaB+t8ul5crdh6OeqT2qXdxJGI0fAYb6/X8pDIyye+c3a7ndOCk5gVeKX+ABwivCGNS56vOAif3TN0qJMpEHw==" + "version": "1.4.24", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz", + "integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg==" }, "node_modules/emittery": { "version": "0.8.1", @@ -5168,7 +5152,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -5177,7 +5160,6 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -5216,7 +5198,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -5328,34 +5309,35 @@ } }, "node_modules/eslint-plugin-deprecation": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-1.2.1.tgz", - "integrity": "sha512-8KFAWPO3AvF0szxIh1ivRtHotd1fzxVOuNR3NI8dfCsQKgcxu9fAgEY+eTKvCRLAwwI8kaDDfImMt+498+EgRw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-1.3.2.tgz", + "integrity": "sha512-z93wbx9w7H/E3ogPw6AZMkkNJ6m51fTZRNZPNQqxQLmx+KKt7aLkMU9wN67s71i+VVHN4tLOZ3zT3QLbnlC0Mg==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "^2.19.2 || ^3.0.0", - "tslib": "^1.10.0", - "tsutils": "^3.0.0" + "@typescript-eslint/experimental-utils": "^5.0.0", + "tslib": "^2.3.1", + "tsutils": "^3.21.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0", "typescript": "^3.7.5 || ^4.0.0" } }, "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/experimental-utils": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz", - "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", + "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/types": "3.10.1", - "@typescript-eslint/typescript-estree": "3.10.1", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -5365,13 +5347,30 @@ "eslint": "*" } }, + "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/scope-manager": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", + "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/types": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz", - "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", + "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", "dev": true, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -5379,22 +5378,21 @@ } }, "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/typescript-estree": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz", - "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", + "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "3.10.1", - "@typescript-eslint/visitor-keys": "3.10.1", - "debug": "^4.1.1", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -5407,15 +5405,16 @@ } }, "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/visitor-keys": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz", - "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", + "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "@typescript-eslint/types": "5.7.0", + "eslint-visitor-keys": "^3.0.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -5423,20 +5422,14 @@ } }, "node_modules/eslint-plugin-deprecation/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", "dev": true, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint-plugin-deprecation/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/eslint-plugin-tsdoc": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.14.tgz", @@ -5460,6 +5453,33 @@ } }, "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", @@ -5474,7 +5494,7 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", @@ -5483,15 +5503,6 @@ "node": ">=4" } }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -5893,8 +5904,7 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/functional-red-black-tree": { "version": "1.0.1", @@ -6012,7 +6022,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -6120,7 +6129,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -6433,7 +6441,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -6445,7 +6452,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6462,7 +6468,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -6474,7 +6479,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -6816,7 +6820,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -6844,14 +6847,12 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -6875,7 +6876,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -6891,7 +6891,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -6915,7 +6914,6 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", - "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -6927,7 +6925,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7029,10 +7026,9 @@ "dev": true }, "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true, + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "engines": { "node": ">= 0.4" }, @@ -7064,7 +7060,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7130,7 +7125,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -7146,7 +7140,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7176,7 +7169,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7191,7 +7183,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -7233,12 +7224,11 @@ } }, "node_modules/is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dependencies": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7277,9 +7267,9 @@ "dev": true }, "node_modules/jest-worker": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.0.tgz", - "integrity": "sha512-4WuKcUxtzxBoKOUFbt1MtTY9fJwPVD4aN/4Cgxee7OLetPZn5as2bjfZz98XSf2Zq1JFfhqPZpS+43BmWXKgCA==", + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", + "integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -7969,6 +7959,14 @@ "node": ">= 4.0.0" } }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", @@ -8198,9 +8196,9 @@ } }, "node_modules/minipass": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", - "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "dev": true, "dependencies": { "yallist": "^4.0.0" @@ -8393,6 +8391,11 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "node_modules/node-fetch": { "version": "2.6.6", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", @@ -8677,95 +8680,310 @@ "node": ">=10" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", "dependencies": { - "path-key": "^3.0.0" + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" }, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, "engines": { - "node": "*" + "node": ">=4.8" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, - "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/npm-run-all/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, + "node_modules/npm-run-all/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/npm-run-all/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=4" } }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, + "node_modules/npm-run-all/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/npm-run-all/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.3", + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", + "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", "integrity": "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==", "dev": true, @@ -9484,8 +9702,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-type": { "version": "4.0.0", @@ -9518,6 +9735,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/pidusage": { "version": "2.0.21", "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", @@ -9790,9 +10018,9 @@ } }, "node_modules/prettier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.0.tgz", - "integrity": "sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -9886,6 +10114,14 @@ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, + "node_modules/proto3-json-serializer": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.6.tgz", + "integrity": "sha512-tGbV6m6Kad8NqxMh5hw87euPS0YoZSAOIfvR01zYkQV8Gpx1V/8yU/0gCKCvfCkhAJsjvzzhnnsdQxA1w7PSog==", + "dependencies": { + "protobufjs": "^6.11.2" + } + }, "node_modules/protobufjs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", @@ -9963,9 +10199,9 @@ } }, "node_modules/qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz", + "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -10555,7 +10791,6 @@ "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, "dependencies": { "is-core-module": "^2.1.0", "path-parse": "^1.0.6" @@ -10833,6 +11068,11 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + }, "node_modules/shimmer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", @@ -10843,7 +11083,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -11012,7 +11251,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -11021,14 +11259,12 @@ "node_modules/spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -11037,8 +11273,7 @@ "node_modules/spdx-license-ids": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==" }, "node_modules/split": { "version": "1.0.1", @@ -11185,11 +11420,26 @@ "node": ">=8" } }, + "node_modules/string.prototype.padend": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", + "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -11202,7 +11452,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -11309,9 +11558,9 @@ } }, "node_modules/table": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.3.tgz", - "integrity": "sha512-5DkIxeA7XERBqMwJq0aHZOdMadBx4e6eDoFRuyT5VR82J0Ycg2DwM6GfA/EQAhJ+toRTaS1lIdSQCqgrmhPnlw==", + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.5.tgz", + "integrity": "sha512-LFNeryOqiQHqCVKzhkymKwt6ozeRhlm8IL1mE8rNUurkir4heF6PzMyRgaTa4tlyPTGGgXuvVOF/OLWiH09Lqw==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -11558,11 +11807,11 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz", - "integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", + "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", "dependencies": { - "jest-worker": "^27.0.6", + "jest-worker": "^27.4.1", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", "source-map": "^0.6.1", @@ -11902,9 +12151,9 @@ } }, "node_modules/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11914,9 +12163,9 @@ } }, "node_modules/uglify-js": { - "version": "3.14.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", - "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", + "version": "3.14.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", + "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", "dev": true, "optional": true, "bin": { @@ -11945,7 +12194,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "has-bigints": "^1.0.1", @@ -12109,7 +12357,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -12138,9 +12385,9 @@ } }, "node_modules/watchpack": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.0.tgz", - "integrity": "sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -12168,9 +12415,9 @@ } }, "node_modules/webpack": { - "version": "5.64.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.4.tgz", - "integrity": "sha512-LWhqfKjCLoYJLKJY8wk2C3h77i8VyHowG3qYNZiIqD6D0ZS40439S/KVuc/PY48jp2yQmy0mhMknq8cys4jFMw==", + "version": "5.65.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", + "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", "dependencies": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.50", @@ -12194,7 +12441,7 @@ "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.0", + "watchpack": "^2.3.1", "webpack-sources": "^3.2.2" }, "bin": { @@ -12262,7 +12509,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -12546,18 +12792,18 @@ } }, "node_modules/yargs": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", - "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", + "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.0.0" }, "engines": { "node": ">=12" @@ -12571,6 +12817,15 @@ "node": ">=10" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -12593,7 +12848,8 @@ } }, "packages/activity": { - "version": "0.16.0", + "name": "@temporalio/activity", + "version": "0.16.4", "license": "MIT", "dependencies": { "@temporalio/common": "file:../common", @@ -12601,7 +12857,8 @@ } }, "packages/client": { - "version": "0.16.0", + "name": "@temporalio/client", + "version": "0.16.4", "license": "MIT", "dependencies": { "@grpc/grpc-js": "^1.3.7", @@ -12621,15 +12878,18 @@ } }, "packages/common": { - "version": "0.16.0", + "name": "@temporalio/common", + "version": "0.16.4", "license": "MIT", "dependencies": { "@temporalio/proto": "file:../proto", - "ms": "^2.1.3" + "ms": "^2.1.3", + "proto3-json-serializer": "^0.1.6" } }, "packages/core-bridge": { - "version": "0.16.0", + "name": "@temporalio/core-bridge", + "version": "0.16.4", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -12641,7 +12901,8 @@ } }, "packages/create-project": { - "version": "0.16.1", + "name": "@temporalio/create", + "version": "0.16.3", "license": "MIT", "dependencies": { "async-retry": "^1.3.3", @@ -12725,7 +12986,8 @@ } }, "packages/interceptors-opentelemetry": { - "version": "0.16.2", + "name": "@temporalio/interceptors-opentelemetry", + "version": "0.16.4", "license": "MIT", "dependencies": { "@opentelemetry/api": "^1.0.3", @@ -12792,7 +13054,8 @@ } }, "packages/meta": { - "version": "0.16.2", + "name": "temporalio", + "version": "0.16.4", "license": "MIT", "dependencies": { "@temporalio/activity": "file:../activity", @@ -12807,6 +13070,7 @@ } }, "packages/proto": { + "name": "@temporalio/proto", "version": "0.16.0", "license": "MIT", "dependencies": { @@ -12819,7 +13083,8 @@ } }, "packages/test": { - "version": "0.16.2", + "name": "@temporalio/test", + "version": "0.16.4", "license": "MIT", "dependencies": { "@opentelemetry/exporter-collector-grpc": "^0.25.0", @@ -12832,11 +13097,14 @@ "@temporalio/workflow": "file:../workflow", "@types/async-retry": "^1.3.3", "async-retry": "^1.3.3", + "npm-run-all": "^4.1.5", + "protobufjs": "^6.11.2", "ramda": "^0.27.1" } }, "packages/worker": { - "version": "0.16.2", + "name": "@temporalio/worker", + "version": "0.16.4", "license": "MIT", "dependencies": { "@opentelemetry/api": "^1.0.3", @@ -12870,7 +13138,8 @@ } }, "packages/workflow": { - "version": "0.16.0", + "name": "@temporalio/workflow", + "version": "0.16.4", "license": "MIT", "dependencies": { "@temporalio/common": "file:../common", @@ -13178,9 +13447,9 @@ "dev": true }, "@grpc/grpc-js": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.4.tgz", - "integrity": "sha512-a6222b7Dl6fIlMgzVl7e+NiRoLiZFbpcwvBH2Oli56Bn7W4/3Ld+86hK4ffPn5rx2DlDidmIcvIJiOQXyhv9gA==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.5.tgz", + "integrity": "sha512-A6cOzSu7dqXZ7rzvh/9JZf+Jg/MOpLEMP0IdT8pT8hrWJZ6TB4ydN/MRuqOtAugInJe/VQ9F8BPricUpYZSaZA==", "requires": { "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" @@ -14315,9 +14584,9 @@ "dev": true }, "@npmcli/fs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz", - "integrity": "sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz", + "integrity": "sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==", "dev": true, "requires": { "@gar/promisify": "^1.0.1", @@ -14885,7 +15154,8 @@ "version": "file:packages/common", "requires": { "@temporalio/proto": "file:../proto", - "ms": "^2.1.3" + "ms": "^2.1.3", + "proto3-json-serializer": "^0.1.6" } }, "@temporalio/core-bridge": { @@ -15029,6 +15299,8 @@ "@temporalio/workflow": "file:../workflow", "@types/async-retry": "^1.3.3", "async-retry": "^1.3.3", + "npm-run-all": "^4.1.5", + "protobufjs": "^6.11.2", "ramda": "^0.27.1" } }, @@ -15130,18 +15402,18 @@ "dev": true }, "@types/eslint": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.0.tgz", - "integrity": "sha512-74hbvsnc+7TEDa1z5YLSe4/q8hGYB3USNvCuzHUJrjPV6hXaq8IXcngCrHkuvFt0+8rFz7xYXrHgNayIX0UZvQ==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz", + "integrity": "sha512-UP9rzNn/XyGwb5RQ2fok+DzcIRIYwc16qTXse5+Smsy8MOIccCChT15KAwnsgQx4PzJkaMq4myFyZ4CL5TjhIQ==", "requires": { "@types/estree": "*", "@types/json-schema": "*" } }, "@types/eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.2.tgz", + "integrity": "sha512-TzgYCWoPiTeRg6RQYgtuW7iODtVoKu3RVL72k3WohqhjfaOLK5Mg2T4Tg1o2bSfu0vPkoI48wdQFv5b/Xe04wQ==", "requires": { "@types/eslint": "*", "@types/estree": "*" @@ -15233,9 +15505,9 @@ "dev": true }, "@types/node": { - "version": "16.11.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.10.tgz", - "integrity": "sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==" + "version": "16.11.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.14.tgz", + "integrity": "sha512-mK6BKLpL0bG6v2CxHbm0ed6RcZrAtTHBTd/ZpnlVPVa3HkumsqLE4BC4u6TQ8D7pnrRbOU0am6epuALs+Ncnzw==" }, "@types/node-fetch": { "version": "2.5.12", @@ -15285,9 +15557,9 @@ "dev": true }, "@types/ramda": { - "version": "0.27.56", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.56.tgz", - "integrity": "sha512-fMloxwXvaL/nsLpYAkDKdoo4U8d6Gm2MSLz6Csv38Pst19MSCVs4tLf0HjsXETkBMj/wZmFZ0kUwoq0kluvi8w==", + "version": "0.27.60", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.60.tgz", + "integrity": "sha512-6ie74xhtl2ducVG8cC8mnYwpvIUWHBoLHbmvEsl5qPqJkqVP9ce5yZW10WgheKd2ua1yPIwd0miMWBsG19UmiQ==", "dev": true, "requires": { "ts-toolbelt": "^6.15.1" @@ -15372,17 +15644,6 @@ "@typescript-eslint/typescript-estree": "4.33.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" - }, - "dependencies": { - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - } } }, "@typescript-eslint/parser": { @@ -15982,9 +16243,9 @@ "dev": true }, "bignumber.js": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==", "dev": true }, "binary-extensions": { @@ -16052,12 +16313,12 @@ } }, "browserslist": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", - "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "requires": { - "caniuse-lite": "^1.0.30001280", - "electron-to-chromium": "^1.3.896", + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", "escalade": "^3.1.1", "node-releases": "^2.0.1", "picocolors": "^1.0.0" @@ -16174,7 +16435,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -16204,9 +16464,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001283", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz", - "integrity": "sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==" + "version": "1.0.30001289", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001289.tgz", + "integrity": "sha512-hV6x4IfrYViN8cJbGFVbjD7KCrhS/O7wfDgvevYRanJ/IN+hhxpTcXXqaxy3CzPNFe5rlqdimdEB/k7H0YzxHg==" }, "cargo-cp-artifact": { "version": "0.1.6", @@ -16965,7 +17225,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -17081,9 +17340,9 @@ } }, "electron-to-chromium": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.5.tgz", - "integrity": "sha512-YKaB+t8ul5crdh6OeqT2qXdxJGI0fAYb6/X8pDIyye+c3a7ndOCk5gVeKX+ABwivCGNS56vOAif3TN0qJMpEHw==" + "version": "1.4.24", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz", + "integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg==" }, "emittery": { "version": "0.8.1", @@ -17172,7 +17431,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -17181,7 +17439,6 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -17214,7 +17471,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -17286,6 +17542,23 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -17302,70 +17575,75 @@ "requires": {} }, "eslint-plugin-deprecation": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-1.2.1.tgz", - "integrity": "sha512-8KFAWPO3AvF0szxIh1ivRtHotd1fzxVOuNR3NI8dfCsQKgcxu9fAgEY+eTKvCRLAwwI8kaDDfImMt+498+EgRw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-1.3.2.tgz", + "integrity": "sha512-z93wbx9w7H/E3ogPw6AZMkkNJ6m51fTZRNZPNQqxQLmx+KKt7aLkMU9wN67s71i+VVHN4tLOZ3zT3QLbnlC0Mg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "^2.19.2 || ^3.0.0", - "tslib": "^1.10.0", - "tsutils": "^3.0.0" + "@typescript-eslint/experimental-utils": "^5.0.0", + "tslib": "^2.3.1", + "tsutils": "^3.21.0" }, "dependencies": { "@typescript-eslint/experimental-utils": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz", - "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", + "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.7.0", + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/typescript-estree": "5.7.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", + "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", "dev": true, "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/types": "3.10.1", - "@typescript-eslint/typescript-estree": "3.10.1", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0" } }, "@typescript-eslint/types": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz", - "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", + "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz", - "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", + "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", "dev": true, "requires": { - "@typescript-eslint/types": "3.10.1", - "@typescript-eslint/visitor-keys": "3.10.1", - "debug": "^4.1.1", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^7.3.2", - "tsutils": "^3.17.1" + "@typescript-eslint/types": "5.7.0", + "@typescript-eslint/visitor-keys": "5.7.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" } }, "@typescript-eslint/visitor-keys": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz", - "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", + "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "@typescript-eslint/types": "5.7.0", + "eslint-visitor-keys": "^3.0.0" } }, "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", "dev": true } } @@ -17390,20 +17668,12 @@ } }, "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "eslint-visitor-keys": "^2.0.0" } }, "eslint-visitor-keys": { @@ -17708,8 +17978,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -17808,7 +18077,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -17894,7 +18162,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, "requires": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -18126,7 +18393,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -18134,8 +18400,7 @@ "has-bigints": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" }, "has-flag": { "version": "4.0.0", @@ -18145,14 +18410,12 @@ "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, "has-tostringtag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -18413,7 +18676,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, "requires": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -18435,14 +18697,12 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, "requires": { "has-bigints": "^1.0.1" } @@ -18460,7 +18720,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -18469,8 +18728,7 @@ "is-callable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" }, "is-ci": { "version": "2.0.0", @@ -18485,7 +18743,6 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -18494,7 +18751,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -18565,10 +18821,9 @@ "dev": true }, "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" }, "is-npm": { "version": "5.0.0", @@ -18585,7 +18840,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", - "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -18630,7 +18884,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -18639,8 +18892,7 @@ "is-shared-array-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" }, "is-ssh": { "version": "1.3.3", @@ -18661,7 +18913,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -18670,7 +18921,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -18697,12 +18947,11 @@ "dev": true }, "is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", - "dev": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "requires": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2" } }, "is-yarn-global": { @@ -18735,9 +18984,9 @@ "dev": true }, "jest-worker": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.0.tgz", - "integrity": "sha512-4WuKcUxtzxBoKOUFbt1MtTY9fJwPVD4aN/4Cgxee7OLetPZn5as2bjfZz98XSf2Zq1JFfhqPZpS+43BmWXKgCA==", + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", + "integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==", "requires": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -19297,6 +19546,11 @@ "fs-monkey": "1.0.3" } }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, "meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", @@ -19463,9 +19717,9 @@ } }, "minipass": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", - "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -19614,6 +19868,11 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "node-fetch": { "version": "2.6.6", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", @@ -19847,6 +20106,167 @@ "npm-package-arg": "^8.0.0" } }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -19887,22 +20307,19 @@ "dev": true }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", - "dev": true + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", + "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==" }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", @@ -20456,8 +20873,7 @@ "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-type": { "version": "4.0.0", @@ -20481,6 +20897,11 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==" + }, "pidusage": { "version": "2.0.21", "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", @@ -20670,9 +21091,9 @@ "dev": true }, "prettier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.0.tgz", - "integrity": "sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", "dev": true }, "pretty-ms": { @@ -20744,6 +21165,14 @@ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, + "proto3-json-serializer": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.6.tgz", + "integrity": "sha512-tGbV6m6Kad8NqxMh5hw87euPS0YoZSAOIfvR01zYkQV8Gpx1V/8yU/0gCKCvfCkhAJsjvzzhnnsdQxA1w7PSog==", + "requires": { + "protobufjs": "^6.11.2" + } + }, "protobufjs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", @@ -20806,9 +21235,9 @@ "dev": true }, "qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz", + "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==", "dev": true, "requires": { "side-channel": "^1.0.4" @@ -21276,7 +21705,6 @@ "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, "requires": { "is-core-module": "^2.1.0", "path-parse": "^1.0.6" @@ -21475,6 +21903,11 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + }, "shimmer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", @@ -21485,7 +21918,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -21614,7 +22046,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -21623,14 +22054,12 @@ "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -21639,8 +22068,7 @@ "spdx-license-ids": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==" }, "split": { "version": "1.0.1", @@ -21748,11 +22176,20 @@ "strip-ansi": "^6.0.1" } }, + "string.prototype.padend": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", + "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -21762,7 +22199,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -21836,9 +22272,9 @@ } }, "table": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.3.tgz", - "integrity": "sha512-5DkIxeA7XERBqMwJq0aHZOdMadBx4e6eDoFRuyT5VR82J0Ycg2DwM6GfA/EQAhJ+toRTaS1lIdSQCqgrmhPnlw==", + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.5.tgz", + "integrity": "sha512-LFNeryOqiQHqCVKzhkymKwt6ozeRhlm8IL1mE8rNUurkir4heF6PzMyRgaTa4tlyPTGGgXuvVOF/OLWiH09Lqw==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -22039,11 +22475,11 @@ } }, "terser-webpack-plugin": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz", - "integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", + "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", "requires": { - "jest-worker": "^27.0.6", + "jest-worker": "^27.4.1", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", "source-map": "^0.6.1", @@ -22282,14 +22718,14 @@ } }, "typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==" + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==" }, "uglify-js": { - "version": "3.14.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", - "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", + "version": "3.14.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", + "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", "dev": true, "optional": true }, @@ -22309,7 +22745,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has-bigints": "^1.0.1", @@ -22447,7 +22882,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -22473,9 +22907,9 @@ } }, "watchpack": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.0.tgz", - "integrity": "sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -22497,9 +22931,9 @@ "dev": true }, "webpack": { - "version": "5.64.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.4.tgz", - "integrity": "sha512-LWhqfKjCLoYJLKJY8wk2C3h77i8VyHowG3qYNZiIqD6D0ZS40439S/KVuc/PY48jp2yQmy0mhMknq8cys4jFMw==", + "version": "5.65.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", + "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", "requires": { "@types/eslint-scope": "^3.7.0", "@types/estree": "^0.0.50", @@ -22523,7 +22957,7 @@ "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.0", + "watchpack": "^2.3.1", "webpack-sources": "^3.2.2" } }, @@ -22561,7 +22995,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, "requires": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -22782,18 +23215,26 @@ "dev": true }, "yargs": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", - "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", + "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "dev": true + } } }, "yargs-parser": { diff --git a/package.json b/package.json index 0297b99ad..9e53021f8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "json5": "^2.2.0", "lerna": "^4.0.0", "long": "^4.0.0", + "npm-run-all": "^4.1.5", "pidusage": "^2.0.21", "prettier": "^2.3.2", "tail": "^2.2.3", diff --git a/packages/common/package.json b/packages/common/package.json index 2f1048f79..8027885ba 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -14,7 +14,8 @@ "license": "MIT", "dependencies": { "@temporalio/proto": "file:../proto", - "ms": "^2.1.3" + "ms": "^2.1.3", + "proto3-json-serializer": "^0.1.6" }, "bugs": { "url": "https://github.com/temporalio/sdk-typescript/issues" @@ -22,5 +23,8 @@ "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/common", "publishConfig": { "access": "public" + }, + "devDependencies": { + "protobufjs": "^6.11.2" } } diff --git a/packages/common/src/converter/data-converter.ts b/packages/common/src/converter/data-converter.ts index 11ba5f8ea..4099da986 100644 --- a/packages/common/src/converter/data-converter.ts +++ b/packages/common/src/converter/data-converter.ts @@ -5,13 +5,15 @@ import { UndefinedPayloadConverter, BinaryPayloadConverter, JsonPayloadConverter, + ProtobufJsonPayloadConverter, + ProtobufBinaryPayloadConverter, } from './payload-converter'; /** * Used by the framework to serialize/deserialize method parameters that need to be sent over the * wire. * - * Implement this in order to customize worker data serialization or use the default data converter which supports `Uint8Array` and JSON serializables. + * Implement this in order to customize worker data serialization or use the default data converter which supports `Uint8Array`, Protobuf, and JSON serializables. */ export interface DataConverter { toPayload(value: T): Promise; @@ -66,6 +68,13 @@ export interface DataConverter { fromPayloadsSync(index: number, content?: Payload[] | null): T; } +export const isValidDataConverter = (dataConverter: unknown): dataConverter is DataConverter => + typeof dataConverter === 'object' && + dataConverter !== null && + ['toPayload', 'toPayloads', 'fromPayload', 'fromPayloads'].every( + (method) => typeof (dataConverter as Record)[method] === 'function' + ); + export class CompositeDataConverter implements DataConverter { readonly converters: PayloadConverter[]; readonly converterByEncoding: Map = new Map(); @@ -182,8 +191,22 @@ export function mapToPayloadsSync( ) as Record; } -export const defaultDataConverter = new CompositeDataConverter( - new UndefinedPayloadConverter(), - new BinaryPayloadConverter(), - new JsonPayloadConverter() -); +export interface DefaultDataConverterOptions { + root?: Record; +} + +export class DefaultDataConverter extends CompositeDataConverter { + constructor({ root }: DefaultDataConverterOptions = {}) { + // Match the order used in other SDKs + // Go SDK: https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28 + super( + new UndefinedPayloadConverter(), + new BinaryPayloadConverter(), + new ProtobufJsonPayloadConverter(root), + new ProtobufBinaryPayloadConverter(root), + new JsonPayloadConverter() + ); + } +} + +export const defaultDataConverter = new DefaultDataConverter(); diff --git a/packages/common/src/converter/helpers.ts b/packages/common/src/converter/helpers.ts new file mode 100644 index 000000000..2e102f074 --- /dev/null +++ b/packages/common/src/converter/helpers.ts @@ -0,0 +1,38 @@ +import { isRecord } from '../type-helpers'; + +export function patchProtobufRoot>(root: T, name?: string): T { + const newRoot = new (root.constructor as any)(isNamespace(root) ? name : {}); + for (const key in root) { + newRoot[key] = root[key]; + } + + if (isRecord(root.nested)) { + for (const typeOrNamespace in root.nested) { + const value = root.nested[typeOrNamespace]; + if (typeOrNamespace in root && !(isType(root[typeOrNamespace]) || isNamespace(root[typeOrNamespace]))) { + console.log( + `patchRoot warning: overriding property '${typeOrNamespace}' that is used by protobufjs with the '${typeOrNamespace}' protobuf namespace. This may result in protobufjs not working property.` + ); + } + + if (isNamespace(value)) { + newRoot[typeOrNamespace] = patchProtobufRoot(value, typeOrNamespace); + } else if (isType(value)) { + newRoot[typeOrNamespace] = value; + } + } + } + + return newRoot; +} + +type Type = Record; +type Namespace = { nested: Record }; + +function isType(value: unknown): value is Type { + return isRecord(value) && value.constructor.name === 'Type'; +} + +function isNamespace(value: unknown): value is Namespace { + return isRecord(value) && value.constructor.name === 'Namespace'; +} diff --git a/packages/common/src/converter/payload-converter.ts b/packages/common/src/converter/payload-converter.ts index 46c2c2907..8599a8b2c 100644 --- a/packages/common/src/converter/payload-converter.ts +++ b/packages/common/src/converter/payload-converter.ts @@ -1,5 +1,17 @@ -import { ValueError } from '../errors'; -import { u8, str, Payload, encodingTypes, encodingKeys, METADATA_ENCODING_KEY } from './types'; +import { ValueError, DataConverterError } from '../errors'; +import { + u8, + str, + Payload, + encodingTypes, + encodingKeys, + METADATA_ENCODING_KEY, + METADATA_MESSAGE_TYPE_KEY, +} from './types'; +import { isRecord, hasOwnProperty, hasOwnProperties } from '../type-helpers'; +import { errorMessage } from '../errors'; +import * as protoJsonSerializer from 'proto3-json-serializer'; +import type { Root, Type, Namespace, Message } from 'protobufjs'; /** * Used by the framework to serialize/deserialize method parameters that need to be sent over the @@ -15,7 +27,7 @@ export interface PayloadConverter { * Implements conversion of value to payload * * @param value JS value to convert. - * @return converted value + * @return converted value or `undefined` if unable to convert. * @throws DataConverterException if conversion of the value passed as parameter failed for any * reason. */ @@ -38,7 +50,7 @@ export interface PayloadConverter { * Implements conversion of value to payload * * @param value JS value to convert. - * @return converted value + * @return converted value or `undefined` if unable to convert. * @throws DataConverterException if conversion of the value passed as parameter failed for any * reason. */ @@ -140,3 +152,142 @@ export class BinaryPayloadConverter extends AsyncFacadePayloadConverter { return content.data as any; } } + +abstract class ProtobufPayloadConverter extends AsyncFacadePayloadConverter { + protected readonly root: Root | undefined; + + // Don't use type Root here because root.d.ts doesn't export Root, so users would have to type assert + constructor(root?: unknown) { + super(); + + if (root) { + if (!isRoot(root)) { + throw new TypeError('root must be an instance of a protobufjs Root'); + } + + this.root = root; + } + } + + protected validatePayload(content: Payload): { messageType: Type; data: Uint8Array } { + if (content.data === undefined || content.data === null) { + throw new ValueError('Got payload with no data'); + } + if (!content.metadata || !(METADATA_MESSAGE_TYPE_KEY in content.metadata)) { + throw new ValueError(`Got protobuf payload without metadata.${METADATA_MESSAGE_TYPE_KEY}`); + } + if (!this.root) { + throw new DataConverterError('Unable to deserialize protobuf message without `root` being provided'); + } + + const messageTypeName = str(content.metadata[METADATA_MESSAGE_TYPE_KEY]); + let messageType; + try { + messageType = this.root.lookupType(messageTypeName); + } catch (e) { + if (errorMessage(e)?.includes('no such type')) { + throw new DataConverterError( + `Got a \`${messageTypeName}\` protobuf message but cannot find corresponding message class in \`root\`` + ); + } + + throw e; + } + + return { messageType, data: content.data }; + } + + protected constructPayload({ messageTypeName, message }: { messageTypeName: string; message: Uint8Array }): Payload { + return { + metadata: { + [METADATA_ENCODING_KEY]: u8(this.encodingType), + [METADATA_MESSAGE_TYPE_KEY]: u8(messageTypeName), + }, + data: message, + }; + } +} + +/** + * Converts between protobufjs Message instances and serialized Protobuf Payload + */ +export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter { + public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF; + + constructor(root?: unknown) { + super(root); + } + + public toDataSync(value: unknown): Payload | undefined { + if (!isProtobufMessage(value)) { + return undefined; + } + + return this.constructPayload({ + messageTypeName: getNamespacedTypeName(value.$type), + message: value.$type.encode(value).finish(), + }); + } + + public fromDataSync(content: Payload): T { + const { messageType, data } = this.validatePayload(content); + return messageType.decode(data) as unknown as T; + } +} + +/** + * Converts between protobufjs Message instances and serialized JSON Payload + */ +export class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter { + public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF_JSON; + + constructor(root?: unknown) { + super(root); + } + + public toDataSync(value: unknown): Payload | undefined { + if (!isProtobufMessage(value)) { + return undefined; + } + + const jsonValue = protoJsonSerializer.toProto3JSON(value); + + return this.constructPayload({ + messageTypeName: getNamespacedTypeName(value.$type), + message: u8(JSON.stringify(jsonValue)), + }); + } + + public fromDataSync(content: Payload): T { + const { messageType, data } = this.validatePayload(content); + return protoJsonSerializer.fromProto3JSON(messageType, JSON.parse(str(data))) as unknown as T; + } +} + +function isProtobufType(type: unknown): type is Type { + return ( + isRecord(type) && + type.constructor.name === 'Type' && + hasOwnProperties(type, ['parent', 'name', 'create', 'encode', 'decode']) && + typeof type.name === 'string' && + typeof type.create === 'function' && + typeof type.encode === 'function' && + typeof type.decode === 'function' + ); +} + +function isProtobufMessage(value: unknown): value is Message { + return isRecord(value) && hasOwnProperty(value, '$type') && isProtobufType(value.$type); +} + +function getNamespacedTypeName(node: Type | Namespace): string { + if (node.parent && !isRoot(node.parent)) { + return getNamespacedTypeName(node.parent) + '.' + node.name; + } else { + return node.name; + } +} + +function isRoot(root: unknown): root is Root { + return isRecord(root) && root.constructor.name === 'Root'; +} diff --git a/packages/common/src/converter/types.ts b/packages/common/src/converter/types.ts index a523e502c..21c40cb90 100644 --- a/packages/common/src/converter/types.ts +++ b/packages/common/src/converter/types.ts @@ -30,3 +30,5 @@ export const encodingKeys = { METADATA_ENCODING_PROTOBUF_JSON: u8(encodingTypes.METADATA_ENCODING_PROTOBUF_JSON), METADATA_ENCODING_PROTOBUF: u8(encodingTypes.METADATA_ENCODING_PROTOBUF), } as const; + +export const METADATA_MESSAGE_TYPE_KEY = 'messageType'; diff --git a/packages/common/src/errors.ts b/packages/common/src/errors.ts index cebe70750..0af49ac18 100644 --- a/packages/common/src/errors.ts +++ b/packages/common/src/errors.ts @@ -25,3 +25,21 @@ export function errorMessage(err: unknown): string | undefined { } return undefined; } + +interface ErrorWithCode { + code: string; +} +/** + * Get error code from an Error or return undefined + */ +export function errorCode(error: unknown): string | undefined { + if ( + typeof error === 'object' && + (error as ErrorWithCode).code !== undefined && + typeof (error as ErrorWithCode).code === 'string' + ) { + return (error as ErrorWithCode).code; + } + + return undefined; +} diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 87254047c..43cb3b823 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -5,6 +5,7 @@ */ export * from './activity-options'; export * from './converter/data-converter'; +export * from './converter/helpers'; export * from './errors'; export * from './failure'; export * from './interceptors'; diff --git a/packages/common/src/type-helpers.ts b/packages/common/src/type-helpers.ts index 84b56b28e..ca7d34eb3 100644 --- a/packages/common/src/type-helpers.ts +++ b/packages/common/src/type-helpers.ts @@ -13,3 +13,21 @@ export function checkExtends<_Orig, _Copy extends _Orig>(): void { export type Replace = Omit & New; export type MakeOptional = Omit & Partial>; + +export function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null; +} + +export function hasOwnProperty, Y extends PropertyKey>( + record: X, + prop: Y +): record is X & Record { + return prop in record; +} + +export function hasOwnProperties, Y extends PropertyKey>( + record: X, + props: Y[] +): record is X & Record { + return props.every((prop) => prop in record); +} diff --git a/packages/proto/scripts/compile-proto.js b/packages/proto/scripts/compile-proto.js index 224eed183..cfbe8483d 100644 --- a/packages/proto/scripts/compile-proto.js +++ b/packages/proto/scripts/compile-proto.js @@ -73,7 +73,7 @@ async function main() { const genMTime = Math.min(mtime(coresdkJsOutputFile), mtime(serviceJsOutputFile)); if (protosMTime < genMTime) { - console.log('Asuming protos are up to date'); + console.log('Assuming protos are up to date'); return; } diff --git a/packages/test/.gitignore b/packages/test/.gitignore new file mode 100644 index 000000000..6cfeae5b1 --- /dev/null +++ b/packages/test/.gitignore @@ -0,0 +1,2 @@ +protos/json-module.js +protos/root.d.ts \ No newline at end of file diff --git a/packages/test/package.json b/packages/test/package.json index ad04af776..6d5e3bda2 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -4,8 +4,11 @@ "version": "0.17.2", "description": "Temporal.io SDK Tests", "scripts": { - "build": "tsc --build", - "build.watch": "tsc --build --watch", + "build": "npm-run-all build:protos build:ts", + "build.watch": "npm-run-all build:protos build:ts-watch", + "build:ts": "tsc --build", + "build:ts-watch": "tsc --build --watch", + "build:protos": "node ./scripts/compile-proto.js", "test": "ava ./lib/test-*.js", "test.watch": "ava --watch ./lib/test-*.js" }, @@ -32,6 +35,8 @@ "@temporalio/workflow": "file:../workflow", "@types/async-retry": "^1.3.3", "async-retry": "^1.3.3", + "npm-run-all": "^4.1.5", + "protobufjs": "^6.11.2", "ramda": "^0.27.1" }, "bugs": { diff --git a/packages/test/protos/messages.proto b/packages/test/protos/messages.proto new file mode 100644 index 000000000..b8b37301c --- /dev/null +++ b/packages/test/protos/messages.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +message ProtoActivityInput { + string name = 1; + int32 age = 2; +} + +message ProtoActivityResult { + string sentence = 1; +} + +message BinaryMessage { + bytes data = 1; +} \ No newline at end of file diff --git a/packages/test/protos/namespaced-messages.proto b/packages/test/protos/namespaced-messages.proto new file mode 100644 index 000000000..eac292cbe --- /dev/null +++ b/packages/test/protos/namespaced-messages.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package foo.bar; + +message ProtoActivityInput { + string name = 1; + int32 age = 2; +} + diff --git a/packages/test/protos/root.js b/packages/test/protos/root.js new file mode 100644 index 000000000..efb3689e3 --- /dev/null +++ b/packages/test/protos/root.js @@ -0,0 +1,3 @@ +const { patchProtobufRoot } = require('@temporalio/common'); +const unpatchedRoot = require('./json-module'); +module.exports = patchProtobufRoot(unpatchedRoot); diff --git a/packages/test/protos/uppercase.proto b/packages/test/protos/uppercase.proto new file mode 100644 index 000000000..49effe64e --- /dev/null +++ b/packages/test/protos/uppercase.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package Uppercase; + +message UpperMessage { + string name = 1; +} + diff --git a/packages/test/scripts/compile-proto.js b/packages/test/scripts/compile-proto.js new file mode 100644 index 000000000..a5a2d6b20 --- /dev/null +++ b/packages/test/scripts/compile-proto.js @@ -0,0 +1,61 @@ +// Script to do the equivalent of: +// pbjs -t json-module -w commonjs -r test -o protos/json-module.js protos/*.proto +// pbjs -t static-module protos/*.proto | pbts -o protos/root.d.ts - +const { resolve } = require('path'); +const { promisify } = require('util'); +const glob = require('glob'); +const { statSync, mkdirsSync } = require('fs-extra'); +const { rm } = require('fs/promises'); +const pbjs = require('protobufjs/cli/pbjs'); +const pbts = require('protobufjs/cli/pbts'); + +const outputDir = resolve(__dirname, '../protos'); +const moduleOutputFile = resolve(outputDir, 'json-module.js'); +const typesOutputFile = resolve(outputDir, 'root.d.ts'); +const tempFile = resolve(outputDir, 'temp.js'); +const protoBaseDir = resolve(__dirname, '../protos'); +const protosPath = protoBaseDir + '/*.proto'; + +function mtime(path) { + try { + return statSync(path).mtimeMs; + } catch (err) { + if (err.code === 'ENOENT') { + return 0; + } + throw err; + } +} + +async function compileProtos(protoPath, outputFile, ...args) { + const pbjsArgs = [...args, '--wrap', 'commonjs', '--force-long', '--no-verify', '--out', outputFile, protoPath]; + return await promisify(pbjs.main)(pbjsArgs); +} + +async function main() { + mkdirsSync(outputDir); + + const protoFiles = glob.sync(resolve(protoBaseDir, '**/*.proto')); + const protosMTime = Math.max(...protoFiles.map(mtime)); + const genMTime = Math.min(mtime(moduleOutputFile), mtime(typesOutputFile)); + + if (protosMTime < genMTime) { + console.log('Assuming protos are up to date'); + return; + } + + console.log(`Creating protobuf JS definitions from ${protosPath}`); + await compileProtos(protosPath, moduleOutputFile, '--target', 'json-module', '--root', 'test'); + + console.log(`Creating protobuf TS definitions from ${protosPath}`); + await compileProtos(protosPath, tempFile, '--target', 'static-module'); + await promisify(pbts.main)(['--out', typesOutputFile, tempFile]); + await rm(tempFile); + + console.log('Done'); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/packages/test/src/activities/index.ts b/packages/test/src/activities/index.ts index 06e4bf547..a08550068 100644 --- a/packages/test/src/activities/index.ts +++ b/packages/test/src/activities/index.ts @@ -7,6 +7,7 @@ import { cancellableFetch as cancellableFetchInner } from './cancellable-fetch'; import { ApplicationFailure, QueryDefinition } from '@temporalio/common'; export { throwSpecificError } from './failure-tester'; +import { ProtoActivityInput, ProtoActivityResult } from '../../protos/root'; // TODO: Get rid of this by providing client via activity context function getTestConnection(): Connection { @@ -106,3 +107,7 @@ export async function queryOwnWf(queryDef: QueryDefinition { + return ProtoActivityResult.create({ sentence: `${args.name} is ${args.age} years old.` }); +} diff --git a/packages/test/src/data-converters/data-converter-bad-export.ts b/packages/test/src/data-converters/data-converter-bad-export.ts new file mode 100644 index 000000000..c7cc61d8e --- /dev/null +++ b/packages/test/src/data-converters/data-converter-bad-export.ts @@ -0,0 +1 @@ +export const dataConverter = { toPayload: Function.prototype }; diff --git a/packages/test/src/data-converters/data-converter-no-export.ts b/packages/test/src/data-converters/data-converter-no-export.ts new file mode 100644 index 000000000..ff464d15b --- /dev/null +++ b/packages/test/src/data-converters/data-converter-no-export.ts @@ -0,0 +1 @@ +null; diff --git a/packages/test/src/data-converters/data-converter.ts b/packages/test/src/data-converters/data-converter.ts new file mode 100644 index 000000000..28f089f25 --- /dev/null +++ b/packages/test/src/data-converters/data-converter.ts @@ -0,0 +1,6 @@ +import { DefaultDataConverter } from '@temporalio/common'; +import root, { foo } from '../../protos/root'; + +export const messageInstance = foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + +export const dataConverter = new DefaultDataConverter({ root }); diff --git a/packages/test/src/mock-native-worker.ts b/packages/test/src/mock-native-worker.ts index ddaf0274b..8c1a7fec4 100644 --- a/packages/test/src/mock-native-worker.ts +++ b/packages/test/src/mock-native-worker.ts @@ -2,7 +2,7 @@ import { lastValueFrom } from 'rxjs'; import { SpanContext } from '@opentelemetry/api'; import { coresdk } from '@temporalio/proto'; -import { defaultDataConverter, msToTs } from '@temporalio/common'; +import { defaultDataConverter, DataConverter, msToTs } from '@temporalio/common'; import { Worker as RealWorker, NativeWorkerLike, errors } from '@temporalio/worker/lib/worker'; import { compileWorkerOptions, @@ -153,9 +153,9 @@ export class Worker extends RealWorker { return this.nativeWorker as MockNativeWorker; } - public constructor(workflowCreator: WorkflowCreator, opts: CompiledWorkerOptions) { + public constructor(workflowCreator: WorkflowCreator, opts: CompiledWorkerOptions, dataConverter: DataConverter) { const nativeWorker = new MockNativeWorker(); - super(nativeWorker, workflowCreator, opts); + super(nativeWorker, workflowCreator, opts, dataConverter); } public runWorkflows(...args: Parameters): Promise { @@ -170,7 +170,8 @@ export const defaultOptions: WorkerOptions = { taskQueue: 'test', }; -export function isolateFreeWorker(options: WorkerOptions = defaultOptions): Worker { +export async function isolateFreeWorker(options: WorkerOptions = defaultOptions): Promise { + const dataConverter = await RealWorker.getDataConverter(options); return new Worker( { async createWorkflow() { @@ -180,6 +181,7 @@ export function isolateFreeWorker(options: WorkerOptions = defaultOptions): Work /* Nothing to destroy */ }, }, - compileWorkerOptions(addDefaultWorkerOptions(options)) + compileWorkerOptions(addDefaultWorkerOptions(options)), + dataConverter ); } diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-data-converter.ts new file mode 100644 index 000000000..e469d3d90 --- /dev/null +++ b/packages/test/src/test-custom-data-converter.ts @@ -0,0 +1,139 @@ +import { Connection, WorkflowClient } from '@temporalio/client'; +import { coresdk } from '@temporalio/proto'; +import { Core, DefaultLogger, Worker } from '@temporalio/worker'; +import test, { ExecutionContext } from 'ava'; +import { v4 as uuid4 } from 'uuid'; +import { protoActivity } from './activities'; +import { dataConverter, messageInstance } from './data-converters/data-converter'; +import { cleanStackTrace, RUN_INTEGRATION_TESTS } from './helpers'; +import { defaultOptions, isolateFreeWorker } from './mock-native-worker'; +import { protobufWorkflow } from './workflows'; + +export async function runWorker(worker: Worker, fn: () => Promise): Promise { + const promise = worker.run(); + await fn(); + worker.shutdown(); + await promise; +} + +function compareCompletion( + t: ExecutionContext, + actual: coresdk.activity_result.IActivityResult | null | undefined, + expected: coresdk.activity_result.IActivityResult +) { + if (actual?.failed?.failure) { + const { stackTrace, ...rest } = actual.failed.failure; + actual = { failed: { failure: { stackTrace: cleanStackTrace(stackTrace), ...rest } } }; + } + t.deepEqual( + new coresdk.activity_result.ActivityResult(actual ?? undefined).toJSON(), + new coresdk.activity_result.ActivityResult(expected).toJSON() + ); +} + +if (RUN_INTEGRATION_TESTS) { + test('Client and Worker work with provided dataConverter/dataConverterPath', async (t) => { + let resolvePromise: (value: unknown) => void; + const receivedExpectedError = new Promise(function (resolve) { + resolvePromise = resolve; + }); + + await Core.install({ + logger: new DefaultLogger('ERROR', (entry) => { + if ( + entry.message === 'Failed to activate workflow' && + entry.meta?.error?.stack?.includes('Activator.startWorkflow') && + entry.meta?.error?.message === 'Unable to deserialize protobuf message without `root` being provided' + ) { + resolvePromise(true); + } + }), + }); + + const taskQueue = 'custom-data-converter'; + const worker = await Worker.create({ + ...defaultOptions, + taskQueue, + dataConverterPath: require.resolve('./data-converters/data-converter'), + }); + const connection = new Connection(); + const client = new WorkflowClient(connection.service, { dataConverter }); + + // For now, just check that the protobuf message gets to the workflow + worker.run(); + await client.start(protobufWorkflow, { + args: [messageInstance], + workflowId: uuid4(), + taskQueue, + }); + await receivedExpectedError; + worker.shutdown(); + t.pass(); + + // const runAndShutdown = async () => { + // const result = await client.execute(protobufWorkflow, { + // args: [messageInstance], + // workflowId: uuid4(), + // taskQueue, + // }); + // t.is(result, messageInstance); + // worker.shutdown(); + // }; + // await Promise.all([worker.run(), runAndShutdown()]); + }); +} + +test('Worker throws on invalid dataConverterPath', async (t) => { + await t.throwsAsync( + isolateFreeWorker({ + ...defaultOptions, + dataConverterPath: './wrong-path', + }), + { + message: /Could not find a file at the specified dataConverterPath/, + } + ); + + await t.throwsAsync( + isolateFreeWorker({ + ...defaultOptions, + dataConverterPath: require.resolve('./data-converters/data-converter-no-export'), + }), + { + message: /The module at dataConverterPath .* does not have a `dataConverter` named export./, + } + ); + + await t.throwsAsync( + isolateFreeWorker({ + ...defaultOptions, + dataConverterPath: require.resolve('./data-converters/data-converter-bad-export'), + }), + { + message: + /The `dataConverter` named export at dataConverterPath .* should be an instance of a class that implements the DataConverter interface./, + } + ); +}); + +test('Worker with proto data converter runs an activity and reports completion', async (t) => { + const worker = await isolateFreeWorker({ + ...defaultOptions, + dataConverterPath: require.resolve('./data-converters/data-converter'), + }); + + await runWorker(worker, async () => { + const taskToken = Buffer.from(uuid4()); + const completion = await worker.native.runActivityTask({ + taskToken, + activityId: 'abc', + start: { + activityType: 'protoActivity', + input: await dataConverter.toPayloads(messageInstance), + }, + }); + compareCompletion(t, completion.result, { + completed: { result: await dataConverter.toPayload(await protoActivity(messageInstance)) }, + }); + }); +}); diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 97d0ad9f8..1ab847bad 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -1,12 +1,20 @@ /* eslint @typescript-eslint/no-non-null-assertion: 0 */ import test from 'ava'; -import { defaultDataConverter, ValueError } from '@temporalio/common'; -import { METADATA_ENCODING_KEY, encodingKeys, u8 } from '@temporalio/common/lib/converter/types'; +import { defaultDataConverter, ValueError, DataConverterError, DefaultDataConverter } from '@temporalio/common'; +import { + METADATA_ENCODING_KEY, + METADATA_MESSAGE_TYPE_KEY, + encodingKeys, + u8, +} from '@temporalio/common/lib/converter/types'; import { UndefinedPayloadConverter, BinaryPayloadConverter, JsonPayloadConverter, + ProtobufBinaryPayloadConverter, + ProtobufJsonPayloadConverter, } from '@temporalio/common/lib/converter/payload-converter'; +import root from '../protos/root'; test('UndefinedPayloadConverter converts from undefined only', async (t) => { const converter = new UndefinedPayloadConverter(); @@ -62,11 +70,116 @@ test('JsonPayloadConverter converts to object', async (t) => { t.deepEqual(await converter.fromData((await converter.toData({ a: 1 }))!), { a: 1 }); }); +test('ProtobufBinaryPayloadConverter converts from an instance', async (t) => { + const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + const converter = new ProtobufBinaryPayloadConverter(root); + t.deepEqual(await converter.toData(instance), { + metadata: { + [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF, + [METADATA_MESSAGE_TYPE_KEY]: u8('ProtoActivityInput'), + }, + data: root.ProtoActivityInput.encode(instance).finish(), + }); +}); + +test('ProtobufBinaryPayloadConverter converts to an instance', async (t) => { + const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + const converter = new ProtobufBinaryPayloadConverter(root); + const testInstance = await converter.fromData((await converter.toData(instance))!); + // tests that both are instances of the same class with the same properties + t.deepEqual(testInstance, instance); +}); + +test('ProtobufBinaryPayloadConverter throws detailed errors', async (t) => { + const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + const converter = new ProtobufBinaryPayloadConverter(root); + + await t.throwsAsync( + async () => + await converter.fromData({ + metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF }, + data: root.ProtoActivityInput.encode(instance).finish(), + }), + { instanceOf: ValueError, message: 'Got protobuf payload without metadata.messageType' } + ); + await t.throwsAsync( + async () => + await converter.fromData({ + metadata: { + [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF, + [METADATA_MESSAGE_TYPE_KEY]: u8('NonExistentMessageClass'), + }, + data: root.ProtoActivityInput.encode(instance).finish(), + }), + { + instanceOf: DataConverterError, + message: 'Got a `NonExistentMessageClass` protobuf message but cannot find corresponding message class in `root`', + } + ); +}); + +test('ProtobufJSONPayloadConverter converts from an instance to JSON', async (t) => { + const instance = root.foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + const converter = new ProtobufJsonPayloadConverter(root); + t.deepEqual(await converter.toData(instance), { + metadata: { + [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF_JSON, + [METADATA_MESSAGE_TYPE_KEY]: u8('foo.bar.ProtoActivityInput'), + }, + data: u8(JSON.stringify(instance)), + }); +}); + +test('ProtobufJSONPayloadConverter converts to an instance from JSON', async (t) => { + const instance = root.foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + const converter = new ProtobufJsonPayloadConverter(root); + const testInstance = await converter.fromData((await converter.toData(instance))!); + // tests that both are instances of the same class with the same properties + t.deepEqual(testInstance, instance); +}); + +test('ProtobufJSONPayloadConverter converts binary', async (t) => { + // binary should be base64-encoded: + // https://developers.google.com/protocol-buffers/docs/proto3#json + const instance = root.BinaryMessage.create({ data: u8('abc') }); + const converter = new ProtobufJsonPayloadConverter(root); + const encoded = await converter.toData(instance); + t.deepEqual(encoded, { + metadata: { + [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF_JSON, + [METADATA_MESSAGE_TYPE_KEY]: u8('BinaryMessage'), + }, + data: u8(JSON.stringify({ data: Buffer.from('abc').toString('base64') })), + }); + + const testInstance = await converter.fromData(encoded!); + t.deepEqual(testInstance.data, Buffer.from(instance.data)); +}); + +test('DefaultDataConverter converts protobufs', async (t) => { + const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + const defaultDataConverterWithProtos = new DefaultDataConverter({ root }); + t.deepEqual( + await defaultDataConverterWithProtos.toPayload(instance), + // It will always use JSON because it appears before binary in the list + await new ProtobufJsonPayloadConverter(root).toData(instance) + ); +}); + test('defaultDataConverter converts to payload by trying each converter in order', async (t) => { + const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + t.deepEqual( + await defaultDataConverter.toPayload(instance), + await new ProtobufJsonPayloadConverter().toData(instance) + ); + t.deepEqual(await defaultDataConverter.toPayload('abc'), await new JsonPayloadConverter().toData('abc')); t.deepEqual(await defaultDataConverter.toPayload(undefined), await new UndefinedPayloadConverter().toData(undefined)); t.deepEqual(await defaultDataConverter.toPayload(u8('abc')), await new BinaryPayloadConverter().toData(u8('abc'))); - // TODO: test non-jsonable value + await t.throwsAsync(async () => await defaultDataConverter.toPayload(0n), { + instanceOf: TypeError, + message: 'Do not know how to serialize a BigInt', + }); }); test('defaultDataConverter converts from payload by payload type', async (t) => { @@ -83,4 +196,27 @@ test('defaultDataConverter converts from payload by payload type', async (t) => async () => await defaultDataConverter.fromPayload({ metadata: { [METADATA_ENCODING_KEY]: u8('not-supported') } }), { instanceOf: ValueError, message: 'Unknown encoding: not-supported' } ); + await t.throwsAsync( + async () => + await defaultDataConverter.fromPayload({ + metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF }, + }), + { instanceOf: ValueError, message: 'Got payload with no data' } + ); + + const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + const protoError = { + instanceOf: DataConverterError, + message: 'Unable to deserialize protobuf message without `root` being provided', + }; + await t.throwsAsync( + async () => + await defaultDataConverter.fromPayload((await new ProtobufBinaryPayloadConverter(root).toData(instance))!), + protoError + ); + await t.throwsAsync( + async () => + await defaultDataConverter.fromPayload((await new ProtobufJsonPayloadConverter(root).toData(instance))!), + protoError + ); }); diff --git a/packages/test/src/test-patch-root.ts b/packages/test/src/test-patch-root.ts new file mode 100644 index 000000000..360d121c1 --- /dev/null +++ b/packages/test/src/test-patch-root.ts @@ -0,0 +1,33 @@ +import test from 'ava'; +import { patchProtobufRoot } from '@temporalio/common'; + +test('patchRoot', (t) => { + const type = new Type(); + t.deepEqual((patchProtobufRoot({ nested: { type } }) as any).type, type); + + const bar = new Namespace({ BarMsg: new Type() }); + const root = { + nested: { + foo: new Namespace({ + Msg: new Type(), + nested: { + bar, + }, + }), + }, + }; + t.like(patchProtobufRoot(root), { + ...root, + foo: { Msg: new Type(), bar: { BarMsg: new Type() }, nested: { bar } }, + } as any); +}); + +class Namespace { + constructor(props: Record) { + for (const key in props) { + (this as any)[key] = props[key]; + } + } +} + +class Type {} diff --git a/packages/test/src/test-worker-activities.ts b/packages/test/src/test-worker-activities.ts index 90345378a..622f47ed0 100644 --- a/packages/test/src/test-worker-activities.ts +++ b/packages/test/src/test-worker-activities.ts @@ -24,7 +24,7 @@ export async function runWorker(t: ExecutionContext, fn: () => Promise< } test.beforeEach(async (t) => { - const worker = isolateFreeWorker(defaultOptions); + const worker = await isolateFreeWorker(defaultOptions); t.context = { worker, diff --git a/packages/test/src/test-worker-lifecycle.ts b/packages/test/src/test-worker-lifecycle.ts index 376cece43..8f1505de7 100644 --- a/packages/test/src/test-worker-lifecycle.ts +++ b/packages/test/src/test-worker-lifecycle.ts @@ -26,7 +26,7 @@ if (RUN_INTEGRATION_TESTS) { } test.serial('Mocked run shuts down gracefully', async (t) => { - const worker = isolateFreeWorker({ + const worker = await isolateFreeWorker({ shutdownGraceTime: '500ms', taskQueue: 'shutdown-test', }); @@ -41,7 +41,7 @@ test.serial('Mocked run shuts down gracefully', async (t) => { }); test('Mocked run throws if not shut down gracefully', async (t) => { - const worker = isolateFreeWorker({ + const worker = await isolateFreeWorker({ shutdownGraceTime: '5ms', taskQueue: 'shutdown-test', }); diff --git a/packages/test/src/test-workflows.ts b/packages/test/src/test-workflows.ts index ccbf0765c..72eee6747 100644 --- a/packages/test/src/test-workflows.ts +++ b/packages/test/src/test-workflows.ts @@ -1505,7 +1505,7 @@ test('globalOverrides', async (t) => { t.deepEqual( logs, ['WeakRef' /* First error happens on startup */, 'FinalizationRegistry', 'WeakRef'].map((type) => [ - `DeterminismViolationError: ${type} cannot be used in workflows because v8 GC is non-deterministic`, + `DeterminismViolationError: ${type} cannot be used in Workflows because v8 GC is non-deterministic`, ]) ); }); diff --git a/packages/test/src/workflows/index.ts b/packages/test/src/workflows/index.ts index 3b3ac40ba..0c9dab3c7 100644 --- a/packages/test/src/workflows/index.ts +++ b/packages/test/src/workflows/index.ts @@ -74,5 +74,6 @@ export * from './sleep-invalid-duration'; export * from './signals-are-always-processed'; export * from './async-activity-completion-tester'; export * from './unhandled-rejection'; +export * from './protobufs'; export { interceptorExample } from './interceptor-example'; export { internalsInterceptorExample } from './internals-interceptor-example'; diff --git a/packages/test/src/workflows/protobufs.ts b/packages/test/src/workflows/protobufs.ts new file mode 100644 index 000000000..66a64a5ad --- /dev/null +++ b/packages/test/src/workflows/protobufs.ts @@ -0,0 +1,5 @@ +import type { ProtoActivityInput } from '../../protos/root'; + +export async function protobufWorkflow(args: ProtoActivityInput): Promise { + return args; +} diff --git a/packages/worker/src/worker-options.ts b/packages/worker/src/worker-options.ts index f1269c80b..f8f6f5fb2 100644 --- a/packages/worker/src/worker-options.ts +++ b/packages/worker/src/worker-options.ts @@ -1,7 +1,7 @@ import os from 'os'; import fs from 'fs'; import { resolve, dirname } from 'path'; -import { ActivityInterface, DataConverter, defaultDataConverter, msToNumber } from '@temporalio/common'; +import { ActivityInterface, msToNumber } from '@temporalio/common'; import { WorkerInterceptors } from './interceptors'; import { InjectedSinks } from './sinks'; import { GiB } from './utils'; @@ -75,9 +75,9 @@ export interface WorkerOptions { shutdownSignals?: NodeJS.Signals[]; /** - * TODO: document, figure out how to propagate this to the workflow isolate + * Path of a module with a `dataConverter` named export. `dataConverter` should be an instance of a class that implements {@link DataConverter}. */ - dataConverter?: DataConverter; + dataConverterPath?: string; /** * Maximum number of Activity tasks to execute concurrently. @@ -202,7 +202,6 @@ export type WorkerOptionsWithDefaults = WorkerOptions & WorkerOptions, | 'shutdownGraceTime' | 'shutdownSignals' - | 'dataConverter' | 'maxConcurrentActivityTaskExecutions' | 'maxConcurrentWorkflowTaskExecutions' | 'maxConcurrentActivityTaskPolls' @@ -295,7 +294,6 @@ export function addDefaultWorkerOptions(options: WorkerOptions): WorkerOptionsWi (options.workflowsPath ? resolveNodeModulesPaths(fs, options.workflowsPath) : undefined), shutdownGraceTime: '5s', shutdownSignals: ['SIGINT', 'SIGTERM', 'SIGQUIT', 'SIGUSR2'], - dataConverter: defaultDataConverter, maxConcurrentActivityTaskExecutions: 100, maxConcurrentWorkflowTaskExecutions: 100, maxConcurrentActivityTaskPolls: 5, diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index f7ba0d36b..f71d40e70 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -25,7 +25,9 @@ import { arrayFromPayloads, DataConverter, defaultDataConverter, + isValidDataConverter, errorMessage, + errorCode, } from '@temporalio/common'; import { extractSpanContextFromHeaders, @@ -162,7 +164,7 @@ function formatTaskToken(taskToken: Uint8Array) { } /** - * The temporal worker connects to the service and runs workflows and activities. + * The temporal Worker connects to Temporal Server and runs Workflows and Activities. */ export class Worker { protected readonly activityHeartbeatSubject = new Subject<{ @@ -186,6 +188,7 @@ export class Worker { const nativeWorkerCtor: WorkerConstructor = this.nativeWorkerCtor; const compiledOptions = compileWorkerOptions(addDefaultWorkerOptions(options)); const nativeWorker = await nativeWorkerCtor.create(compiledOptions); + const dataConverter = await this.getDataConverter(options); try { let bundle: string | undefined = undefined; let workflowCreator: WorkflowCreator | undefined = undefined; @@ -219,7 +222,7 @@ export class Worker { }); } } - return new this(nativeWorker, workflowCreator, compiledOptions); + return new this(nativeWorker, workflowCreator, compiledOptions, dataConverter); } catch (err) { // Deregister our worker in case Worker creation (Webpack) failed await nativeWorker.completeShutdown(); @@ -227,16 +230,48 @@ export class Worker { } } + public static async getDataConverter(options: WorkerOptions): Promise { + if (options.dataConverterPath) { + let dataConverter: DataConverter; + + try { + const dataConverterModule = await import(options.dataConverterPath); + dataConverter = dataConverterModule.dataConverter; + } catch (error) { + if (errorCode(error) === 'MODULE_NOT_FOUND') { + throw new Error(`Could not find a file at the specified dataConverterPath: '${options.dataConverterPath}'.`); + } + throw error; + } + + if (dataConverter === undefined) { + throw new Error( + `The module at dataConverterPath ('${options.dataConverterPath}') does not have a \`dataConverter\` named export.` + ); + } + if (!isValidDataConverter(dataConverter)) { + throw new Error( + `The \`dataConverter\` named export at dataConverterPath (${options.dataConverterPath}) should be an instance of a class that implements the DataConverter interface.` + ); + } + + return dataConverter; + } + + return defaultDataConverter; + } + /** * Create a new Worker from nativeWorker. */ protected constructor( protected readonly nativeWorker: NativeWorkerLike, /** - * Optional WorkflowCreator - if not provided, Worker will not poll on workflows + * Optional WorkflowCreator - if not provided, Worker will not poll on Workflows */ protected readonly workflowCreator: WorkflowCreator | undefined, - public readonly options: CompiledWorkerOptions + public readonly options: CompiledWorkerOptions, + protected readonly dataConverter: DataConverter = defaultDataConverter ) { this.tracer = getTracer(options.enableSDKTracing); } @@ -380,7 +415,7 @@ export class Worker { const info = await extractActivityInfo( task, false, - this.options.dataConverter, + this.dataConverter, this.nativeWorker.namespace ); const { activityType } = info; @@ -404,7 +439,7 @@ export class Worker { } let args: unknown[]; try { - args = await arrayFromPayloads(this.options.dataConverter, task.start?.input); + args = await arrayFromPayloads(this.dataConverter, task.start?.input); } catch (err) { output = { type: 'result', @@ -436,7 +471,7 @@ export class Worker { activity = new Activity( info, fn, - this.options.dataConverter, + this.dataConverter, (details) => this.activityHeartbeatSubject.next({ taskToken, @@ -662,7 +697,7 @@ export class Worker { const completion = coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited({ runId: activation.runId, failed: { - failure: await errorToFailure(error, this.options.dataConverter), + failure: await errorToFailure(error, this.dataConverter), }, }).finish(); // We do not dispose of the Workflow yet, wait to be evicted from Core. @@ -735,7 +770,7 @@ export class Worker { complete: () => this.log.debug('Heartbeats complete'), }), mergeMap(async ({ taskToken, details }) => { - const payload = await this.options.dataConverter.toPayload(details); + const payload = await this.dataConverter.toPayload(details); const arr = coresdk.ActivityHeartbeat.encodeDelimited({ taskToken, details: [payload], diff --git a/packages/worker/src/workflow/bundler.ts b/packages/worker/src/workflow/bundler.ts index 76123c56d..80c4d1c9e 100644 --- a/packages/worker/src/workflow/bundler.ts +++ b/packages/worker/src/workflow/bundler.ts @@ -10,7 +10,6 @@ import { resolveNodeModulesPaths } from '../worker-options'; /** * Builds a V8 Isolate by bundling provided Workflows using webpack. - * Activities are replaced with stubs. * * @param nodeModulesPath node_modules path with required Workflow dependencies * @param workflowsPath all Workflows found in path will be put in the bundle @@ -107,6 +106,7 @@ export class WorkflowCodeBundler { resolve: { modules: this.nodeModulesPaths, extensions: ['.ts', '.js'], + fallback: { assert: false }, // used by `proto3-json-serializer` }, module: { rules: [ diff --git a/packages/worker/src/workflow/threaded-vm.ts b/packages/worker/src/workflow/threaded-vm.ts index 112fd2894..aa0d178af 100644 --- a/packages/worker/src/workflow/threaded-vm.ts +++ b/packages/worker/src/workflow/threaded-vm.ts @@ -1,5 +1,6 @@ /** - * Wrapper for starting VM Workflows in Worker threads. + * Wrapper for starting VM Workflows in Node Worker threads. + * https://nodejs.org/api/worker_threads.html * * Worker threads are used here because creating vm contexts is a long running * operation which blocks the Node.js event loop causing the SDK Worker to @@ -10,7 +11,7 @@ import { coresdk } from '@temporalio/proto'; import { IllegalStateError, SinkCall } from '@temporalio/workflow'; -import { Worker } from 'worker_threads'; +import { Worker as NodeWorker } from 'worker_threads'; import { UnexpectedError } from '../errors'; import { Workflow, WorkflowCreator, WorkflowCreateOptions } from './interface'; import { WorkerThreadInput, WorkerThreadRequest } from './workflow-worker-thread/input'; @@ -51,7 +52,7 @@ export class WorkerThreadClient { shutDownRequested = false; workerExited = false; - constructor(protected workerThread: Worker) { + constructor(protected workerThread: NodeWorker) { workerThread.on('message', ({ requestId, result }: WorkerThreadResponse) => { const completion = this.requestIdToCompletion.get(requestId); if (completion === undefined) { @@ -140,7 +141,7 @@ export class ThreadedVMWorkflowCreator implements WorkflowCreator { }: ThreadedVMWorkflowCreatorOptions): Promise { const workerThreadClients = Array(threadPoolSize) .fill(0) - .map(() => new WorkerThreadClient(new Worker(require.resolve('./workflow-worker-thread')))); + .map(() => new WorkerThreadClient(new NodeWorker(require.resolve('./workflow-worker-thread')))); await Promise.all( workerThreadClients.map((client) => client.send({ type: 'init', code, isolateExecutionTimeoutMs })) ); @@ -189,7 +190,7 @@ export class VMWorkflowThreadProxy implements Workflow { */ async getAndResetSinkCalls(): Promise { const output = await this.workerThreadClient.send({ - type: 'exteract-sink-calls', + type: 'extract-sink-calls', runId: this.runId, }); if (output?.type !== 'sink-calls') { diff --git a/packages/worker/src/workflow/vm.ts b/packages/worker/src/workflow/vm.ts index fa26b789a..c781d1c16 100644 --- a/packages/worker/src/workflow/vm.ts +++ b/packages/worker/src/workflow/vm.ts @@ -84,7 +84,7 @@ export class VMWorkflowCreator implements WorkflowCreator { } /** - * Cleanup the precompiled script + * Cleanup the pre-compiled script */ public async destroy(): Promise { delete this.script; diff --git a/packages/worker/src/workflow/workflow-worker-thread.ts b/packages/worker/src/workflow/workflow-worker-thread.ts index 047212105..baa4ac55b 100644 --- a/packages/worker/src/workflow/workflow-worker-thread.ts +++ b/packages/worker/src/workflow/workflow-worker-thread.ts @@ -74,7 +74,7 @@ async function handleRequest({ requestId, input }: WorkerThreadRequest): Promise result: { type: 'ok', output: { type: 'activation-completion', completion } }, }; } - case 'exteract-sink-calls': { + case 'extract-sink-calls': { const workflow = workflowByRunId.get(input.runId); if (workflow === undefined) { throw new IllegalStateError(`Tried to activate non running workflow with runId: ${input.runId}`); diff --git a/packages/worker/src/workflow/workflow-worker-thread/input.ts b/packages/worker/src/workflow/workflow-worker-thread/input.ts index 248a3643b..c2a677eb8 100644 --- a/packages/worker/src/workflow/workflow-worker-thread/input.ts +++ b/packages/worker/src/workflow/workflow-worker-thread/input.ts @@ -40,7 +40,7 @@ export interface ActivateWorkflow { * Extract buffered sink calls from Workflow by runId */ export interface ExtractSinkCalls { - type: 'exteract-sink-calls'; + type: 'extract-sink-calls'; runId: string; } diff --git a/packages/workflow/src/internals.ts b/packages/workflow/src/internals.ts index 29cd93a63..21929530d 100644 --- a/packages/workflow/src/internals.ts +++ b/packages/workflow/src/internals.ts @@ -11,7 +11,6 @@ import { arrayFromPayloadsSync, Workflow, WorkflowQueryType, - ApplicationFailure, TemporalFailure, } from '@temporalio/common'; import { checkExtends } from '@temporalio/common/lib/type-helpers'; diff --git a/packages/workflow/src/worker-interface.ts b/packages/workflow/src/worker-interface.ts index a7295c031..5ab3d176f 100644 --- a/packages/workflow/src/worker-interface.ts +++ b/packages/workflow/src/worker-interface.ts @@ -43,11 +43,11 @@ export function overrideGlobals(): void { // WeakRef is implemented in V8 8.4 which is embedded in node >=14.6.0. // Workflow developer will get a meaningful exception if they try to use these. global.WeakRef = function () { - throw new DeterminismViolationError('WeakRef cannot be used in workflows because v8 GC is non-deterministic'); + throw new DeterminismViolationError('WeakRef cannot be used in Workflows because v8 GC is non-deterministic'); }; global.FinalizationRegistry = function () { throw new DeterminismViolationError( - 'FinalizationRegistry cannot be used in workflows because v8 GC is non-deterministic' + 'FinalizationRegistry cannot be used in Workflows because v8 GC is non-deterministic' ); }; @@ -118,7 +118,7 @@ export async function initRuntime({ info, randomnessSeed, now, patches }: Workfl // Globals are overridden while building the isolate before loading user code. // For some reason the `WeakRef` mock is not restored properly when creating an isolate from snapshot in node 14 (at least on ubuntu), override again. (globalThis as any).WeakRef = function () { - throw new DeterminismViolationError('WeakRef cannot be used in workflows because v8 GC is non-deterministic'); + throw new DeterminismViolationError('WeakRef cannot be used in Workflows because v8 GC is non-deterministic'); }; state.info = info; state.now = now; From cc0d41435c75dc224496f78f8fc4a2e2a14213ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Sat, 22 Jan 2022 00:50:36 -0500 Subject: [PATCH 02/33] fix: Make node `assert` module available to webpack --- packages/test/src/test-custom-data-converter.ts | 9 ++++----- packages/worker/src/workflow/bundler.ts | 5 +++-- packages/worker/src/workflow/module-overrides/assert.ts | 1 + packages/worker/src/workflow/vm.ts | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 packages/worker/src/workflow/module-overrides/assert.ts diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-data-converter.ts index e469d3d90..f4572c985 100644 --- a/packages/test/src/test-custom-data-converter.ts +++ b/packages/test/src/test-custom-data-converter.ts @@ -18,16 +18,16 @@ export async function runWorker(worker: Worker, fn: () => Promise): Promise function compareCompletion( t: ExecutionContext, - actual: coresdk.activity_result.IActivityResult | null | undefined, - expected: coresdk.activity_result.IActivityResult + actual: coresdk.activity_result.IActivityExecutionResult | null | undefined, + expected: coresdk.activity_result.IActivityExecutionResult ) { if (actual?.failed?.failure) { const { stackTrace, ...rest } = actual.failed.failure; actual = { failed: { failure: { stackTrace: cleanStackTrace(stackTrace), ...rest } } }; } t.deepEqual( - new coresdk.activity_result.ActivityResult(actual ?? undefined).toJSON(), - new coresdk.activity_result.ActivityResult(expected).toJSON() + new coresdk.activity_result.ActivityExecutionResult(actual ?? undefined).toJSON(), + new coresdk.activity_result.ActivityExecutionResult(expected).toJSON() ); } @@ -126,7 +126,6 @@ test('Worker with proto data converter runs an activity and reports completion', const taskToken = Buffer.from(uuid4()); const completion = await worker.native.runActivityTask({ taskToken, - activityId: 'abc', start: { activityType: 'protoActivity', input: await dataConverter.toPayloads(messageInstance), diff --git a/packages/worker/src/workflow/bundler.ts b/packages/worker/src/workflow/bundler.ts index 80c4d1c9e..4102fb53a 100644 --- a/packages/worker/src/workflow/bundler.ts +++ b/packages/worker/src/workflow/bundler.ts @@ -104,9 +104,8 @@ export class WorkflowCodeBundler { protected async bundle(filesystem: typeof unionfs.ufs, entry: string, distDir: string): Promise { const compiler = webpack({ resolve: { - modules: this.nodeModulesPaths, + modules: [path.resolve(__dirname, 'module-overrides'), ...this.nodeModulesPaths], extensions: ['.ts', '.js'], - fallback: { assert: false }, // used by `proto3-json-serializer` }, module: { rules: [ @@ -137,6 +136,8 @@ export class WorkflowCodeBundler { compiler.run((err, stats) => { if (stats !== undefined) { const hasError = stats.hasErrors(); + // To debug webpack build: + // const lines = stats.toString({ preset: 'verbose' }).split('\n'); const lines = stats.toString({ chunks: false, colors: true }).split('\n'); for (const line of lines) { this.logger[hasError ? 'error' : 'info'](line); diff --git a/packages/worker/src/workflow/module-overrides/assert.ts b/packages/worker/src/workflow/module-overrides/assert.ts new file mode 100644 index 000000000..fa55754a0 --- /dev/null +++ b/packages/worker/src/workflow/module-overrides/assert.ts @@ -0,0 +1 @@ +export default (global as any).assert; diff --git a/packages/worker/src/workflow/vm.ts b/packages/worker/src/workflow/vm.ts index c781d1c16..c825b7273 100644 --- a/packages/worker/src/workflow/vm.ts +++ b/packages/worker/src/workflow/vm.ts @@ -7,6 +7,7 @@ import { partition } from '../utils'; import { Workflow, WorkflowCreator, WorkflowCreateOptions } from './interface'; import { SinkCall } from '@temporalio/workflow/lib/sinks'; import { AsyncLocalStorage } from 'async_hooks'; +import assert from 'assert'; /** * A WorkflowCreator that creates VMWorkflows in the current isolate @@ -49,7 +50,7 @@ export class VMWorkflowCreator implements WorkflowCreator { if (this.script === undefined) { throw new IllegalStateError('Isolate context provider was destroyed'); } - const context = vm.createContext({ AsyncLocalStorage }); + const context = vm.createContext({ AsyncLocalStorage, assert }); this.script.runInContext(context); return context; } From 158b3bbe0d2aa82efec515d20c68bf63a34fa0e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Tue, 7 Dec 2021 18:13:40 -0500 Subject: [PATCH 03/33] test(worker): Add failing proto integration test --- .../test/src/test-custom-data-converter.ts | 55 +++++-------------- packages/worker/src/worker.ts | 6 ++ 2 files changed, 20 insertions(+), 41 deletions(-) diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-data-converter.ts index f4572c985..7e38c378e 100644 --- a/packages/test/src/test-custom-data-converter.ts +++ b/packages/test/src/test-custom-data-converter.ts @@ -1,6 +1,6 @@ import { Connection, WorkflowClient } from '@temporalio/client'; import { coresdk } from '@temporalio/proto'; -import { Core, DefaultLogger, Worker } from '@temporalio/worker'; +import { Worker } from '@temporalio/worker'; import test, { ExecutionContext } from 'ava'; import { v4 as uuid4 } from 'uuid'; import { protoActivity } from './activities'; @@ -32,54 +32,27 @@ function compareCompletion( } if (RUN_INTEGRATION_TESTS) { - test('Client and Worker work with provided dataConverter/dataConverterPath', async (t) => { - let resolvePromise: (value: unknown) => void; - const receivedExpectedError = new Promise(function (resolve) { - resolvePromise = resolve; - }); - - await Core.install({ - logger: new DefaultLogger('ERROR', (entry) => { - if ( - entry.message === 'Failed to activate workflow' && - entry.meta?.error?.stack?.includes('Activator.startWorkflow') && - entry.meta?.error?.message === 'Unable to deserialize protobuf message without `root` being provided' - ) { - resolvePromise(true); - } - }), - }); - + test.skip('Client and Worker work with provided dataConverter/dataConverterPath', async (t) => { const taskQueue = 'custom-data-converter'; const worker = await Worker.create({ ...defaultOptions, taskQueue, - dataConverterPath: require.resolve('./data-converters/data-converter'), + + dataConverterPath: require.resolve('./data-converter'), }); const connection = new Connection(); const client = new WorkflowClient(connection.service, { dataConverter }); - - // For now, just check that the protobuf message gets to the workflow - worker.run(); - await client.start(protobufWorkflow, { - args: [messageInstance], - workflowId: uuid4(), - taskQueue, - }); - await receivedExpectedError; - worker.shutdown(); + const runAndShutdown = async () => { + const result = await client.execute(protobufWorkflow, { + args: [messageInstance], + workflowId: uuid4(), + taskQueue, + }); + t.is(result, messageInstance); + worker.shutdown(); + }; + await Promise.all([worker.run(), runAndShutdown()]); t.pass(); - - // const runAndShutdown = async () => { - // const result = await client.execute(protobufWorkflow, { - // args: [messageInstance], - // workflowId: uuid4(), - // taskQueue, - // }); - // t.is(result, messageInstance); - // worker.shutdown(); - // }; - // await Promise.all([worker.run(), runAndShutdown()]); }); } diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index f71d40e70..ee287a852 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -190,6 +190,12 @@ export class Worker { const nativeWorker = await nativeWorkerCtor.create(compiledOptions); const dataConverter = await this.getDataConverter(options); try { + let dataConverter; + if (options.dataConverterPath) { + const dataConverterModule = await import(options.dataConverterPath); + dataConverter = dataConverterModule.dataConverter; + } + let bundle: string | undefined = undefined; let workflowCreator: WorkflowCreator | undefined = undefined; // nodeModulesPaths should not be undefined if workflowsPath is provided From dcf79d47d83b798ec512ec7994b16c34f2b7400d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Sun, 23 Jan 2022 19:22:03 -0500 Subject: [PATCH 04/33] feat: Use custom data converter in Workflow --- docs/data-converter.md | 57 +++++++++++++++++++ docs/protobuf-libraries.md | 12 +++- .../test/src/test-custom-data-converter.ts | 8 +-- packages/test/src/test-worker-lifecycle.ts | 6 +- packages/test/src/workflows/data-converter.ts | 6 ++ packages/worker/src/rxutils.ts | 2 +- packages/worker/src/worker.ts | 24 ++++---- packages/worker/src/workflow/bundler.ts | 35 +++++++++--- .../src/workflow/module-overrides/assert.ts | 3 +- packages/worker/src/workflow/threaded-vm.ts | 8 ++- packages/worker/src/workflow/vm.ts | 28 ++++++--- .../src/workflow/workflow-worker-thread.ts | 6 +- .../workflow/workflow-worker-thread/input.ts | 1 + packages/workflow/src/worker-interface.ts | 16 +++++- 14 files changed, 167 insertions(+), 45 deletions(-) create mode 100644 docs/data-converter.md create mode 100644 packages/test/src/workflows/data-converter.ts diff --git a/docs/data-converter.md b/docs/data-converter.md new file mode 100644 index 000000000..ae03a0d63 --- /dev/null +++ b/docs/data-converter.md @@ -0,0 +1,57 @@ +When designing the custom data converter feature, we considered two routes: + +- Doing conversion outside of Workflow vm +- Doing conversion inside and outside of Workflow vm + +### Outside vm + +- Pro: Users can use any node module in their custom data converter code. +- Con: Only works because `vm` allows for passing complex objects into/out of vm. If we switch to an isolation method like `isolated-vm` or `rusty_v8`, conversion needs to be done inside. +- Con: `object instanceof Class` doesn't work on object that come from the vm, because the `Class` definition inside the vm is from a different instance of the code. A workaround like this must be used: + +```ts +function workflowInclusiveInstanceOf(instance: unknown, type: Function): boolean { + let proto = Object.getPrototypeOf(instance); + while (proto) { + if (proto.constructor?.toString() === type.toString()) return true; + proto = Object.getPrototypeOf(proto); + } + return false; +} +``` + +## Decision + +Given the possibility of switching or adding other isolation methods in future, we opted for inside the vm. We'll also have another data transformer / payload interceptor layer that runs outside the vm, can use node modules, and operates on Payloads. + +### General flow + +When `WorkerOptions.dataConverterPath` is provided, the code at that location is loaded into the main thread, the worker threads, and the webpack Workflow bundle. + +### Specific flow + +Worker main thread: + +- imports and validates `dataConverterPath` +- passes `dataConverterPath` to either `ThreadedVMWorkflowCreator.create` or `VMWorkflowCreator.create` +- passes `dataConverterPath` to `WorkflowCodeBundler` + +`ThreadedVMWorkflowCreator.create`: + +- sends `dataConverterPath` to each worker thread +- thread sends `dataConverterPath` to VMWorkflowCreator.create + +`VMWorkflowCreator.create`: +(This is usually running in a worker thread, but in debug mode is running in the main thread) + +- imports `dataConverterPath` +- passes `dataConverterPath` to `VMWorkflowCreator` constructor + +`VMWorkflowCreator.createWorkflow`: + +- passes `useCustomDataConverter` to `initRuntime` inside Workflow vm + +`worker-interface.ts#initRuntime`: +(Inside vm) + +- if `useCustomDataConverter`, imports `__temporal_custom_data_converter` and sets `state.dataConverter` diff --git a/docs/protobuf-libraries.md b/docs/protobuf-libraries.md index dd2d0525f..cafa8bca2 100644 --- a/docs/protobuf-libraries.md +++ b/docs/protobuf-libraries.md @@ -51,7 +51,7 @@ A and B - Use `protobufjs` with `proto3-json-serializer` - Have users use runtime-loaded messages (not generated classes) and `Class.create` (not `new Class()`) -- Patch `json-module` output (which adds `nested` attributes to lowercase namespaces [which causes TS error](https://github.com/protobufjs/protobuf.js/issues/1014)) +- Patch `json-module` output (which adds `nested` attributes to lowercase namespaces [which causes a TS error](https://github.com/protobufjs/protobuf.js/issues/1014)) ```ts // json-module.js generated with: @@ -69,7 +69,7 @@ module.exports = patchProtobufRoot(unpatchedRoot); import { foo } from '../protos/root'; await client.start(protoWorkflow, { - args: [foo.bar.ProtoInput.create({ name: 'Proto', age: 1 })], + args: [foo.bar.ProtoInput.create({ name: 'Proto', age: 1 })], // can't use `new foo.bar.ProtoInput()` taskQueue: 'tutorial', workflowId: 'my-business-id', }); @@ -80,11 +80,17 @@ import { foo } from '../protos/root'; export async function protoWorkflow(input: foo.bar.ProtoInput): Promise { return foo.bar.ProtoResult.create({ sentence: `Name is ${input.name}` }); } + +// src/data-converter.ts +import { DefaultDataConverter } from '@temporalio/common'; +import root from '../protos/root'; + +export const dataConverter = new DefaultDataConverter({ root }); ``` We originally were thinking of this, but the namespaces in `json-module.js` get lost through `patchProtobufRoot()`: -``` +```ts import * as generatedRoot from '../protos/json-module'; const patchProtobufRoot = (x: T): T => x; diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-data-converter.ts index 7e38c378e..9b6510c7f 100644 --- a/packages/test/src/test-custom-data-converter.ts +++ b/packages/test/src/test-custom-data-converter.ts @@ -32,13 +32,12 @@ function compareCompletion( } if (RUN_INTEGRATION_TESTS) { - test.skip('Client and Worker work with provided dataConverter/dataConverterPath', async (t) => { + test('Client and Worker work with provided dataConverter/dataConverterPath', async (t) => { const taskQueue = 'custom-data-converter'; const worker = await Worker.create({ ...defaultOptions, taskQueue, - - dataConverterPath: require.resolve('./data-converter'), + dataConverterPath: require.resolve('./data-converters/data-converter'), }); const connection = new Connection(); const client = new WorkflowClient(connection.service, { dataConverter }); @@ -48,11 +47,10 @@ if (RUN_INTEGRATION_TESTS) { workflowId: uuid4(), taskQueue, }); - t.is(result, messageInstance); + t.deepEqual(result, messageInstance); worker.shutdown(); }; await Promise.all([worker.run(), runAndShutdown()]); - t.pass(); }); } diff --git a/packages/test/src/test-worker-lifecycle.ts b/packages/test/src/test-worker-lifecycle.ts index 8f1505de7..5b46701b4 100644 --- a/packages/test/src/test-worker-lifecycle.ts +++ b/packages/test/src/test-worker-lifecycle.ts @@ -21,7 +21,7 @@ if (RUN_INTEGRATION_TESTS) { t.is(worker.getState(), 'STOPPING'); await p; t.is(worker.getState(), 'STOPPED'); - await t.throwsAsync(worker.run(), { message: 'Poller was aleady started' }); + await t.throwsAsync(worker.run(), { message: 'Poller was already started' }); }); } @@ -37,7 +37,7 @@ test.serial('Mocked run shuts down gracefully', async (t) => { t.is(worker.getState(), 'STOPPING'); await p; t.is(worker.getState(), 'STOPPED'); - await t.throwsAsync(worker.run(), { message: 'Poller was aleady started' }); + await t.throwsAsync(worker.run(), { message: 'Poller was already started' }); }); test('Mocked run throws if not shut down gracefully', async (t) => { @@ -55,5 +55,5 @@ test('Mocked run throws if not shut down gracefully', async (t) => { message: 'Timed out while waiting for worker to shutdown gracefully', }); t.is(worker.getState(), 'FAILED'); - await t.throwsAsync(worker.run(), { message: 'Poller was aleady started' }); + await t.throwsAsync(worker.run(), { message: 'Poller was already started' }); }); diff --git a/packages/test/src/workflows/data-converter.ts b/packages/test/src/workflows/data-converter.ts new file mode 100644 index 000000000..28f089f25 --- /dev/null +++ b/packages/test/src/workflows/data-converter.ts @@ -0,0 +1,6 @@ +import { DefaultDataConverter } from '@temporalio/common'; +import root, { foo } from '../../protos/root'; + +export const messageInstance = foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); + +export const dataConverter = new DefaultDataConverter({ root }); diff --git a/packages/worker/src/rxutils.ts b/packages/worker/src/rxutils.ts index c663cd7ac..e966d2adc 100644 --- a/packages/worker/src/rxutils.ts +++ b/packages/worker/src/rxutils.ts @@ -1,5 +1,5 @@ import { GroupedObservable, OperatorFunction, ObservableInput, pipe, Subject } from 'rxjs'; -import { groupBy, map, scan, mergeScan } from 'rxjs/operators'; +import { groupBy, map, mergeScan } from 'rxjs/operators'; interface StateAndOptionalOutput { state: T; diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index ee287a852..95c2933cb 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -190,12 +190,6 @@ export class Worker { const nativeWorker = await nativeWorkerCtor.create(compiledOptions); const dataConverter = await this.getDataConverter(options); try { - let dataConverter; - if (options.dataConverterPath) { - const dataConverterModule = await import(options.dataConverterPath); - dataConverter = dataConverterModule.dataConverter; - } - let bundle: string | undefined = undefined; let workflowCreator: WorkflowCreator | undefined = undefined; // nodeModulesPaths should not be undefined if workflowsPath is provided @@ -204,7 +198,8 @@ export class Worker { nativeWorker.logger, compiledOptions.nodeModulesPaths, compiledOptions.workflowsPath, - compiledOptions.interceptors?.workflowModules + compiledOptions.interceptors?.workflowModules, + options.dataConverterPath ); bundle = await bundler.createBundle(); nativeWorker.logger.info('Workflow bundle created', { size: `${toMB(bundle.length)}MB` }); @@ -219,12 +214,17 @@ export class Worker { } if (bundle) { if (compiledOptions.debugMode) { - workflowCreator = await VMWorkflowCreator.create(bundle, compiledOptions.isolateExecutionTimeoutMs); + workflowCreator = await VMWorkflowCreator.create( + bundle, + compiledOptions.isolateExecutionTimeoutMs, + options.dataConverterPath + ); } else { workflowCreator = await ThreadedVMWorkflowCreator.create({ code: bundle, threadPoolSize: compiledOptions.workflowThreadPoolSize, isolateExecutionTimeoutMs: compiledOptions.isolateExecutionTimeoutMs, + dataConverterPath: options.dataConverterPath, }); } } @@ -537,7 +537,7 @@ export class Worker { } /** - * Process workflow activations + * Process Workflow activations */ protected workflowOperator(): OperatorFunction> { const { workflowCreator } = this; @@ -743,7 +743,7 @@ export class Worker { externalCalls.map(async ({ ifaceName, fnName, args }) => { const dep = sinks?.[ifaceName]?.[fnName]; if (dep === undefined) { - this.log.error('Workflow referenced an unregistrered external sink', { + this.log.error('Workflow referenced an unregistered external sink', { ifaceName, fnName, }); @@ -905,7 +905,7 @@ export class Worker { protected activity$(): Observable { // Note that we poll on activities even if there are no activities registered. - // This is so workflows invoking activities on this task queue get a non-retriable error. + // This is so workflows invoking activities on this task queue get a non-retryable error. return this.activityPoll$().pipe( this.activityOperator(), mergeMap(async ({ completion, parentSpan }) => { @@ -949,7 +949,7 @@ export class Worker { */ async run(): Promise { if (this.state !== 'INITIALIZED') { - throw new IllegalStateError('Poller was aleady started'); + throw new IllegalStateError('Poller was already started'); } this.state = 'RUNNING'; diff --git a/packages/worker/src/workflow/bundler.ts b/packages/worker/src/workflow/bundler.ts index 4102fb53a..9b246a7c2 100644 --- a/packages/worker/src/workflow/bundler.ts +++ b/packages/worker/src/workflow/bundler.ts @@ -20,7 +20,8 @@ export class WorkflowCodeBundler { public readonly logger: Logger, public readonly nodeModulesPaths: string[], public readonly workflowsPath: string, - public readonly workflowInterceptorModules: string[] = [] + public readonly workflowInterceptorModules: string[] = [], + protected readonly dataConverterPath?: string ) {} /** @@ -58,7 +59,7 @@ export class WorkflowCodeBundler { const entrypointPath = '/src/main.js'; this.genEntrypoint(vol, entrypointPath); - await this.bundle(ufs, entrypointPath, distDir); + await this.bundle(ufs, entrypointPath, distDir, this.dataConverterPath); return ufs.readFileSync(path.join(distDir, 'main.js'), 'utf8'); } @@ -74,7 +75,7 @@ export class WorkflowCodeBundler { const code = dedent` import * as api from '@temporalio/workflow/lib/worker-interface.js'; - + // Bundle all Workflows and interceptor modules for lazy evaluation api.overrideGlobals(); api.setImportFuncs({ @@ -101,11 +102,18 @@ export class WorkflowCodeBundler { /** * Run webpack */ - protected async bundle(filesystem: typeof unionfs.ufs, entry: string, distDir: string): Promise { - const compiler = webpack({ + protected async bundle( + filesystem: typeof unionfs.ufs, + entry: string, + distDir: string, + dataConverterPath?: string + ): Promise { + const webpackConfig: webpack.Configuration = { resolve: { modules: [path.resolve(__dirname, 'module-overrides'), ...this.nodeModulesPaths], extensions: ['.ts', '.js'], + // If we don't set an alias for this below, then it won't be imported, so webpack can safely ignore it + fallback: { __temporal_custom_data_converter: false }, }, module: { rules: [ @@ -125,7 +133,12 @@ export class WorkflowCodeBundler { filename: 'main.js', library: '__TEMPORAL__', }, - }); + }; + if (dataConverterPath && webpackConfig.resolve) { + webpackConfig.resolve.alias = { __temporal_custom_data_converter$: dataConverterPath }; + } + + const compiler = webpack(webpackConfig); // Cast to any because the type declarations are inaccurate compiler.inputFileSystem = filesystem as any; @@ -138,7 +151,7 @@ export class WorkflowCodeBundler { const hasError = stats.hasErrors(); // To debug webpack build: // const lines = stats.toString({ preset: 'verbose' }).split('\n'); - const lines = stats.toString({ chunks: false, colors: true }).split('\n'); + const lines = stats.toString({ chunks: false, colors: true, errorDetails: true }).split('\n'); for (const line of lines) { this.logger[hasError ? 'error' : 'info'](line); } @@ -186,6 +199,11 @@ export interface BundleOptions { * Optional logger for logging Webpack output */ logger?: Logger; + /** + * Path to a module with a `dataConverter` named export. + * `dataConverter` should be an instance of a class that extends `DataConverter`. + */ + dataConverterPath?: string; } export async function bundleWorkflowCode(options: BundleOptions): Promise<{ code: string }> { @@ -197,7 +215,8 @@ export async function bundleWorkflowCode(options: BundleOptions): Promise<{ code logger, nodeModulesPaths, options.workflowsPath, - options.workflowInterceptorModules + options.workflowInterceptorModules, + options.dataConverterPath ); return { code: await bundler.createBundle() }; } diff --git a/packages/worker/src/workflow/module-overrides/assert.ts b/packages/worker/src/workflow/module-overrides/assert.ts index fa55754a0..3ae95e3a5 100644 --- a/packages/worker/src/workflow/module-overrides/assert.ts +++ b/packages/worker/src/workflow/module-overrides/assert.ts @@ -1 +1,2 @@ -export default (global as any).assert; +// Don't use `export default` because then `require('assert')` will be `{ default: assertFn }`. It needs to be `assertFn`. +module.exports = (global as any).assert; diff --git a/packages/worker/src/workflow/threaded-vm.ts b/packages/worker/src/workflow/threaded-vm.ts index aa0d178af..b23f0c8cf 100644 --- a/packages/worker/src/workflow/threaded-vm.ts +++ b/packages/worker/src/workflow/threaded-vm.ts @@ -121,6 +121,7 @@ export interface ThreadedVMWorkflowCreatorOptions { code: string; threadPoolSize: number; isolateExecutionTimeoutMs: number; + dataConverterPath?: string; } /** @@ -130,7 +131,7 @@ export class ThreadedVMWorkflowCreator implements WorkflowCreator { protected workflowThreadIdx = 0; /** - * Create an instance of ThreadedVMWorkflowCreator asynchronouly. + * Create an instance of ThreadedVMWorkflowCreator asynchronously. * * This method creates and initializes the workflow-worker-thread instances. */ @@ -138,12 +139,15 @@ export class ThreadedVMWorkflowCreator implements WorkflowCreator { threadPoolSize, code, isolateExecutionTimeoutMs, + dataConverterPath, }: ThreadedVMWorkflowCreatorOptions): Promise { const workerThreadClients = Array(threadPoolSize) .fill(0) .map(() => new WorkerThreadClient(new NodeWorker(require.resolve('./workflow-worker-thread')))); await Promise.all( - workerThreadClients.map((client) => client.send({ type: 'init', code, isolateExecutionTimeoutMs })) + workerThreadClients.map((client) => + client.send({ type: 'init', code, isolateExecutionTimeoutMs, dataConverterPath }) + ) ); return new this(workerThreadClients); } diff --git a/packages/worker/src/workflow/vm.ts b/packages/worker/src/workflow/vm.ts index c825b7273..f7b2612b3 100644 --- a/packages/worker/src/workflow/vm.ts +++ b/packages/worker/src/workflow/vm.ts @@ -2,7 +2,7 @@ import vm from 'vm'; import { coresdk } from '@temporalio/proto'; import * as internals from '@temporalio/workflow/lib/worker-interface'; import { WorkflowInfo } from '@temporalio/workflow'; -import { defaultDataConverter, errorToFailure, IllegalStateError } from '@temporalio/common'; +import { defaultDataConverter, DataConverter, errorToFailure, IllegalStateError } from '@temporalio/common'; import { partition } from '../utils'; import { Workflow, WorkflowCreator, WorkflowCreateOptions } from './interface'; import { SinkCall } from '@temporalio/workflow/lib/sinks'; @@ -15,7 +15,12 @@ import assert from 'assert'; export class VMWorkflowCreator implements WorkflowCreator { script?: vm.Script; - constructor(script: vm.Script, public readonly isolateExecutionTimeoutMs: number) { + constructor( + script: vm.Script, + public readonly isolateExecutionTimeoutMs: number, + protected readonly dataConverter: DataConverter, + protected readonly dataConverterPath?: string + ) { this.script = script; } @@ -41,9 +46,9 @@ export class VMWorkflowCreator implements WorkflowCreator { } ) as any; - await workflowModule.initRuntime(options); + await workflowModule.initRuntime({ useCustomDataConverter: !!this.dataConverterPath, ...options }); - return new VMWorkflow(options.info, context, workflowModule, isolateExecutionTimeoutMs); + return new VMWorkflow(options.info, context, workflowModule, isolateExecutionTimeoutMs, this.dataConverter); } protected async getContext(): Promise { @@ -78,10 +83,16 @@ export class VMWorkflowCreator implements WorkflowCreator { public static async create( this: T, code: string, - isolateExecutionTimeoutMs: number + isolateExecutionTimeoutMs: number, + dataConverterPath?: string ): Promise> { const script = new vm.Script(code, { filename: 'workflow-isolate' }); - return new this(script, isolateExecutionTimeoutMs) as InstanceType; + let dataConverter = defaultDataConverter; + if (dataConverterPath) { + // dataConverterPath was validated in Worker.getDataConverter + dataConverter = (await import(dataConverterPath)).dataConverter; + } + return new this(script, isolateExecutionTimeoutMs, dataConverter, dataConverterPath) as InstanceType; } /** @@ -104,7 +115,8 @@ export class VMWorkflow implements Workflow { public readonly info: WorkflowInfo, protected context: vm.Context | undefined, readonly workflowModule: WorkflowModule, - public readonly isolateExecutionTimeoutMs: number + public readonly isolateExecutionTimeoutMs: number, + protected readonly dataConverter: DataConverter ) {} /** @@ -175,7 +187,7 @@ export class VMWorkflow implements Workflow { if (this.unhandledRejection) { return coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited({ runId: activation.runId, - failed: { failure: await errorToFailure(this.unhandledRejection, defaultDataConverter) }, + failed: { failure: await errorToFailure(this.unhandledRejection, this.dataConverter) }, }).finish(); } return coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited(completion).finish(); diff --git a/packages/worker/src/workflow/workflow-worker-thread.ts b/packages/worker/src/workflow/workflow-worker-thread.ts index baa4ac55b..1fb2863c0 100644 --- a/packages/worker/src/workflow/workflow-worker-thread.ts +++ b/packages/worker/src/workflow/workflow-worker-thread.ts @@ -49,7 +49,11 @@ process.on('unhandledRejection', (err, promise) => { async function handleRequest({ requestId, input }: WorkerThreadRequest): Promise { switch (input.type) { case 'init': - workflowCreator = await VMWorkflowCreator.create(input.code, input.isolateExecutionTimeoutMs); + workflowCreator = await VMWorkflowCreator.create( + input.code, + input.isolateExecutionTimeoutMs, + input.dataConverterPath + ); return ok(requestId); case 'destroy': await workflowCreator?.destroy(); diff --git a/packages/worker/src/workflow/workflow-worker-thread/input.ts b/packages/worker/src/workflow/workflow-worker-thread/input.ts index c2a677eb8..343c3c88b 100644 --- a/packages/worker/src/workflow/workflow-worker-thread/input.ts +++ b/packages/worker/src/workflow/workflow-worker-thread/input.ts @@ -7,6 +7,7 @@ export interface Init { type: 'init'; isolateExecutionTimeoutMs: number; code: string; + dataConverterPath?: string; } /** diff --git a/packages/workflow/src/worker-interface.ts b/packages/workflow/src/worker-interface.ts index 5ab3d176f..a913fb769 100644 --- a/packages/workflow/src/worker-interface.ts +++ b/packages/workflow/src/worker-interface.ts @@ -26,6 +26,7 @@ export interface WorkflowCreateOptions { randomnessSeed: number[]; now: number; patches: string[]; + useCustomDataConverter?: boolean; } export interface ImportFunctions { @@ -110,7 +111,13 @@ export function overrideGlobals(): void { * * Sets required internal state and instantiates the workflow and interceptors. */ -export async function initRuntime({ info, randomnessSeed, now, patches }: WorkflowCreateOptions): Promise { +export async function initRuntime({ + info, + randomnessSeed, + now, + patches, + useCustomDataConverter, +}: WorkflowCreateOptions): Promise { // Set the runId globally on the context so it can be retrieved in the case // of an unhandled promise rejection. (globalThis as any).__TEMPORAL__.runId = info.runId; @@ -148,6 +155,13 @@ export async function initRuntime({ info, randomnessSeed, now, patches }: Workfl } } + if (useCustomDataConverter) { + // @ts-expect-error this is a webpack alias to dataConverterPath + state.dataConverter = (await import('__temporal_custom_data_converter')).dataConverter; + // webpack doesn't know what to bundle given a dynamic import expression, so we can't do: + // state.dataConverter = (await import(dataConverterPath)).dataConverter; + } + let workflow: Workflow; try { const mod = await importWorkflows(); From 629abe57886d85804b2c772887670b85eabe4fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Thu, 27 Jan 2022 23:46:25 -0500 Subject: [PATCH 05/33] chore: Create workflow-common package, fixes #434 --- docs/sdk-structure.md | 5 ++-- packages/activity/README.md | 8 ++--- packages/activity/tsconfig.json | 3 +- packages/client/README.md | 8 ++--- packages/client/tsconfig.json | 3 +- packages/common/README.md | 17 ++++------- packages/common/package.json | 7 +---- packages/common/src/index.ts | 13 ++------- packages/common/src/otel.ts | 4 +-- .../helpers.ts => patch-protobuf-root.ts} | 2 +- packages/common/tsconfig.json | 3 +- packages/core-bridge/README.md | 4 +-- packages/create-project/README.md | 4 +-- packages/create-project/tsconfig.json | 3 +- packages/interceptors-opentelemetry/README.md | 5 ++-- .../interceptors-opentelemetry/tsconfig.json | 3 +- packages/meta/tsconfig.json | 3 +- packages/proto/README.md | 17 ++++------- packages/test/package.json | 1 + packages/test/src/test-data-converter.ts | 4 +-- packages/test/tsconfig.json | 3 +- packages/worker/README.md | 8 ++--- packages/worker/src/core.ts | 2 +- packages/worker/tsconfig.json | 3 +- packages/workflow-common/README.md | 12 ++++++++ packages/workflow-common/package.json | 29 +++++++++++++++++++ .../src/activity-options.ts | 0 .../src/converter/data-converter.ts | 0 .../src/converter}/encoding.ts | 0 .../src/converter/payload-converter.ts | 0 .../src/converter/types.ts | 2 +- .../{common => workflow-common}/src/errors.ts | 0 .../src/failure.ts | 0 packages/workflow-common/src/index.ts | 15 ++++++++++ .../src/interceptors.ts | 0 .../src/interfaces.ts | 0 .../src/retry-policy.ts | 0 .../{common => workflow-common}/src/time.ts | 0 .../src/type-helpers.ts | 0 .../src/workflow-handle.ts | 0 .../src/workflow-options.ts | 0 packages/workflow-common/tsconfig.json | 8 +++++ packages/workflow/README.md | 8 ++--- packages/workflow/package.json | 22 +++++++------- packages/workflow/src/cancellation-scope.ts | 2 +- packages/workflow/src/errors.ts | 2 +- packages/workflow/src/index.ts | 2 +- packages/workflow/src/interceptors.ts | 2 +- packages/workflow/src/interfaces.ts | 4 +-- packages/workflow/src/internals.ts | 4 +-- packages/workflow/src/worker-interface.ts | 2 +- packages/workflow/src/workflow-handle.ts | 2 +- packages/workflow/src/workflow.ts | 2 +- packages/workflow/tsconfig.json | 5 ++-- tsconfig.prune.json | 1 + 55 files changed, 146 insertions(+), 111 deletions(-) rename packages/common/src/{converter/helpers.ts => patch-protobuf-root.ts} (94%) create mode 100644 packages/workflow-common/README.md create mode 100644 packages/workflow-common/package.json rename packages/{common => workflow-common}/src/activity-options.ts (100%) rename packages/{common => workflow-common}/src/converter/data-converter.ts (100%) rename packages/{common/src => workflow-common/src/converter}/encoding.ts (100%) rename packages/{common => workflow-common}/src/converter/payload-converter.ts (100%) rename packages/{common => workflow-common}/src/converter/types.ts (95%) rename packages/{common => workflow-common}/src/errors.ts (100%) rename packages/{common => workflow-common}/src/failure.ts (100%) create mode 100644 packages/workflow-common/src/index.ts rename packages/{common => workflow-common}/src/interceptors.ts (100%) rename packages/{common => workflow-common}/src/interfaces.ts (100%) rename packages/{common => workflow-common}/src/retry-policy.ts (100%) rename packages/{common => workflow-common}/src/time.ts (100%) rename packages/{common => workflow-common}/src/type-helpers.ts (100%) rename packages/{common => workflow-common}/src/workflow-handle.ts (100%) rename packages/{common => workflow-common}/src/workflow-options.ts (100%) create mode 100644 packages/workflow-common/tsconfig.json diff --git a/docs/sdk-structure.md b/docs/sdk-structure.md index 5ca06e02a..2c1c3dab9 100644 --- a/docs/sdk-structure.md +++ b/docs/sdk-structure.md @@ -1,6 +1,6 @@ # SDK Structure -The TypeScript SDK is developed in a mono-repo consisting of several packages managed with [`lerna`](https://lerna.js.org/), the public packages are listed below. +The TypeScript SDK is developed in a monorepo consisting of several packages managed with [`lerna`](https://lerna.js.org/). The public packages are: - [`temporalio`](../packages/meta) - Meta package, bundles the common packages for ease of installation. - [`@temporalio/worker`](../packages/worker) - Communicates with the Temporal service and runs workflows and activities @@ -8,7 +8,8 @@ The TypeScript SDK is developed in a mono-repo consisting of several packages ma - [`@temporalio/activity`](../packages/activity) - Access to current activity context - [`@temporalio/client`](../packages/client) - Communicate with the Temporal service for things like administration and scheduling workflows - [`@temporalio/proto`](../packages/proto) - Compiled protobuf definitions -- [`@temporalio/common`](../packages/common) - Common package for other SDK packages +- [`@temporalio/workflow-common`](../packages/workflow-common) - Code shared between `@temporalio/workflow` and other packages +- [`@temporalio/common`](../packages/common) - All shared code (re-exports everything in `@temporalio/workflow-common`, and also has code shared between packages other than `@temporalio/workflow`) - [`@temporalio/create`](../packages/create-project) - NPM package initializer [Repo visualization](https://octo-repo-visualization.vercel.app/?repo=temporalio%2Fsdk-typescript) diff --git a/packages/activity/README.md b/packages/activity/README.md index 094126a57..7a94114a5 100644 --- a/packages/activity/README.md +++ b/packages/activity/README.md @@ -2,8 +2,8 @@ [![NPM](https://img.shields.io/npm/v/@temporalio/activity?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/activity) -Part of the [Temporal](https://temporal.io) [TypeScript SDK](https://www.npmjs.com/package/temporalio). +Part of [Temporal](https://temporal.io)'s [TypeScript SDK](https://docs.temporal.io/docs/typescript/introduction/). -- See API reference [here](https://typescript.temporal.io/api/namespaces/activity) -- See code samples [here](https://github.com/temporalio/samples-typescript) -- See full introduction on the [docs site](https://docs.temporal.io/docs/typescript/introduction) +- [Activity docs](https://docs.temporal.io/docs/typescript/activities/) +- [API reference](https://typescript.temporal.io/api/namespaces/activity) +- [Sample projects](https://github.com/temporalio/samples-typescript) diff --git a/packages/activity/tsconfig.json b/packages/activity/tsconfig.json index 97ed59823..1a63b5edd 100644 --- a/packages/activity/tsconfig.json +++ b/packages/activity/tsconfig.json @@ -9,6 +9,5 @@ "path": "../common/tsconfig.json" } ], - "include": ["./src/**/*.ts"], - "exclude": ["node_modules"] + "include": ["./src/**/*.ts"] } diff --git a/packages/client/README.md b/packages/client/README.md index 6f2649925..c9b5b2ad0 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -2,8 +2,8 @@ [![NPM](https://img.shields.io/npm/v/@temporalio/client?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/client) -Part of the [Temporal](https://temporal.io) [TypeScript SDK](https://www.npmjs.com/package/temporalio). +Part of [Temporal](https://temporal.io)'s [TypeScript SDK](https://docs.temporal.io/docs/typescript/introduction/). -- See API reference [here](https://typescript.temporal.io/api/namespaces/client) -- See code samples [here](https://github.com/temporalio/samples-typescript) -- See full introduction on the [docs site](https://docs.temporal.io/docs/typescript/introduction) +- [Client docs](https://docs.temporal.io/docs/typescript/clients) +- [API reference](https://typescript.temporal.io/api/namespaces/client) +- [Sample projects](https://github.com/temporalio/samples-typescript) diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index dd107b18b..bc49e15b5 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -5,6 +5,5 @@ "rootDir": "./src" }, "references": [{ "path": "../common/tsconfig.json" }], - "include": ["./src/**/*.ts"], - "exclude": ["node_modules"] + "include": ["./src/**/*.ts"] } diff --git a/packages/common/README.md b/packages/common/README.md index 2988d0cc5..a18b14fd1 100644 --- a/packages/common/README.md +++ b/packages/common/README.md @@ -2,16 +2,11 @@ [![NPM](https://img.shields.io/npm/v/@temporalio/common?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/common) -Part of the [Temporal](https://temporal.io) [TypeScript SDK](https://www.npmjs.com/package/temporalio). +Part of [Temporal](https://temporal.io)'s TypeScript SDK (see [docs](https://docs.temporal.io/docs/typescript/introduction/) and [samples](https://github.com/temporalio/samples-typescript)). -You should probably not be using this package directly. See instead: +You should usually not be using this package directly. Instead use: -- https://typescript.temporal.io/api/namespaces/client -- https://typescript.temporal.io/api/namespaces/activity -- https://typescript.temporal.io/api/namespaces/worker -- https://typescript.temporal.io/api/namespaces/workflow - -More docs: - -- See code samples [here](https://github.com/temporalio/samples-typescript) -- See full introduction on the [docs site](https://docs.temporal.io/docs/typescript/introduction) +- [`@temporalio/client`](https://typescript.temporal.io/api/namespaces/client) +- [`@temporalio/worker`](https://typescript.temporal.io/api/namespaces/worker) +- [`@temporalio/workflow`](https://typescript.temporal.io/api/namespaces/workflow) +- [`@temporalio/activity`](https://typescript.temporal.io/api/namespaces/activity) diff --git a/packages/common/package.json b/packages/common/package.json index 8027885ba..48dee6123 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -13,9 +13,7 @@ "author": "Roey Berman ", "license": "MIT", "dependencies": { - "@temporalio/proto": "file:../proto", - "ms": "^2.1.3", - "proto3-json-serializer": "^0.1.6" + "@temporalio/workflow-common": "file:../workflow-common" }, "bugs": { "url": "https://github.com/temporalio/sdk-typescript/issues" @@ -23,8 +21,5 @@ "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/common", "publishConfig": { "access": "public" - }, - "devDependencies": { - "protobufjs": "^6.11.2" } } diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 43cb3b823..6377ce8e7 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -3,16 +3,7 @@ * * @module */ -export * from './activity-options'; -export * from './converter/data-converter'; -export * from './converter/helpers'; -export * from './errors'; -export * from './failure'; -export * from './interceptors'; -export * from './interfaces'; -export * from './retry-policy'; -export * from './time'; +export * from '@temporalio/workflow-common'; +export * from './patch-protobuf-root'; export * from './tls-config'; -export * from './workflow-handle'; -export * from './workflow-options'; export * from './utils'; diff --git a/packages/common/src/otel.ts b/packages/common/src/otel.ts index dc426617c..b94994678 100644 --- a/packages/common/src/otel.ts +++ b/packages/common/src/otel.ts @@ -1,6 +1,6 @@ import * as otel from '@opentelemetry/api'; -import { defaultDataConverter } from './converter/data-converter'; -import { Headers } from './interceptors'; +import { defaultDataConverter } from '@temporalio/workflow-common'; +import { Headers } from '@temporalio/workflow-common'; /** Default trace header for opentelemetry interceptors */ export const TRACE_HEADER = '_tracer-data'; diff --git a/packages/common/src/converter/helpers.ts b/packages/common/src/patch-protobuf-root.ts similarity index 94% rename from packages/common/src/converter/helpers.ts rename to packages/common/src/patch-protobuf-root.ts index 2e102f074..e1f36b9da 100644 --- a/packages/common/src/converter/helpers.ts +++ b/packages/common/src/patch-protobuf-root.ts @@ -1,4 +1,4 @@ -import { isRecord } from '../type-helpers'; +import { isRecord } from '@temporalio/workflow-common/lib/type-helpers'; export function patchProtobufRoot>(root: T, name?: string): T { const newRoot = new (root.constructor as any)(isNamespace(root) ? name : {}); diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json index 9a050d2ff..1e09513a4 100644 --- a/packages/common/tsconfig.json +++ b/packages/common/tsconfig.json @@ -4,6 +4,5 @@ "outDir": "./lib", "rootDir": "./src" }, - "include": ["./src/**/*.ts"], - "exclude": ["node_modules"] + "include": ["./src/**/*.ts"] } diff --git a/packages/core-bridge/README.md b/packages/core-bridge/README.md index 00d82f819..716ed194f 100644 --- a/packages/core-bridge/README.md +++ b/packages/core-bridge/README.md @@ -2,6 +2,4 @@ [![NPM](https://img.shields.io/npm/v/@temporalio/core-bridge?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/core-bridge) -Part of the [Temporal](https://temporal.io) [TypeScript SDK](https://www.npmjs.com/package/temporalio). - -See documentation on the [documentation site](https://docs.temporal.io/docs/typescript/introduction). +Part of [Temporal](https://temporal.io)'s [TypeScript SDK](https://docs.temporal.io/docs/typescript/introduction/). diff --git a/packages/create-project/README.md b/packages/create-project/README.md index 2b6c2e019..ef395eb20 100644 --- a/packages/create-project/README.md +++ b/packages/create-project/README.md @@ -2,6 +2,6 @@ [![NPM](https://img.shields.io/npm/v/@temporalio/create?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/create) -Part of the [Temporal](https://temporal.io) [TypeScript SDK](https://www.npmjs.com/package/temporalio). +Project initializer for [Temporal](https://temporal.io)'s [TypeScript SDK](https://docs.temporal.io/docs/typescript/introduction/). -See documentation on the [documentation site](https://docs.temporal.io/docs/typescript/introduction). +[`@temporalio/create` documentation](https://docs.temporal.io/docs/typescript/package-initializer) diff --git a/packages/create-project/tsconfig.json b/packages/create-project/tsconfig.json index 8d5ba8c60..9110c3177 100644 --- a/packages/create-project/tsconfig.json +++ b/packages/create-project/tsconfig.json @@ -7,6 +7,5 @@ "lib": ["dom"], "module": "esnext" }, - "include": ["./src/**/*.ts"], - "exclude": ["node_modules"] + "include": ["./src/**/*.ts"] } diff --git a/packages/interceptors-opentelemetry/README.md b/packages/interceptors-opentelemetry/README.md index 93fd4c889..1f252c54a 100644 --- a/packages/interceptors-opentelemetry/README.md +++ b/packages/interceptors-opentelemetry/README.md @@ -2,6 +2,7 @@ [![NPM](https://img.shields.io/npm/v/@temporalio/interceptors-opentelemetry?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/interceptors-opentelemetry) -[Temporal](https://temporal.io) [SDK](https://www.npmjs.com/package/temporalio) interceptors bundle for tracing Workflow and Activity executions with opentelemetry. +[Temporal](https://temporal.io)'s [TypeScript SDK](https://docs.temporal.io/docs/typescript/introduction) interceptors bundle for tracing Workflow and Activity executions with [OpenTelemetry](https://opentelemetry.io/). -[Temporal documentation site](https://docs.temporal.io/docs/typescript/introduction). +- [Interceptors docs](https://docs.temporal.io/docs/typescript/interceptors) +- [OpenTelemetry sample project](https://github.com/temporalio/samples-typescript/tree/main/interceptors-opentelemetry) diff --git a/packages/interceptors-opentelemetry/tsconfig.json b/packages/interceptors-opentelemetry/tsconfig.json index f65883b8c..d7987d497 100644 --- a/packages/interceptors-opentelemetry/tsconfig.json +++ b/packages/interceptors-opentelemetry/tsconfig.json @@ -5,6 +5,5 @@ "rootDir": "./src" }, "references": [{ "path": "../client" }, { "path": "../common" }, { "path": "../worker" }, { "path": "../workflow" }], - "include": ["./src/**/*.ts"], - "exclude": ["node_modules"] + "include": ["./src/**/*.ts"] } diff --git a/packages/meta/tsconfig.json b/packages/meta/tsconfig.json index edcff4b5a..8cdb53cef 100644 --- a/packages/meta/tsconfig.json +++ b/packages/meta/tsconfig.json @@ -12,6 +12,5 @@ { "path": "../worker/tsconfig.json" }, { "path": "../workflow/tsconfig.json" } ], - "include": ["./src/**/*.ts"], - "exclude": ["node_modules"] + "include": ["./src/**/*.ts"] } diff --git a/packages/proto/README.md b/packages/proto/README.md index f70291b46..63de5f09a 100644 --- a/packages/proto/README.md +++ b/packages/proto/README.md @@ -2,16 +2,11 @@ [![NPM](https://img.shields.io/npm/v/@temporalio/proto?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/proto) -Part of the [Temporal](https://temporal.io) [TypeScript SDK](https://www.npmjs.com/package/temporalio). +Part of [Temporal](https://temporal.io)'s TypeScript SDK (see [docs](https://docs.temporal.io/docs/typescript/introduction/) and [samples](https://github.com/temporalio/samples-typescript)). -You should probably not be using this package directly. See instead: +You should usually not be using this package directly. Instead use: -- https://typescript.temporal.io/api/namespaces/client -- https://typescript.temporal.io/api/namespaces/activity -- https://typescript.temporal.io/api/namespaces/worker -- https://typescript.temporal.io/api/namespaces/workflow - -More docs: - -- See code samples [here](https://github.com/temporalio/samples-typescript) -- See full introduction on the [docs site](https://docs.temporal.io/docs/typescript/introduction) +- [`@temporalio/client`](https://typescript.temporal.io/api/namespaces/client) +- [`@temporalio/worker`](https://typescript.temporal.io/api/namespaces/worker) +- [`@temporalio/workflow`](https://typescript.temporal.io/api/namespaces/workflow) +- [`@temporalio/activity`](https://typescript.temporal.io/api/namespaces/activity) diff --git a/packages/test/package.json b/packages/test/package.json index 6d5e3bda2..d8c17947d 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -29,6 +29,7 @@ "@temporalio/activity": "file:../activity", "@temporalio/client": "file:../client", "@temporalio/common": "file:../common", + "@temporalio/workflow-common": "file:../workflow-common", "@temporalio/interceptors-opentelemetry": "file:../interceptors-opentelemetry", "@temporalio/proto": "file:../proto", "@temporalio/worker": "file:../worker", diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 1ab847bad..91a24c151 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -6,14 +6,14 @@ import { METADATA_MESSAGE_TYPE_KEY, encodingKeys, u8, -} from '@temporalio/common/lib/converter/types'; +} from '@temporalio/workflow-common/lib/converter/types'; import { UndefinedPayloadConverter, BinaryPayloadConverter, JsonPayloadConverter, ProtobufBinaryPayloadConverter, ProtobufJsonPayloadConverter, -} from '@temporalio/common/lib/converter/payload-converter'; +} from '@temporalio/workflow-common/lib/converter/payload-converter'; import root from '../protos/root'; test('UndefinedPayloadConverter converts from undefined only', async (t) => { diff --git a/packages/test/tsconfig.json b/packages/test/tsconfig.json index 0ec793510..50f275391 100644 --- a/packages/test/tsconfig.json +++ b/packages/test/tsconfig.json @@ -12,6 +12,5 @@ { "path": "../worker" }, { "path": "../workflow" } ], - "include": ["./src/**/*.ts"], - "exclude": ["node_modules"] + "include": ["./src/**/*.ts"] } diff --git a/packages/worker/README.md b/packages/worker/README.md index 21f3736d1..c9368db8e 100644 --- a/packages/worker/README.md +++ b/packages/worker/README.md @@ -2,8 +2,8 @@ [![NPM](https://img.shields.io/npm/v/@temporalio/worker?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/worker) -Part of the [Temporal](https://temporal.io) [TypeScript SDK](https://www.npmjs.com/package/temporalio). +Part of [Temporal](https://temporal.io)'s [TypeScript SDK](https://docs.temporal.io/docs/typescript/introduction/). -- See API reference [here](https://typescript.temporal.io/api/namespaces/worker) -- See code samples [here](https://github.com/temporalio/samples-typescript) -- See full introduction on the [docs site](https://docs.temporal.io/docs/typescript/introduction) +- [Worker docs](https://docs.temporal.io/docs/typescript/workers) +- [API reference](https://typescript.temporal.io/api/namespaces/worker) +- [Sample projects](https://github.com/temporalio/samples-typescript) diff --git a/packages/worker/src/core.ts b/packages/worker/src/core.ts index 0c620c840..d2453a767 100644 --- a/packages/worker/src/core.ts +++ b/packages/worker/src/core.ts @@ -13,7 +13,7 @@ import { import { compileServerOptions, getDefaultServerOptions, RequiredServerOptions, ServerOptions } from './server-options'; import { DefaultLogger, Logger, LogEntry, LogTimestamp, timeOfDayToBigint } from './logger'; import * as errors from './errors'; -import { MakeOptional } from '@temporalio/common/src/type-helpers'; +import { MakeOptional } from '@temporalio/workflow-common/src/type-helpers'; export type TelemetryOptions = MakeOptional; diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index 050e2b520..9dda6fa0c 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -10,6 +10,5 @@ { "path": "../workflow/tsconfig.json" }, { "path": "../activity/tsconfig.json" } ], - "include": ["./src/**/*.ts"], - "exclude": ["node_modules"] + "include": ["./src/**/*.ts"] } diff --git a/packages/workflow-common/README.md b/packages/workflow-common/README.md new file mode 100644 index 000000000..fcfcddc18 --- /dev/null +++ b/packages/workflow-common/README.md @@ -0,0 +1,12 @@ +# `@temporalio/workflow-common` + +[![NPM](https://img.shields.io/npm/v/@temporalio/workflow-common?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/workflow-common) + +Part of [Temporal](https://temporal.io)'s TypeScript SDK (see [docs](https://docs.temporal.io/docs/typescript/introduction/) and [samples](https://github.com/temporalio/samples-typescript)). + +You should usually not be using this package directly. Instead use: + +- [`@temporalio/client`](https://typescript.temporal.io/api/namespaces/client) +- [`@temporalio/worker`](https://typescript.temporal.io/api/namespaces/worker) +- [`@temporalio/workflow`](https://typescript.temporal.io/api/namespaces/workflow) +- [`@temporalio/activity`](https://typescript.temporal.io/api/namespaces/activity) diff --git a/packages/workflow-common/package.json b/packages/workflow-common/package.json new file mode 100644 index 000000000..a3bf98b05 --- /dev/null +++ b/packages/workflow-common/package.json @@ -0,0 +1,29 @@ +{ + "name": "@temporalio/workflow-common", + "version": "0.17.2", + "description": "Internal SDK library: users should usually use `@temporalio/common` instead", + "main": "lib/index.js", + "types": "./lib/index.d.ts", + "keywords": [ + "temporal", + "workflow", + "worker" + ], + "author": "Roey Berman ", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3", + "proto3-json-serializer": "^0.1.6" + }, + "bugs": { + "url": "https://github.com/temporalio/sdk-typescript/issues" + }, + "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/common", + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@temporalio/proto": "file:../proto", + "protobufjs": "^6.11.2" + } +} diff --git a/packages/common/src/activity-options.ts b/packages/workflow-common/src/activity-options.ts similarity index 100% rename from packages/common/src/activity-options.ts rename to packages/workflow-common/src/activity-options.ts diff --git a/packages/common/src/converter/data-converter.ts b/packages/workflow-common/src/converter/data-converter.ts similarity index 100% rename from packages/common/src/converter/data-converter.ts rename to packages/workflow-common/src/converter/data-converter.ts diff --git a/packages/common/src/encoding.ts b/packages/workflow-common/src/converter/encoding.ts similarity index 100% rename from packages/common/src/encoding.ts rename to packages/workflow-common/src/converter/encoding.ts diff --git a/packages/common/src/converter/payload-converter.ts b/packages/workflow-common/src/converter/payload-converter.ts similarity index 100% rename from packages/common/src/converter/payload-converter.ts rename to packages/workflow-common/src/converter/payload-converter.ts diff --git a/packages/common/src/converter/types.ts b/packages/workflow-common/src/converter/types.ts similarity index 95% rename from packages/common/src/converter/types.ts rename to packages/workflow-common/src/converter/types.ts index 21c40cb90..434b39b82 100644 --- a/packages/common/src/converter/types.ts +++ b/packages/workflow-common/src/converter/types.ts @@ -1,5 +1,5 @@ import type * as iface from '@temporalio/proto/lib/coresdk'; -import { TextEncoder, TextDecoder } from '../encoding'; +import { TextEncoder, TextDecoder } from './encoding'; export type Payload = iface.coresdk.common.IPayload; diff --git a/packages/common/src/errors.ts b/packages/workflow-common/src/errors.ts similarity index 100% rename from packages/common/src/errors.ts rename to packages/workflow-common/src/errors.ts diff --git a/packages/common/src/failure.ts b/packages/workflow-common/src/failure.ts similarity index 100% rename from packages/common/src/failure.ts rename to packages/workflow-common/src/failure.ts diff --git a/packages/workflow-common/src/index.ts b/packages/workflow-common/src/index.ts new file mode 100644 index 000000000..68dbe2a9a --- /dev/null +++ b/packages/workflow-common/src/index.ts @@ -0,0 +1,15 @@ +/** + * Common library for both isolated Workflow and normal non-Workflow code + * + * @module + */ +export * from './activity-options'; +export * from './converter/data-converter'; +export * from './errors'; +export * from './failure'; +export * from './interceptors'; +export * from './interfaces'; +export * from './retry-policy'; +export * from './time'; +export * from './workflow-handle'; +export * from './workflow-options'; diff --git a/packages/common/src/interceptors.ts b/packages/workflow-common/src/interceptors.ts similarity index 100% rename from packages/common/src/interceptors.ts rename to packages/workflow-common/src/interceptors.ts diff --git a/packages/common/src/interfaces.ts b/packages/workflow-common/src/interfaces.ts similarity index 100% rename from packages/common/src/interfaces.ts rename to packages/workflow-common/src/interfaces.ts diff --git a/packages/common/src/retry-policy.ts b/packages/workflow-common/src/retry-policy.ts similarity index 100% rename from packages/common/src/retry-policy.ts rename to packages/workflow-common/src/retry-policy.ts diff --git a/packages/common/src/time.ts b/packages/workflow-common/src/time.ts similarity index 100% rename from packages/common/src/time.ts rename to packages/workflow-common/src/time.ts diff --git a/packages/common/src/type-helpers.ts b/packages/workflow-common/src/type-helpers.ts similarity index 100% rename from packages/common/src/type-helpers.ts rename to packages/workflow-common/src/type-helpers.ts diff --git a/packages/common/src/workflow-handle.ts b/packages/workflow-common/src/workflow-handle.ts similarity index 100% rename from packages/common/src/workflow-handle.ts rename to packages/workflow-common/src/workflow-handle.ts diff --git a/packages/common/src/workflow-options.ts b/packages/workflow-common/src/workflow-options.ts similarity index 100% rename from packages/common/src/workflow-options.ts rename to packages/workflow-common/src/workflow-options.ts diff --git a/packages/workflow-common/tsconfig.json b/packages/workflow-common/tsconfig.json new file mode 100644 index 000000000..1e09513a4 --- /dev/null +++ b/packages/workflow-common/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src" + }, + "include": ["./src/**/*.ts"] +} diff --git a/packages/workflow/README.md b/packages/workflow/README.md index 36490cee0..4a0592fc9 100644 --- a/packages/workflow/README.md +++ b/packages/workflow/README.md @@ -2,8 +2,8 @@ [![NPM](https://img.shields.io/npm/v/@temporalio/workflow?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/workflow) -Part of the [Temporal](https://temporal.io) [TypeScript SDK](https://www.npmjs.com/package/temporalio). +Part of [Temporal](https://temporal.io)'s [TypeScript SDK](https://docs.temporal.io/docs/typescript/introduction/). -- See API reference [here](https://typescript.temporal.io/api/namespaces/workflow) -- See code samples [here](https://github.com/temporalio/samples-typescript) -- See full introduction on the [docs site](https://docs.temporal.io/docs/typescript/introduction) +- [Workflow docs](https://docs.temporal.io/docs/typescript/workflows) +- [API reference](https://typescript.temporal.io/api/namespaces/workflow) +- [Sample projects](https://github.com/temporalio/samples-typescript) diff --git a/packages/workflow/package.json b/packages/workflow/package.json index bab8c83a9..eb91e9f7e 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -2,27 +2,29 @@ "name": "@temporalio/workflow", "version": "0.17.2", "description": "Temporal.io SDK Workflow sub-package", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "scripts": {}, "keywords": [ "temporal", "workflow", "isolate" ], - "author": "Roey Berman ", - "license": "MIT", - "dependencies": { - "@temporalio/common": "file:../common", - "@temporalio/proto": "file:../proto" - }, + "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/workflow", "bugs": { "url": "https://github.com/temporalio/sdk-typescript/issues" }, - "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/workflow", + "license": "MIT", + "author": "Roey Berman ", + "main": "lib/index.js", + "types": "lib/index.d.ts", "files": [ "lib" ], + "scripts": {}, + "dependencies": { + "@temporalio/workflow-common": "file:../workflow-common" + }, + "devDependencies": { + "@temporalio/proto": "file:../proto" + }, "publishConfig": { "access": "public" } diff --git a/packages/workflow/src/cancellation-scope.ts b/packages/workflow/src/cancellation-scope.ts index 437070f69..5beda2800 100644 --- a/packages/workflow/src/cancellation-scope.ts +++ b/packages/workflow/src/cancellation-scope.ts @@ -1,4 +1,4 @@ -import { CancelledFailure, IllegalStateError } from '@temporalio/common'; +import { CancelledFailure, IllegalStateError } from '@temporalio/workflow-common'; import type { AsyncLocalStorage as ALS } from 'async_hooks'; // AsyncLocalStorage is injected via vm module into global scope. diff --git a/packages/workflow/src/errors.ts b/packages/workflow/src/errors.ts index 90bc017ab..621f26dc8 100644 --- a/packages/workflow/src/errors.ts +++ b/packages/workflow/src/errors.ts @@ -1,4 +1,4 @@ -import { CancelledFailure, ActivityFailure, ChildWorkflowFailure } from '@temporalio/common'; +import { CancelledFailure, ActivityFailure, ChildWorkflowFailure } from '@temporalio/workflow-common'; /** * Base class for all workflow errors diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts index 2da646469..19a87e6df 100644 --- a/packages/workflow/src/index.ts +++ b/packages/workflow/src/index.ts @@ -72,7 +72,7 @@ export { TerminatedFailure, TimeoutFailure, ValueError, -} from '@temporalio/common'; +} from '@temporalio/workflow-common'; export { ChildWorkflowOptions, ChildWorkflowCancellationType, diff --git a/packages/workflow/src/interceptors.ts b/packages/workflow/src/interceptors.ts index c94ec932a..18a9d413a 100644 --- a/packages/workflow/src/interceptors.ts +++ b/packages/workflow/src/interceptors.ts @@ -6,7 +6,7 @@ * @module */ -import { ActivityOptions, WorkflowExecution, Headers, Next } from '@temporalio/common'; +import { ActivityOptions, WorkflowExecution, Headers, Next } from '@temporalio/workflow-common'; import type { coresdk } from '@temporalio/proto/lib/coresdk'; import { ChildWorkflowOptionsWithDefaults, ContinueAsNewOptions } from './interfaces'; diff --git a/packages/workflow/src/interfaces.ts b/packages/workflow/src/interfaces.ts index d28ec8b01..44aa027cd 100644 --- a/packages/workflow/src/interfaces.ts +++ b/packages/workflow/src/interfaces.ts @@ -1,6 +1,6 @@ import type { coresdk } from '@temporalio/proto/lib/coresdk'; -import { CommonWorkflowOptions } from '@temporalio/common'; -import { checkExtends } from '@temporalio/common/lib/type-helpers'; +import { CommonWorkflowOptions } from '@temporalio/workflow-common'; +import { checkExtends } from '@temporalio/workflow-common/lib/type-helpers'; /** * Workflow execution information diff --git a/packages/workflow/src/internals.ts b/packages/workflow/src/internals.ts index 21929530d..497fbd2f5 100644 --- a/packages/workflow/src/internals.ts +++ b/packages/workflow/src/internals.ts @@ -12,8 +12,8 @@ import { Workflow, WorkflowQueryType, TemporalFailure, -} from '@temporalio/common'; -import { checkExtends } from '@temporalio/common/lib/type-helpers'; +} from '@temporalio/workflow-common'; +import { checkExtends } from '@temporalio/workflow-common/lib/type-helpers'; import type { coresdk } from '@temporalio/proto/lib/coresdk'; import { alea, RNG } from './alea'; import { ContinueAsNew, WorkflowInfo } from './interfaces'; diff --git a/packages/workflow/src/worker-interface.ts b/packages/workflow/src/worker-interface.ts index a913fb769..19f64e007 100644 --- a/packages/workflow/src/worker-interface.ts +++ b/packages/workflow/src/worker-interface.ts @@ -11,7 +11,7 @@ import { Workflow, ApplicationFailure, errorMessage, -} from '@temporalio/common'; +} from '@temporalio/workflow-common'; import type { coresdk } from '@temporalio/proto/lib/coresdk'; import { WorkflowInfo } from './interfaces'; import { handleWorkflowFailure, InterceptorsImportFunc, state, WorkflowsImportFunc } from './internals'; diff --git a/packages/workflow/src/workflow-handle.ts b/packages/workflow/src/workflow-handle.ts index ccc75f6df..0678532c5 100644 --- a/packages/workflow/src/workflow-handle.ts +++ b/packages/workflow/src/workflow-handle.ts @@ -1,4 +1,4 @@ -import { BaseWorkflowHandle, SignalDefinition, Workflow } from '@temporalio/common'; +import { BaseWorkflowHandle, SignalDefinition, Workflow } from '@temporalio/workflow-common'; /** * Handle representing an external Workflow execution diff --git a/packages/workflow/src/workflow.ts b/packages/workflow/src/workflow.ts index 742b7ae54..83017517b 100644 --- a/packages/workflow/src/workflow.ts +++ b/packages/workflow/src/workflow.ts @@ -15,7 +15,7 @@ import { WorkflowReturnType, compileRetryPolicy, ActivityInterface, -} from '@temporalio/common'; +} from '@temporalio/workflow-common'; import { ChildWorkflowCancellationType, ChildWorkflowOptions, diff --git a/packages/workflow/tsconfig.json b/packages/workflow/tsconfig.json index eb3da3aea..840091b7c 100644 --- a/packages/workflow/tsconfig.json +++ b/packages/workflow/tsconfig.json @@ -6,9 +6,8 @@ }, "references": [ { - "path": "../common/tsconfig.json" + "path": "../workflow-common/tsconfig.json" } ], - "include": ["./src/**/*.ts"], - "exclude": ["node_modules"] + "include": ["./src/**/*.ts"] } diff --git a/tsconfig.prune.json b/tsconfig.prune.json index 40d41eba4..6b39b6014 100644 --- a/tsconfig.prune.json +++ b/tsconfig.prune.json @@ -9,6 +9,7 @@ "./packages/activity/src/index.ts", "./packages/client/src/index.ts", "./packages/common/src/index.ts", + "./packages/workflow-common/src/index.ts", "./packages/create-project/src/index.ts", "./packages/meta/src/index.ts", "./packages/worker/src/index.ts", From 3e501b062322d944bab77fa7bdc8f8e4f9eac982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Fri, 28 Jan 2022 23:41:02 -0500 Subject: [PATCH 06/33] feat: Only include proto3-json-serializer when needed --- .../test/src/test-custom-data-converter.ts | 2 +- packages/test/src/test-data-converter.ts | 66 ++++++++++++++++--- packages/worker/src/workflow/bundler.ts | 14 +++- .../workflow/empty-proto3-json-serializer.ts | 8 +++ packages/workflow-common/package.json | 1 + 5 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 packages/worker/src/workflow/empty-proto3-json-serializer.ts diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-data-converter.ts index 9b6510c7f..fd012a66f 100644 --- a/packages/test/src/test-custom-data-converter.ts +++ b/packages/test/src/test-custom-data-converter.ts @@ -33,7 +33,7 @@ function compareCompletion( if (RUN_INTEGRATION_TESTS) { test('Client and Worker work with provided dataConverter/dataConverterPath', async (t) => { - const taskQueue = 'custom-data-converter'; + const taskQueue = 'test-custom-data-converter'; const worker = await Worker.create({ ...defaultOptions, taskQueue, diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 91a24c151..7ddc3c942 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -1,20 +1,28 @@ /* eslint @typescript-eslint/no-non-null-assertion: 0 */ -import test from 'ava'; -import { defaultDataConverter, ValueError, DataConverterError, DefaultDataConverter } from '@temporalio/common'; -import { - METADATA_ENCODING_KEY, - METADATA_MESSAGE_TYPE_KEY, - encodingKeys, - u8, -} from '@temporalio/workflow-common/lib/converter/types'; +import { Connection, WorkflowClient } from '@temporalio/client'; +import { DataConverterError, defaultDataConverter, DefaultDataConverter, ValueError } from '@temporalio/common'; +import { Core, DefaultLogger, Worker } from '@temporalio/worker'; +import { CompositeDataConverter } from '@temporalio/workflow-common'; import { - UndefinedPayloadConverter, BinaryPayloadConverter, JsonPayloadConverter, ProtobufBinaryPayloadConverter, ProtobufJsonPayloadConverter, + UndefinedPayloadConverter, } from '@temporalio/workflow-common/lib/converter/payload-converter'; +import { + encodingKeys, + METADATA_ENCODING_KEY, + METADATA_MESSAGE_TYPE_KEY, + u8, +} from '@temporalio/workflow-common/lib/converter/types'; +import test from 'ava'; +import { v4 as uuid4 } from 'uuid'; import root from '../protos/root'; +import { messageInstance } from './data-converters/data-converter'; +import { RUN_INTEGRATION_TESTS } from './helpers'; +import { defaultOptions } from './mock-native-worker'; +import { protobufWorkflow } from './workflows'; test('UndefinedPayloadConverter converts from undefined only', async (t) => { const converter = new UndefinedPayloadConverter(); @@ -156,6 +164,46 @@ test('ProtobufJSONPayloadConverter converts binary', async (t) => { t.deepEqual(testInstance.data, Buffer.from(instance.data)); }); +if (RUN_INTEGRATION_TESTS) { + test('Worker throws decoding proto JSON without WorkerOptions.dataConverter', async (t) => { + t.timeout(5 * 1000); + let markErrorThrown: any; + const expectedErrorWasThrown = new Promise(function (resolve) { + markErrorThrown = resolve; + }); + const logger = new DefaultLogger('ERROR', (entry) => { + if ( + entry.meta?.error.stack.includes( + 'DataConverterError: Unable to deserialize protobuf message without `root` being provided' + ) + ) { + markErrorThrown(); + } + }); + await Core.install({ logger }); + + const taskQueue = 'test-data-converter'; + const worker = await Worker.create({ + ...defaultOptions, + taskQueue, + enableSDKTracing: true, + }); + const connection = new Connection(); + const client = new WorkflowClient(connection.service, { + dataConverter: new CompositeDataConverter(new ProtobufJsonPayloadConverter(root)), + }); + worker.run(); + client.execute(protobufWorkflow, { + args: [messageInstance], + workflowId: uuid4(), + taskQueue, + }); + await expectedErrorWasThrown; + t.pass(); + worker.shutdown(); + }); +} + test('DefaultDataConverter converts protobufs', async (t) => { const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); const defaultDataConverterWithProtos = new DefaultDataConverter({ root }); diff --git a/packages/worker/src/workflow/bundler.ts b/packages/worker/src/workflow/bundler.ts index 9b246a7c2..ef4dfd24e 100644 --- a/packages/worker/src/workflow/bundler.ts +++ b/packages/worker/src/workflow/bundler.ts @@ -134,8 +134,18 @@ export class WorkflowCodeBundler { library: '__TEMPORAL__', }, }; - if (dataConverterPath && webpackConfig.resolve) { - webpackConfig.resolve.alias = { __temporal_custom_data_converter$: dataConverterPath }; + if (webpackConfig.resolve) { + if (dataConverterPath) { + webpackConfig.resolve.alias = { + __temporal_custom_data_converter$: dataConverterPath, + }; + } else { + // Save 100KB from the bundle by not including the npm module, + // since we can't decode without a `root` anyway. + webpackConfig.resolve.alias = { + 'proto3-json-serializer$': path.resolve(__dirname, 'empty-proto3-json-serializer.js'), + }; + } } const compiler = webpack(webpackConfig); diff --git a/packages/worker/src/workflow/empty-proto3-json-serializer.ts b/packages/worker/src/workflow/empty-proto3-json-serializer.ts new file mode 100644 index 000000000..46a258a8f --- /dev/null +++ b/packages/worker/src/workflow/empty-proto3-json-serializer.ts @@ -0,0 +1,8 @@ +import { DataConverterError } from '@temporalio/workflow-common'; + +function throwError(): void { + throw new DataConverterError('In order to JSON-encode/decode protobufs, specify a `WorkerOptions.dataConverter`'); +} + +export const toProto3JSON = throwError; +export const fromProto3JSON = throwError; diff --git a/packages/workflow-common/package.json b/packages/workflow-common/package.json index a3bf98b05..c5addc786 100644 --- a/packages/workflow-common/package.json +++ b/packages/workflow-common/package.json @@ -12,6 +12,7 @@ "author": "Roey Berman ", "license": "MIT", "dependencies": { + "long": "^4.0.0", "ms": "^2.1.3", "proto3-json-serializer": "^0.1.6" }, From fd30a390ee3087559133340fda7dee9a2cbc6ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Sun, 23 Jan 2022 20:24:52 -0500 Subject: [PATCH 07/33] feat: Use the new DataConverter API export interface DataConverter { payloadConverterPath?: string; payloadCodec?: PayloadCodec; } export interface PayloadConverter { toPayload(value: T): Payload; fromPayload(payload: Payload): T; } export interface PayloadCodec { encode(payloads: Payload[]): Promise; decode(payloads: Payload[]): Promise; } --- docs/data-converter.md | 3 +- package.json | 1 + .../client/src/async-completion-client.ts | 47 ++- packages/client/src/index.ts | 2 +- packages/client/src/workflow-client.ts | 45 ++- packages/common/src/codec-helpers.ts | 66 ++++ packages/common/src/otel.ts | 6 +- .../src/client/index.ts | 4 +- .../src/worker/index.ts | 4 +- packages/test/src/mock-native-worker.ts | 4 +- .../payload-converter-bad-export.ts} | 0 .../payload-converter-no-export.ts} | 0 .../payload-converter.ts} | 4 +- .../test/src/test-custom-data-converter.ts | 27 +- packages/test/src/test-data-converter.ts | 49 +-- packages/test/src/test-integration.ts | 10 +- packages/test/src/test-interceptors.ts | 8 +- packages/test/src/test-worker-activities.ts | 18 +- packages/test/src/test-workflows.ts | 100 ++--- packages/test/src/workflows/data-converter.ts | 4 +- .../test/src/workflows/interceptor-example.ts | 6 +- packages/worker/src/activity.ts | 10 +- packages/worker/src/index.ts | 2 +- packages/worker/src/worker-options.ts | 6 +- packages/worker/src/worker.ts | 40 +- packages/worker/src/workflow/bundler.ts | 18 +- packages/worker/src/workflow/threaded-vm.ts | 6 +- packages/worker/src/workflow/vm.ts | 24 +- .../src/workflow/workflow-worker-thread.ts | 2 +- .../workflow/workflow-worker-thread/input.ts | 2 +- .../src/converter/data-converter.ts | 222 +---------- .../src/converter/payload-codec.ts | 29 ++ .../src/converter/payload-converter.ts | 373 ++++++------------ .../src/converter/payload-converters.ts | 230 +++++++++++ .../workflow-common/src/converter/types.ts | 1 + packages/workflow-common/src/errors.ts | 8 + packages/workflow-common/src/failure.ts | 66 ++-- packages/workflow-common/src/index.ts | 4 + packages/workflow/src/index.ts | 4 +- packages/workflow/src/internals.ts | 26 +- packages/workflow/src/worker-interface.ts | 4 +- packages/workflow/src/workflow.ts | 2 +- 42 files changed, 778 insertions(+), 709 deletions(-) create mode 100644 packages/common/src/codec-helpers.ts rename packages/test/src/{data-converters/data-converter-bad-export.ts => payload-converters/payload-converter-bad-export.ts} (100%) rename packages/test/src/{data-converters/data-converter-no-export.ts => payload-converters/payload-converter-no-export.ts} (100%) rename packages/test/src/{data-converters/data-converter.ts => payload-converters/payload-converter.ts} (51%) create mode 100644 packages/workflow-common/src/converter/payload-codec.ts create mode 100644 packages/workflow-common/src/converter/payload-converters.ts diff --git a/docs/data-converter.md b/docs/data-converter.md index ae03a0d63..084486802 100644 --- a/docs/data-converter.md +++ b/docs/data-converter.md @@ -6,6 +6,7 @@ When designing the custom data converter feature, we considered two routes: ### Outside vm - Pro: Users can use any node module in their custom data converter code. +- Pro: Methods can be async (users can use Promises). - Con: Only works because `vm` allows for passing complex objects into/out of vm. If we switch to an isolation method like `isolated-vm` or `rusty_v8`, conversion needs to be done inside. - Con: `object instanceof Class` doesn't work on object that come from the vm, because the `Class` definition inside the vm is from a different instance of the code. A workaround like this must be used: @@ -22,7 +23,7 @@ function workflowInclusiveInstanceOf(instance: unknown, type: Function): boolean ## Decision -Given the possibility of switching or adding other isolation methods in future, we opted for inside the vm. We'll also have another data transformer / payload interceptor layer that runs outside the vm, can use node modules, and operates on Payloads. +Given the possibility of switching or adding other isolation methods in future, we opted for inside the vm. We'll also have another data transformer / payload interceptor layer that runs outside the vm, can use node modules and Promises, and operates on Payloads. ### General flow diff --git a/package.json b/package.json index 9e53021f8..aa35e668e 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "dependencies": { "@temporalio/client": "file:packages/client", "@temporalio/common": "file:packages/common", + "@temporalio/workflow-common": "file:packages/workflow-common", "@temporalio/create": "file:packages/create-project", "@temporalio/interceptors-opentelemetry": "file:packages/interceptors-opentelemetry", "@temporalio/proto": "file:packages/proto", diff --git a/packages/client/src/async-completion-client.ts b/packages/client/src/async-completion-client.ts index d632fcee1..18b2046b8 100644 --- a/packages/client/src/async-completion-client.ts +++ b/packages/client/src/async-completion-client.ts @@ -3,10 +3,17 @@ import { ServerErrorResponse } from '@grpc/grpc-js'; import { Status } from '@grpc/grpc-js/build/src/constants'; import { DataConverter, - defaultDataConverter, + defaultPayloadConverter, + requirePayloadConverter, + defaultPayloadCodec, + encodeFailure, + PayloadCodec, + Payload, + toPayloads, ensureTemporalFailure, errorToFailure, filterNullAndUndefined, + PayloadConverter, } from '@temporalio/common'; import { Connection, WorkflowService } from './connection'; @@ -70,7 +77,7 @@ export type AsyncCompletionClientOptionsWithDefaults = Required { + const payloads = toPayloads(this.payloadConverter, value); + if (payloads === undefined) { + return undefined; + } else { + return this.payloadCodec.encode(payloads); + } + } + /** * Transforms grpc errors into well defined TS errors. */ @@ -112,7 +134,7 @@ export class AsyncCompletionClient { } throw new ActivityCompletionError(err.details || err.message); } - throw new ActivityCompletionError('Unexpeced failure'); + throw new ActivityCompletionError('Unexpected failure'); } /** @@ -131,14 +153,14 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, taskToken: taskTokenOrFullActivityId, - result: { payloads: await this.options.dataConverter.toPayloads(result) }, + result: { payloads: await this.convertToWire(result) }, }); } else { await this.service.respondActivityTaskCompletedById({ identity: this.options.identity, namespace: this.options.namespace, ...taskTokenOrFullActivityId, - result: { payloads: await this.options.dataConverter.toPayloads(result) }, + result: { payloads: await this.convertToWire(result) }, }); } } catch (err) { @@ -162,14 +184,17 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, taskToken: taskTokenOrFullActivityId, - failure: await errorToFailure(ensureTemporalFailure(err), this.options.dataConverter), + failure: await encodeFailure( + errorToFailure(ensureTemporalFailure(err), this.payloadConverter), + this.payloadCodec + ), }); } else { await this.service.respondActivityTaskFailedById({ identity: this.options.identity, namespace: this.options.namespace, ...taskTokenOrFullActivityId, - failure: await errorToFailure(err, this.options.dataConverter), + failure: await encodeFailure(errorToFailure(err, this.payloadConverter), this.payloadCodec), }); } } catch (err) { @@ -193,14 +218,14 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, taskToken: taskTokenOrFullActivityId, - details: { payloads: await this.options.dataConverter.toPayloads(details) }, + details: { payloads: toPayloads(this.payloadConverter, details) }, }); } else { await this.service.respondActivityTaskCanceledById({ identity: this.options.identity, namespace: this.options.namespace, ...taskTokenOrFullActivityId, - details: { payloads: await this.options.dataConverter.toPayloads(details) }, + details: { payloads: toPayloads(this.payloadConverter, details) }, }); } } catch (err) { @@ -224,7 +249,7 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, taskToken: taskTokenOrFullActivityId, - details: { payloads: await this.options.dataConverter.toPayloads(details) }, + details: { payloads: toPayloads(this.payloadConverter, details) }, }); if (cancelRequested) { throw new ActivityCancelledError('cancelled'); @@ -234,7 +259,7 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, ...taskTokenOrFullActivityId, - details: { payloads: await this.options.dataConverter.toPayloads(details) }, + details: { payloads: toPayloads(this.payloadConverter, details) }, }); if (cancelRequested) { throw new ActivityCancelledError('cancelled'); diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index d2f2fbd58..076f3e17b 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -18,7 +18,7 @@ export * from './workflow-options'; export * from './interceptors'; export { DataConverter, - defaultDataConverter, + defaultPayloadConverter, ActivityFailure, ApplicationFailure, ChildWorkflowFailure, diff --git a/packages/client/src/workflow-client.ts b/packages/client/src/workflow-client.ts index 8ac0e4050..ccba58242 100644 --- a/packages/client/src/workflow-client.ts +++ b/packages/client/src/workflow-client.ts @@ -6,7 +6,11 @@ import { arrayFromPayloads, mapToPayloads, DataConverter, - defaultDataConverter, + PayloadConverter, + defaultPayloadConverter, + requirePayloadConverter, + PayloadCodec, + defaultPayloadCodec, composeInterceptors, optionalFailureToOptionalError, Workflow, @@ -153,7 +157,7 @@ export type WorkflowClientOptionsWithDefaults = Required; export function defaultWorkflowClientOptions(): WorkflowClientOptionsWithDefaults { return { - dataConverter: defaultDataConverter, + dataConverter: {}, // The equivalent in Java is ManagementFactory.getRuntimeMXBean().getName() identity: `${process.pid}@${os.hostname()}`, interceptors: {}, @@ -203,8 +207,14 @@ export type WorkflowStartOptions = WithWorkflowAr */ export class WorkflowClient { public readonly options: WorkflowClientOptionsWithDefaults; + protected readonly payloadConverter: PayloadConverter = defaultPayloadConverter; + protected readonly payloadCodec: PayloadCodec; constructor(public readonly service: WorkflowService = new Connection().service, options?: WorkflowClientOptions) { + if (options?.dataConverter?.payloadConverterPath) { + this.payloadConverter = requirePayloadConverter(options.dataConverter.payloadConverterPath); + } + this.payloadCodec = options?.dataConverter?.payloadCodec || defaultPayloadCodec; this.options = { ...defaultWorkflowClientOptions(), ...options }; } @@ -367,7 +377,7 @@ export class WorkflowClient { // Note that we can only return one value from our workflow function in JS. // Ignore any other payloads in result const [result] = await arrayFromPayloads( - this.options.dataConverter, + this.payloadConverter, ev.workflowExecutionCompletedEventAttributes.result?.payloads ); return result as any; @@ -380,16 +390,13 @@ export class WorkflowClient { const { failure } = ev.workflowExecutionFailedEventAttributes; throw new WorkflowFailedError( 'Workflow execution failed', - await optionalFailureToOptionalError(failure, this.options.dataConverter), + optionalFailureToOptionalError(failure, this.payloadConverter), RetryState.RETRY_STATE_NON_RETRYABLE_FAILURE ); } else if (ev.workflowExecutionCanceledEventAttributes) { const failure = new CancelledFailure( 'Workflow canceled', - await arrayFromPayloads( - this.options.dataConverter, - ev.workflowExecutionCanceledEventAttributes.details?.payloads - ) + await arrayFromPayloads(this.payloadConverter, ev.workflowExecutionCanceledEventAttributes.details?.payloads) ); failure.stack = ''; throw new WorkflowFailedError( @@ -451,7 +458,7 @@ export class WorkflowClient { execution: input.workflowExecution, query: { queryType: input.queryType, - queryArgs: { payloads: await this.options.dataConverter.toPayloads(...input.args) }, + queryArgs: { payloads: this.payloadConverter.toPayloads(...input.args) }, }, }); if (response.queryRejected) { @@ -464,7 +471,7 @@ export class WorkflowClient { throw new TypeError('Invalid response from server'); } // We ignore anything but the first result - return this.options.dataConverter.fromPayloads(0, response.queryResult?.payloads); + return this.payloadConverter.fromPayloads(0, response.queryResult?.payloads); } /** @@ -480,7 +487,7 @@ export class WorkflowClient { requestId: uuid4(), // control is unused, signalName: input.signalName, - input: { payloads: await this.options.dataConverter.toPayloads(...input.args) }, + input: { payloads: this.payloadConverter.toPayloads(...input.args) }, }); } @@ -499,9 +506,9 @@ export class WorkflowClient { workflowId: options.workflowId, workflowIdReusePolicy: options.workflowIdReusePolicy, workflowType: { name: workflowType }, - input: { payloads: await dataConverter.toPayloads(...options.args) }, + input: { payloads: dataConverter.toPayloads(...options.args) }, signalName, - signalInput: { payloads: await dataConverter.toPayloads(...signalArgs) }, + signalInput: { payloads: dataConverter.toPayloads(...signalArgs) }, taskQueue: { kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED, name: options.taskQueue, @@ -510,10 +517,10 @@ export class WorkflowClient { workflowRunTimeout: options.workflowRunTimeout, workflowTaskTimeout: options.workflowTaskTimeout, retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined, - memo: options.memo ? { fields: await mapToPayloads(dataConverter, options.memo) } : undefined, + memo: options.memo ? { fields: mapToPayloads(dataConverter, options.memo) } : undefined, searchAttributes: options.searchAttributes ? { - indexedFields: await mapToPayloads(dataConverter, options.searchAttributes), + indexedFields: mapToPayloads(dataConverter, options.searchAttributes), } : undefined, cronSchedule: options.cronSchedule, @@ -537,7 +544,7 @@ export class WorkflowClient { workflowId: opts.workflowId, workflowIdReusePolicy: opts.workflowIdReusePolicy, workflowType: { name }, - input: { payloads: await dataConverter.toPayloads(...opts.args) }, + input: { payloads: dataConverter.toPayloads(...opts.args) }, taskQueue: { kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED, name: opts.taskQueue, @@ -546,10 +553,10 @@ export class WorkflowClient { workflowRunTimeout: opts.workflowRunTimeout, workflowTaskTimeout: opts.workflowTaskTimeout, retryPolicy: opts.retry ? compileRetryPolicy(opts.retry) : undefined, - memo: opts.memo ? { fields: await mapToPayloads(dataConverter, opts.memo) } : undefined, + memo: opts.memo ? { fields: mapToPayloads(dataConverter, opts.memo) } : undefined, searchAttributes: opts.searchAttributes ? { - indexedFields: await mapToPayloads(dataConverter, opts.searchAttributes), + indexedFields: mapToPayloads(dataConverter, opts.searchAttributes), } : undefined, cronSchedule: opts.cronSchedule, @@ -571,7 +578,7 @@ export class WorkflowClient { namespace: this.options.namespace, identity: this.options.identity, ...input, - details: { payloads: await this.options.dataConverter.toPayloads(input.details) }, + details: { payloads: this.payloadConverter.toPayloads(input.details) }, }); } diff --git a/packages/common/src/codec-helpers.ts b/packages/common/src/codec-helpers.ts new file mode 100644 index 000000000..a67858efd --- /dev/null +++ b/packages/common/src/codec-helpers.ts @@ -0,0 +1,66 @@ +import { PayloadCodec, ProtoFailure } from '@temporalio/workflow-common'; + +/** + * Run `codec.encode()` on the {@link Payload}s in a {@link ProtoFailure}. + */ +export async function encodeFailure(failure: ProtoFailure, codec: PayloadCodec): Promise { + if (failure.cause) { + await encodeFailure(failure.cause, codec); + } + + if (failure.applicationFailureInfo?.details?.payloads?.length) { + failure.applicationFailureInfo.details.payloads = await codec.encode( + failure.applicationFailureInfo.details.payloads + ); + } + if (failure.timeoutFailureInfo?.lastHeartbeatDetails?.payloads?.length) { + failure.timeoutFailureInfo.lastHeartbeatDetails.payloads = await codec.encode( + failure.timeoutFailureInfo.lastHeartbeatDetails.payloads + ); + } + if (failure.canceledFailureInfo?.details?.payloads?.length) { + failure.canceledFailureInfo.details.payloads = await codec.encode(failure.canceledFailureInfo.details.payloads); + } + if (failure.resetWorkflowFailureInfo?.lastHeartbeatDetails?.payloads?.length) { + failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads = await codec.encode( + failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads + ); + } + return failure; +} + +// export async function deserializeFailure( +// failure: ProtoFailure, +// dataConverter: DataConverter +// ): Promise { +// const deserializedFailure = failure as DeserializedFailure; +// if (failure.cause) { +// await deserializeFailure(failure.cause, dataConverter); +// } + +// if (failure.applicationFailureInfo?.details) { +// deserializedFailure.applicationFailureInfo.details.payloads = await arrayFromPayloads( +// dataConverter, +// failure.applicationFailureInfo.details.payloads +// ); +// } +// if (failure.timeoutFailureInfo?.lastHeartbeatDetails?.payloads) { +// deserializedFailure.timeoutFailureInfo.lastHeartbeatDetails.payloads = await dataConverter.fromPayloads( +// 0, +// failure.timeoutFailureInfo.lastHeartbeatDetails?.payloads +// ); +// } +// if (failure.canceledFailureInfo?.details?.payloads) { +// deserializedFailure.canceledFailureInfo.details.payloads = await arrayFromPayloads( +// dataConverter, +// failure.canceledFailureInfo.details.payloads +// ); +// } +// if (failure.resetWorkflowFailureInfo?.lastHeartbeatDetails?.payloads) { +// deserializedFailure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads = await arrayFromPayloads( +// dataConverter, +// failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads +// ); +// } +// return deserializedFailure; +// } diff --git a/packages/common/src/otel.ts b/packages/common/src/otel.ts index b94994678..b185fd44c 100644 --- a/packages/common/src/otel.ts +++ b/packages/common/src/otel.ts @@ -1,5 +1,5 @@ import * as otel from '@opentelemetry/api'; -import { defaultDataConverter } from '@temporalio/workflow-common'; +import { defaultPayloadConverter } from '@temporalio/workflow-common'; import { Headers } from '@temporalio/workflow-common'; /** Default trace header for opentelemetry interceptors */ @@ -19,7 +19,7 @@ export async function extractContextFromHeaders(headers: Headers): Promise = await defaultDataConverter.fromPayload(encodedSpanContext); + const textMap: Record = defaultPayloadConverter.fromPayload(encodedSpanContext); return otel.propagation.extract(otel.context.active(), textMap, otel.defaultTextMapGetter); } @@ -41,7 +41,7 @@ export async function extractSpanContextFromHeaders(headers: Headers): Promise { const carrier = {}; otel.propagation.inject(otel.context.active(), carrier, otel.defaultTextMapSetter); - return { ...headers, [TRACE_HEADER]: await defaultDataConverter.toPayload(carrier) }; + return { ...headers, [TRACE_HEADER]: defaultPayloadConverter.toPayload(carrier) }; } /** diff --git a/packages/interceptors-opentelemetry/src/client/index.ts b/packages/interceptors-opentelemetry/src/client/index.ts index d1b391277..ae95a1167 100644 --- a/packages/interceptors-opentelemetry/src/client/index.ts +++ b/packages/interceptors-opentelemetry/src/client/index.ts @@ -1,7 +1,7 @@ import * as otel from '@opentelemetry/api'; import { DataConverter, - defaultDataConverter, + defaultPayloadConverter, Next, WorkflowClientCallsInterceptor, WorkflowStartInput, @@ -25,7 +25,7 @@ export class OpenTelemetryWorkflowClientCallsInterceptor implements WorkflowClie protected readonly dataConverter: DataConverter; constructor(options?: InterceptorOptions) { - this.dataConverter = options?.dataConverter ?? defaultDataConverter; + this.dataConverter = options?.dataConverter ?? defaultPayloadConverter; this.tracer = options?.tracer ?? otel.trace.getTracer('@temporalio/interceptor-client'); } diff --git a/packages/interceptors-opentelemetry/src/worker/index.ts b/packages/interceptors-opentelemetry/src/worker/index.ts index 958004966..b1cae2a2d 100644 --- a/packages/interceptors-opentelemetry/src/worker/index.ts +++ b/packages/interceptors-opentelemetry/src/worker/index.ts @@ -7,7 +7,7 @@ import { ActivityExecuteInput, ActivityInboundCallsInterceptor, DataConverter, - defaultDataConverter, + defaultPayloadConverter, InjectedSink, Next, } from '@temporalio/worker'; @@ -30,7 +30,7 @@ export class OpenTelemetryActivityInboundInterceptor implements ActivityInboundC protected readonly dataConverter: DataConverter; constructor(protected readonly ctx: ActivityContext, options?: InterceptorOptions) { - this.dataConverter = options?.dataConverter ?? defaultDataConverter; + this.dataConverter = options?.dataConverter ?? defaultPayloadConverter; this.tracer = options?.tracer ?? otel.trace.getTracer('@temporalio/interceptor-activity'); } diff --git a/packages/test/src/mock-native-worker.ts b/packages/test/src/mock-native-worker.ts index 8c1a7fec4..f95b77a1b 100644 --- a/packages/test/src/mock-native-worker.ts +++ b/packages/test/src/mock-native-worker.ts @@ -2,7 +2,7 @@ import { lastValueFrom } from 'rxjs'; import { SpanContext } from '@opentelemetry/api'; import { coresdk } from '@temporalio/proto'; -import { defaultDataConverter, DataConverter, msToTs } from '@temporalio/common'; +import { defaultPayloadConverter, DataConverter, msToTs } from '@temporalio/common'; import { Worker as RealWorker, NativeWorkerLike, errors } from '@temporalio/worker/lib/worker'; import { compileWorkerOptions, @@ -131,7 +131,7 @@ export class MockNativeWorker implements NativeWorkerLike { public recordActivityHeartbeat(buffer: ArrayBuffer): void { const { taskToken, details } = coresdk.ActivityHeartbeat.decodeDelimited(new Uint8Array(buffer)); - const arg = defaultDataConverter.fromPayloads(0, details); + const arg = defaultPayloadConverter.fromPayloads(0, details); this.activityHeartbeatCallback!(taskToken, arg); } diff --git a/packages/test/src/data-converters/data-converter-bad-export.ts b/packages/test/src/payload-converters/payload-converter-bad-export.ts similarity index 100% rename from packages/test/src/data-converters/data-converter-bad-export.ts rename to packages/test/src/payload-converters/payload-converter-bad-export.ts diff --git a/packages/test/src/data-converters/data-converter-no-export.ts b/packages/test/src/payload-converters/payload-converter-no-export.ts similarity index 100% rename from packages/test/src/data-converters/data-converter-no-export.ts rename to packages/test/src/payload-converters/payload-converter-no-export.ts diff --git a/packages/test/src/data-converters/data-converter.ts b/packages/test/src/payload-converters/payload-converter.ts similarity index 51% rename from packages/test/src/data-converters/data-converter.ts rename to packages/test/src/payload-converters/payload-converter.ts index 28f089f25..e04aab7fc 100644 --- a/packages/test/src/data-converters/data-converter.ts +++ b/packages/test/src/payload-converters/payload-converter.ts @@ -1,6 +1,6 @@ -import { DefaultDataConverter } from '@temporalio/common'; +import { DefaultPayloadConverter } from '@temporalio/common'; import root, { foo } from '../../protos/root'; export const messageInstance = foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); -export const dataConverter = new DefaultDataConverter({ root }); +export const payloadConverter = new DefaultPayloadConverter({ root }); diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-data-converter.ts index fd012a66f..7393d7ddf 100644 --- a/packages/test/src/test-custom-data-converter.ts +++ b/packages/test/src/test-custom-data-converter.ts @@ -4,7 +4,7 @@ import { Worker } from '@temporalio/worker'; import test, { ExecutionContext } from 'ava'; import { v4 as uuid4 } from 'uuid'; import { protoActivity } from './activities'; -import { dataConverter, messageInstance } from './data-converters/data-converter'; +import { payloadConverter, messageInstance } from './payload-converters/payload-converter'; import { cleanStackTrace, RUN_INTEGRATION_TESTS } from './helpers'; import { defaultOptions, isolateFreeWorker } from './mock-native-worker'; import { protobufWorkflow } from './workflows'; @@ -32,12 +32,13 @@ function compareCompletion( } if (RUN_INTEGRATION_TESTS) { - test('Client and Worker work with provided dataConverter/dataConverterPath', async (t) => { + test('Client and Worker work with provided dataConverter', async (t) => { + const dataConverter = { payloadConverterPath: require.resolve('./payload-converters/payload-converter') }; const taskQueue = 'test-custom-data-converter'; const worker = await Worker.create({ ...defaultOptions, taskQueue, - dataConverterPath: require.resolve('./data-converters/data-converter'), + dataConverter, }); const connection = new Connection(); const client = new WorkflowClient(connection.service, { dataConverter }); @@ -54,35 +55,35 @@ if (RUN_INTEGRATION_TESTS) { }); } -test('Worker throws on invalid dataConverterPath', async (t) => { +test('Worker throws on invalid payloadConverterPath', async (t) => { await t.throwsAsync( isolateFreeWorker({ ...defaultOptions, - dataConverterPath: './wrong-path', + dataConverter: { payloadConverterPath: './wrong-path' }, }), { - message: /Could not find a file at the specified dataConverterPath/, + message: /Could not find a file at the specified payloadConverterPath/, } ); await t.throwsAsync( isolateFreeWorker({ ...defaultOptions, - dataConverterPath: require.resolve('./data-converters/data-converter-no-export'), + dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter-no-export') }, }), { - message: /The module at dataConverterPath .* does not have a `dataConverter` named export./, + message: /The module at payloadConverterPath .* does not have a `dataConverter` named export./, } ); await t.throwsAsync( isolateFreeWorker({ ...defaultOptions, - dataConverterPath: require.resolve('./data-converters/data-converter-bad-export'), + dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter-bad-export') }, }), { message: - /The `dataConverter` named export at dataConverterPath .* should be an instance of a class that implements the DataConverter interface./, + /The `dataConverter` named export at payloadConverterPath .* should be an instance of a class that implements the DataConverter interface./, } ); }); @@ -90,7 +91,7 @@ test('Worker throws on invalid dataConverterPath', async (t) => { test('Worker with proto data converter runs an activity and reports completion', async (t) => { const worker = await isolateFreeWorker({ ...defaultOptions, - dataConverterPath: require.resolve('./data-converters/data-converter'), + dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter') }, }); await runWorker(worker, async () => { @@ -99,11 +100,11 @@ test('Worker with proto data converter runs an activity and reports completion', taskToken, start: { activityType: 'protoActivity', - input: await dataConverter.toPayloads(messageInstance), + input: payloadConverter.toPayloads(messageInstance), }, }); compareCompletion(t, completion.result, { - completed: { result: await dataConverter.toPayload(await protoActivity(messageInstance)) }, + completed: { result: payloadConverter.toPayload(await protoActivity(messageInstance)) }, }); }); }); diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 7ddc3c942..135a14ff0 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -1,6 +1,6 @@ /* eslint @typescript-eslint/no-non-null-assertion: 0 */ import { Connection, WorkflowClient } from '@temporalio/client'; -import { DataConverterError, defaultDataConverter, DefaultDataConverter, ValueError } from '@temporalio/common'; +import { DataConverterError, defaultPayloadConverter, DefaultPayloadConverter, ValueError } from '@temporalio/common'; import { Core, DefaultLogger, Worker } from '@temporalio/worker'; import { CompositeDataConverter } from '@temporalio/workflow-common'; import { @@ -19,7 +19,7 @@ import { import test from 'ava'; import { v4 as uuid4 } from 'uuid'; import root from '../protos/root'; -import { messageInstance } from './data-converters/data-converter'; +import { messageInstance } from './payload-converters/payload-converter'; import { RUN_INTEGRATION_TESTS } from './helpers'; import { defaultOptions } from './mock-native-worker'; import { protobufWorkflow } from './workflows'; @@ -204,49 +204,40 @@ if (RUN_INTEGRATION_TESTS) { }); } -test('DefaultDataConverter converts protobufs', async (t) => { +test('DefaultPayloadConverter converts protobufs', async (t) => { const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); - const defaultDataConverterWithProtos = new DefaultDataConverter({ root }); + const defaultPayloadConverterWithProtos = new DefaultPayloadConverter({ root }); t.deepEqual( - await defaultDataConverterWithProtos.toPayload(instance), + defaultPayloadConverterWithProtos.toPayload(instance), // It will always use JSON because it appears before binary in the list await new ProtobufJsonPayloadConverter(root).toData(instance) ); }); -test('defaultDataConverter converts to payload by trying each converter in order', async (t) => { +test('defaultPayloadConverter converts to payload by trying each converter in order', async (t) => { const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); - t.deepEqual( - await defaultDataConverter.toPayload(instance), - await new ProtobufJsonPayloadConverter().toData(instance) - ); + t.deepEqual(defaultPayloadConverter.toPayload(instance), await new ProtobufJsonPayloadConverter().toData(instance)); - t.deepEqual(await defaultDataConverter.toPayload('abc'), await new JsonPayloadConverter().toData('abc')); - t.deepEqual(await defaultDataConverter.toPayload(undefined), await new UndefinedPayloadConverter().toData(undefined)); - t.deepEqual(await defaultDataConverter.toPayload(u8('abc')), await new BinaryPayloadConverter().toData(u8('abc'))); - await t.throwsAsync(async () => await defaultDataConverter.toPayload(0n), { + t.deepEqual(defaultPayloadConverter.toPayload('abc'), await new JsonPayloadConverter().toData('abc')); + t.deepEqual(defaultPayloadConverter.toPayload(undefined), await new UndefinedPayloadConverter().toData(undefined)); + t.deepEqual(defaultPayloadConverter.toPayload(u8('abc')), await new BinaryPayloadConverter().toData(u8('abc'))); + await t.throws(() => defaultPayloadConverter.toPayload(0n), { instanceOf: TypeError, message: 'Do not know how to serialize a BigInt', }); }); -test('defaultDataConverter converts from payload by payload type', async (t) => { - t.deepEqual(await defaultDataConverter.fromPayload((await new JsonPayloadConverter().toData('abc'))!), 'abc'); - t.deepEqual( - await defaultDataConverter.fromPayload((await new UndefinedPayloadConverter().toData(undefined))!), - undefined - ); - t.deepEqual( - await defaultDataConverter.fromPayload((await new BinaryPayloadConverter().toData(u8('abc')))!), - u8('abc') - ); +test('defaultPayloadConverter converts from payload by payload type', async (t) => { + t.deepEqual(defaultPayloadConverter.fromPayload(new JsonPayloadConverter().toData('abc')!), 'abc'); + t.deepEqual(defaultPayloadConverter.fromPayload(new UndefinedPayloadConverter().toData(undefined)!), undefined); + t.deepEqual(defaultPayloadConverter.fromPayload(new BinaryPayloadConverter().toData(u8('abc'))!), u8('abc')); await t.throwsAsync( - async () => await defaultDataConverter.fromPayload({ metadata: { [METADATA_ENCODING_KEY]: u8('not-supported') } }), + async () => defaultPayloadConverter.fromPayload({ metadata: { [METADATA_ENCODING_KEY]: u8('not-supported') } }), { instanceOf: ValueError, message: 'Unknown encoding: not-supported' } ); await t.throwsAsync( async () => - await defaultDataConverter.fromPayload({ + defaultPayloadConverter.fromPayload({ metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF }, }), { instanceOf: ValueError, message: 'Got payload with no data' } @@ -258,13 +249,11 @@ test('defaultDataConverter converts from payload by payload type', async (t) => message: 'Unable to deserialize protobuf message without `root` being provided', }; await t.throwsAsync( - async () => - await defaultDataConverter.fromPayload((await new ProtobufBinaryPayloadConverter(root).toData(instance))!), + async () => defaultPayloadConverter.fromPayload(new ProtobufBinaryPayloadConverter(root).toData(instance)!), protoError ); await t.throwsAsync( - async () => - await defaultDataConverter.fromPayload((await new ProtobufJsonPayloadConverter(root).toData(instance))!), + async () => defaultPayloadConverter.fromPayload(new ProtobufJsonPayloadConverter(root).toData(instance)!), protoError ); }); diff --git a/packages/test/src/test-integration.ts b/packages/test/src/test-integration.ts index 6db9728ab..cd2be5413 100644 --- a/packages/test/src/test-integration.ts +++ b/packages/test/src/test-integration.ts @@ -6,7 +6,7 @@ import dedent from 'dedent'; import { WorkflowClient } from '@temporalio/client'; import { ChildWorkflowFailure, - defaultDataConverter, + defaultPayloadConverter, RetryState, TerminatedFailure, TimeoutFailure, @@ -535,7 +535,7 @@ if (RUN_INTEGRATION_TESTS) { t.deepEqual(execution.workflowExecutionInfo?.memo, new iface.temporal.api.common.v1.Memo({ fields: {} })); t.deepEqual(Object.keys(execution.workflowExecutionInfo!.searchAttributes!.indexedFields!), ['BinaryChecksums']); - const checksums = await defaultDataConverter.fromPayload( + const checksums = defaultPayloadConverter.fromPayload( execution.workflowExecutionInfo!.searchAttributes!.indexedFields!.BinaryChecksums! ); t.true(checksums instanceof Array && checksums.length === 1); @@ -568,9 +568,9 @@ if (RUN_INTEGRATION_TESTS) { execution.workflowExecutionInfo?.type, new iface.temporal.api.common.v1.WorkflowType({ name: 'sleeper' }) ); - t.deepEqual(await defaultDataConverter.fromPayload(execution.workflowExecutionInfo!.memo!.fields!.a!), 'b'); + t.deepEqual(defaultPayloadConverter.fromPayload(execution.workflowExecutionInfo!.memo!.fields!.a!), 'b'); t.deepEqual( - await defaultDataConverter.fromPayload( + defaultPayloadConverter.fromPayload( execution.workflowExecutionInfo!.searchAttributes!.indexedFields!.CustomIntField! ), 3 @@ -653,7 +653,7 @@ if (RUN_INTEGRATION_TESTS) { namespace, execution: { workflowId: workflow.workflowId, runId: err.newExecutionRunId }, }); - const timeSlept = await defaultDataConverter.fromPayloads( + const timeSlept = defaultPayloadConverter.fromPayloads( 0, history?.events?.[0].workflowExecutionStartedEventAttributes?.input?.payloads ); diff --git a/packages/test/src/test-interceptors.ts b/packages/test/src/test-interceptors.ts index 6323db5e7..51a2e318d 100644 --- a/packages/test/src/test-interceptors.ts +++ b/packages/test/src/test-interceptors.ts @@ -11,7 +11,7 @@ import dedent from 'dedent'; import { Core, DefaultLogger, Worker } from '@temporalio/worker'; import { ApplicationFailure, TerminatedFailure } from '@temporalio/common'; import { Connection, WorkflowClient, WorkflowFailedError } from '@temporalio/client'; -import { defaultDataConverter, WorkflowInfo } from '@temporalio/workflow'; +import { defaultPayloadConverter, WorkflowInfo } from '@temporalio/workflow'; import { defaultOptions } from './mock-native-worker'; import { cleanStackTrace, RUN_INTEGRATION_TESTS } from './helpers'; import { @@ -39,7 +39,7 @@ if (RUN_INTEGRATION_TESTS) { () => ({ async execute(input, next) { const encoded = input.headers.message; - const receivedMessage = encoded ? defaultDataConverter.fromPayload(encoded) : ''; + const receivedMessage = encoded ? defaultPayloadConverter.fromPayload(encoded) : ''; return next({ ...input, args: [receivedMessage] }); }, }), @@ -58,7 +58,7 @@ if (RUN_INTEGRATION_TESTS) { ...input, headers: { ...input.headers, - message: await defaultDataConverter.toPayload(message), + message: defaultPayloadConverter.toPayload(message), }, }); }, @@ -75,7 +75,7 @@ if (RUN_INTEGRATION_TESTS) { signalArgs: [encoded], headers: { ...input.headers, - message: await defaultDataConverter.toPayload(message), + message: defaultPayloadConverter.toPayload(message), }, }); }, diff --git a/packages/test/src/test-worker-activities.ts b/packages/test/src/test-worker-activities.ts index 622f47ed0..810a212e9 100644 --- a/packages/test/src/test-worker-activities.ts +++ b/packages/test/src/test-worker-activities.ts @@ -3,7 +3,7 @@ import anyTest, { TestInterface, ExecutionContext } from 'ava'; import { v4 as uuid4 } from 'uuid'; import dedent from 'dedent'; import { coresdk } from '@temporalio/proto'; -import { defaultDataConverter } from '@temporalio/common'; +import { defaultPayloadConverter } from '@temporalio/common'; import { httpGet } from './activities'; import { Worker, isolateFreeWorker, defaultOptions } from './mock-native-worker'; import { withZeroesHTTPServer } from './zeroes-http-server'; @@ -55,11 +55,11 @@ test('Worker runs an activity and reports completion', async (t) => { taskToken, start: { activityType: 'httpGet', - input: await defaultDataConverter.toPayloads(url), + input: defaultPayloadConverter.toPayloads(url), }, }); compareCompletion(t, completion.result, { - completed: { result: await defaultDataConverter.toPayload(await httpGet(url)) }, + completed: { result: defaultPayloadConverter.toPayload(await httpGet(url)) }, }); }); }); @@ -73,7 +73,7 @@ test('Worker runs an activity and reports failure', async (t) => { taskToken, start: { activityType: 'throwAnError', - input: await defaultDataConverter.toPayloads(false, message), + input: defaultPayloadConverter.toPayloads(false, message), }, }); compareCompletion(t, completion.result, { @@ -101,7 +101,7 @@ test('Worker cancels activity and reports cancellation', async (t) => { taskToken, start: { activityType: 'waitForCancellation', - input: await defaultDataConverter.toPayloads(), + input: defaultPayloadConverter.toPayloads(), }, }, }); @@ -125,7 +125,7 @@ test('Activity Context AbortSignal cancels a fetch request', async (t) => { taskToken, start: { activityType: 'cancellableFetch', - input: await defaultDataConverter.toPayloads(`http://127.0.0.1:${port}`, false), + input: defaultPayloadConverter.toPayloads(`http://127.0.0.1:${port}`, false), }, }, }); @@ -148,7 +148,7 @@ test('Activity Context heartbeat is sent to core', async (t) => { taskToken, start: { activityType: 'progressiveSleep', - input: await defaultDataConverter.toPayloads(), + input: defaultPayloadConverter.toPayloads(), }, }); console.log('waiting heartbeat 1'); @@ -158,7 +158,7 @@ test('Activity Context heartbeat is sent to core', async (t) => { t.is(await worker.native.untilHeartbeat(taskToken), 3); console.log('waiting completion'); compareCompletion(t, (await completionPromise).result, { - completed: { result: await defaultDataConverter.toPayload(undefined) }, + completed: { result: defaultPayloadConverter.toPayload(undefined) }, }); }); }); @@ -171,7 +171,7 @@ test('Worker fails activity with proper message when it is not registered', asyn taskToken, start: { activityType: 'notFound', - input: await defaultDataConverter.toPayloads(), + input: defaultPayloadConverter.toPayloads(), }, }); t.regex( diff --git a/packages/test/src/test-workflows.ts b/packages/test/src/test-workflows.ts index 72eee6747..11d2009d7 100644 --- a/packages/test/src/test-workflows.ts +++ b/packages/test/src/test-workflows.ts @@ -4,7 +4,7 @@ import path from 'path'; import Long from 'long'; import dedent from 'dedent'; import { coresdk } from '@temporalio/proto'; -import { ApplicationFailure, defaultDataConverter, errorToFailure, msToTs, RetryState } from '@temporalio/common'; +import { ApplicationFailure, defaultPayloadConverter, errorToFailure, msToTs, RetryState } from '@temporalio/common'; import { WorkflowCodeBundler } from '@temporalio/worker/lib/workflow/bundler'; import { VMWorkflow, VMWorkflowCreator } from '@temporalio/worker/lib/workflow/vm'; import { DefaultLogger } from '@temporalio/worker/lib/logger'; @@ -214,7 +214,7 @@ async function makeQueryWorkflowJob( queryWorkflow: { queryId, queryType, - arguments: await defaultDataConverter.toPayloads(...queryArgs), + arguments: defaultPayloadConverter.toPayloads(...queryArgs), }, }; } @@ -225,7 +225,7 @@ async function makeSignalWorkflow( timestamp: number = Date.now() ): Promise { return makeActivation(timestamp, { - signalWorkflow: { signalName, input: await defaultDataConverter.toPayloads(...args) }, + signalWorkflow: { signalName, input: defaultPayloadConverter.toPayloads(...args) }, }); } @@ -322,7 +322,7 @@ test('successString', async (t) => { compareCompletion( t, req, - makeSuccess([makeCompleteWorkflowExecution(defaultDataConverter.toPayloadSync('success'))]) + makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayloadSync('success'))]) ); }); @@ -386,7 +386,11 @@ test('date', async (t) => { test('asyncWorkflow', async (t) => { const { workflowType } = t.context; const req = await activate(t, makeStartWorkflow(workflowType)); - compareCompletion(t, req, makeSuccess([makeCompleteWorkflowExecution(defaultDataConverter.toPayloadSync('async'))])); + compareCompletion( + t, + req, + makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayloadSync('async'))]) + ); }); test('deferredResolve', async (t) => { @@ -412,7 +416,7 @@ test('sleeper', async (t) => { test('with ms string - sleeper', async (t) => { const { logs, workflowType } = t.context; { - const req = await activate(t, makeStartWorkflow(workflowType, [defaultDataConverter.toPayloadSync('10s')])); + const req = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayloadSync('10s')])); compareCompletion(t, req, makeSuccess([makeStartTimerCommand({ seq: 1, startToFireTimeout: msToTs('10s') })])); } { @@ -491,7 +495,7 @@ test('trailingTimer', async (t) => { completion, makeSuccess([ makeStartTimerCommand({ seq: 3, startToFireTimeout: msToTs(1) }), - makeCompleteWorkflowExecution(await defaultDataConverter.toPayload('first')), + makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload('first')), ]) ); } @@ -742,7 +746,7 @@ test('cancelWorkflow', async (t) => { const url = 'https://temporal.io'; const { workflowType } = t.context; { - const req = await activate(t, makeStartWorkflow(workflowType, await defaultDataConverter.toPayloads(url))); + const req = await activate(t, makeStartWorkflow(workflowType, defaultPayloadConverter.toPayloads(url))); compareCompletion( t, req, @@ -751,7 +755,7 @@ test('cancelWorkflow', async (t) => { seq: 1, activityId: '1', activityType: 'httpGet', - arguments: await defaultDataConverter.toPayloads(url), + arguments: defaultPayloadConverter.toPayloads(url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -784,14 +788,14 @@ test('cancelWorkflow', async (t) => { seq: 3, activityId: '3', activityType: 'httpGet', - arguments: await defaultDataConverter.toPayloads(url), + arguments: defaultPayloadConverter.toPayloads(url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), ]) ); } - const result = await defaultDataConverter.toPayload(await activityFunctions.httpGet(url)); + const result = defaultPayloadConverter.toPayload(await activityFunctions.httpGet(url)); { const req = await activate( t, @@ -833,7 +837,7 @@ test('unblock - unblockOrCancel', async (t) => { makeSuccess([ makeRespondToQueryCommand({ queryId: '1', - succeeded: { response: await defaultDataConverter.toPayload(true) }, + succeeded: { response: defaultPayloadConverter.toPayload(true) }, }), ]) ); @@ -850,7 +854,7 @@ test('unblock - unblockOrCancel', async (t) => { makeSuccess([ makeRespondToQueryCommand({ queryId: '2', - succeeded: { response: await defaultDataConverter.toPayload(false) }, + succeeded: { response: defaultPayloadConverter.toPayload(false) }, }), ]) ); @@ -890,9 +894,9 @@ test('cancelTimerAltImpl', async (t) => { test('nonCancellable', async (t) => { const { workflowType } = t.context; const url = 'https://temporal.io'; - const result = await defaultDataConverter.toPayload({ test: true }); + const result = defaultPayloadConverter.toPayload({ test: true }); { - const completion = await activate(t, makeStartWorkflow(workflowType, [await defaultDataConverter.toPayload(url)])); + const completion = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayload(url)])); compareCompletion( t, completion, @@ -901,7 +905,7 @@ test('nonCancellable', async (t) => { seq: 1, activityId: '1', activityType: 'httpGetJSON', - arguments: await defaultDataConverter.toPayloads(url), + arguments: defaultPayloadConverter.toPayloads(url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -917,9 +921,9 @@ test('nonCancellable', async (t) => { test('resumeAfterCancellation', async (t) => { const { workflowType } = t.context; const url = 'https://temporal.io'; - const result = await defaultDataConverter.toPayload({ test: true }); + const result = defaultPayloadConverter.toPayload({ test: true }); { - const completion = await activate(t, makeStartWorkflow(workflowType, [await defaultDataConverter.toPayload(url)])); + const completion = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayload(url)])); compareCompletion( t, completion, @@ -928,7 +932,7 @@ test('resumeAfterCancellation', async (t) => { seq: 1, activityId: '1', activityType: 'httpGetJSON', - arguments: await defaultDataConverter.toPayloads(url), + arguments: defaultPayloadConverter.toPayloads(url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -953,7 +957,7 @@ test('handleExternalWorkflowCancellationWhileActivityRunning', async (t) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const completion = await activate( t, - makeStartWorkflow(workflowType, (await defaultDataConverter.toPayloads(url, data)) ?? []) + makeStartWorkflow(workflowType, defaultPayloadConverter.toPayloads(url, data) ?? []) ); compareCompletion( @@ -964,7 +968,7 @@ test('handleExternalWorkflowCancellationWhileActivityRunning', async (t) => { seq: 1, activityId: '1', activityType: 'httpPostJSON', - arguments: await defaultDataConverter.toPayloads(url, data), + arguments: defaultPayloadConverter.toPayloads(url, data), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -988,7 +992,7 @@ test('handleExternalWorkflowCancellationWhileActivityRunning', async (t) => { seq: 2, activityId: '2', activityType: 'cleanup', - arguments: await defaultDataConverter.toPayloads(url), + arguments: defaultPayloadConverter.toPayloads(url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -998,7 +1002,7 @@ test('handleExternalWorkflowCancellationWhileActivityRunning', async (t) => { { const completion = await activate( t, - makeResolveActivity(2, { completed: { result: await defaultDataConverter.toPayload(undefined) } }) + makeResolveActivity(2, { completed: { result: defaultPayloadConverter.toPayload(undefined) } }) ); compareCompletion(t, completion, makeSuccess([{ cancelWorkflowExecution: {} }])); } @@ -1008,7 +1012,7 @@ test('nestedCancellation', async (t) => { const { workflowType } = t.context; const url = 'https://temporal.io'; { - const completion = await activate(t, makeStartWorkflow(workflowType, [await defaultDataConverter.toPayload(url)])); + const completion = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayload(url)])); compareCompletion( t, @@ -1027,7 +1031,7 @@ test('nestedCancellation', async (t) => { { const completion = await activate( t, - makeResolveActivity(1, { completed: { result: await defaultDataConverter.toPayload(undefined) } }) + makeResolveActivity(1, { completed: { result: defaultPayloadConverter.toPayload(undefined) } }) ); compareCompletion( @@ -1039,7 +1043,7 @@ test('nestedCancellation', async (t) => { seq: 2, activityId: '2', activityType: 'httpPostJSON', - arguments: await defaultDataConverter.toPayloads(url, { some: 'data' }), + arguments: defaultPayloadConverter.toPayloads(url, { some: 'data' }), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1061,7 +1065,7 @@ test('nestedCancellation', async (t) => { activityId: '3', seq: 3, activityType: 'cleanup', - arguments: await defaultDataConverter.toPayloads(url), + arguments: defaultPayloadConverter.toPayloads(url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1071,7 +1075,7 @@ test('nestedCancellation', async (t) => { { const completion = await activate( t, - makeResolveActivity(3, { completed: { result: await defaultDataConverter.toPayload(undefined) } }) + makeResolveActivity(3, { completed: { result: defaultPayloadConverter.toPayload(undefined) } }) ); compareCompletion( t, @@ -1100,7 +1104,7 @@ test('sharedScopes', async (t) => { seq: idx, activityId: `${idx}`, activityType: 'httpGetJSON', - arguments: await defaultDataConverter.toPayloads(`http://url${idx}.ninja`), + arguments: defaultPayloadConverter.toPayloads(`http://url${idx}.ninja`), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }) @@ -1112,12 +1116,12 @@ test('sharedScopes', async (t) => { { const completion = await activate( t, - makeResolveActivity(2, { completed: { result: await defaultDataConverter.toPayload(result) } }) + makeResolveActivity(2, { completed: { result: defaultPayloadConverter.toPayload(result) } }) ); compareCompletion( t, completion, - makeSuccess([makeCompleteWorkflowExecution(await defaultDataConverter.toPayload(result))]) + makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload(result))]) ); } }); @@ -1135,7 +1139,7 @@ test('shieldAwaitedInRootScope', async (t) => { seq: 1, activityId: '1', activityType: 'httpGetJSON', - arguments: await defaultDataConverter.toPayloads(`http://example.com`), + arguments: defaultPayloadConverter.toPayloads(`http://example.com`), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1150,12 +1154,12 @@ test('shieldAwaitedInRootScope', async (t) => { { const completion = await activate( t, - makeResolveActivity(1, { completed: { result: await defaultDataConverter.toPayload(result) } }) + makeResolveActivity(1, { completed: { result: defaultPayloadConverter.toPayload(result) } }) ); compareCompletion( t, completion, - makeSuccess([makeCompleteWorkflowExecution(await defaultDataConverter.toPayload(result))]) + makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload(result))]) ); } }); @@ -1319,7 +1323,7 @@ test('cancelActivityAfterFirstCompletion', async (t) => { const url = 'https://temporal.io'; const { workflowType, logs } = t.context; { - const req = await activate(t, makeStartWorkflow(workflowType, await defaultDataConverter.toPayloads(url))); + const req = await activate(t, makeStartWorkflow(workflowType, defaultPayloadConverter.toPayloads(url))); compareCompletion( t, req, @@ -1328,7 +1332,7 @@ test('cancelActivityAfterFirstCompletion', async (t) => { seq: 1, activityId: '1', activityType: 'httpGet', - arguments: await defaultDataConverter.toPayloads(url), + arguments: defaultPayloadConverter.toPayloads(url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1338,7 +1342,7 @@ test('cancelActivityAfterFirstCompletion', async (t) => { { const req = await activate( t, - makeResolveActivity(1, { completed: { result: await defaultDataConverter.toPayload('response1') } }) + makeResolveActivity(1, { completed: { result: defaultPayloadConverter.toPayload('response1') } }) ); compareCompletion( t, @@ -1348,7 +1352,7 @@ test('cancelActivityAfterFirstCompletion', async (t) => { seq: 2, activityId: '2', activityType: 'httpGet', - arguments: await defaultDataConverter.toPayloads(url), + arguments: defaultPayloadConverter.toPayloads(url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1362,12 +1366,12 @@ test('cancelActivityAfterFirstCompletion', async (t) => { { const req = await activate( t, - makeResolveActivity(2, { completed: { result: await defaultDataConverter.toPayload('response2') } }) + makeResolveActivity(2, { completed: { result: defaultPayloadConverter.toPayload('response2') } }) ); compareCompletion( t, req, - makeSuccess([makeCompleteWorkflowExecution(await defaultDataConverter.toPayload(['response1', 'response2']))]) + makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload(['response1', 'response2']))]) ); } t.deepEqual(logs, [['Workflow cancelled while waiting on non cancellable scope']]); @@ -1379,7 +1383,7 @@ test('multipleActivitiesSingleTimeout', async (t) => { { const completion = await activate( t, - makeStartWorkflow(workflowType, await defaultDataConverter.toPayloads(urls, 1000)) + makeStartWorkflow(workflowType, defaultPayloadConverter.toPayloads(urls, 1000)) ); compareCompletion( t, @@ -1392,7 +1396,7 @@ test('multipleActivitiesSingleTimeout', async (t) => { seq: index + 1, activityId: `${index + 1}`, activityType: 'httpGetJSON', - arguments: await defaultDataConverter.toPayloads(url), + arguments: defaultPayloadConverter.toPayloads(url), startToCloseTimeout: msToTs('1s'), taskQueue: 'test', }) @@ -1432,7 +1436,7 @@ test('resolve activity with result - http', async (t) => { seq: 1, activityId: '1', activityType: 'httpGet', - arguments: await defaultDataConverter.toPayloads('https://temporal.io'), + arguments: defaultPayloadConverter.toPayloads('https://temporal.io'), startToCloseTimeout: msToTs('1 minute'), taskQueue: 'test', }), @@ -1443,13 +1447,13 @@ test('resolve activity with result - http', async (t) => { { const completion = await activate( t, - makeResolveActivity(1, { completed: { result: await defaultDataConverter.toPayload(result) } }) + makeResolveActivity(1, { completed: { result: defaultPayloadConverter.toPayload(result) } }) ); compareCompletion( t, completion, - makeSuccess([makeCompleteWorkflowExecution(defaultDataConverter.toPayloadSync(result))]) + makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayloadSync(result))]) ); } }); @@ -1466,7 +1470,7 @@ test('resolve activity with failure - http', async (t) => { seq: 1, activityId: '1', activityType: 'httpGet', - arguments: await defaultDataConverter.toPayloads('https://temporal.io'), + arguments: defaultPayloadConverter.toPayloads('https://temporal.io'), startToCloseTimeout: msToTs('1 minute'), taskQueue: 'test', }), @@ -1482,7 +1486,7 @@ test('resolve activity with failure - http', async (t) => { t, makeResolveActivity(1, { failed: { - failure: await errorToFailure(failure, defaultDataConverter), + failure: errorToFailure(failure, defaultPayloadConverter), }, }) ); @@ -1532,7 +1536,7 @@ test('continueAsNewSameWorkflow', async (t) => { continueAsNewWorkflowExecution: { workflowType, taskQueue: 'test', - arguments: await defaultDataConverter.toPayloads('signal'), + arguments: defaultPayloadConverter.toPayloads('signal'), }, }, ]) diff --git a/packages/test/src/workflows/data-converter.ts b/packages/test/src/workflows/data-converter.ts index 28f089f25..d22d2006a 100644 --- a/packages/test/src/workflows/data-converter.ts +++ b/packages/test/src/workflows/data-converter.ts @@ -1,6 +1,6 @@ -import { DefaultDataConverter } from '@temporalio/common'; +import { DefaultPayloadConverter } from '@temporalio/common'; import root, { foo } from '../../protos/root'; export const messageInstance = foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); -export const dataConverter = new DefaultDataConverter({ root }); +export const dataConverter = new DefaultPayloadConverter({ root }); diff --git a/packages/test/src/workflows/interceptor-example.ts b/packages/test/src/workflows/interceptor-example.ts index 271ffe22c..aaed91e76 100644 --- a/packages/test/src/workflows/interceptor-example.ts +++ b/packages/test/src/workflows/interceptor-example.ts @@ -1,7 +1,7 @@ import { executeChild, WorkflowInterceptors, - defaultDataConverter, + defaultPayloadConverter, sleep, condition, defineSignal, @@ -52,7 +52,7 @@ export const interceptors = (): WorkflowInterceptors => ({ { async execute(input, next) { const encoded = input.headers.message; - receivedMessage = encoded ? await defaultDataConverter.fromPayload(encoded) : ''; + receivedMessage = encoded ? defaultPayloadConverter.fromPayload(encoded) : ''; return next(input); }, async handleSignal(input, next) { @@ -71,7 +71,7 @@ export const interceptors = (): WorkflowInterceptors => ({ async scheduleActivity(input, next) { return next({ ...input, - headers: { ...input.headers, message: await defaultDataConverter.toPayload(receivedMessage) }, + headers: { ...input.headers, message: defaultPayloadConverter.toPayload(receivedMessage) }, }); }, async startTimer(input, next) { diff --git a/packages/worker/src/activity.ts b/packages/worker/src/activity.ts index 9cfd062e5..fa734380b 100644 --- a/packages/worker/src/activity.ts +++ b/packages/worker/src/activity.ts @@ -2,7 +2,7 @@ import { AbortController } from 'abort-controller'; import { ActivityFunction, composeInterceptors, - DataConverter, + PayloadConverter, ensureTemporalFailure, errorToFailure, CancelledFailure, @@ -28,7 +28,7 @@ export class Activity { constructor( public readonly info: Info, public readonly fn: ActivityFunction, - public readonly dataConverter: DataConverter, + public readonly dataConverter: PayloadConverter, public readonly heartbeatCallback: Context['heartbeat'], interceptors?: { inbound?: ActivityInboundCallsInterceptorFactory[]; @@ -63,7 +63,7 @@ export class Activity { try { const execute = composeInterceptors(this.interceptors.inbound, 'execute', (inp) => this.execute(inp)); const result = await execute(input); - return { completed: { result: await this.dataConverter.toPayload(result) } }; + return { completed: { result: this.dataConverter.toPayload(result) } }; } catch (err) { if (err instanceof Error && err.name === 'CompleteAsyncError') { return { willCompleteAsync: {} }; @@ -71,7 +71,7 @@ export class Activity { if (this.cancelRequested) { // Either a CancelledFailure that we threw or AbortError from AbortController if (err instanceof CancelledFailure) { - const failure = await errorToFailure(err, this.dataConverter); + const failure = errorToFailure(err, this.dataConverter); failure.stackTrace = undefined; return { cancelled: { failure } }; } else if (err instanceof Error && err.name === 'AbortError') { @@ -80,7 +80,7 @@ export class Activity { } return { failed: { - failure: await errorToFailure(ensureTemporalFailure(err), this.dataConverter), + failure: errorToFailure(ensureTemporalFailure(err), this.dataConverter), }, }; } diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 8d5dde913..7b9f482ed 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -8,7 +8,7 @@ * @module */ -export { State, Worker, DataConverter, defaultDataConverter, errors } from './worker'; +export { State, Worker, DataConverter, defaultPayloadConverter, errors } from './worker'; export { WorkerOptions, CompiledWorkerOptions } from './worker-options'; export { ServerOptions, TLSConfig } from './server-options'; export { Core, CompiledCoreOptions, CoreOptions, TelemetryOptions } from './core'; diff --git a/packages/worker/src/worker-options.ts b/packages/worker/src/worker-options.ts index f8f6f5fb2..1ddac27ff 100644 --- a/packages/worker/src/worker-options.ts +++ b/packages/worker/src/worker-options.ts @@ -1,7 +1,7 @@ import os from 'os'; import fs from 'fs'; import { resolve, dirname } from 'path'; -import { ActivityInterface, msToNumber } from '@temporalio/common'; +import { ActivityInterface, msToNumber, DataConverter } from '@temporalio/common'; import { WorkerInterceptors } from './interceptors'; import { InjectedSinks } from './sinks'; import { GiB } from './utils'; @@ -75,9 +75,9 @@ export interface WorkerOptions { shutdownSignals?: NodeJS.Signals[]; /** - * Path of a module with a `dataConverter` named export. `dataConverter` should be an instance of a class that implements {@link DataConverter}. + * Provide a custom {@link DataConverter}. */ - dataConverterPath?: string; + dataConverter?: DataConverter; /** * Maximum number of Activity tasks to execute concurrently. diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index 95c2933cb..0749c2ad6 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -23,8 +23,8 @@ import { tsToMs, errorToFailure, arrayFromPayloads, - DataConverter, - defaultDataConverter, + PayloadConverter, + defaultPayloadConverter, isValidDataConverter, errorMessage, errorCode, @@ -47,7 +47,7 @@ import * as errors from './errors'; import { childSpan, instrument, getTracer } from './tracing'; import { ActivityExecuteInput } from './interceptors'; export { IllegalStateError } from '@temporalio/common'; -export { DataConverter, defaultDataConverter, errors }; +export { PayloadConverter as DataConverter, defaultPayloadConverter, errors }; import { Core } from './core'; import { SpanContext } from '@opentelemetry/api'; import IWorkflowActivationJob = coresdk.workflow_activation.IWorkflowActivationJob; @@ -199,7 +199,7 @@ export class Worker { compiledOptions.nodeModulesPaths, compiledOptions.workflowsPath, compiledOptions.interceptors?.workflowModules, - options.dataConverterPath + options.dataConverter?.payloadConverterPath ); bundle = await bundler.createBundle(); nativeWorker.logger.info('Workflow bundle created', { size: `${toMB(bundle.length)}MB` }); @@ -217,14 +217,14 @@ export class Worker { workflowCreator = await VMWorkflowCreator.create( bundle, compiledOptions.isolateExecutionTimeoutMs, - options.dataConverterPath + options.dataConverter?.payloadConverterPath ); } else { workflowCreator = await ThreadedVMWorkflowCreator.create({ code: bundle, threadPoolSize: compiledOptions.workflowThreadPoolSize, isolateExecutionTimeoutMs: compiledOptions.isolateExecutionTimeoutMs, - dataConverterPath: options.dataConverterPath, + payloadConverterPath: options.dataConverter?.payloadConverterPath, }); } } @@ -236,35 +236,37 @@ export class Worker { } } - public static async getDataConverter(options: WorkerOptions): Promise { - if (options.dataConverterPath) { - let dataConverter: DataConverter; + public static async getDataConverter(options: WorkerOptions): Promise { + if (options.dataConverter?.payloadConverterPath) { + let dataConverter: PayloadConverter; try { - const dataConverterModule = await import(options.dataConverterPath); + const dataConverterModule = await import(options.dataConverter?.payloadConverterPath); dataConverter = dataConverterModule.dataConverter; } catch (error) { if (errorCode(error) === 'MODULE_NOT_FOUND') { - throw new Error(`Could not find a file at the specified dataConverterPath: '${options.dataConverterPath}'.`); + throw new Error( + `Could not find a file at the specified payloadConverterPath: '${options.dataConverter?.payloadConverterPath}'.` + ); } throw error; } if (dataConverter === undefined) { throw new Error( - `The module at dataConverterPath ('${options.dataConverterPath}') does not have a \`dataConverter\` named export.` + `The module at payloadConverterPath ('${options.dataConverter?.payloadConverterPath}') does not have a \`dataConverter\` named export.` ); } if (!isValidDataConverter(dataConverter)) { throw new Error( - `The \`dataConverter\` named export at dataConverterPath (${options.dataConverterPath}) should be an instance of a class that implements the DataConverter interface.` + `The \`dataConverter\` named export at payloadConverterPath (${options.dataConverter?.payloadConverterPath}) should be an instance of a class that implements the DataConverter interface.` ); } return dataConverter; } - return defaultDataConverter; + return defaultPayloadConverter; } /** @@ -277,7 +279,7 @@ export class Worker { */ protected readonly workflowCreator: WorkflowCreator | undefined, public readonly options: CompiledWorkerOptions, - protected readonly dataConverter: DataConverter = defaultDataConverter + protected readonly dataConverter: PayloadConverter = defaultPayloadConverter ) { this.tracer = getTracer(options.enableSDKTracing); } @@ -703,7 +705,7 @@ export class Worker { const completion = coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited({ runId: activation.runId, failed: { - failure: await errorToFailure(error, this.dataConverter), + failure: errorToFailure(error, this.dataConverter), }, }).finish(); // We do not dispose of the Workflow yet, wait to be evicted from Core. @@ -776,7 +778,7 @@ export class Worker { complete: () => this.log.debug('Heartbeats complete'), }), mergeMap(async ({ taskToken, details }) => { - const payload = await this.dataConverter.toPayload(details); + const payload = this.dataConverter.toPayload(details); const arr = coresdk.ActivityHeartbeat.encodeDelimited({ taskToken, details: [payload], @@ -1003,7 +1005,7 @@ type NonNullableObject = { [P in keyof T]-?: NonNullable }; async function extractActivityInfo( task: coresdk.activity_task.IActivityTask, isLocal: boolean, - dataConverter: DataConverter, + dataConverter: PayloadConverter, activityNamespace: string ): Promise { // NOTE: We trust core to supply all of these fields instead of checking for null and undefined everywhere @@ -1018,7 +1020,7 @@ async function extractActivityInfo( isLocal, activityType: start.activityType, workflowType: start.workflowType, - heartbeatDetails: await dataConverter.fromPayloads(0, start.heartbeatDetails), + heartbeatDetails: dataConverter.fromPayloads(0, start.heartbeatDetails), activityNamespace, workflowNamespace: start.workflowNamespace, scheduledTimestampMs: tsToMs(start.scheduledTime), diff --git a/packages/worker/src/workflow/bundler.ts b/packages/worker/src/workflow/bundler.ts index ef4dfd24e..0d7a72379 100644 --- a/packages/worker/src/workflow/bundler.ts +++ b/packages/worker/src/workflow/bundler.ts @@ -21,7 +21,7 @@ export class WorkflowCodeBundler { public readonly nodeModulesPaths: string[], public readonly workflowsPath: string, public readonly workflowInterceptorModules: string[] = [], - protected readonly dataConverterPath?: string + protected readonly payloadConverterPath?: string ) {} /** @@ -59,7 +59,7 @@ export class WorkflowCodeBundler { const entrypointPath = '/src/main.js'; this.genEntrypoint(vol, entrypointPath); - await this.bundle(ufs, entrypointPath, distDir, this.dataConverterPath); + await this.bundle(ufs, entrypointPath, distDir, this.payloadConverterPath); return ufs.readFileSync(path.join(distDir, 'main.js'), 'utf8'); } @@ -106,7 +106,7 @@ export class WorkflowCodeBundler { filesystem: typeof unionfs.ufs, entry: string, distDir: string, - dataConverterPath?: string + payloadConverterPath?: string ): Promise { const webpackConfig: webpack.Configuration = { resolve: { @@ -135,9 +135,9 @@ export class WorkflowCodeBundler { }, }; if (webpackConfig.resolve) { - if (dataConverterPath) { + if (payloadConverterPath) { webpackConfig.resolve.alias = { - __temporal_custom_data_converter$: dataConverterPath, + __temporal_custom_data_converter$: payloadConverterPath, }; } else { // Save 100KB from the bundle by not including the npm module, @@ -210,10 +210,10 @@ export interface BundleOptions { */ logger?: Logger; /** - * Path to a module with a `dataConverter` named export. - * `dataConverter` should be an instance of a class that extends `DataConverter`. + * Path to a module with a `payloadConverter` named export. + * `payloadConverter` should be an instance of a class that extends {@link DataConverter}. */ - dataConverterPath?: string; + payloadConverterPath?: string; } export async function bundleWorkflowCode(options: BundleOptions): Promise<{ code: string }> { @@ -226,7 +226,7 @@ export async function bundleWorkflowCode(options: BundleOptions): Promise<{ code nodeModulesPaths, options.workflowsPath, options.workflowInterceptorModules, - options.dataConverterPath + options.payloadConverterPath ); return { code: await bundler.createBundle() }; } diff --git a/packages/worker/src/workflow/threaded-vm.ts b/packages/worker/src/workflow/threaded-vm.ts index b23f0c8cf..189462319 100644 --- a/packages/worker/src/workflow/threaded-vm.ts +++ b/packages/worker/src/workflow/threaded-vm.ts @@ -121,7 +121,7 @@ export interface ThreadedVMWorkflowCreatorOptions { code: string; threadPoolSize: number; isolateExecutionTimeoutMs: number; - dataConverterPath?: string; + payloadConverterPath?: string; } /** @@ -139,14 +139,14 @@ export class ThreadedVMWorkflowCreator implements WorkflowCreator { threadPoolSize, code, isolateExecutionTimeoutMs, - dataConverterPath, + payloadConverterPath, }: ThreadedVMWorkflowCreatorOptions): Promise { const workerThreadClients = Array(threadPoolSize) .fill(0) .map(() => new WorkerThreadClient(new NodeWorker(require.resolve('./workflow-worker-thread')))); await Promise.all( workerThreadClients.map((client) => - client.send({ type: 'init', code, isolateExecutionTimeoutMs, dataConverterPath }) + client.send({ type: 'init', code, isolateExecutionTimeoutMs, payloadConverterPath }) ) ); return new this(workerThreadClients); diff --git a/packages/worker/src/workflow/vm.ts b/packages/worker/src/workflow/vm.ts index f7b2612b3..5028c4197 100644 --- a/packages/worker/src/workflow/vm.ts +++ b/packages/worker/src/workflow/vm.ts @@ -2,7 +2,7 @@ import vm from 'vm'; import { coresdk } from '@temporalio/proto'; import * as internals from '@temporalio/workflow/lib/worker-interface'; import { WorkflowInfo } from '@temporalio/workflow'; -import { defaultDataConverter, DataConverter, errorToFailure, IllegalStateError } from '@temporalio/common'; +import { defaultPayloadConverter, PayloadConverter, errorToFailure, IllegalStateError } from '@temporalio/common'; import { partition } from '../utils'; import { Workflow, WorkflowCreator, WorkflowCreateOptions } from './interface'; import { SinkCall } from '@temporalio/workflow/lib/sinks'; @@ -18,8 +18,8 @@ export class VMWorkflowCreator implements WorkflowCreator { constructor( script: vm.Script, public readonly isolateExecutionTimeoutMs: number, - protected readonly dataConverter: DataConverter, - protected readonly dataConverterPath?: string + protected readonly dataConverter: PayloadConverter, + protected readonly payloadConverterPath?: string ) { this.script = script; } @@ -46,7 +46,7 @@ export class VMWorkflowCreator implements WorkflowCreator { } ) as any; - await workflowModule.initRuntime({ useCustomDataConverter: !!this.dataConverterPath, ...options }); + await workflowModule.initRuntime({ useCustomDataConverter: !!this.payloadConverterPath, ...options }); return new VMWorkflow(options.info, context, workflowModule, isolateExecutionTimeoutMs, this.dataConverter); } @@ -84,15 +84,15 @@ export class VMWorkflowCreator implements WorkflowCreator { this: T, code: string, isolateExecutionTimeoutMs: number, - dataConverterPath?: string + payloadConverterPath?: string ): Promise> { const script = new vm.Script(code, { filename: 'workflow-isolate' }); - let dataConverter = defaultDataConverter; - if (dataConverterPath) { - // dataConverterPath was validated in Worker.getDataConverter - dataConverter = (await import(dataConverterPath)).dataConverter; + let dataConverter = defaultPayloadConverter; + if (payloadConverterPath) { + // payloadConverterPath was validated in Worker.getDataConverter + dataConverter = (await import(payloadConverterPath)).dataConverter; } - return new this(script, isolateExecutionTimeoutMs, dataConverter, dataConverterPath) as InstanceType; + return new this(script, isolateExecutionTimeoutMs, dataConverter, payloadConverterPath) as InstanceType; } /** @@ -116,7 +116,7 @@ export class VMWorkflow implements Workflow { protected context: vm.Context | undefined, readonly workflowModule: WorkflowModule, public readonly isolateExecutionTimeoutMs: number, - protected readonly dataConverter: DataConverter + protected readonly dataConverter: PayloadConverter ) {} /** @@ -187,7 +187,7 @@ export class VMWorkflow implements Workflow { if (this.unhandledRejection) { return coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited({ runId: activation.runId, - failed: { failure: await errorToFailure(this.unhandledRejection, this.dataConverter) }, + failed: { failure: errorToFailure(this.unhandledRejection, this.dataConverter) }, }).finish(); } return coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited(completion).finish(); diff --git a/packages/worker/src/workflow/workflow-worker-thread.ts b/packages/worker/src/workflow/workflow-worker-thread.ts index 1fb2863c0..a3319fea4 100644 --- a/packages/worker/src/workflow/workflow-worker-thread.ts +++ b/packages/worker/src/workflow/workflow-worker-thread.ts @@ -52,7 +52,7 @@ async function handleRequest({ requestId, input }: WorkerThreadRequest): Promise workflowCreator = await VMWorkflowCreator.create( input.code, input.isolateExecutionTimeoutMs, - input.dataConverterPath + input.payloadConverterPath ); return ok(requestId); case 'destroy': diff --git a/packages/worker/src/workflow/workflow-worker-thread/input.ts b/packages/worker/src/workflow/workflow-worker-thread/input.ts index 343c3c88b..612057b28 100644 --- a/packages/worker/src/workflow/workflow-worker-thread/input.ts +++ b/packages/worker/src/workflow/workflow-worker-thread/input.ts @@ -7,7 +7,7 @@ export interface Init { type: 'init'; isolateExecutionTimeoutMs: number; code: string; - dataConverterPath?: string; + payloadConverterPath?: string; } /** diff --git a/packages/workflow-common/src/converter/data-converter.ts b/packages/workflow-common/src/converter/data-converter.ts index 4099da986..35b739772 100644 --- a/packages/workflow-common/src/converter/data-converter.ts +++ b/packages/workflow-common/src/converter/data-converter.ts @@ -1,212 +1,32 @@ -import { ValueError } from '../errors'; -import { str, METADATA_ENCODING_KEY, Payload } from './types'; -import { - PayloadConverter, - UndefinedPayloadConverter, - BinaryPayloadConverter, - JsonPayloadConverter, - ProtobufJsonPayloadConverter, - ProtobufBinaryPayloadConverter, -} from './payload-converter'; +import { PayloadCodec } from './payload-codec'; /** - * Used by the framework to serialize/deserialize method parameters that need to be sent over the - * wire. + * When your data (arguments and return values) is sent over the wire and stored by Temporal Server, + * it is encoded in binary in a {@link Payload} Protobuf message. * - * Implement this in order to customize worker data serialization or use the default data converter which supports `Uint8Array`, Protobuf, and JSON serializables. + * The default `DataConverter` supports `Uint8Array`, protobuf.js objects, and JSON serializables (so if `JSON.stringify(yourArg)` works, the default data converter will work). + * + * Use a custom `DataConverter` to control the contents of your {@link Payload}s. + * Common reasons for using a custom `DataConverter` are: + * - Converting values that are not supported by the default `DataConverter` (for example, `JSON.stringify()` doesn't handle `BigInt`s, so if you want to return `{ total: 1000n }` from a Workflow, Signal, or Activity, you need your own `DataConverter`). + * - Encrypting values that may contain private information that you don't want stored in plaintext in Temporal Server's database. + * - Compressing values to reduce disk or network usage. + * + * To use your custom `DataConverter`, provide it to the {@link WorkflowClient}, {@link Worker}, and {@link bundleWorkflowCode} (if you use it): + * - `new WorkflowClient(..., dataConverter)` + * - `Worker.create(..., dataConverter)` + * - `bundleWorkflowCode(..., payloadConverterPath)` */ export interface DataConverter { - toPayload(value: T): Promise; - - fromPayload(payload: Payload): Promise; - /** - * Implements conversion of a list of values. - * - * @param values JS values to convert to Payloads. - * @return converted value - * @throws DataConverterError if conversion of the value passed as parameter failed for any - * reason. - */ - toPayloads(...values: unknown[]): Promise; - - /** - * Implements conversion of an array of values of different types. Useful for deserializing - * arguments of function invocations. - * - * @param index index of the value in the payloads - * @param content serialized value to convert to JS values. - * @return converted JS value - * @throws DataConverterError if conversion of the data passed as parameter failed for any - * reason. - */ - fromPayloads(index: number, content?: Payload[] | null): Promise; - - /** - * Sync conversion of single payload, used in the Workflow runtime - */ - toPayloadSync(value: T): Payload; - /** - * Sync conversion from a single payload, used in the Workflow runtime + * Path of a file that has a `payloadConverter` named export. + * `payloadConverter` should be an instance of a class that implements {@link PayloadConverter}. + * If no path is provided, {@link defaultPayloadConverter} is used. */ - fromPayloadSync(payload: Payload): T; - /** - * Sync conversion of all arguments, used in the Workflow runtime - * - * Implements conversion of a list of values. - * - * @param values JS values to convert to Payloads. - * @return converted value - * @throws DataConverterError if conversion of the value passed as parameter failed for any - * reason. - */ - toPayloadsSync(...values: unknown[]): Payload[] | undefined; + payloadConverterPath?: string; /** - * Sync version of {@link fromPayloads} + * A {@link PayloadCodec} instance. The default codec is a no-op. */ - fromPayloadsSync(index: number, content?: Payload[] | null): T; + payloadCodec?: PayloadCodec; } - -export const isValidDataConverter = (dataConverter: unknown): dataConverter is DataConverter => - typeof dataConverter === 'object' && - dataConverter !== null && - ['toPayload', 'toPayloads', 'fromPayload', 'fromPayloads'].every( - (method) => typeof (dataConverter as Record)[method] === 'function' - ); - -export class CompositeDataConverter implements DataConverter { - readonly converters: PayloadConverter[]; - readonly converterByEncoding: Map = new Map(); - - constructor(...converters: PayloadConverter[]) { - this.converters = converters; - for (const converter of converters) { - this.converterByEncoding.set(converter.encodingType, converter); - } - } - - public async toPayload(value: T): Promise { - for (const converter of this.converters) { - const result = await converter.toData(value); - if (result !== undefined) return result; - } - throw new ValueError(`Cannot serialize ${value}`); - } - - public toPayloadSync(value: T): Payload { - for (const converter of this.converters) { - const result = converter.toDataSync(value); - if (result !== undefined) return result; - } - throw new ValueError(`Cannot serialize ${value}`); - } - - public async fromPayload(payload: Payload): Promise { - if (payload.metadata === undefined || payload.metadata === null) { - throw new ValueError('Missing payload metadata'); - } - const encoding = str(payload.metadata[METADATA_ENCODING_KEY]); - const converter = this.converterByEncoding.get(encoding); - if (converter === undefined) { - throw new ValueError(`Unknown encoding: ${encoding}`); - } - return await converter.fromData(payload); - } - - public fromPayloadSync(payload: Payload): T { - if (payload.metadata === undefined || payload.metadata === null) { - throw new ValueError('Missing payload metadata'); - } - const encoding = str(payload.metadata[METADATA_ENCODING_KEY]); - const converter = this.converterByEncoding.get(encoding); - if (converter === undefined) { - throw new ValueError(`Unknown encoding: ${encoding}`); - } - return converter.fromDataSync(payload); - } - - public async toPayloads(...values: unknown[]): Promise { - if (values.length === 0) { - return undefined; - } - return await Promise.all(values.map((value) => this.toPayload(value))); - } - - public toPayloadsSync(...values: unknown[]): Payload[] | undefined { - if (values.length === 0) { - return undefined; - } - return values.map((value) => this.toPayloadSync(value)); - } - - public async fromPayloads(index: number, payloads?: Payload[] | null): Promise { - // To make adding arguments a backwards compatible change - if (payloads === undefined || payloads === null || index >= payloads.length) { - return undefined as any; - } - return await this.fromPayload(payloads[index]); - } - - public fromPayloadsSync(index: number, payloads?: Payload[] | null): T { - // To make adding arguments a backwards compatible change - if (payloads === undefined || payloads === null || index >= payloads.length) { - return undefined as any; - } - return this.fromPayloadSync(payloads[index]); - } -} - -export async function arrayFromPayloads(converter: DataConverter, content?: Payload[] | null): Promise { - if (!content) { - return []; - } - return await Promise.all(content.map((payload: Payload) => converter.fromPayload(payload))); -} - -export async function mapToPayloads( - converter: DataConverter, - source: Record -): Promise> { - return Object.fromEntries( - await Promise.all( - Object.entries(source).map(async ([k, v]): Promise<[K, Payload]> => [k as K, await converter.toPayload(v)]) - ) - ) as Record; -} - -export function arrayFromPayloadsSync(converter: DataConverter, content?: Payload[] | null): unknown[] { - if (!content) { - return []; - } - return content.map((payload: Payload) => converter.fromPayloadSync(payload)); -} - -export function mapToPayloadsSync( - converter: DataConverter, - source: Record -): Record { - return Object.fromEntries( - Object.entries(source).map(([k, v]): [K, Payload] => [k as K, converter.toPayloadSync(v)]) - ) as Record; -} - -export interface DefaultDataConverterOptions { - root?: Record; -} - -export class DefaultDataConverter extends CompositeDataConverter { - constructor({ root }: DefaultDataConverterOptions = {}) { - // Match the order used in other SDKs - // Go SDK: https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28 - super( - new UndefinedPayloadConverter(), - new BinaryPayloadConverter(), - new ProtobufJsonPayloadConverter(root), - new ProtobufBinaryPayloadConverter(root), - new JsonPayloadConverter() - ); - } -} - -export const defaultDataConverter = new DefaultDataConverter(); diff --git a/packages/workflow-common/src/converter/payload-codec.ts b/packages/workflow-common/src/converter/payload-codec.ts new file mode 100644 index 000000000..40cc02e1b --- /dev/null +++ b/packages/workflow-common/src/converter/payload-codec.ts @@ -0,0 +1,29 @@ +import { Payload } from './types'; + +/** + * `PayloadCodec` is an optional step that happens between the wire and the {@link PayloadConverter}: + * Temporal Server ↔️ Wire ↔️ PayloadCodec ↔️ PayloadConverter ↔️ User code + * + * Implement this to transform an array of {@link Payload}s to/from the format sent over the wire and stored by Temporal Server. + * Common transformations are encryption and compression. + */ +export interface PayloadCodec { + /** + * Encode an array of {@link Payload}s for sending over the wire. + * @param payloads May have length 0. + */ + encode(payloads: Payload[]): Promise; + + /** + * Decode an array of {@link Payload}s received from the wire. + */ + decode(payloads: Payload[]): Promise; +} + +/** + * No-op implementation of {@link PayloadCodec}. + */ +export const defaultPayloadCodec = { + encode: async (payloads: Payload[]): Promise => payloads, + decode: async (payloads: Payload[]): Promise => payloads, +}; diff --git a/packages/workflow-common/src/converter/payload-converter.ts b/packages/workflow-common/src/converter/payload-converter.ts index 8599a8b2c..a3e80c6a8 100644 --- a/packages/workflow-common/src/converter/payload-converter.ts +++ b/packages/workflow-common/src/converter/payload-converter.ts @@ -1,293 +1,180 @@ -import { ValueError, DataConverterError } from '../errors'; +import { ValueError, UnsupportedTypeError } from '../errors'; +import { str, METADATA_ENCODING_KEY, Payload } from './types'; import { - u8, - str, - Payload, - encodingTypes, - encodingKeys, - METADATA_ENCODING_KEY, - METADATA_MESSAGE_TYPE_KEY, -} from './types'; -import { isRecord, hasOwnProperty, hasOwnProperties } from '../type-helpers'; -import { errorMessage } from '../errors'; -import * as protoJsonSerializer from 'proto3-json-serializer'; -import type { Root, Type, Namespace, Message } from 'protobufjs'; + PayloadConverterWithEncoding, + UndefinedPayloadConverter, + BinaryPayloadConverter, + JsonPayloadConverter, + ProtobufJsonPayloadConverter, + ProtobufBinaryPayloadConverter, +} from './payload-converters'; +import { hasOwnProperty } from '../type-helpers'; /** - * Used by the framework to serialize/deserialize method parameters that need to be sent over the - * wire. + * Used by the framework to serialize/deserialize parameters and return values that need to be + * sent over the wire. * - * @author fateev + * This is called inside the [Workflow isolate](https://docs.temporal.io/docs/typescript/determinism). + * To write async code or use Node APIs (or use packages that use Node APIs), use a {@link PayloadCodec}. */ export interface PayloadConverter { - encodingType: string; - /** - * TODO: Fix comment in https://github.com/temporalio/sdk-java/blob/85593dbfa99bddcdf54c7196d2b73eeb23e94e9e/temporal-sdk/src/main/java/io/temporal/common/converter/DataConverter.java#L46 - * Implements conversion of value to payload - * - * @param value JS value to convert. - * @return converted value or `undefined` if unable to convert. - * @throws DataConverterException if conversion of the value passed as parameter failed for any - * reason. + * Converts a value to a {@link Payload}. + * @param value The value to convert. Example values include the Workflow args sent by the client and the values returned by a Workflow or Activity. */ - toData(value: unknown): Promise; + toPayload(value: T): Payload; /** - * Implements conversion of payload to value. - * - * @param content Serialized value to convert to a JS value. - * @return converted JS value - * @throws DataConverterException if conversion of the data passed as parameter failed for any - * reason. + * Converts a {@link Payload} back to a value. */ - fromData(content: Payload): Promise; - - /** - * Synchronous version of {@link toData}, used in the Workflow runtime because - * the async version limits the functionality of the runtime. - * - * Implements conversion of value to payload - * - * @param value JS value to convert. - * @return converted value or `undefined` if unable to convert. - * @throws DataConverterException if conversion of the value passed as parameter failed for any - * reason. - */ - toDataSync(value: unknown): Payload | undefined; - - /** - * Synchronous version of {@link fromData}, used in the Workflow runtime because - * the async version limits the functionality of the runtime. - * - * Implements conversion of payload to value. - * - * @param content Serialized value to convert to a JS value. - * @return converted JS value - * @throws DataConverterException if conversion of the data passed as parameter failed for any - * reason. - */ - fromDataSync(content: Payload): T; -} - -export abstract class AsyncFacadePayloadConverter implements PayloadConverter { - abstract encodingType: string; - abstract toDataSync(value: unknown): Payload | undefined; - abstract fromDataSync(content: Payload): T; - - public async toData(value: unknown): Promise { - return this.toDataSync(value); - } - - public async fromData(content: Payload): Promise { - return this.fromDataSync(content); - } -} - -/** - * Converts between JS undefined and NULL Payload - */ -export class UndefinedPayloadConverter extends AsyncFacadePayloadConverter { - public encodingType = encodingTypes.METADATA_ENCODING_NULL; - - public toDataSync(value: unknown): Payload | undefined { - if (value !== undefined) return undefined; // Can't encode - return { - metadata: { - [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL, - }, - }; - } - - public fromDataSync(_content: Payload): T { - return undefined as any; // Just return undefined - } + fromPayload(payload: Payload): T; } -/** - * Converts between non-undefined values and serialized JSON Payload - */ -export class JsonPayloadConverter extends AsyncFacadePayloadConverter { - public encodingType = encodingTypes.METADATA_ENCODING_JSON; - - public toDataSync(value: unknown): Payload | undefined { - if (value === undefined) return undefined; // Should be encoded with the UndefinedPayloadConverter - return { - metadata: { - [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_JSON, - }, - data: u8(JSON.stringify(value)), - }; - } - - public fromDataSync(content: Payload): T { - if (content.data === undefined || content.data === null) { - throw new ValueError('Got payload with no data'); - } - return JSON.parse(str(content.data)); - } -} +const isValidPayloadConverter = (PayloadConverter: unknown): PayloadConverter is PayloadConverter => + typeof PayloadConverter === 'object' && + PayloadConverter !== null && + ['toPayload', 'fromPayload'].every( + (method) => typeof (PayloadConverter as Record)[method] === 'function' + ); -/** - * Converts between binary data types and RAW Payload - */ -export class BinaryPayloadConverter extends AsyncFacadePayloadConverter { - public encodingType = encodingTypes.METADATA_ENCODING_RAW; +export class CompositePayloadConverter implements PayloadConverter { + readonly converters: PayloadConverterWithEncoding[]; + readonly converterByEncoding: Map = new Map(); - public toDataSync(value: unknown): Payload | undefined { - // TODO: support any DataView or ArrayBuffer? - if (!(value instanceof Uint8Array)) { - return undefined; + constructor(...converters: PayloadConverterWithEncoding[]) { + this.converters = converters; + for (const converter of converters) { + this.converterByEncoding.set(converter.encodingType, converter); } - return { - metadata: { - [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_RAW, - }, - data: value, - }; } - public fromDataSync(content: Payload): T { - // TODO: support any DataView or ArrayBuffer? - return content.data as any; - } -} - -abstract class ProtobufPayloadConverter extends AsyncFacadePayloadConverter { - protected readonly root: Root | undefined; - - // Don't use type Root here because root.d.ts doesn't export Root, so users would have to type assert - constructor(root?: unknown) { - super(); - - if (root) { - if (!isRoot(root)) { - throw new TypeError('root must be an instance of a protobufjs Root'); + public toPayload(value: T): Payload { + for (const converter of this.converters) { + try { + const result = converter.toPayload(value); + return result; + } catch (e: unknown) { + if (e instanceof UnsupportedTypeError) { + continue; + } else { + throw e; + } } - - this.root = root; } + throw new ValueError(`Cannot serialize ${value}`); } - protected validatePayload(content: Payload): { messageType: Type; data: Uint8Array } { - if (content.data === undefined || content.data === null) { - throw new ValueError('Got payload with no data'); - } - if (!content.metadata || !(METADATA_MESSAGE_TYPE_KEY in content.metadata)) { - throw new ValueError(`Got protobuf payload without metadata.${METADATA_MESSAGE_TYPE_KEY}`); + public fromPayload(payload: Payload): T { + if (payload.metadata === undefined || payload.metadata === null) { + throw new ValueError('Missing payload metadata'); } - if (!this.root) { - throw new DataConverterError('Unable to deserialize protobuf message without `root` being provided'); - } - - const messageTypeName = str(content.metadata[METADATA_MESSAGE_TYPE_KEY]); - let messageType; - try { - messageType = this.root.lookupType(messageTypeName); - } catch (e) { - if (errorMessage(e)?.includes('no such type')) { - throw new DataConverterError( - `Got a \`${messageTypeName}\` protobuf message but cannot find corresponding message class in \`root\`` - ); - } - - throw e; + const encoding = str(payload.metadata[METADATA_ENCODING_KEY]); + const converter = this.converterByEncoding.get(encoding); + if (converter === undefined) { + throw new ValueError(`Unknown encoding: ${encoding}`); } - - return { messageType, data: content.data }; - } - - protected constructPayload({ messageTypeName, message }: { messageTypeName: string; message: Uint8Array }): Payload { - return { - metadata: { - [METADATA_ENCODING_KEY]: u8(this.encodingType), - [METADATA_MESSAGE_TYPE_KEY]: u8(messageTypeName), - }, - data: message, - }; + return converter.fromPayload(payload); } } /** - * Converts between protobufjs Message instances and serialized Protobuf Payload + * Implements conversion of a list of values. + * + * @param converter + * @param values JS values to convert to Payloads. + * @return converted value + * @throws PayloadConverterError if conversion of the value passed as parameter failed for any + * reason. */ -export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter { - public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF; - - constructor(root?: unknown) { - super(root); - } - - public toDataSync(value: unknown): Payload | undefined { - if (!isProtobufMessage(value)) { - return undefined; - } - - return this.constructPayload({ - messageTypeName: getNamespacedTypeName(value.$type), - message: value.$type.encode(value).finish(), - }); - } - - public fromDataSync(content: Payload): T { - const { messageType, data } = this.validatePayload(content); - return messageType.decode(data) as unknown as T; +export function toPayloads(converter: PayloadConverter, ...values: unknown[]): Payload[] | undefined { + if (values.length === 0) { + return undefined; } + return values.map((value) => converter.toPayload(value)); } /** - * Converts between protobufjs Message instances and serialized JSON Payload + * Implements conversion of an array of values of different types. Useful for deserializing + * arguments of function invocations. + * + * @param converter + * @param index index of the value in the payloads + * @param payloads serialized value to convert to JS values. + * @return converted JS value + * @throws PayloadConverterError if conversion of the data passed as parameter failed for any + * reason. */ -export class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter { - public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF_JSON; - - constructor(root?: unknown) { - super(root); +export function fromPayloadsAtIndex(converter: PayloadConverter, index: number, payloads?: Payload[] | null): T { + // To make adding arguments a backwards compatible change + if (payloads === undefined || payloads === null || index >= payloads.length) { + return undefined as any; } + return converter.fromPayload(payloads[index]); +} - public toDataSync(value: unknown): Payload | undefined { - if (!isProtobufMessage(value)) { - return undefined; +export async function importPayloadConverter(path: string): Promise { + const module = await import(path); + if (hasOwnProperty(module, 'payloadConverter')) { + if (isValidPayloadConverter(module.payloadConverter)) { + return module.payloadConverter; + } else { + throw new ValueError( + `payloadConverter export at ${path} must be an object with toPayload and fromPayload methods` + ); } + } else { + throw new ValueError(`Module ${path} does not have a \`payloadConverter\` named export`); + } +} - const jsonValue = protoJsonSerializer.toProto3JSON(value); - - return this.constructPayload({ - messageTypeName: getNamespacedTypeName(value.$type), - message: u8(JSON.stringify(jsonValue)), - }); +// For use outside of the Workflow vm +export function requirePayloadConverter(path: string): PayloadConverter { + const module = require(path); // eslint-disable-line @typescript-eslint/no-var-requires + if (hasOwnProperty(module, 'payloadConverter')) { + if (isValidPayloadConverter(module.payloadConverter)) { + return module.payloadConverter; + } else { + throw new ValueError( + `payloadConverter export at ${path} must be an object with toPayload and fromPayload methods` + ); + } + } else { + throw new ValueError(`Module ${path} does not have a \`payloadConverter\` named export`); } +} - public fromDataSync(content: Payload): T { - const { messageType, data } = this.validatePayload(content); - return protoJsonSerializer.fromProto3JSON(messageType, JSON.parse(str(data))) as unknown as T; +export function arrayFromPayloads(converter: PayloadConverter, content?: Payload[] | null): unknown[] { + if (!content) { + return []; } + return content.map((payload: Payload) => converter.fromPayload(payload)); } -function isProtobufType(type: unknown): type is Type { - return ( - isRecord(type) && - type.constructor.name === 'Type' && - hasOwnProperties(type, ['parent', 'name', 'create', 'encode', 'decode']) && - typeof type.name === 'string' && - typeof type.create === 'function' && - typeof type.encode === 'function' && - typeof type.decode === 'function' - ); +export function mapToPayloads( + converter: PayloadConverter, + source: Record +): Record { + return Object.fromEntries( + Object.entries(source).map(([k, v]): [K, Payload] => [k as K, converter.toPayload(v)]) + ) as Record; } -function isProtobufMessage(value: unknown): value is Message { - return isRecord(value) && hasOwnProperty(value, '$type') && isProtobufType(value.$type); +export interface DefaultPayloadConverterOptions { + root?: Record; } -function getNamespacedTypeName(node: Type | Namespace): string { - if (node.parent && !isRoot(node.parent)) { - return getNamespacedTypeName(node.parent) + '.' + node.name; - } else { - return node.name; +export class DefaultPayloadConverter extends CompositePayloadConverter { + constructor({ root }: DefaultPayloadConverterOptions = {}) { + // Match the order used in other SDKs + // Go SDK: https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28 + super( + new UndefinedPayloadConverter(), + new BinaryPayloadConverter(), + new ProtobufJsonPayloadConverter(root), + new ProtobufBinaryPayloadConverter(root), + new JsonPayloadConverter() + ); } } -function isRoot(root: unknown): root is Root { - return isRecord(root) && root.constructor.name === 'Root'; -} +export const defaultPayloadConverter = new DefaultPayloadConverter(); diff --git a/packages/workflow-common/src/converter/payload-converters.ts b/packages/workflow-common/src/converter/payload-converters.ts new file mode 100644 index 000000000..f63b0aea2 --- /dev/null +++ b/packages/workflow-common/src/converter/payload-converters.ts @@ -0,0 +1,230 @@ +import { ValueError, PayloadConverterError, UnsupportedTypeError } from '../errors'; +import { + u8, + str, + Payload, + encodingTypes, + EncodingType, + encodingKeys, + METADATA_ENCODING_KEY, + METADATA_MESSAGE_TYPE_KEY, +} from './types'; +import { isRecord, hasOwnProperty, hasOwnProperties } from '../type-helpers'; +import { errorMessage } from '../errors'; +import * as protoJsonSerializer from 'proto3-json-serializer'; +import type { Root, Type, Namespace, Message } from 'protobufjs'; +import { PayloadConverter } from './payload-converter'; + +export interface PayloadConverterWithEncoding extends PayloadConverter { + readonly encodingType: EncodingType; +} + +/** + * Converts between JS undefined and NULL Payload + */ +export class UndefinedPayloadConverter implements PayloadConverterWithEncoding { + public encodingType = encodingTypes.METADATA_ENCODING_NULL; + + public toPayload(value: unknown): Payload { + if (value !== undefined) throw new UnsupportedTypeError(); // Can't encode + return { + metadata: { + [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL, + }, + }; + } + + public fromPayload(_content: Payload): T { + return undefined as any; // Just return undefined + } +} + +/** + * Converts between non-undefined values and serialized JSON Payload + */ +export class JsonPayloadConverter implements PayloadConverterWithEncoding { + public encodingType = encodingTypes.METADATA_ENCODING_JSON; + + public toPayload(value: unknown): Payload { + if (value === undefined) throw new UnsupportedTypeError(); // Should be encoded with the UndefinedPayloadConverter + return { + metadata: { + [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_JSON, + }, + data: u8(JSON.stringify(value)), + }; + } + + public fromPayload(content: Payload): T { + if (content.data === undefined || content.data === null) { + throw new ValueError('Got payload with no data'); + } + return JSON.parse(str(content.data)); + } +} + +/** + * Converts between binary data types and RAW Payload + */ +export class BinaryPayloadConverter implements PayloadConverterWithEncoding { + public encodingType = encodingTypes.METADATA_ENCODING_RAW; + + public toPayload(value: unknown): Payload { + // TODO: support any DataView or ArrayBuffer? + if (!(value instanceof Uint8Array)) { + throw new UnsupportedTypeError(); + } + return { + metadata: { + [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_RAW, + }, + data: value, + }; + } + + public fromPayload(content: Payload): T { + // TODO: support any DataView or ArrayBuffer? + return content.data as any; + } +} + +abstract class ProtobufPayloadConverter implements PayloadConverterWithEncoding { + protected readonly root: Root | undefined; + public abstract encodingType: EncodingType; + + public abstract toPayload(value: T): Payload; + public abstract fromPayload(payload: Payload): T; + + // Don't use type Root here because root.d.ts doesn't export Root, so users would have to type assert + constructor(root?: unknown) { + if (root) { + if (!isRoot(root)) { + throw new TypeError('root must be an instance of a protobufjs Root'); + } + + this.root = root; + } + } + + protected validatePayload(content: Payload): { messageType: Type; data: Uint8Array } { + if (content.data === undefined || content.data === null) { + throw new ValueError('Got payload with no data'); + } + if (!content.metadata || !(METADATA_MESSAGE_TYPE_KEY in content.metadata)) { + throw new ValueError(`Got protobuf payload without metadata.${METADATA_MESSAGE_TYPE_KEY}`); + } + if (!this.root) { + throw new PayloadConverterError('Unable to deserialize protobuf message without `root` being provided'); + } + + const messageTypeName = str(content.metadata[METADATA_MESSAGE_TYPE_KEY]); + let messageType; + try { + messageType = this.root.lookupType(messageTypeName); + } catch (e) { + if (errorMessage(e)?.includes('no such type')) { + throw new PayloadConverterError( + `Got a \`${messageTypeName}\` protobuf message but cannot find corresponding message class in \`root\`` + ); + } + + throw e; + } + + return { messageType, data: content.data }; + } + + protected constructPayload({ messageTypeName, message }: { messageTypeName: string; message: Uint8Array }): Payload { + return { + metadata: { + [METADATA_ENCODING_KEY]: u8(this.encodingType), + [METADATA_MESSAGE_TYPE_KEY]: u8(messageTypeName), + }, + data: message, + }; + } +} + +/** + * Converts between protobufjs Message instances and serialized Protobuf Payload + */ +export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter { + public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF; + + constructor(root?: unknown) { + super(root); + } + + public toPayload(value: unknown): Payload { + if (!isProtobufMessage(value)) { + throw new UnsupportedTypeError(); + } + + return this.constructPayload({ + messageTypeName: getNamespacedTypeName(value.$type), + message: value.$type.encode(value).finish(), + }); + } + + public fromPayload(content: Payload): T { + const { messageType, data } = this.validatePayload(content); + return messageType.decode(data) as unknown as T; + } +} + +/** + * Converts between protobufjs Message instances and serialized JSON Payload + */ +export class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter { + public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF_JSON; + + constructor(root?: unknown) { + super(root); + } + + public toPayload(value: unknown): Payload { + if (!isProtobufMessage(value)) { + throw new UnsupportedTypeError(); + } + + const jsonValue = protoJsonSerializer.toProto3JSON(value); + + return this.constructPayload({ + messageTypeName: getNamespacedTypeName(value.$type), + message: u8(JSON.stringify(jsonValue)), + }); + } + + public fromPayload(content: Payload): T { + const { messageType, data } = this.validatePayload(content); + return protoJsonSerializer.fromProto3JSON(messageType, JSON.parse(str(data))) as unknown as T; + } +} + +function isProtobufType(type: unknown): type is Type { + return ( + isRecord(type) && + type.constructor.name === 'Type' && + hasOwnProperties(type, ['parent', 'name', 'create', 'encode', 'decode']) && + typeof type.name === 'string' && + typeof type.create === 'function' && + typeof type.encode === 'function' && + typeof type.decode === 'function' + ); +} + +function isProtobufMessage(value: unknown): value is Message { + return isRecord(value) && hasOwnProperty(value, '$type') && isProtobufType(value.$type); +} + +function getNamespacedTypeName(node: Type | Namespace): string { + if (node.parent && !isRoot(node.parent)) { + return getNamespacedTypeName(node.parent) + '.' + node.name; + } else { + return node.name; + } +} + +function isRoot(root: unknown): root is Root { + return isRecord(root) && root.constructor.name === 'Root'; +} diff --git a/packages/workflow-common/src/converter/types.ts b/packages/workflow-common/src/converter/types.ts index 434b39b82..40d26fe82 100644 --- a/packages/workflow-common/src/converter/types.ts +++ b/packages/workflow-common/src/converter/types.ts @@ -22,6 +22,7 @@ export const encodingTypes = { METADATA_ENCODING_PROTOBUF_JSON: 'json/protobuf', METADATA_ENCODING_PROTOBUF: 'binary/protobuf', } as const; +export type EncodingType = typeof encodingTypes[keyof typeof encodingTypes]; export const encodingKeys = { METADATA_ENCODING_NULL: u8(encodingTypes.METADATA_ENCODING_NULL), diff --git a/packages/workflow-common/src/errors.ts b/packages/workflow-common/src/errors.ts index 0af49ac18..3fed23be8 100644 --- a/packages/workflow-common/src/errors.ts +++ b/packages/workflow-common/src/errors.ts @@ -6,6 +6,14 @@ export class DataConverterError extends Error { public readonly name: string = 'DataConverterError'; } +export class PayloadConverterError extends Error { + public readonly name: string = 'DataConverterError'; +} + +export class UnsupportedTypeError extends PayloadConverterError { + public readonly name: string = 'UnsupportedTypeError'; +} + /** * Used in different parts of the project to signal that something unexpected has happened */ diff --git a/packages/workflow-common/src/failure.ts b/packages/workflow-common/src/failure.ts index 6a29a1f09..e79eecd1f 100644 --- a/packages/workflow-common/src/failure.ts +++ b/packages/workflow-common/src/failure.ts @@ -1,9 +1,10 @@ import type { temporal } from '@temporalio/proto/lib/coresdk'; -import { DataConverter, arrayFromPayloads } from './converter/data-converter'; +import { arrayFromPayloads, fromPayloadsAtIndex, PayloadConverter, toPayloads } from './converter/payload-converter'; import { checkExtends } from './type-helpers'; export const FAILURE_SOURCE = 'TypeScriptSDK'; export type ProtoFailure = temporal.api.failure.v1.IFailure; + // Avoid importing the proto implementation to reduce workflow bundle size // Copied from temporal.api.enums.v1.TimeoutType export enum TimeoutType { @@ -210,11 +211,11 @@ export class ChildWorkflowFailure extends TemporalFailure { /** * Converts an error to a Failure proto message if defined or returns undefined */ -export async function optionalErrorToOptionalFailure( +export function optionalErrorToOptionalFailure( err: unknown, - dataConverter: DataConverter -): Promise { - return err ? await errorToFailure(err, dataConverter) : undefined; + payloadConverter: PayloadConverter +): ProtoFailure | undefined { + return err ? errorToFailure(err, payloadConverter) : undefined; } /** @@ -245,14 +246,14 @@ export function cutoffStackTrace(stack?: string): string { /** * Converts a caught error to a Failure proto message */ -export async function errorToFailure(err: unknown, dataConverter: DataConverter): Promise { +export function errorToFailure(err: unknown, payloadConverter: PayloadConverter): ProtoFailure { if (err instanceof TemporalFailure) { if (err.failure) return err.failure; const base = { message: err.message, stackTrace: cutoffStackTrace(err.stack), - cause: await optionalErrorToOptionalFailure(err.cause, dataConverter), + cause: optionalErrorToOptionalFailure(err.cause, payloadConverter), source: FAILURE_SOURCE, }; if (err instanceof ActivityFailure) { @@ -281,9 +282,7 @@ export async function errorToFailure(err: unknown, dataConverter: DataConverter) type: err.type, nonRetryable: err.nonRetryable, details: - err.details && err.details.length - ? { payloads: await dataConverter.toPayloads(...err.details) } - : undefined, + err.details && err.details.length ? { payloads: toPayloads(payloadConverter, ...err.details) } : undefined, }, }; } @@ -292,9 +291,7 @@ export async function errorToFailure(err: unknown, dataConverter: DataConverter) ...base, canceledFailureInfo: { details: - err.details && err.details.length - ? { payloads: await dataConverter.toPayloads(...err.details) } - : undefined, + err.details && err.details.length ? { payloads: toPayloads(payloadConverter, ...err.details) } : undefined, }, }; } @@ -304,7 +301,7 @@ export async function errorToFailure(err: unknown, dataConverter: DataConverter) timeoutFailureInfo: { timeoutType: err.timeoutType, lastHeartbeatDetails: err.lastHeartbeatDetails - ? { payloads: await dataConverter.toPayloads(err.lastHeartbeatDetails) } + ? { payloads: toPayloads(payloadConverter, err.lastHeartbeatDetails) } : undefined, }, }; @@ -364,11 +361,11 @@ export function ensureTemporalFailure(err: unknown): TemporalFailure { /** * Converts a Failure proto message to a JS Error object if defined or returns undefined. */ -export async function optionalFailureToOptionalError( +export function optionalFailureToOptionalError( failure: ProtoFailure | undefined | null, - dataConverter: DataConverter -): Promise { - return failure ? await failureToError(failure, dataConverter) : undefined; + payloadConverter: PayloadConverter +): TemporalFailure | undefined { + return failure ? failureToError(failure, payloadConverter) : undefined; } /** @@ -376,44 +373,41 @@ export async function optionalFailureToOptionalError( * * Does not set common properties, that is done in {@link failureToError}. */ -export async function failureToErrorInner( - failure: ProtoFailure, - dataConverter: DataConverter -): Promise { +export function failureToErrorInner(failure: ProtoFailure, payloadConverter: PayloadConverter): TemporalFailure { if (failure.applicationFailureInfo) { return new ApplicationFailure( failure.message ?? undefined, failure.applicationFailureInfo.type, Boolean(failure.applicationFailureInfo.nonRetryable), - await arrayFromPayloads(dataConverter, failure.applicationFailureInfo.details?.payloads), - await optionalFailureToOptionalError(failure.cause, dataConverter) + arrayFromPayloads(payloadConverter, failure.applicationFailureInfo.details?.payloads), + optionalFailureToOptionalError(failure.cause, payloadConverter) ); } if (failure.serverFailureInfo) { return new ServerFailure( failure.message ?? undefined, Boolean(failure.serverFailureInfo.nonRetryable), - await optionalFailureToOptionalError(failure.cause, dataConverter) + optionalFailureToOptionalError(failure.cause, payloadConverter) ); } if (failure.timeoutFailureInfo) { return new TimeoutFailure( failure.message ?? undefined, - await dataConverter.fromPayloads(0, failure.timeoutFailureInfo.lastHeartbeatDetails?.payloads), + fromPayloadsAtIndex(payloadConverter, 0, failure.timeoutFailureInfo.lastHeartbeatDetails?.payloads), failure.timeoutFailureInfo.timeoutType ?? TimeoutType.TIMEOUT_TYPE_UNSPECIFIED ); } if (failure.terminatedFailureInfo) { return new TerminatedFailure( failure.message ?? undefined, - await optionalFailureToOptionalError(failure.cause, dataConverter) + optionalFailureToOptionalError(failure.cause, payloadConverter) ); } if (failure.canceledFailureInfo) { return new CancelledFailure( failure.message ?? undefined, - await arrayFromPayloads(dataConverter, failure.canceledFailureInfo.details?.payloads), - await optionalFailureToOptionalError(failure.cause, dataConverter) + arrayFromPayloads(payloadConverter, failure.canceledFailureInfo.details?.payloads), + optionalFailureToOptionalError(failure.cause, payloadConverter) ); } if (failure.resetWorkflowFailureInfo) { @@ -421,8 +415,8 @@ export async function failureToErrorInner( failure.message ?? undefined, 'ResetWorkflow', false, - await arrayFromPayloads(dataConverter, failure.resetWorkflowFailureInfo.lastHeartbeatDetails?.payloads), - await optionalFailureToOptionalError(failure.cause, dataConverter) + arrayFromPayloads(payloadConverter, failure.resetWorkflowFailureInfo.lastHeartbeatDetails?.payloads), + optionalFailureToOptionalError(failure.cause, payloadConverter) ); } if (failure.childWorkflowExecutionFailureInfo) { @@ -435,7 +429,7 @@ export async function failureToErrorInner( workflowExecution, workflowType.name, retryState ?? RetryState.RETRY_STATE_UNSPECIFIED, - await optionalFailureToOptionalError(failure.cause, dataConverter) + optionalFailureToOptionalError(failure.cause, payloadConverter) ); } if (failure.activityFailureInfo) { @@ -447,20 +441,20 @@ export async function failureToErrorInner( failure.activityFailureInfo.activityId ?? undefined, failure.activityFailureInfo.retryState ?? RetryState.RETRY_STATE_UNSPECIFIED, failure.activityFailureInfo.identity ?? undefined, - await optionalFailureToOptionalError(failure.cause, dataConverter) + optionalFailureToOptionalError(failure.cause, payloadConverter) ); } return new TemporalFailure( failure.message ?? undefined, - await optionalFailureToOptionalError(failure.cause, dataConverter) + optionalFailureToOptionalError(failure.cause, payloadConverter) ); } /** * Converts a Failure proto message to a JS Error object. */ -export async function failureToError(failure: ProtoFailure, dataConverter: DataConverter): Promise { - const err = await failureToErrorInner(failure, dataConverter); +export function failureToError(failure: ProtoFailure, payloadConverter: PayloadConverter): TemporalFailure { + const err = failureToErrorInner(failure, payloadConverter); err.stack = failure.stackTrace ?? ''; err.failure = failure; return err; diff --git a/packages/workflow-common/src/index.ts b/packages/workflow-common/src/index.ts index 68dbe2a9a..40fbdbbe8 100644 --- a/packages/workflow-common/src/index.ts +++ b/packages/workflow-common/src/index.ts @@ -5,6 +5,10 @@ */ export * from './activity-options'; export * from './converter/data-converter'; +export * from './converter/payload-converter'; +export * from './converter/payload-converters'; +export * from './converter/payload-codec'; +export * from './converter/types'; export * from './errors'; export * from './failure'; export * from './interceptors'; diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts index 19a87e6df..e0ae8f3bc 100644 --- a/packages/workflow/src/index.ts +++ b/packages/workflow/src/index.ts @@ -60,8 +60,8 @@ export { RetryPolicy, rootCause, IllegalStateError, - defaultDataConverter, - DataConverter, + defaultPayloadConverter, + PayloadConverter as DataConverter, WorkflowIdReusePolicy, ActivityFailure, ApplicationFailure, diff --git a/packages/workflow/src/internals.ts b/packages/workflow/src/internals.ts index 497fbd2f5..53158121e 100644 --- a/packages/workflow/src/internals.ts +++ b/packages/workflow/src/internals.ts @@ -6,8 +6,8 @@ import { optionalFailureToOptionalError, IllegalStateError, WorkflowSignalType, - DataConverter, - defaultDataConverter, + PayloadConverter, + defaultPayloadConverter, arrayFromPayloadsSync, Workflow, WorkflowQueryType, @@ -119,11 +119,11 @@ export class Activator implements ActivationHandler { resolve(result); } else if (activation.result.failed) { const { failure } = activation.result.failed; - const err = await optionalFailureToOptionalError(failure, state.dataConverter); + const err = optionalFailureToOptionalError(failure, state.dataConverter); reject(err); } else if (activation.result.cancelled) { const { failure } = activation.result.cancelled; - const err = await optionalFailureToOptionalError(failure, state.dataConverter); + const err = optionalFailureToOptionalError(failure, state.dataConverter); reject(err); } } @@ -155,7 +155,7 @@ export class Activator implements ActivationHandler { if (!activation.cancelled.failure) { throw new TypeError('Got no failure in cancelled variant'); } - reject(await failureToError(activation.cancelled.failure, state.dataConverter)); + reject(failureToError(activation.cancelled.failure, state.dataConverter)); } else { throw new TypeError('Got ResolveChildWorkflowExecutionStart with no status'); } @@ -170,20 +170,20 @@ export class Activator implements ActivationHandler { const { resolve, reject } = consumeCompletion('childWorkflowComplete', getSeq(activation)); if (activation.result.completed) { const completed = activation.result.completed; - const result = completed.result ? await state.dataConverter.fromPayload(completed.result) : undefined; + const result = completed.result ? state.dataConverter.fromPayload(completed.result) : undefined; resolve(result); } else if (activation.result.failed) { const { failure } = activation.result.failed; if (failure === undefined || failure === null) { throw new TypeError('Got failed result with no failure attribute'); } - reject(await failureToError(failure, state.dataConverter)); + reject(failureToError(failure, state.dataConverter)); } else if (activation.result.cancelled) { const { failure } = activation.result.cancelled; if (failure === undefined || failure === null) { throw new TypeError('Got cancelled result with no failure attribute'); } - reject(await failureToError(failure, state.dataConverter)); + reject(failureToError(failure, state.dataConverter)); } } @@ -267,7 +267,7 @@ export class Activator implements ActivationHandler { ): Promise { const { resolve, reject } = consumeCompletion('signalWorkflow', getSeq(activation)); if (activation.failure) { - reject(await failureToError(activation.failure, state.dataConverter)); + reject(failureToError(activation.failure, state.dataConverter)); } else { resolve(undefined); } @@ -278,7 +278,7 @@ export class Activator implements ActivationHandler { ): Promise { const { resolve, reject } = consumeCompletion('cancelWorkflow', getSeq(activation)); if (activation.failure) { - reject(await failureToError(activation.failure, state.dataConverter)); + reject(failureToError(activation.failure, state.dataConverter)); } else { resolve(undefined); } @@ -438,7 +438,7 @@ export class State { */ public importInterceptors?: InterceptorsImportFunc; - public dataConverter: DataConverter = defaultDataConverter; + public dataConverter: PayloadConverter = defaultPayloadConverter; /** * Patches we know the status of for this workflow, as in {@link patched} @@ -505,7 +505,7 @@ export async function handleWorkflowFailure(error: unknown): Promise { state.pushCommand( { failWorkflowExecution: { - failure: await errorToFailure(error, state.dataConverter), + failure: errorToFailure(error, state.dataConverter), }, }, true @@ -521,7 +521,7 @@ function completeQuery(queryId: string, result: unknown) { async function failQuery(queryId: string, error: any) { state.pushCommand({ - respondToQuery: { queryId, failed: await errorToFailure(ensureTemporalFailure(error), state.dataConverter) }, + respondToQuery: { queryId, failed: errorToFailure(ensureTemporalFailure(error), state.dataConverter) }, }); } diff --git a/packages/workflow/src/worker-interface.ts b/packages/workflow/src/worker-interface.ts index 19f64e007..964110c7e 100644 --- a/packages/workflow/src/worker-interface.ts +++ b/packages/workflow/src/worker-interface.ts @@ -156,10 +156,10 @@ export async function initRuntime({ } if (useCustomDataConverter) { - // @ts-expect-error this is a webpack alias to dataConverterPath + // @ts-expect-error this is a webpack alias to payloadConverterPath state.dataConverter = (await import('__temporal_custom_data_converter')).dataConverter; // webpack doesn't know what to bundle given a dynamic import expression, so we can't do: - // state.dataConverter = (await import(dataConverterPath)).dataConverter; + // state.dataConverter = (await import(payloadConverterPath)).dataConverter; } let workflow: Workflow; diff --git a/packages/workflow/src/workflow.ts b/packages/workflow/src/workflow.ts index 83017517b..ee4821b8f 100644 --- a/packages/workflow/src/workflow.ts +++ b/packages/workflow/src/workflow.ts @@ -692,7 +692,7 @@ export function makeContinueAsNewFunc( const { headers, args, options } = input; throw new ContinueAsNew({ workflowType: options.workflowType, - arguments: await state.dataConverter.toPayloads(...args), + arguments: state.dataConverter.toPayloads(...args), header: headers, taskQueue: options.taskQueue, memo: options.memo, From e0152bbd1cff4c3cb7985746a572b7fc8c503123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Tue, 8 Feb 2022 12:22:32 -0500 Subject: [PATCH 08/33] chore: Remove unused data converter from otel interceptors --- .../src/client/index.ts | 15 +++------------ .../src/worker/index.ts | 14 ++------------ 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/packages/interceptors-opentelemetry/src/client/index.ts b/packages/interceptors-opentelemetry/src/client/index.ts index ae95a1167..83ffbcceb 100644 --- a/packages/interceptors-opentelemetry/src/client/index.ts +++ b/packages/interceptors-opentelemetry/src/client/index.ts @@ -1,18 +1,11 @@ import * as otel from '@opentelemetry/api'; -import { - DataConverter, - defaultPayloadConverter, - Next, - WorkflowClientCallsInterceptor, - WorkflowStartInput, -} from '@temporalio/client'; -import { SpanName, SPAN_DELIMITER } from '../workflow'; -import { instrument } from '../instrumentation'; +import { Next, WorkflowClientCallsInterceptor, WorkflowStartInput } from '@temporalio/client'; import { headersWithContext, RUN_ID_ATTR_KEY } from '@temporalio/common/lib/otel'; +import { instrument } from '../instrumentation'; +import { SpanName, SPAN_DELIMITER } from '../workflow'; export interface InterceptorOptions { readonly tracer?: otel.Tracer; - readonly dataConverter?: DataConverter; } /** @@ -22,10 +15,8 @@ export interface InterceptorOptions { */ export class OpenTelemetryWorkflowClientCallsInterceptor implements WorkflowClientCallsInterceptor { protected readonly tracer: otel.Tracer; - protected readonly dataConverter: DataConverter; constructor(options?: InterceptorOptions) { - this.dataConverter = options?.dataConverter ?? defaultPayloadConverter; this.tracer = options?.tracer ?? otel.trace.getTracer('@temporalio/interceptor-client'); } diff --git a/packages/interceptors-opentelemetry/src/worker/index.ts b/packages/interceptors-opentelemetry/src/worker/index.ts index b1cae2a2d..5d29083ba 100644 --- a/packages/interceptors-opentelemetry/src/worker/index.ts +++ b/packages/interceptors-opentelemetry/src/worker/index.ts @@ -3,20 +3,12 @@ import { Resource } from '@opentelemetry/resources'; import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; import { Context as ActivityContext } from '@temporalio/activity'; import { extractContextFromHeaders } from '@temporalio/common/lib/otel'; -import { - ActivityExecuteInput, - ActivityInboundCallsInterceptor, - DataConverter, - defaultPayloadConverter, - InjectedSink, - Next, -} from '@temporalio/worker'; -import { OpenTelemetryWorkflowExporter, SerializableSpan, SpanName, SPAN_DELIMITER } from '../workflow'; +import { ActivityExecuteInput, ActivityInboundCallsInterceptor, InjectedSink, Next } from '@temporalio/worker'; import { instrument } from '../instrumentation'; +import { OpenTelemetryWorkflowExporter, SerializableSpan, SpanName, SPAN_DELIMITER } from '../workflow'; export interface InterceptorOptions { readonly tracer?: otel.Tracer; - readonly dataConverter?: DataConverter; } /** @@ -27,10 +19,8 @@ export interface InterceptorOptions { */ export class OpenTelemetryActivityInboundInterceptor implements ActivityInboundCallsInterceptor { protected readonly tracer: otel.Tracer; - protected readonly dataConverter: DataConverter; constructor(protected readonly ctx: ActivityContext, options?: InterceptorOptions) { - this.dataConverter = options?.dataConverter ?? defaultPayloadConverter; this.tracer = options?.tracer ?? otel.trace.getTracer('@temporalio/interceptor-activity'); } From 3430625dcebabadb93659fe9608601dd0cabcc10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Fri, 11 Feb 2022 17:41:37 -0500 Subject: [PATCH 09/33] feat: Complete new DC API conversion --- docs/data-converter.md | 29 ++-- packages/activity/src/index.ts | 2 +- .../client/src/async-completion-client.ts | 51 ++---- packages/client/src/workflow-client.ts | 94 ++++++----- packages/common/src/codec-helpers.ts | 147 +++++++++++++----- packages/common/src/data-converter-helpers.ts | 52 +++++++ packages/common/src/index.ts | 2 + packages/test/src/mock-native-worker.ts | 25 +-- .../payload-converter-bad-export.ts | 2 +- .../test/src/test-custom-data-converter.ts | 3 +- packages/test/src/test-data-converter.ts | 87 ++++++----- packages/test/src/test-integration.ts | 8 +- packages/test/src/test-worker-activities.ts | 13 +- packages/test/src/test-workflows.ts | 68 ++++---- packages/test/src/workflows/data-converter.ts | 2 +- packages/test/tsconfig.json | 1 + packages/worker/src/activity.ts | 19 +-- packages/worker/src/worker.ts | 134 +++++++--------- packages/worker/src/workflow-codec-runner.ts | 142 +++++++++++++++++ packages/worker/src/workflow/bundler.ts | 3 +- packages/worker/src/workflow/empty-module.ts | 0 packages/worker/src/workflow/vm.ts | 21 +-- .../src/converter/data-converter.ts | 6 + .../src/converter/payload-converter.ts | 38 ----- packages/workflow-common/src/errors.ts | 2 +- packages/workflow-common/src/index.ts | 3 +- packages/workflow/src/index.ts | 2 +- packages/workflow/src/internals.ts | 36 ++--- packages/workflow/src/worker-interface.ts | 13 +- packages/workflow/src/workflow.ts | 15 +- 30 files changed, 612 insertions(+), 408 deletions(-) create mode 100644 packages/common/src/data-converter-helpers.ts create mode 100644 packages/worker/src/workflow-codec-runner.ts create mode 100644 packages/worker/src/workflow/empty-module.ts diff --git a/docs/data-converter.md b/docs/data-converter.md index 084486802..0aac39ac6 100644 --- a/docs/data-converter.md +++ b/docs/data-converter.md @@ -27,32 +27,35 @@ Given the possibility of switching or adding other isolation methods in future, ### General flow -When `WorkerOptions.dataConverterPath` is provided, the code at that location is loaded into the main thread, the worker threads, and the webpack Workflow bundle. +When `WorkerOptions.dataConverter.payloadConverterPath` is provided, the code at that location is loaded into the main thread, the worker threads, and the webpack Workflow bundle. ### Specific flow -Worker main thread: +`Worker.create`: +*main thread* -- imports and validates `dataConverterPath` -- passes `dataConverterPath` to either `ThreadedVMWorkflowCreator.create` or `VMWorkflowCreator.create` -- passes `dataConverterPath` to `WorkflowCodeBundler` +- imports and validates `options.dataConverter.payloadConverterPath` +- passes `payloadConverterPath` to either `ThreadedVMWorkflowCreator.create` or `VMWorkflowCreator.create` +- passes `payloadConverterPath` to `WorkflowCodeBundler` `ThreadedVMWorkflowCreator.create`: +*main thread* -- sends `dataConverterPath` to each worker thread -- thread sends `dataConverterPath` to VMWorkflowCreator.create +- sends `payloadConverterPath` to each worker thread +- thread sends `payloadConverterPath` to VMWorkflowCreator.create `VMWorkflowCreator.create`: -(This is usually running in a worker thread, but in debug mode is running in the main thread) +*worker thread (unless in debug mode)* -- imports `dataConverterPath` -- passes `dataConverterPath` to `VMWorkflowCreator` constructor +- imports `payloadConverterPath` +- passes `payloadConverterPath` to `VMWorkflowCreator` constructor `VMWorkflowCreator.createWorkflow`: +*worker thread (unless in debug mode)* -- passes `useCustomDataConverter` to `initRuntime` inside Workflow vm +- passes `useCustomPayloadConverter` to `initRuntime` inside Workflow vm `worker-interface.ts#initRuntime`: -(Inside vm) +*workflow vm* -- if `useCustomDataConverter`, imports `__temporal_custom_data_converter` and sets `state.dataConverter` +- if `useCustomPayloadConverter`, imports `__temporal_custom_payload_converter` and sets `state.payloadConverter` diff --git a/packages/activity/src/index.ts b/packages/activity/src/index.ts index 4750cc639..5b8b6b75e 100644 --- a/packages/activity/src/index.ts +++ b/packages/activity/src/index.ts @@ -3,7 +3,7 @@ * * Import this module from Activity code - must **not** be used in Workflows. * - * Any function can be used as an Activity as long as its parameters and return value are serialiable using a [`DataConverter`](../interfaces/worker.DataConverter.md). + * Any function can be used as an Activity as long as its parameters and return value are serializable using a [`DataConverter`](../interfaces/worker.DataConverter.md). * * ### Cancellation * Activities may be cancelled only if they [emit heartbeats](../classes/activity.Context.md#heartbeat).
diff --git a/packages/client/src/async-completion-client.ts b/packages/client/src/async-completion-client.ts index 18b2046b8..b4ebe8174 100644 --- a/packages/client/src/async-completion-client.ts +++ b/packages/client/src/async-completion-client.ts @@ -1,20 +1,15 @@ -import os from 'os'; import { ServerErrorResponse } from '@grpc/grpc-js'; import { Status } from '@grpc/grpc-js/build/src/constants'; import { DataConverter, - defaultPayloadConverter, - requirePayloadConverter, - defaultPayloadCodec, - encodeFailure, - PayloadCodec, - Payload, - toPayloads, + encodeErrorToFailure, + encodeToPayloads, ensureTemporalFailure, - errorToFailure, filterNullAndUndefined, - PayloadConverter, + loadDataConverter, + LoadedDataConverter, } from '@temporalio/common'; +import os from 'os'; import { Connection, WorkflowService } from './connection'; /** @@ -101,29 +96,16 @@ export interface FullActivityId { */ export class AsyncCompletionClient { public readonly options: AsyncCompletionClientOptionsWithDefaults; - protected readonly payloadConverter: PayloadConverter = defaultPayloadConverter; - protected readonly payloadCodec: PayloadCodec; + protected readonly dataConverter: LoadedDataConverter; constructor( public readonly service: WorkflowService = new Connection().service, options?: AsyncCompletionClientOptions ) { - if (options?.dataConverter?.payloadConverterPath) { - this.payloadConverter = requirePayloadConverter(options.dataConverter.payloadConverterPath); - } - this.payloadCodec = options?.dataConverter?.payloadCodec || defaultPayloadCodec; + this.dataConverter = loadDataConverter(options?.dataConverter); this.options = { ...defaultAsyncCompletionClientOptions(), ...filterNullAndUndefined(options ?? {}) }; } - protected async convertToWire(value: unknown): Promise { - const payloads = toPayloads(this.payloadConverter, value); - if (payloads === undefined) { - return undefined; - } else { - return this.payloadCodec.encode(payloads); - } - } - /** * Transforms grpc errors into well defined TS errors. */ @@ -153,14 +135,14 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, taskToken: taskTokenOrFullActivityId, - result: { payloads: await this.convertToWire(result) }, + result: { payloads: await encodeToPayloads(this.dataConverter, result) }, }); } else { await this.service.respondActivityTaskCompletedById({ identity: this.options.identity, namespace: this.options.namespace, ...taskTokenOrFullActivityId, - result: { payloads: await this.convertToWire(result) }, + result: { payloads: await encodeToPayloads(this.dataConverter, result) }, }); } } catch (err) { @@ -184,17 +166,14 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, taskToken: taskTokenOrFullActivityId, - failure: await encodeFailure( - errorToFailure(ensureTemporalFailure(err), this.payloadConverter), - this.payloadCodec - ), + failure: await encodeErrorToFailure(this.dataConverter, ensureTemporalFailure(err)), }); } else { await this.service.respondActivityTaskFailedById({ identity: this.options.identity, namespace: this.options.namespace, ...taskTokenOrFullActivityId, - failure: await encodeFailure(errorToFailure(err, this.payloadConverter), this.payloadCodec), + failure: await encodeErrorToFailure(this.dataConverter, err), }); } } catch (err) { @@ -218,14 +197,14 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, taskToken: taskTokenOrFullActivityId, - details: { payloads: toPayloads(this.payloadConverter, details) }, + details: { payloads: await encodeToPayloads(this.dataConverter, details) }, }); } else { await this.service.respondActivityTaskCanceledById({ identity: this.options.identity, namespace: this.options.namespace, ...taskTokenOrFullActivityId, - details: { payloads: toPayloads(this.payloadConverter, details) }, + details: { payloads: await encodeToPayloads(this.dataConverter, details) }, }); } } catch (err) { @@ -249,7 +228,7 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, taskToken: taskTokenOrFullActivityId, - details: { payloads: toPayloads(this.payloadConverter, details) }, + details: { payloads: await encodeToPayloads(this.dataConverter, details) }, }); if (cancelRequested) { throw new ActivityCancelledError('cancelled'); @@ -259,7 +238,7 @@ export class AsyncCompletionClient { identity: this.options.identity, namespace: this.options.namespace, ...taskTokenOrFullActivityId, - details: { payloads: toPayloads(this.payloadConverter, details) }, + details: { payloads: await encodeToPayloads(this.dataConverter, details) }, }); if (cancelRequested) { throw new ActivityCancelledError('cancelled'); diff --git a/packages/client/src/workflow-client.ts b/packages/client/src/workflow-client.ts index ccba58242..4fe8da827 100644 --- a/packages/client/src/workflow-client.ts +++ b/packages/client/src/workflow-client.ts @@ -1,35 +1,35 @@ -import os from 'os'; -import { temporal } from '@temporalio/proto'; -import { WorkflowClientInterceptors } from './interceptors'; -import { v4 as uuid4 } from 'uuid'; import { - arrayFromPayloads, - mapToPayloads, - DataConverter, - PayloadConverter, - defaultPayloadConverter, - requirePayloadConverter, - PayloadCodec, - defaultPayloadCodec, - composeInterceptors, - optionalFailureToOptionalError, - Workflow, BaseWorkflowHandle, + CancelledFailure, + compileRetryPolicy, + composeInterceptors, + DataConverter, + decodeArrayFromPayloads, + decodeFromPayloadsAtIndex, + decodeOptionalFailureToOptionalError, + encodeMapToPayloads, + encodeToPayloads, + loadDataConverter, + LoadedDataConverter, QueryDefinition, + RetryState, SignalDefinition, - WorkflowResultType, - WithWorkflowArgs, - CancelledFailure, TerminatedFailure, - RetryState, TimeoutFailure, TimeoutType, - compileRetryPolicy, + WithWorkflowArgs, + Workflow, + WorkflowResultType, } from '@temporalio/common'; -import { WorkflowOptions, compileWorkflowOptions, WorkflowSignalWithStartOptions } from './workflow-options'; +import { temporal } from '@temporalio/proto'; +import os from 'os'; +import { v4 as uuid4 } from 'uuid'; +import { Connection, WorkflowService } from './connection'; +import { WorkflowContinuedAsNewError, WorkflowFailedError } from './errors'; import { WorkflowCancelInput, WorkflowClientCallsInterceptor, + WorkflowClientInterceptors, WorkflowQueryInput, WorkflowSignalInput, WorkflowSignalWithStartInput, @@ -37,14 +37,13 @@ import { WorkflowTerminateInput, } from './interceptors'; import { - GetWorkflowExecutionHistoryRequest, DescribeWorkflowExecutionResponse, + GetWorkflowExecutionHistoryRequest, + RequestCancelWorkflowExecutionResponse, StartWorkflowExecutionRequest, TerminateWorkflowExecutionResponse, - RequestCancelWorkflowExecutionResponse, } from './types'; -import { WorkflowFailedError, WorkflowContinuedAsNewError } from './errors'; -import { Connection, WorkflowService } from './connection'; +import { compileWorkflowOptions, WorkflowOptions, WorkflowSignalWithStartOptions } from './workflow-options'; /** * A client side handle to a single Workflow instance. @@ -207,14 +206,10 @@ export type WorkflowStartOptions = WithWorkflowAr */ export class WorkflowClient { public readonly options: WorkflowClientOptionsWithDefaults; - protected readonly payloadConverter: PayloadConverter = defaultPayloadConverter; - protected readonly payloadCodec: PayloadCodec; + protected readonly dataConverter: LoadedDataConverter; constructor(public readonly service: WorkflowService = new Connection().service, options?: WorkflowClientOptions) { - if (options?.dataConverter?.payloadConverterPath) { - this.payloadConverter = requirePayloadConverter(options.dataConverter.payloadConverterPath); - } - this.payloadCodec = options?.dataConverter?.payloadCodec || defaultPayloadCodec; + this.dataConverter = loadDataConverter(options?.dataConverter); this.options = { ...defaultWorkflowClientOptions(), ...options }; } @@ -376,8 +371,8 @@ export class WorkflowClient { } // Note that we can only return one value from our workflow function in JS. // Ignore any other payloads in result - const [result] = await arrayFromPayloads( - this.payloadConverter, + const [result] = await decodeArrayFromPayloads( + this.dataConverter, ev.workflowExecutionCompletedEventAttributes.result?.payloads ); return result as any; @@ -390,13 +385,16 @@ export class WorkflowClient { const { failure } = ev.workflowExecutionFailedEventAttributes; throw new WorkflowFailedError( 'Workflow execution failed', - optionalFailureToOptionalError(failure, this.payloadConverter), + await decodeOptionalFailureToOptionalError(this.dataConverter, failure), RetryState.RETRY_STATE_NON_RETRYABLE_FAILURE ); } else if (ev.workflowExecutionCanceledEventAttributes) { const failure = new CancelledFailure( 'Workflow canceled', - await arrayFromPayloads(this.payloadConverter, ev.workflowExecutionCanceledEventAttributes.details?.payloads) + await decodeArrayFromPayloads( + this.dataConverter, + ev.workflowExecutionCanceledEventAttributes.details?.payloads + ) ); failure.stack = ''; throw new WorkflowFailedError( @@ -458,7 +456,7 @@ export class WorkflowClient { execution: input.workflowExecution, query: { queryType: input.queryType, - queryArgs: { payloads: this.payloadConverter.toPayloads(...input.args) }, + queryArgs: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) }, }, }); if (response.queryRejected) { @@ -471,7 +469,7 @@ export class WorkflowClient { throw new TypeError('Invalid response from server'); } // We ignore anything but the first result - return this.payloadConverter.fromPayloads(0, response.queryResult?.payloads); + return await decodeFromPayloadsAtIndex(this.dataConverter, 0, response.queryResult?.payloads); } /** @@ -487,7 +485,7 @@ export class WorkflowClient { requestId: uuid4(), // control is unused, signalName: input.signalName, - input: { payloads: this.payloadConverter.toPayloads(...input.args) }, + input: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) }, }); } @@ -497,7 +495,7 @@ export class WorkflowClient { * Used as the final function of the signalWithStart interceptor chain */ protected async _signalWithStartWorkflowHandler(input: WorkflowSignalWithStartInput): Promise { - const { identity, dataConverter } = this.options; + const { identity } = this.options; const { options, workflowType, signalName, signalArgs, headers } = input; const { runId } = await this.service.signalWithStartWorkflowExecution({ namespace: this.options.namespace, @@ -506,9 +504,9 @@ export class WorkflowClient { workflowId: options.workflowId, workflowIdReusePolicy: options.workflowIdReusePolicy, workflowType: { name: workflowType }, - input: { payloads: dataConverter.toPayloads(...options.args) }, + input: { payloads: await encodeToPayloads(this.dataConverter, ...options.args) }, signalName, - signalInput: { payloads: dataConverter.toPayloads(...signalArgs) }, + signalInput: { payloads: await encodeToPayloads(this.dataConverter, ...signalArgs) }, taskQueue: { kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED, name: options.taskQueue, @@ -517,10 +515,10 @@ export class WorkflowClient { workflowRunTimeout: options.workflowRunTimeout, workflowTaskTimeout: options.workflowTaskTimeout, retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined, - memo: options.memo ? { fields: mapToPayloads(dataConverter, options.memo) } : undefined, + memo: options.memo ? { fields: await encodeMapToPayloads(this.dataConverter, options.memo) } : undefined, searchAttributes: options.searchAttributes ? { - indexedFields: mapToPayloads(dataConverter, options.searchAttributes), + indexedFields: await encodeMapToPayloads(this.dataConverter, options.searchAttributes), } : undefined, cronSchedule: options.cronSchedule, @@ -536,7 +534,7 @@ export class WorkflowClient { */ protected async _startWorkflowHandler(input: WorkflowStartInput): Promise { const { options: opts, workflowType: name, headers } = input; - const { identity, dataConverter } = this.options; + const { identity } = this.options; const req: StartWorkflowExecutionRequest = { namespace: this.options.namespace, identity, @@ -544,7 +542,7 @@ export class WorkflowClient { workflowId: opts.workflowId, workflowIdReusePolicy: opts.workflowIdReusePolicy, workflowType: { name }, - input: { payloads: dataConverter.toPayloads(...opts.args) }, + input: { payloads: await encodeToPayloads(this.dataConverter, ...opts.args) }, taskQueue: { kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED, name: opts.taskQueue, @@ -553,10 +551,10 @@ export class WorkflowClient { workflowRunTimeout: opts.workflowRunTimeout, workflowTaskTimeout: opts.workflowTaskTimeout, retryPolicy: opts.retry ? compileRetryPolicy(opts.retry) : undefined, - memo: opts.memo ? { fields: mapToPayloads(dataConverter, opts.memo) } : undefined, + memo: opts.memo ? { fields: await encodeMapToPayloads(this.dataConverter, opts.memo) } : undefined, searchAttributes: opts.searchAttributes ? { - indexedFields: mapToPayloads(dataConverter, opts.searchAttributes), + indexedFields: await encodeMapToPayloads(this.dataConverter, opts.searchAttributes), } : undefined, cronSchedule: opts.cronSchedule, @@ -578,7 +576,7 @@ export class WorkflowClient { namespace: this.options.namespace, identity: this.options.identity, ...input, - details: { payloads: this.payloadConverter.toPayloads(input.details) }, + details: { payloads: input.details ? await encodeToPayloads(this.dataConverter, ...input.details) : undefined }, }); } diff --git a/packages/common/src/codec-helpers.ts b/packages/common/src/codec-helpers.ts index a67858efd..154c68dfd 100644 --- a/packages/common/src/codec-helpers.ts +++ b/packages/common/src/codec-helpers.ts @@ -1,11 +1,89 @@ -import { PayloadCodec, ProtoFailure } from '@temporalio/workflow-common'; +import { + arrayFromPayloads, + errorToFailure, + failureToError, + fromPayloadsAtIndex, + LoadedDataConverter, + Payload, + PayloadCodec, + ProtoFailure, + TemporalFailure, + toPayloads, +} from '@temporalio/workflow-common'; + +export async function decodeFromPayloadsAtIndex( + converter: LoadedDataConverter, + index: number, + payloads?: Payload[] | null +): Promise { + const { payloadConverter, payloadCodec } = converter; + return fromPayloadsAtIndex(payloadConverter, index, payloads ? await payloadCodec.decode(payloads) : payloads); +} + +export async function decodeArrayFromPayloads( + converter: LoadedDataConverter, + content?: Payload[] | null +): Promise { + const { payloadConverter, payloadCodec } = converter; + let decodedPayloads = content; + if (content) { + decodedPayloads = await payloadCodec.decode(content); + } + return arrayFromPayloads(payloadConverter, decodedPayloads); +} + +export async function decodeOptionalFailureToOptionalError( + converter: LoadedDataConverter, + failure: ProtoFailure | undefined | null +): Promise { + const { payloadConverter, payloadCodec } = converter; + return failure ? failureToError(await decodeFailure(payloadCodec, failure), payloadConverter) : undefined; +} + +export async function encodeMapToPayloads( + converter: LoadedDataConverter, + source: Record +): Promise> { + const { payloadConverter, payloadCodec } = converter; + return Object.fromEntries( + await Promise.all( + Object.entries(source).map(async ([k, v]): Promise<[K, Payload]> => { + const [payload] = await payloadCodec.encode([payloadConverter.toPayload(v)]); + return [k as K, payload]; + }) + ) + ) as Record; +} + +export async function encodeToPayload(converter: LoadedDataConverter, value: unknown): Promise { + const { payloadConverter, payloadCodec } = converter; + const [payload] = await payloadCodec.encode([payloadConverter.toPayload(value)]); + return payload; +} + +export async function encodeToPayloads( + converter: LoadedDataConverter, + ...values: unknown[] +): Promise { + const { payloadConverter, payloadCodec } = converter; + if (values.length === 0) { + return undefined; + } + const payloads = toPayloads(payloadConverter, values); + return payloads ? await payloadCodec.encode(payloads) : undefined; +} + +export async function encodeErrorToFailure(dataConverter: LoadedDataConverter, error: unknown): Promise { + const { payloadConverter, payloadCodec } = dataConverter; + return await encodeFailure(payloadCodec, errorToFailure(error, payloadConverter)); +} /** - * Run `codec.encode()` on the {@link Payload}s in a {@link ProtoFailure}. + * Run `codec.encode()` on the {@link Payload}s in a {@link ProtoFailure}. Mutates `failure`. */ -export async function encodeFailure(failure: ProtoFailure, codec: PayloadCodec): Promise { +export async function encodeFailure(codec: PayloadCodec, failure: ProtoFailure): Promise { if (failure.cause) { - await encodeFailure(failure.cause, codec); + await encodeFailure(codec, failure.cause); } if (failure.applicationFailureInfo?.details?.payloads?.length) { @@ -29,38 +107,31 @@ export async function encodeFailure(failure: ProtoFailure, codec: PayloadCodec): return failure; } -// export async function deserializeFailure( -// failure: ProtoFailure, -// dataConverter: DataConverter -// ): Promise { -// const deserializedFailure = failure as DeserializedFailure; -// if (failure.cause) { -// await deserializeFailure(failure.cause, dataConverter); -// } +/** + * Run `codec.decode()` on the {@link Payload}s in a {@link ProtoFailure}. Mutates `failure`. + */ +export async function decodeFailure(codec: PayloadCodec, failure: ProtoFailure): Promise { + if (failure.cause) { + await decodeFailure(codec, failure.cause); + } -// if (failure.applicationFailureInfo?.details) { -// deserializedFailure.applicationFailureInfo.details.payloads = await arrayFromPayloads( -// dataConverter, -// failure.applicationFailureInfo.details.payloads -// ); -// } -// if (failure.timeoutFailureInfo?.lastHeartbeatDetails?.payloads) { -// deserializedFailure.timeoutFailureInfo.lastHeartbeatDetails.payloads = await dataConverter.fromPayloads( -// 0, -// failure.timeoutFailureInfo.lastHeartbeatDetails?.payloads -// ); -// } -// if (failure.canceledFailureInfo?.details?.payloads) { -// deserializedFailure.canceledFailureInfo.details.payloads = await arrayFromPayloads( -// dataConverter, -// failure.canceledFailureInfo.details.payloads -// ); -// } -// if (failure.resetWorkflowFailureInfo?.lastHeartbeatDetails?.payloads) { -// deserializedFailure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads = await arrayFromPayloads( -// dataConverter, -// failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads -// ); -// } -// return deserializedFailure; -// } + if (failure.applicationFailureInfo?.details?.payloads?.length) { + failure.applicationFailureInfo.details.payloads = await codec.decode( + failure.applicationFailureInfo.details.payloads + ); + } + if (failure.timeoutFailureInfo?.lastHeartbeatDetails?.payloads?.length) { + failure.timeoutFailureInfo.lastHeartbeatDetails.payloads = await codec.decode( + failure.timeoutFailureInfo.lastHeartbeatDetails.payloads + ); + } + if (failure.canceledFailureInfo?.details?.payloads?.length) { + failure.canceledFailureInfo.details.payloads = await codec.decode(failure.canceledFailureInfo.details.payloads); + } + if (failure.resetWorkflowFailureInfo?.lastHeartbeatDetails?.payloads?.length) { + failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads = await codec.decode( + failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads + ); + } + return failure; +} diff --git a/packages/common/src/data-converter-helpers.ts b/packages/common/src/data-converter-helpers.ts new file mode 100644 index 000000000..9b14dd524 --- /dev/null +++ b/packages/common/src/data-converter-helpers.ts @@ -0,0 +1,52 @@ +import { + DataConverter, + defaultPayloadCodec, + defaultPayloadConverter, + errorCode, + hasOwnProperty, + LoadedDataConverter, + PayloadConverter, + ValueError, +} from '@temporalio/workflow-common'; + +const isValidPayloadConverter = (PayloadConverter: unknown): PayloadConverter is PayloadConverter => + typeof PayloadConverter === 'object' && + PayloadConverter !== null && + ['toPayload', 'fromPayload'].every( + (method) => typeof (PayloadConverter as Record)[method] === 'function' + ); + +function requirePayloadConverter(path: string): PayloadConverter { + let module; + try { + module = require(path); // eslint-disable-line @typescript-eslint/no-var-requires + } catch (error) { + if (errorCode(error) === 'MODULE_NOT_FOUND') { + throw new ValueError(`Could not find a file at the specified payloadConverterPath: '${path}'.`); + } + throw error; + } + + if (hasOwnProperty(module, 'payloadConverter')) { + if (isValidPayloadConverter(module.payloadConverter)) { + return module.payloadConverter; + } else { + throw new ValueError( + `payloadConverter export at ${path} must be an object with toPayload and fromPayload methods` + ); + } + } else { + throw new ValueError(`Module ${path} does not have a \`payloadConverter\` named export`); + } +} + +export function loadDataConverter(dataConverter?: DataConverter): LoadedDataConverter { + let payloadConverter: PayloadConverter = defaultPayloadConverter; + if (dataConverter?.payloadConverterPath) { + payloadConverter = requirePayloadConverter(dataConverter.payloadConverterPath); + } + return { + payloadConverter, + payloadCodec: dataConverter?.payloadCodec ?? defaultPayloadCodec, + }; +} diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 6377ce8e7..a3a7c288f 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -4,6 +4,8 @@ * @module */ export * from '@temporalio/workflow-common'; +export * from './codec-helpers'; +export * from './data-converter-helpers'; export * from './patch-protobuf-root'; export * from './tls-config'; export * from './utils'; diff --git a/packages/test/src/mock-native-worker.ts b/packages/test/src/mock-native-worker.ts index f95b77a1b..37b64a94e 100644 --- a/packages/test/src/mock-native-worker.ts +++ b/packages/test/src/mock-native-worker.ts @@ -1,18 +1,19 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { lastValueFrom } from 'rxjs'; import { SpanContext } from '@opentelemetry/api'; +import { defaultPayloadConverter, loadDataConverter, msToTs } from '@temporalio/common'; import { coresdk } from '@temporalio/proto'; -import { defaultPayloadConverter, DataConverter, msToTs } from '@temporalio/common'; -import { Worker as RealWorker, NativeWorkerLike, errors } from '@temporalio/worker/lib/worker'; +import { DefaultLogger } from '@temporalio/worker'; +import { errors, NativeWorkerLike, Worker as RealWorker } from '@temporalio/worker/lib/worker'; import { - compileWorkerOptions, + addDefaultWorkerOptions, CompiledWorkerOptions, + compileWorkerOptions, WorkerOptions, - addDefaultWorkerOptions, } from '@temporalio/worker/lib/worker-options'; -import { DefaultLogger } from '@temporalio/worker'; -import * as activities from './activities'; import { WorkflowCreator } from '@temporalio/worker/src/workflow/interface'; +import { fromPayloadsAtIndex, LoadedDataConverter } from '@temporalio/workflow-common'; +import { lastValueFrom } from 'rxjs'; +import * as activities from './activities'; const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)); @@ -131,7 +132,7 @@ export class MockNativeWorker implements NativeWorkerLike { public recordActivityHeartbeat(buffer: ArrayBuffer): void { const { taskToken, details } = coresdk.ActivityHeartbeat.decodeDelimited(new Uint8Array(buffer)); - const arg = defaultPayloadConverter.fromPayloads(0, details); + const arg = fromPayloadsAtIndex(defaultPayloadConverter, 0, details); this.activityHeartbeatCallback!(taskToken, arg); } @@ -153,7 +154,11 @@ export class Worker extends RealWorker { return this.nativeWorker as MockNativeWorker; } - public constructor(workflowCreator: WorkflowCreator, opts: CompiledWorkerOptions, dataConverter: DataConverter) { + public constructor( + workflowCreator: WorkflowCreator, + opts: CompiledWorkerOptions, + dataConverter: LoadedDataConverter + ) { const nativeWorker = new MockNativeWorker(); super(nativeWorker, workflowCreator, opts, dataConverter); } @@ -171,7 +176,7 @@ export const defaultOptions: WorkerOptions = { }; export async function isolateFreeWorker(options: WorkerOptions = defaultOptions): Promise { - const dataConverter = await RealWorker.getDataConverter(options); + const dataConverter = await loadDataConverter(options.dataConverter); return new Worker( { async createWorkflow() { diff --git a/packages/test/src/payload-converters/payload-converter-bad-export.ts b/packages/test/src/payload-converters/payload-converter-bad-export.ts index c7cc61d8e..4378d7f97 100644 --- a/packages/test/src/payload-converters/payload-converter-bad-export.ts +++ b/packages/test/src/payload-converters/payload-converter-bad-export.ts @@ -1 +1 @@ -export const dataConverter = { toPayload: Function.prototype }; +export const payloadConverter = { toPayload: Function.prototype }; diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-data-converter.ts index 7393d7ddf..f4d359e96 100644 --- a/packages/test/src/test-custom-data-converter.ts +++ b/packages/test/src/test-custom-data-converter.ts @@ -8,6 +8,7 @@ import { payloadConverter, messageInstance } from './payload-converters/payload- import { cleanStackTrace, RUN_INTEGRATION_TESTS } from './helpers'; import { defaultOptions, isolateFreeWorker } from './mock-native-worker'; import { protobufWorkflow } from './workflows'; +import { toPayloads } from '@temporalio/workflow-common'; export async function runWorker(worker: Worker, fn: () => Promise): Promise { const promise = worker.run(); @@ -100,7 +101,7 @@ test('Worker with proto data converter runs an activity and reports completion', taskToken, start: { activityType: 'protoActivity', - input: payloadConverter.toPayloads(messageInstance), + input: toPayloads(payloadConverter, messageInstance), }, }); compareCompletion(t, completion.result, { diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 135a14ff0..1074214a8 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -2,14 +2,14 @@ import { Connection, WorkflowClient } from '@temporalio/client'; import { DataConverterError, defaultPayloadConverter, DefaultPayloadConverter, ValueError } from '@temporalio/common'; import { Core, DefaultLogger, Worker } from '@temporalio/worker'; -import { CompositeDataConverter } from '@temporalio/workflow-common'; +import { CompositePayloadConverter } from '@temporalio/workflow-common'; import { BinaryPayloadConverter, JsonPayloadConverter, ProtobufBinaryPayloadConverter, ProtobufJsonPayloadConverter, UndefinedPayloadConverter, -} from '@temporalio/workflow-common/lib/converter/payload-converter'; +} from '@temporalio/workflow-common/lib/converter/payload-converters'; import { encodingKeys, METADATA_ENCODING_KEY, @@ -26,29 +26,29 @@ import { protobufWorkflow } from './workflows'; test('UndefinedPayloadConverter converts from undefined only', async (t) => { const converter = new UndefinedPayloadConverter(); - t.is(await converter.toData(null), undefined); - t.is(await converter.toData({}), undefined); - t.is(await converter.toData(1), undefined); - t.is(await converter.toData(0), undefined); - t.is(await converter.toData('abc'), undefined); - t.deepEqual(await converter.toData(undefined), { + t.is(await converter.toPayload(null), undefined); + t.is(await converter.toPayload({}), undefined); + t.is(await converter.toPayload(1), undefined); + t.is(await converter.toPayload(0), undefined); + t.is(await converter.toPayload('abc'), undefined); + t.deepEqual(await converter.toPayload(undefined), { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL }, }); }); test('UndefinedPayloadConverter converts to undefined', async (t) => { const converter = new UndefinedPayloadConverter(); - t.is(await converter.fromData((await converter.toData(undefined))!), undefined); + t.is(await converter.fromPayload((await converter.toPayload(undefined))!), undefined); }); test('BinaryPayloadConverter converts from Uint8Array', async (t) => { const converter = new BinaryPayloadConverter(); - t.is(await converter.toData(null), undefined); - t.is(await converter.toData({}), undefined); - t.is(await converter.toData(1), undefined); - t.is(await converter.toData(0), undefined); - t.is(await converter.toData('abc'), undefined); - t.deepEqual(await converter.toData(u8('abc')), { + t.is(await converter.toPayload(null), undefined); + t.is(await converter.toPayload({}), undefined); + t.is(await converter.toPayload(1), undefined); + t.is(await converter.toPayload(0), undefined); + t.is(await converter.toPayload('abc'), undefined); + t.deepEqual(await converter.toPayload(u8('abc')), { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_RAW }, data: u8('abc'), }); @@ -56,7 +56,7 @@ test('BinaryPayloadConverter converts from Uint8Array', async (t) => { test('BinaryPayloadConverter converts to Uint8Array', async (t) => { const converter = new BinaryPayloadConverter(); - t.deepEqual(await converter.fromData((await converter.toData(u8('abc')))!), u8('abc')); + t.deepEqual(await converter.fromPayload((await converter.toPayload(u8('abc')))!), u8('abc')); }); test('JsonPayloadConverter converts from non undefined', async (t) => { @@ -65,23 +65,23 @@ test('JsonPayloadConverter converts from non undefined', async (t) => { data: u8(JSON.stringify(val)), }); const converter = new JsonPayloadConverter(); - t.deepEqual(await converter.toData(null), payload(null)); - t.deepEqual(await converter.toData({ a: 1 }), payload({ a: 1 })); - t.deepEqual(await converter.toData(1), payload(1)); - t.deepEqual(await converter.toData(0), payload(0)); - t.deepEqual(await converter.toData('abc'), payload('abc')); - t.is(await converter.toData(undefined), undefined); + t.deepEqual(await converter.toPayload(null), payload(null)); + t.deepEqual(await converter.toPayload({ a: 1 }), payload({ a: 1 })); + t.deepEqual(await converter.toPayload(1), payload(1)); + t.deepEqual(await converter.toPayload(0), payload(0)); + t.deepEqual(await converter.toPayload('abc'), payload('abc')); + t.is(await converter.toPayload(undefined), undefined); }); test('JsonPayloadConverter converts to object', async (t) => { const converter = new JsonPayloadConverter(); - t.deepEqual(await converter.fromData((await converter.toData({ a: 1 }))!), { a: 1 }); + t.deepEqual(await converter.fromPayload((await converter.toPayload({ a: 1 }))!), { a: 1 }); }); test('ProtobufBinaryPayloadConverter converts from an instance', async (t) => { const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); const converter = new ProtobufBinaryPayloadConverter(root); - t.deepEqual(await converter.toData(instance), { + t.deepEqual(await converter.toPayload(instance), { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF, [METADATA_MESSAGE_TYPE_KEY]: u8('ProtoActivityInput'), @@ -93,7 +93,7 @@ test('ProtobufBinaryPayloadConverter converts from an instance', async (t) => { test('ProtobufBinaryPayloadConverter converts to an instance', async (t) => { const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); const converter = new ProtobufBinaryPayloadConverter(root); - const testInstance = await converter.fromData((await converter.toData(instance))!); + const testInstance = await converter.fromPayload((await converter.toPayload(instance))!); // tests that both are instances of the same class with the same properties t.deepEqual(testInstance, instance); }); @@ -104,7 +104,7 @@ test('ProtobufBinaryPayloadConverter throws detailed errors', async (t) => { await t.throwsAsync( async () => - await converter.fromData({ + await converter.fromPayload({ metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF }, data: root.ProtoActivityInput.encode(instance).finish(), }), @@ -112,7 +112,7 @@ test('ProtobufBinaryPayloadConverter throws detailed errors', async (t) => { ); await t.throwsAsync( async () => - await converter.fromData({ + await converter.fromPayload({ metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF, [METADATA_MESSAGE_TYPE_KEY]: u8('NonExistentMessageClass'), @@ -129,7 +129,7 @@ test('ProtobufBinaryPayloadConverter throws detailed errors', async (t) => { test('ProtobufJSONPayloadConverter converts from an instance to JSON', async (t) => { const instance = root.foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); const converter = new ProtobufJsonPayloadConverter(root); - t.deepEqual(await converter.toData(instance), { + t.deepEqual(await converter.toPayload(instance), { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF_JSON, [METADATA_MESSAGE_TYPE_KEY]: u8('foo.bar.ProtoActivityInput'), @@ -141,7 +141,7 @@ test('ProtobufJSONPayloadConverter converts from an instance to JSON', async (t) test('ProtobufJSONPayloadConverter converts to an instance from JSON', async (t) => { const instance = root.foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); const converter = new ProtobufJsonPayloadConverter(root); - const testInstance = await converter.fromData((await converter.toData(instance))!); + const testInstance = await converter.fromPayload((await converter.toPayload(instance))!); // tests that both are instances of the same class with the same properties t.deepEqual(testInstance, instance); }); @@ -151,7 +151,7 @@ test('ProtobufJSONPayloadConverter converts binary', async (t) => { // https://developers.google.com/protocol-buffers/docs/proto3#json const instance = root.BinaryMessage.create({ data: u8('abc') }); const converter = new ProtobufJsonPayloadConverter(root); - const encoded = await converter.toData(instance); + const encoded = await converter.toPayload(instance); t.deepEqual(encoded, { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF_JSON, @@ -160,7 +160,7 @@ test('ProtobufJSONPayloadConverter converts binary', async (t) => { data: u8(JSON.stringify({ data: Buffer.from('abc').toString('base64') })), }); - const testInstance = await converter.fromData(encoded!); + const testInstance = await converter.fromPayload(encoded!); t.deepEqual(testInstance.data, Buffer.from(instance.data)); }); @@ -190,7 +190,7 @@ if (RUN_INTEGRATION_TESTS) { }); const connection = new Connection(); const client = new WorkflowClient(connection.service, { - dataConverter: new CompositeDataConverter(new ProtobufJsonPayloadConverter(root)), + dataConverter: { payloadConverterPath: './payload-converters/payload-converter' }, }); worker.run(); client.execute(protobufWorkflow, { @@ -210,17 +210,20 @@ test('DefaultPayloadConverter converts protobufs', async (t) => { t.deepEqual( defaultPayloadConverterWithProtos.toPayload(instance), // It will always use JSON because it appears before binary in the list - await new ProtobufJsonPayloadConverter(root).toData(instance) + await new ProtobufJsonPayloadConverter(root).toPayload(instance) ); }); test('defaultPayloadConverter converts to payload by trying each converter in order', async (t) => { const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); - t.deepEqual(defaultPayloadConverter.toPayload(instance), await new ProtobufJsonPayloadConverter().toData(instance)); + t.deepEqual( + defaultPayloadConverter.toPayload(instance), + await new ProtobufJsonPayloadConverter().toPayload(instance) + ); - t.deepEqual(defaultPayloadConverter.toPayload('abc'), await new JsonPayloadConverter().toData('abc')); - t.deepEqual(defaultPayloadConverter.toPayload(undefined), await new UndefinedPayloadConverter().toData(undefined)); - t.deepEqual(defaultPayloadConverter.toPayload(u8('abc')), await new BinaryPayloadConverter().toData(u8('abc'))); + t.deepEqual(defaultPayloadConverter.toPayload('abc'), await new JsonPayloadConverter().toPayload('abc')); + t.deepEqual(defaultPayloadConverter.toPayload(undefined), await new UndefinedPayloadConverter().toPayload(undefined)); + t.deepEqual(defaultPayloadConverter.toPayload(u8('abc')), await new BinaryPayloadConverter().toPayload(u8('abc'))); await t.throws(() => defaultPayloadConverter.toPayload(0n), { instanceOf: TypeError, message: 'Do not know how to serialize a BigInt', @@ -228,9 +231,9 @@ test('defaultPayloadConverter converts to payload by trying each converter in or }); test('defaultPayloadConverter converts from payload by payload type', async (t) => { - t.deepEqual(defaultPayloadConverter.fromPayload(new JsonPayloadConverter().toData('abc')!), 'abc'); - t.deepEqual(defaultPayloadConverter.fromPayload(new UndefinedPayloadConverter().toData(undefined)!), undefined); - t.deepEqual(defaultPayloadConverter.fromPayload(new BinaryPayloadConverter().toData(u8('abc'))!), u8('abc')); + t.deepEqual(defaultPayloadConverter.fromPayload(new JsonPayloadConverter().toPayload('abc')!), 'abc'); + t.deepEqual(defaultPayloadConverter.fromPayload(new UndefinedPayloadConverter().toPayload(undefined)!), undefined); + t.deepEqual(defaultPayloadConverter.fromPayload(new BinaryPayloadConverter().toPayload(u8('abc'))!), u8('abc')); await t.throwsAsync( async () => defaultPayloadConverter.fromPayload({ metadata: { [METADATA_ENCODING_KEY]: u8('not-supported') } }), { instanceOf: ValueError, message: 'Unknown encoding: not-supported' } @@ -249,11 +252,11 @@ test('defaultPayloadConverter converts from payload by payload type', async (t) message: 'Unable to deserialize protobuf message without `root` being provided', }; await t.throwsAsync( - async () => defaultPayloadConverter.fromPayload(new ProtobufBinaryPayloadConverter(root).toData(instance)!), + async () => defaultPayloadConverter.fromPayload(new ProtobufBinaryPayloadConverter(root).toPayload(instance)!), protoError ); await t.throwsAsync( - async () => defaultPayloadConverter.fromPayload(new ProtobufJsonPayloadConverter(root).toData(instance)!), + async () => defaultPayloadConverter.fromPayload(new ProtobufJsonPayloadConverter(root).toPayload(instance)!), protoError ); }); diff --git a/packages/test/src/test-integration.ts b/packages/test/src/test-integration.ts index cd2be5413..f7da9a58a 100644 --- a/packages/test/src/test-integration.ts +++ b/packages/test/src/test-integration.ts @@ -27,6 +27,7 @@ import * as workflows from './workflows'; import { u8, RUN_INTEGRATION_TESTS, cleanStackTrace } from './helpers'; import { withZeroesHTTPServer } from './zeroes-http-server'; import asyncRetry from 'async-retry'; +import { fromPayloadsAtIndex } from '@temporalio/workflow-common'; const { EVENT_TYPE_TIMER_STARTED, EVENT_TYPE_TIMER_FIRED, EVENT_TYPE_TIMER_CANCELED } = iface.temporal.api.enums.v1.EventType; @@ -653,7 +654,8 @@ if (RUN_INTEGRATION_TESTS) { namespace, execution: { workflowId: workflow.workflowId, runId: err.newExecutionRunId }, }); - const timeSlept = defaultPayloadConverter.fromPayloads( + const timeSlept = fromPayloadsAtIndex( + defaultPayloadConverter, 0, history?.events?.[0].workflowExecutionStartedEventAttributes?.input?.payloads ); @@ -748,8 +750,8 @@ if (RUN_INTEGRATION_TESTS) { }, }); await t.throwsAsync(handle.result()); - const handleForSecondAtttempt = client.getHandle(workflowId); - const { workflowExecutionInfo } = await handleForSecondAtttempt.describe(); + const handleForSecondAttempt = client.getHandle(workflowId); + const { workflowExecutionInfo } = await handleForSecondAttempt.describe(); t.not(workflowExecutionInfo?.execution?.runId, handle.originalRunId); }); diff --git a/packages/test/src/test-worker-activities.ts b/packages/test/src/test-worker-activities.ts index 810a212e9..913656a8f 100644 --- a/packages/test/src/test-worker-activities.ts +++ b/packages/test/src/test-worker-activities.ts @@ -8,6 +8,7 @@ import { httpGet } from './activities'; import { Worker, isolateFreeWorker, defaultOptions } from './mock-native-worker'; import { withZeroesHTTPServer } from './zeroes-http-server'; import { cleanStackTrace } from './helpers'; +import { toPayloads } from '@temporalio/workflow-common'; export interface Context { worker: Worker; @@ -55,7 +56,7 @@ test('Worker runs an activity and reports completion', async (t) => { taskToken, start: { activityType: 'httpGet', - input: defaultPayloadConverter.toPayloads(url), + input: toPayloads(defaultPayloadConverter, url), }, }); compareCompletion(t, completion.result, { @@ -73,7 +74,7 @@ test('Worker runs an activity and reports failure', async (t) => { taskToken, start: { activityType: 'throwAnError', - input: defaultPayloadConverter.toPayloads(false, message), + input: toPayloads(defaultPayloadConverter, false, message), }, }); compareCompletion(t, completion.result, { @@ -101,7 +102,7 @@ test('Worker cancels activity and reports cancellation', async (t) => { taskToken, start: { activityType: 'waitForCancellation', - input: defaultPayloadConverter.toPayloads(), + input: toPayloads(defaultPayloadConverter), }, }, }); @@ -125,7 +126,7 @@ test('Activity Context AbortSignal cancels a fetch request', async (t) => { taskToken, start: { activityType: 'cancellableFetch', - input: defaultPayloadConverter.toPayloads(`http://127.0.0.1:${port}`, false), + input: toPayloads(defaultPayloadConverter, `http://127.0.0.1:${port}`, false), }, }, }); @@ -148,7 +149,7 @@ test('Activity Context heartbeat is sent to core', async (t) => { taskToken, start: { activityType: 'progressiveSleep', - input: defaultPayloadConverter.toPayloads(), + input: toPayloads(defaultPayloadConverter), }, }); console.log('waiting heartbeat 1'); @@ -171,7 +172,7 @@ test('Worker fails activity with proper message when it is not registered', asyn taskToken, start: { activityType: 'notFound', - input: defaultPayloadConverter.toPayloads(), + input: toPayloads(defaultPayloadConverter), }, }); t.regex( diff --git a/packages/test/src/test-workflows.ts b/packages/test/src/test-workflows.ts index 11d2009d7..878d14dd7 100644 --- a/packages/test/src/test-workflows.ts +++ b/packages/test/src/test-workflows.ts @@ -11,6 +11,7 @@ import { DefaultLogger } from '@temporalio/worker/lib/logger'; import * as activityFunctions from './activities'; import { u8 } from './helpers'; import { WorkflowInfo } from '@temporalio/workflow'; +import { toPayloads } from '@temporalio/workflow-common'; export interface Context { workflow: VMWorkflow; @@ -214,7 +215,7 @@ async function makeQueryWorkflowJob( queryWorkflow: { queryId, queryType, - arguments: defaultPayloadConverter.toPayloads(...queryArgs), + arguments: toPayloads(defaultPayloadConverter, ...queryArgs), }, }; } @@ -225,7 +226,7 @@ async function makeSignalWorkflow( timestamp: number = Date.now() ): Promise { return makeActivation(timestamp, { - signalWorkflow: { signalName, input: defaultPayloadConverter.toPayloads(...args) }, + signalWorkflow: { signalName, input: toPayloads(defaultPayloadConverter, ...args) }, }); } @@ -319,11 +320,7 @@ test('random', async (t) => { test('successString', async (t) => { const { workflowType } = t.context; const req = await activate(t, makeStartWorkflow(workflowType)); - compareCompletion( - t, - req, - makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayloadSync('success'))]) - ); + compareCompletion(t, req, makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload('success'))])); }); /** @@ -386,11 +383,7 @@ test('date', async (t) => { test('asyncWorkflow', async (t) => { const { workflowType } = t.context; const req = await activate(t, makeStartWorkflow(workflowType)); - compareCompletion( - t, - req, - makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayloadSync('async'))]) - ); + compareCompletion(t, req, makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload('async'))])); }); test('deferredResolve', async (t) => { @@ -416,7 +409,7 @@ test('sleeper', async (t) => { test('with ms string - sleeper', async (t) => { const { logs, workflowType } = t.context; { - const req = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayloadSync('10s')])); + const req = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayload('10s')])); compareCompletion(t, req, makeSuccess([makeStartTimerCommand({ seq: 1, startToFireTimeout: msToTs('10s') })])); } { @@ -746,7 +739,7 @@ test('cancelWorkflow', async (t) => { const url = 'https://temporal.io'; const { workflowType } = t.context; { - const req = await activate(t, makeStartWorkflow(workflowType, defaultPayloadConverter.toPayloads(url))); + const req = await activate(t, makeStartWorkflow(workflowType, toPayloads(defaultPayloadConverter, url))); compareCompletion( t, req, @@ -755,7 +748,7 @@ test('cancelWorkflow', async (t) => { seq: 1, activityId: '1', activityType: 'httpGet', - arguments: defaultPayloadConverter.toPayloads(url), + arguments: toPayloads(defaultPayloadConverter, url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -788,7 +781,7 @@ test('cancelWorkflow', async (t) => { seq: 3, activityId: '3', activityType: 'httpGet', - arguments: defaultPayloadConverter.toPayloads(url), + arguments: toPayloads(defaultPayloadConverter, url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -905,7 +898,7 @@ test('nonCancellable', async (t) => { seq: 1, activityId: '1', activityType: 'httpGetJSON', - arguments: defaultPayloadConverter.toPayloads(url), + arguments: toPayloads(defaultPayloadConverter, url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -932,7 +925,7 @@ test('resumeAfterCancellation', async (t) => { seq: 1, activityId: '1', activityType: 'httpGetJSON', - arguments: defaultPayloadConverter.toPayloads(url), + arguments: toPayloads(defaultPayloadConverter, url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -957,7 +950,7 @@ test('handleExternalWorkflowCancellationWhileActivityRunning', async (t) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const completion = await activate( t, - makeStartWorkflow(workflowType, defaultPayloadConverter.toPayloads(url, data) ?? []) + makeStartWorkflow(workflowType, toPayloads(defaultPayloadConverter, url, data) ?? []) ); compareCompletion( @@ -968,7 +961,7 @@ test('handleExternalWorkflowCancellationWhileActivityRunning', async (t) => { seq: 1, activityId: '1', activityType: 'httpPostJSON', - arguments: defaultPayloadConverter.toPayloads(url, data), + arguments: toPayloads(defaultPayloadConverter, url, data), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -992,7 +985,7 @@ test('handleExternalWorkflowCancellationWhileActivityRunning', async (t) => { seq: 2, activityId: '2', activityType: 'cleanup', - arguments: defaultPayloadConverter.toPayloads(url), + arguments: toPayloads(defaultPayloadConverter, url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1043,7 +1036,7 @@ test('nestedCancellation', async (t) => { seq: 2, activityId: '2', activityType: 'httpPostJSON', - arguments: defaultPayloadConverter.toPayloads(url, { some: 'data' }), + arguments: toPayloads(defaultPayloadConverter, url, { some: 'data' }), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1065,7 +1058,7 @@ test('nestedCancellation', async (t) => { activityId: '3', seq: 3, activityType: 'cleanup', - arguments: defaultPayloadConverter.toPayloads(url), + arguments: toPayloads(defaultPayloadConverter, url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1104,7 +1097,7 @@ test('sharedScopes', async (t) => { seq: idx, activityId: `${idx}`, activityType: 'httpGetJSON', - arguments: defaultPayloadConverter.toPayloads(`http://url${idx}.ninja`), + arguments: toPayloads(defaultPayloadConverter, `http://url${idx}.ninja`), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }) @@ -1139,7 +1132,7 @@ test('shieldAwaitedInRootScope', async (t) => { seq: 1, activityId: '1', activityType: 'httpGetJSON', - arguments: defaultPayloadConverter.toPayloads(`http://example.com`), + arguments: toPayloads(defaultPayloadConverter, `http://example.com`), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1323,7 +1316,7 @@ test('cancelActivityAfterFirstCompletion', async (t) => { const url = 'https://temporal.io'; const { workflowType, logs } = t.context; { - const req = await activate(t, makeStartWorkflow(workflowType, defaultPayloadConverter.toPayloads(url))); + const req = await activate(t, makeStartWorkflow(workflowType, toPayloads(defaultPayloadConverter, url))); compareCompletion( t, req, @@ -1332,7 +1325,7 @@ test('cancelActivityAfterFirstCompletion', async (t) => { seq: 1, activityId: '1', activityType: 'httpGet', - arguments: defaultPayloadConverter.toPayloads(url), + arguments: toPayloads(defaultPayloadConverter, url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1352,7 +1345,7 @@ test('cancelActivityAfterFirstCompletion', async (t) => { seq: 2, activityId: '2', activityType: 'httpGet', - arguments: defaultPayloadConverter.toPayloads(url), + arguments: toPayloads(defaultPayloadConverter, url), startToCloseTimeout: msToTs('10m'), taskQueue: 'test', }), @@ -1383,7 +1376,7 @@ test('multipleActivitiesSingleTimeout', async (t) => { { const completion = await activate( t, - makeStartWorkflow(workflowType, defaultPayloadConverter.toPayloads(urls, 1000)) + makeStartWorkflow(workflowType, toPayloads(defaultPayloadConverter, urls, 1000)) ); compareCompletion( t, @@ -1396,7 +1389,7 @@ test('multipleActivitiesSingleTimeout', async (t) => { seq: index + 1, activityId: `${index + 1}`, activityType: 'httpGetJSON', - arguments: defaultPayloadConverter.toPayloads(url), + arguments: toPayloads(defaultPayloadConverter, url), startToCloseTimeout: msToTs('1s'), taskQueue: 'test', }) @@ -1436,7 +1429,7 @@ test('resolve activity with result - http', async (t) => { seq: 1, activityId: '1', activityType: 'httpGet', - arguments: defaultPayloadConverter.toPayloads('https://temporal.io'), + arguments: toPayloads(defaultPayloadConverter, 'https://temporal.io'), startToCloseTimeout: msToTs('1 minute'), taskQueue: 'test', }), @@ -1453,7 +1446,7 @@ test('resolve activity with result - http', async (t) => { compareCompletion( t, completion, - makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayloadSync(result))]) + makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload(result))]) ); } }); @@ -1470,7 +1463,7 @@ test('resolve activity with failure - http', async (t) => { seq: 1, activityId: '1', activityType: 'httpGet', - arguments: defaultPayloadConverter.toPayloads('https://temporal.io'), + arguments: toPayloads(defaultPayloadConverter, 'https://temporal.io'), startToCloseTimeout: msToTs('1 minute'), taskQueue: 'test', }), @@ -1536,7 +1529,7 @@ test('continueAsNewSameWorkflow', async (t) => { continueAsNewWorkflowExecution: { workflowType, taskQueue: 'test', - arguments: defaultPayloadConverter.toPayloads('signal'), + arguments: toPayloads(defaultPayloadConverter, 'signal'), }, }, ]) @@ -1659,10 +1652,13 @@ test('patchedTopLevel', async (t) => { t.deepEqual(logs, [[['Patches cannot be used before Workflow starts']]]); }); -test('tryToContinueAfterCompletion', async (t) => { +test.only('tryToContinueAfterCompletion', async (t) => { const { workflowType } = t.context; { - const completion = cleanWorkflowFailureStackTrace(await activate(t, makeStartWorkflow(workflowType))); + const failure = await activate(t, makeStartWorkflow(workflowType)); + console.log('failure:', failure); + + const completion = cleanWorkflowFailureStackTrace(failure); compareCompletion( t, completion, diff --git a/packages/test/src/workflows/data-converter.ts b/packages/test/src/workflows/data-converter.ts index d22d2006a..e04aab7fc 100644 --- a/packages/test/src/workflows/data-converter.ts +++ b/packages/test/src/workflows/data-converter.ts @@ -3,4 +3,4 @@ import root, { foo } from '../../protos/root'; export const messageInstance = foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); -export const dataConverter = new DefaultPayloadConverter({ root }); +export const payloadConverter = new DefaultPayloadConverter({ root }); diff --git a/packages/test/tsconfig.json b/packages/test/tsconfig.json index 50f275391..01dbcd3d9 100644 --- a/packages/test/tsconfig.json +++ b/packages/test/tsconfig.json @@ -5,6 +5,7 @@ "rootDir": "./src" }, "references": [ + { "path": "../workflow-common" }, { "path": "../activity" }, { "path": "../client" }, { "path": "../common" }, diff --git a/packages/worker/src/activity.ts b/packages/worker/src/activity.ts index fa734380b..87c459532 100644 --- a/packages/worker/src/activity.ts +++ b/packages/worker/src/activity.ts @@ -1,15 +1,16 @@ -import { AbortController } from 'abort-controller'; +import { asyncLocalStorage, Context, Info } from '@temporalio/activity'; import { ActivityFunction, + CancelledFailure, composeInterceptors, - PayloadConverter, + encodeErrorToFailure, + encodeToPayload, ensureTemporalFailure, - errorToFailure, - CancelledFailure, FAILURE_SOURCE, } from '@temporalio/common'; import { coresdk } from '@temporalio/proto'; -import { asyncLocalStorage, Context, Info } from '@temporalio/activity'; +import { LoadedDataConverter } from '@temporalio/workflow-common'; +import { AbortController } from 'abort-controller'; import { ActivityExecuteInput, ActivityInboundCallsInterceptor, @@ -28,7 +29,7 @@ export class Activity { constructor( public readonly info: Info, public readonly fn: ActivityFunction, - public readonly dataConverter: PayloadConverter, + public readonly dataConverter: LoadedDataConverter, public readonly heartbeatCallback: Context['heartbeat'], interceptors?: { inbound?: ActivityInboundCallsInterceptorFactory[]; @@ -63,7 +64,7 @@ export class Activity { try { const execute = composeInterceptors(this.interceptors.inbound, 'execute', (inp) => this.execute(inp)); const result = await execute(input); - return { completed: { result: this.dataConverter.toPayload(result) } }; + return { completed: { result: await encodeToPayload(this.dataConverter, result) } }; } catch (err) { if (err instanceof Error && err.name === 'CompleteAsyncError') { return { willCompleteAsync: {} }; @@ -71,7 +72,7 @@ export class Activity { if (this.cancelRequested) { // Either a CancelledFailure that we threw or AbortError from AbortController if (err instanceof CancelledFailure) { - const failure = errorToFailure(err, this.dataConverter); + const failure = await encodeErrorToFailure(this.dataConverter, err); failure.stackTrace = undefined; return { cancelled: { failure } }; } else if (err instanceof Error && err.name === 'AbortError') { @@ -80,7 +81,7 @@ export class Activity { } return { failed: { - failure: errorToFailure(ensureTemporalFailure(err), this.dataConverter), + failure: await encodeErrorToFailure(this.dataConverter, ensureTemporalFailure(err)), }, }; } diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index 0749c2ad6..74bdae3e3 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -1,9 +1,35 @@ -import fs from 'fs/promises'; -import { promisify } from 'util'; import * as otel from '@opentelemetry/api'; +import { SpanContext } from '@opentelemetry/api'; +import { Info as ActivityInfo } from '@temporalio/activity'; +import { + DataConverter, + decodeArrayFromPayloads, + decodeFromPayloadsAtIndex, + defaultPayloadConverter, + encodeErrorToFailure, + encodeToPayload, + errorMessage, + IllegalStateError, + loadDataConverter, + tsToMs, +} from '@temporalio/common'; +import { + extractSpanContextFromHeaders, + linkSpans, + NUM_JOBS_ATTR_KEY, + RUN_ID_ATTR_KEY, + TASK_TOKEN_ATTR_KEY, +} from '@temporalio/common/lib/otel'; +import * as native from '@temporalio/core-bridge'; +import { coresdk } from '@temporalio/proto'; +import { SinkCall, WorkflowInfo } from '@temporalio/workflow'; +import { LoadedDataConverter } from '@temporalio/workflow-common'; +import fs from 'fs/promises'; import { BehaviorSubject, + EMPTY, from, + lastValueFrom, merge, MonoTypeOperatorFunction, Observable, @@ -11,48 +37,17 @@ import { pipe, race, Subject, - lastValueFrom, - EMPTY, } from 'rxjs'; import { delay, filter, first, ignoreElements, map, mergeMap, takeUntil, takeWhile, tap } from 'rxjs/operators'; -import * as native from '@temporalio/core-bridge'; -import { coresdk } from '@temporalio/proto'; -import { Info as ActivityInfo } from '@temporalio/activity'; -import { - IllegalStateError, - tsToMs, - errorToFailure, - arrayFromPayloads, - PayloadConverter, - defaultPayloadConverter, - isValidDataConverter, - errorMessage, - errorCode, -} from '@temporalio/common'; -import { - extractSpanContextFromHeaders, - linkSpans, - RUN_ID_ATTR_KEY, - NUM_JOBS_ATTR_KEY, - TASK_TOKEN_ATTR_KEY, -} from '@temporalio/common/lib/otel'; - -import { closeableGroupBy, mergeMapWithState } from './rxutils'; -import { toMB } from './utils'; -import { Workflow, WorkflowCreator } from './workflow/interface'; -import { WorkflowCodeBundler } from './workflow/bundler'; +import { promisify } from 'util'; import { Activity } from './activity'; -import { Logger } from './logger'; +import { Core } from './core'; import * as errors from './errors'; -import { childSpan, instrument, getTracer } from './tracing'; import { ActivityExecuteInput } from './interceptors'; -export { IllegalStateError } from '@temporalio/common'; -export { PayloadConverter as DataConverter, defaultPayloadConverter, errors }; -import { Core } from './core'; -import { SpanContext } from '@opentelemetry/api'; -import IWorkflowActivationJob = coresdk.workflow_activation.IWorkflowActivationJob; -import { ThreadedVMWorkflowCreator } from './workflow/threaded-vm'; -import { SinkCall, WorkflowInfo } from '@temporalio/workflow'; +import { Logger } from './logger'; +import { closeableGroupBy, mergeMapWithState } from './rxutils'; +import { childSpan, getTracer, instrument } from './tracing'; +import { toMB } from './utils'; import { addDefaultWorkerOptions, CompiledWorkerOptions, @@ -61,8 +56,16 @@ import { isPathBundleOption, WorkerOptions, } from './worker-options'; +import { WorkflowCodecRunner } from './workflow-codec-runner'; +import { WorkflowCodeBundler } from './workflow/bundler'; +import { Workflow, WorkflowCreator } from './workflow/interface'; +import { ThreadedVMWorkflowCreator } from './workflow/threaded-vm'; import { VMWorkflowCreator } from './workflow/vm'; +export { IllegalStateError } from '@temporalio/common'; +export { DataConverter, defaultPayloadConverter, errors }; +import IWorkflowActivationJob = coresdk.workflow_activation.IWorkflowActivationJob; + native.registerErrors(errors); /** * The worker's possible states @@ -179,6 +182,7 @@ export class Worker { protected static nativeWorkerCtor: WorkerConstructor = NativeWorker; protected readonly tracer: otel.Tracer; + protected readonly workflowCodecRunner: WorkflowCodecRunner; /** * Create a new Worker. @@ -188,7 +192,7 @@ export class Worker { const nativeWorkerCtor: WorkerConstructor = this.nativeWorkerCtor; const compiledOptions = compileWorkerOptions(addDefaultWorkerOptions(options)); const nativeWorker = await nativeWorkerCtor.create(compiledOptions); - const dataConverter = await this.getDataConverter(options); + const dataConverter = loadDataConverter(options.dataConverter); try { let bundle: string | undefined = undefined; let workflowCreator: WorkflowCreator | undefined = undefined; @@ -236,39 +240,6 @@ export class Worker { } } - public static async getDataConverter(options: WorkerOptions): Promise { - if (options.dataConverter?.payloadConverterPath) { - let dataConverter: PayloadConverter; - - try { - const dataConverterModule = await import(options.dataConverter?.payloadConverterPath); - dataConverter = dataConverterModule.dataConverter; - } catch (error) { - if (errorCode(error) === 'MODULE_NOT_FOUND') { - throw new Error( - `Could not find a file at the specified payloadConverterPath: '${options.dataConverter?.payloadConverterPath}'.` - ); - } - throw error; - } - - if (dataConverter === undefined) { - throw new Error( - `The module at payloadConverterPath ('${options.dataConverter?.payloadConverterPath}') does not have a \`dataConverter\` named export.` - ); - } - if (!isValidDataConverter(dataConverter)) { - throw new Error( - `The \`dataConverter\` named export at payloadConverterPath (${options.dataConverter?.payloadConverterPath}) should be an instance of a class that implements the DataConverter interface.` - ); - } - - return dataConverter; - } - - return defaultPayloadConverter; - } - /** * Create a new Worker from nativeWorker. */ @@ -279,9 +250,10 @@ export class Worker { */ protected readonly workflowCreator: WorkflowCreator | undefined, public readonly options: CompiledWorkerOptions, - protected readonly dataConverter: PayloadConverter = defaultPayloadConverter + protected readonly dataConverter: LoadedDataConverter ) { this.tracer = getTracer(options.enableSDKTracing); + this.workflowCodecRunner = new WorkflowCodecRunner(dataConverter.payloadCodec); } /** @@ -447,7 +419,7 @@ export class Worker { } let args: unknown[]; try { - args = await arrayFromPayloads(this.dataConverter, task.start?.input); + args = await decodeArrayFromPayloads(this.dataConverter, task.start?.input); } catch (err) { output = { type: 'result', @@ -672,7 +644,9 @@ export class Worker { let isFatalError = false; try { - const completion = await state.workflow.activate(activation); + await this.workflowCodecRunner.decodeActivation(activation); + const unencodedCompletion = await state.workflow.activate(activation); + const completion = await this.workflowCodecRunner.encodeCompletion(unencodedCompletion); this.log.debug('Completed activation', { runId: activation.runId, }); @@ -705,7 +679,7 @@ export class Worker { const completion = coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited({ runId: activation.runId, failed: { - failure: errorToFailure(error, this.dataConverter), + failure: await encodeErrorToFailure(this.dataConverter, error), }, }).finish(); // We do not dispose of the Workflow yet, wait to be evicted from Core. @@ -778,7 +752,7 @@ export class Worker { complete: () => this.log.debug('Heartbeats complete'), }), mergeMap(async ({ taskToken, details }) => { - const payload = this.dataConverter.toPayload(details); + const payload = await encodeToPayload(this.dataConverter, details); const arr = coresdk.ActivityHeartbeat.encodeDelimited({ taskToken, details: [payload], @@ -1005,7 +979,7 @@ type NonNullableObject = { [P in keyof T]-?: NonNullable }; async function extractActivityInfo( task: coresdk.activity_task.IActivityTask, isLocal: boolean, - dataConverter: PayloadConverter, + dataConverter: LoadedDataConverter, activityNamespace: string ): Promise { // NOTE: We trust core to supply all of these fields instead of checking for null and undefined everywhere @@ -1020,7 +994,7 @@ async function extractActivityInfo( isLocal, activityType: start.activityType, workflowType: start.workflowType, - heartbeatDetails: dataConverter.fromPayloads(0, start.heartbeatDetails), + heartbeatDetails: await decodeFromPayloadsAtIndex(dataConverter, 0, start.heartbeatDetails), activityNamespace, workflowNamespace: start.workflowNamespace, scheduledTimestampMs: tsToMs(start.scheduledTime), diff --git a/packages/worker/src/workflow-codec-runner.ts b/packages/worker/src/workflow-codec-runner.ts new file mode 100644 index 000000000..a4b8bc0a2 --- /dev/null +++ b/packages/worker/src/workflow-codec-runner.ts @@ -0,0 +1,142 @@ +import { decodeFailure, encodeFailure, Payload } from '@temporalio/common'; +import { coresdk, temporal } from '@temporalio/proto'; +import { PayloadCodec, ProtoFailure } from '@temporalio/workflow-common'; + +export class WorkflowCodecRunner { + constructor(protected readonly codec: PayloadCodec) {} + + /** + * Run codec.decode on the Payloads in the Activation message + */ + public async decodeActivation(activation: coresdk.workflow_activation.IWorkflowActivation): Promise { + if (activation.jobs?.length) { + await Promise.all( + activation.jobs.flatMap((job) => [ + this.decodeArray(job.startWorkflow, 'arguments'), + ...this.decodeMap(job.startWorkflow, 'headers'), + this.decodeArray(job.queryWorkflow, 'arguments'), + this.decodeArray(job.cancelWorkflow, 'details'), + this.decodeArray(job.signalWorkflow, 'input'), + this.decodeField(job.resolveActivity?.result?.completed, 'result'), + this.decodeField(job.resolveChildWorkflowExecution?.result?.completed, 'result'), + this.decodeFailure(job.resolveActivity?.result?.failed), + this.decodeFailure(job.resolveActivity?.result?.cancelled), + this.decodeFailure(job.resolveChildWorkflowExecutionStart?.cancelled), + this.decodeFailure(job.resolveSignalExternalWorkflow), + this.decodeFailure(job.resolveChildWorkflowExecution?.result?.failed), + this.decodeFailure(job.resolveChildWorkflowExecution?.result?.cancelled), + this.decodeFailure(job.resolveRequestCancelExternalWorkflow), + ]) + ); + } + } + + protected async decodeField(object: unknown, field: string): Promise { + if (!object) return; + const record = object as Record; + if (!record[field]) return; + + const [decodedPayload] = await this.codec.decode([record[field] as Payload]); + record[field] = decodedPayload; + } + + protected async decodeArray(object: unknown, field: string): Promise { + if (!object) return; + const record = object as Record; + if (!record[field]) return; + + record[field] = await this.codec.decode(record[field] as Payload[]); + } + + protected decodeMap(object: unknown, field: string): Promise[] { + if (!object) return []; + const record = object as Record; + if (!record[field]) return []; + + return Object.entries(record[field] as Record).map(async ([k, v]) => { + const [decodedPayload] = await this.codec.decode([v]); + (record[field] as Record)[k] = decodedPayload; + }); + } + + protected async decodeFailure(failureParent: unknown): Promise { + if (!failureParent) return; + const accessibleFailureParent = failureParent as Record; + if (!accessibleFailureParent.failure) return; + + accessibleFailureParent['failure'] = await decodeFailure( + this.codec, + accessibleFailureParent['failure'] as temporal.api.failure.v1.IFailure + ); + } + + /** + * Run codec.encode on the Payloads inside the Completion message + */ + public async encodeCompletion(completionBytes: Uint8Array): Promise { + const completion = coresdk.workflow_completion.WorkflowActivationCompletion.decode(completionBytes); + + await Promise.all([ + ...(completion.successful?.commands?.flatMap((command) => + command + ? [ + ...this.encodeMap(command.scheduleActivity, 'headerFields'), + this.encodeArray(command.scheduleActivity, 'arguments'), + this.encodeField(command.respondToQuery?.succeeded, 'response'), + this.encodeFailure(command.respondToQuery, 'failed'), + this.encodeField(command.completeWorkflowExecution, 'result'), + this.encodeFailure(command.failWorkflowExecution, 'failure'), + this.encodeArray(command.continueAsNewWorkflowExecution, 'arguments'), + ...this.encodeMap(command.continueAsNewWorkflowExecution, 'memo'), + ...this.encodeMap(command.continueAsNewWorkflowExecution, 'header'), + ...this.encodeMap(command.continueAsNewWorkflowExecution, 'searchAttributes'), + this.encodeArray(command.startChildWorkflowExecution, 'input'), + ...this.encodeMap(command.startChildWorkflowExecution, 'memo'), + ...this.encodeMap(command.startChildWorkflowExecution, 'header'), + ...this.encodeMap(command.startChildWorkflowExecution, 'searchAttributes'), + this.encodeArray(command.signalExternalWorkflowExecution, 'args'), + ] + : [] + ) ?? []), + this.encodeFailure(completion, 'failed'), + ]); + + return coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited(completion).finish(); + } + + protected async encodeField(object: unknown, field: string): Promise { + if (!object) return; + const record = object as Record; + if (!(field in record)) return; + + const [encodedPayload] = await this.codec.encode([record[field] as Payload]); + record[field] = encodedPayload; + } + + protected async encodeArray(object: unknown, field: string): Promise { + if (!object) return; + const record = object as Record; + if (!record[field]) return; + + record[field] = await this.codec.encode(record[field] as Payload[]); + } + + protected encodeMap(object: unknown, field: string): Promise[] { + if (!object) return []; + const record = object as Record; + if (!record[field]) return []; + + return Object.entries(record[field] as Record).map(async ([k, v]) => { + const [encodedPayload] = await this.codec.encode([v]); + (record[field] as Record)[k] = encodedPayload; + }); + } + + protected async encodeFailure(object: unknown, field: string): Promise { + if (!object) return; + const record = object as Record; + if (!record[field]) return; + + record[field] = await encodeFailure(this.codec, record[field] as ProtoFailure); + } +} diff --git a/packages/worker/src/workflow/bundler.ts b/packages/worker/src/workflow/bundler.ts index 0d7a72379..b38b7ae50 100644 --- a/packages/worker/src/workflow/bundler.ts +++ b/packages/worker/src/workflow/bundler.ts @@ -137,13 +137,14 @@ export class WorkflowCodeBundler { if (webpackConfig.resolve) { if (payloadConverterPath) { webpackConfig.resolve.alias = { - __temporal_custom_data_converter$: payloadConverterPath, + __temporal_custom_payload_converter$: payloadConverterPath, }; } else { // Save 100KB from the bundle by not including the npm module, // since we can't decode without a `root` anyway. webpackConfig.resolve.alias = { 'proto3-json-serializer$': path.resolve(__dirname, 'empty-proto3-json-serializer.js'), + __temporal_custom_payload_converter$: path.resolve(__dirname, 'empty-module.js'), }; } } diff --git a/packages/worker/src/workflow/empty-module.ts b/packages/worker/src/workflow/empty-module.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/worker/src/workflow/vm.ts b/packages/worker/src/workflow/vm.ts index 5028c4197..9f9a6e907 100644 --- a/packages/worker/src/workflow/vm.ts +++ b/packages/worker/src/workflow/vm.ts @@ -18,8 +18,8 @@ export class VMWorkflowCreator implements WorkflowCreator { constructor( script: vm.Script, public readonly isolateExecutionTimeoutMs: number, - protected readonly dataConverter: PayloadConverter, - protected readonly payloadConverterPath?: string + protected readonly payloadConverter: PayloadConverter, + protected readonly useCustomPayloadConverter: boolean ) { this.script = script; } @@ -46,9 +46,9 @@ export class VMWorkflowCreator implements WorkflowCreator { } ) as any; - await workflowModule.initRuntime({ useCustomDataConverter: !!this.payloadConverterPath, ...options }); + await workflowModule.initRuntime({ useCustomPayloadConverter: this.useCustomPayloadConverter, ...options }); - return new VMWorkflow(options.info, context, workflowModule, isolateExecutionTimeoutMs, this.dataConverter); + return new VMWorkflow(options.info, context, workflowModule, isolateExecutionTimeoutMs, this.payloadConverter); } protected async getContext(): Promise { @@ -87,12 +87,13 @@ export class VMWorkflowCreator implements WorkflowCreator { payloadConverterPath?: string ): Promise> { const script = new vm.Script(code, { filename: 'workflow-isolate' }); - let dataConverter = defaultPayloadConverter; + let payloadConverter = defaultPayloadConverter; if (payloadConverterPath) { - // payloadConverterPath was validated in Worker.getDataConverter - dataConverter = (await import(payloadConverterPath)).dataConverter; + // payloadConverterPath was validated in Worker.create + payloadConverter = (await import(payloadConverterPath)).payloadConverter; } - return new this(script, isolateExecutionTimeoutMs, dataConverter, payloadConverterPath) as InstanceType; + + return new this(script, isolateExecutionTimeoutMs, payloadConverter, !!payloadConverterPath) as InstanceType; } /** @@ -116,7 +117,7 @@ export class VMWorkflow implements Workflow { protected context: vm.Context | undefined, readonly workflowModule: WorkflowModule, public readonly isolateExecutionTimeoutMs: number, - protected readonly dataConverter: PayloadConverter + protected readonly payloadConverter: PayloadConverter ) {} /** @@ -187,7 +188,7 @@ export class VMWorkflow implements Workflow { if (this.unhandledRejection) { return coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited({ runId: activation.runId, - failed: { failure: errorToFailure(this.unhandledRejection, this.dataConverter) }, + failed: { failure: errorToFailure(this.unhandledRejection, this.payloadConverter) }, }).finish(); } return coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited(completion).finish(); diff --git a/packages/workflow-common/src/converter/data-converter.ts b/packages/workflow-common/src/converter/data-converter.ts index 35b739772..7e6249808 100644 --- a/packages/workflow-common/src/converter/data-converter.ts +++ b/packages/workflow-common/src/converter/data-converter.ts @@ -1,4 +1,5 @@ import { PayloadCodec } from './payload-codec'; +import { PayloadConverter } from './payload-converter'; /** * When your data (arguments and return values) is sent over the wire and stored by Temporal Server, @@ -30,3 +31,8 @@ export interface DataConverter { */ payloadCodec?: PayloadCodec; } + +export interface LoadedDataConverter { + payloadConverter: PayloadConverter; + payloadCodec: PayloadCodec; +} diff --git a/packages/workflow-common/src/converter/payload-converter.ts b/packages/workflow-common/src/converter/payload-converter.ts index a3e80c6a8..1b10b5599 100644 --- a/packages/workflow-common/src/converter/payload-converter.ts +++ b/packages/workflow-common/src/converter/payload-converter.ts @@ -30,13 +30,6 @@ export interface PayloadConverter { fromPayload(payload: Payload): T; } -const isValidPayloadConverter = (PayloadConverter: unknown): PayloadConverter is PayloadConverter => - typeof PayloadConverter === 'object' && - PayloadConverter !== null && - ['toPayload', 'fromPayload'].every( - (method) => typeof (PayloadConverter as Record)[method] === 'function' - ); - export class CompositePayloadConverter implements PayloadConverter { readonly converters: PayloadConverterWithEncoding[]; readonly converterByEncoding: Map = new Map(); @@ -112,37 +105,6 @@ export function fromPayloadsAtIndex(converter: PayloadConverter, index: numbe return converter.fromPayload(payloads[index]); } -export async function importPayloadConverter(path: string): Promise { - const module = await import(path); - if (hasOwnProperty(module, 'payloadConverter')) { - if (isValidPayloadConverter(module.payloadConverter)) { - return module.payloadConverter; - } else { - throw new ValueError( - `payloadConverter export at ${path} must be an object with toPayload and fromPayload methods` - ); - } - } else { - throw new ValueError(`Module ${path} does not have a \`payloadConverter\` named export`); - } -} - -// For use outside of the Workflow vm -export function requirePayloadConverter(path: string): PayloadConverter { - const module = require(path); // eslint-disable-line @typescript-eslint/no-var-requires - if (hasOwnProperty(module, 'payloadConverter')) { - if (isValidPayloadConverter(module.payloadConverter)) { - return module.payloadConverter; - } else { - throw new ValueError( - `payloadConverter export at ${path} must be an object with toPayload and fromPayload methods` - ); - } - } else { - throw new ValueError(`Module ${path} does not have a \`payloadConverter\` named export`); - } -} - export function arrayFromPayloads(converter: PayloadConverter, content?: Payload[] | null): unknown[] { if (!content) { return []; diff --git a/packages/workflow-common/src/errors.ts b/packages/workflow-common/src/errors.ts index 3fed23be8..1f9d905b8 100644 --- a/packages/workflow-common/src/errors.ts +++ b/packages/workflow-common/src/errors.ts @@ -7,7 +7,7 @@ export class DataConverterError extends Error { } export class PayloadConverterError extends Error { - public readonly name: string = 'DataConverterError'; + public readonly name: string = 'PayloadConverterError'; } export class UnsupportedTypeError extends PayloadConverterError { diff --git a/packages/workflow-common/src/index.ts b/packages/workflow-common/src/index.ts index 40fbdbbe8..2294136b7 100644 --- a/packages/workflow-common/src/index.ts +++ b/packages/workflow-common/src/index.ts @@ -5,9 +5,9 @@ */ export * from './activity-options'; export * from './converter/data-converter'; +export * from './converter/payload-codec'; export * from './converter/payload-converter'; export * from './converter/payload-converters'; -export * from './converter/payload-codec'; export * from './converter/types'; export * from './errors'; export * from './failure'; @@ -15,5 +15,6 @@ export * from './interceptors'; export * from './interfaces'; export * from './retry-policy'; export * from './time'; +export * from './type-helpers'; export * from './workflow-handle'; export * from './workflow-options'; diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts index e0ae8f3bc..a27db128b 100644 --- a/packages/workflow/src/index.ts +++ b/packages/workflow/src/index.ts @@ -61,7 +61,7 @@ export { rootCause, IllegalStateError, defaultPayloadConverter, - PayloadConverter as DataConverter, + PayloadConverter, WorkflowIdReusePolicy, ActivityFailure, ApplicationFailure, diff --git a/packages/workflow/src/internals.ts b/packages/workflow/src/internals.ts index 53158121e..3f6feb926 100644 --- a/packages/workflow/src/internals.ts +++ b/packages/workflow/src/internals.ts @@ -8,7 +8,7 @@ import { WorkflowSignalType, PayloadConverter, defaultPayloadConverter, - arrayFromPayloadsSync, + arrayFromPayloads, Workflow, WorkflowQueryType, TemporalFailure, @@ -92,7 +92,7 @@ export class Activator implements ActivationHandler { ); execute({ headers: activation.headers ?? {}, - args: arrayFromPayloadsSync(state.dataConverter, activation.arguments), + args: arrayFromPayloads(state.payloadConverter, activation.arguments), }) .then(completeWorkflow) .catch(handleWorkflowFailure); @@ -115,15 +115,15 @@ export class Activator implements ActivationHandler { const { resolve, reject } = consumeCompletion('activity', getSeq(activation)); if (activation.result.completed) { const completed = activation.result.completed; - const result = completed.result ? state.dataConverter.fromPayloadSync(completed.result) : undefined; + const result = completed.result ? state.payloadConverter.fromPayload(completed.result) : undefined; resolve(result); } else if (activation.result.failed) { const { failure } = activation.result.failed; - const err = optionalFailureToOptionalError(failure, state.dataConverter); + const err = optionalFailureToOptionalError(failure, state.payloadConverter); reject(err); } else if (activation.result.cancelled) { const { failure } = activation.result.cancelled; - const err = optionalFailureToOptionalError(failure, state.dataConverter); + const err = optionalFailureToOptionalError(failure, state.payloadConverter); reject(err); } } @@ -155,7 +155,7 @@ export class Activator implements ActivationHandler { if (!activation.cancelled.failure) { throw new TypeError('Got no failure in cancelled variant'); } - reject(failureToError(activation.cancelled.failure, state.dataConverter)); + reject(failureToError(activation.cancelled.failure, state.payloadConverter)); } else { throw new TypeError('Got ResolveChildWorkflowExecutionStart with no status'); } @@ -170,20 +170,20 @@ export class Activator implements ActivationHandler { const { resolve, reject } = consumeCompletion('childWorkflowComplete', getSeq(activation)); if (activation.result.completed) { const completed = activation.result.completed; - const result = completed.result ? state.dataConverter.fromPayload(completed.result) : undefined; + const result = completed.result ? state.payloadConverter.fromPayload(completed.result) : undefined; resolve(result); } else if (activation.result.failed) { const { failure } = activation.result.failed; if (failure === undefined || failure === null) { throw new TypeError('Got failed result with no failure attribute'); } - reject(failureToError(failure, state.dataConverter)); + reject(failureToError(failure, state.payloadConverter)); } else if (activation.result.cancelled) { const { failure } = activation.result.cancelled; if (failure === undefined || failure === null) { throw new TypeError('Got cancelled result with no failure attribute'); } - reject(failureToError(failure, state.dataConverter)); + reject(failureToError(failure, state.payloadConverter)); } } @@ -218,7 +218,7 @@ export class Activator implements ActivationHandler { ); execute({ queryName: queryType, - args: arrayFromPayloadsSync(state.dataConverter, activation.arguments), + args: arrayFromPayloads(state.payloadConverter, activation.arguments), queryId, }).then( (result) => completeQuery(queryId, result), @@ -257,7 +257,7 @@ export class Activator implements ActivationHandler { this.signalWorkflowNextHandler.bind(this) ); execute({ - args: arrayFromPayloadsSync(state.dataConverter, activation.input), + args: arrayFromPayloads(state.payloadConverter, activation.input), signalName, }).catch(handleWorkflowFailure); } @@ -267,7 +267,7 @@ export class Activator implements ActivationHandler { ): Promise { const { resolve, reject } = consumeCompletion('signalWorkflow', getSeq(activation)); if (activation.failure) { - reject(failureToError(activation.failure, state.dataConverter)); + reject(failureToError(activation.failure, state.payloadConverter)); } else { resolve(undefined); } @@ -278,7 +278,7 @@ export class Activator implements ActivationHandler { ): Promise { const { resolve, reject } = consumeCompletion('cancelWorkflow', getSeq(activation)); if (activation.failure) { - reject(failureToError(activation.failure, state.dataConverter)); + reject(failureToError(activation.failure, state.payloadConverter)); } else { resolve(undefined); } @@ -438,7 +438,7 @@ export class State { */ public importInterceptors?: InterceptorsImportFunc; - public dataConverter: PayloadConverter = defaultPayloadConverter; + public payloadConverter: PayloadConverter = defaultPayloadConverter; /** * Patches we know the status of for this workflow, as in {@link patched} @@ -479,7 +479,7 @@ function completeWorkflow(result: any) { state.pushCommand( { completeWorkflowExecution: { - result: state.dataConverter.toPayloadSync(result), + result: state.payloadConverter.toPayload(result), }, }, true @@ -505,7 +505,7 @@ export async function handleWorkflowFailure(error: unknown): Promise { state.pushCommand( { failWorkflowExecution: { - failure: errorToFailure(error, state.dataConverter), + failure: errorToFailure(error, state.payloadConverter), }, }, true @@ -515,13 +515,13 @@ export async function handleWorkflowFailure(error: unknown): Promise { function completeQuery(queryId: string, result: unknown) { state.pushCommand({ - respondToQuery: { queryId, succeeded: { response: state.dataConverter.toPayloadSync(result) } }, + respondToQuery: { queryId, succeeded: { response: state.payloadConverter.toPayload(result) } }, }); } async function failQuery(queryId: string, error: any) { state.pushCommand({ - respondToQuery: { queryId, failed: errorToFailure(ensureTemporalFailure(error), state.dataConverter) }, + respondToQuery: { queryId, failed: errorToFailure(ensureTemporalFailure(error), state.payloadConverter) }, }); } diff --git a/packages/workflow/src/worker-interface.ts b/packages/workflow/src/worker-interface.ts index 964110c7e..d6219d068 100644 --- a/packages/workflow/src/worker-interface.ts +++ b/packages/workflow/src/worker-interface.ts @@ -26,7 +26,7 @@ export interface WorkflowCreateOptions { randomnessSeed: number[]; now: number; patches: string[]; - useCustomDataConverter?: boolean; + useCustomPayloadConverter?: boolean; } export interface ImportFunctions { @@ -116,7 +116,7 @@ export async function initRuntime({ randomnessSeed, now, patches, - useCustomDataConverter, + useCustomPayloadConverter, }: WorkflowCreateOptions): Promise { // Set the runId globally on the context so it can be retrieved in the case // of an unhandled promise rejection. @@ -155,11 +155,12 @@ export async function initRuntime({ } } - if (useCustomDataConverter) { - // @ts-expect-error this is a webpack alias to payloadConverterPath - state.dataConverter = (await import('__temporal_custom_data_converter')).dataConverter; + if (useCustomPayloadConverter) { // webpack doesn't know what to bundle given a dynamic import expression, so we can't do: - // state.dataConverter = (await import(payloadConverterPath)).dataConverter; + // state.payloadConverter = (await import(payloadConverterPath)).payloadConverter; + // @ts-expect-error this is a webpack alias to payloadConverterPath + state.payloadConverter = (await import('__temporal_custom_payload_converter')).payloadConverter; + // The `payloadConverter` export is validated in the Worker } let workflow: Workflow; diff --git a/packages/workflow/src/workflow.ts b/packages/workflow/src/workflow.ts index ee4821b8f..42a09d6ae 100644 --- a/packages/workflow/src/workflow.ts +++ b/packages/workflow/src/workflow.ts @@ -7,7 +7,7 @@ import { msOptionalToTs, Workflow, composeInterceptors, - mapToPayloadsSync, + mapToPayloads, WorkflowResultType, SignalDefinition, QueryDefinition, @@ -15,6 +15,7 @@ import { WorkflowReturnType, compileRetryPolicy, ActivityInterface, + toPayloads, } from '@temporalio/workflow-common'; import { ChildWorkflowCancellationType, @@ -158,7 +159,7 @@ async function scheduleActivityNextHandler({ seq, activityId: options.activityId ?? `${seq}`, activityType, - arguments: state.dataConverter.toPayloadsSync(...args), + arguments: toPayloads(state.payloadConverter, ...args), retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined, taskQueue: options.taskQueue || state.info?.taskQueue, heartbeatTimeout: msOptionalToTs(options.heartbeatTimeout), @@ -238,7 +239,7 @@ async function startChildWorkflowExecutionNextHandler({ seq, workflowId, workflowType, - input: state.dataConverter.toPayloadsSync(...options.args), + input: toPayloads(state.payloadConverter, ...options.args), retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined, taskQueue: options.taskQueue || state.info?.taskQueue, workflowExecutionTimeout: msOptionalToTs(options.workflowExecutionTimeout), @@ -251,9 +252,9 @@ async function startChildWorkflowExecutionNextHandler({ parentClosePolicy: options.parentClosePolicy, cronSchedule: options.cronSchedule, searchAttributes: options.searchAttributes - ? mapToPayloadsSync(state.dataConverter, options.searchAttributes) + ? mapToPayloads(state.payloadConverter, options.searchAttributes) : undefined, - memo: options.memo && mapToPayloadsSync(state.dataConverter, options.memo), + memo: options.memo && mapToPayloads(state.payloadConverter, options.memo), }, }); }); @@ -295,7 +296,7 @@ function signalWorkflowNextHandler({ seq, signalName, args, target }: SignalWork state.pushCommand({ signalExternalWorkflowExecution: { seq, - args: state.dataConverter.toPayloadsSync(...args), + args: toPayloads(state.payloadConverter, ...args), signalName, ...(target.type === 'external' ? { @@ -692,7 +693,7 @@ export function makeContinueAsNewFunc( const { headers, args, options } = input; throw new ContinueAsNew({ workflowType: options.workflowType, - arguments: state.dataConverter.toPayloads(...args), + arguments: toPayloads(state.payloadConverter, ...args), header: headers, taskQueue: options.taskQueue, memo: options.memo, From 9720e70cd34aa473f71bd91f6e033b0166128e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Fri, 11 Feb 2022 17:55:14 -0500 Subject: [PATCH 10/33] docs: Update docs/data-converter.md; fix linting --- docs/data-converter.md | 35 ++++++++++++++----- packages/test/src/test-data-converter.ts | 3 +- .../src/converter/payload-converter.ts | 11 +++--- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/docs/data-converter.md b/docs/data-converter.md index 0aac39ac6..325e61740 100644 --- a/docs/data-converter.md +++ b/docs/data-converter.md @@ -23,39 +23,56 @@ function workflowInclusiveInstanceOf(instance: unknown, type: Function): boolean ## Decision -Given the possibility of switching or adding other isolation methods in future, we opted for inside the vm. We'll also have another data transformer / payload interceptor layer that runs outside the vm, can use node modules and Promises, and operates on Payloads. +Given the possibility of switching or adding other isolation methods in future, we opted to convert to/from Payloads inside the vm (`PayloadConverter`). We also added another transformer layer called `PayloadCodec` that runs outside the vm, can use node modules and Promises, and operates on Payloads. A `DataConverter` is a `PayloadConverter` and a `PayloadCodec`: -### General flow +```ts +export interface DataConverter { + payloadConverterPath?: string; + payloadCodec?: PayloadCodec; +} -When `WorkerOptions.dataConverter.payloadConverterPath` is provided, the code at that location is loaded into the main thread, the worker threads, and the webpack Workflow bundle. +export interface PayloadConverter { + toPayload(value: T): Payload; + fromPayload(payload: Payload): T; +} + +export interface PayloadCodec { + encode(payloads: Payload[]): Promise; + decode(payloads: Payload[]): Promise; +} +``` + +### Worker converter flow -### Specific flow +`PayloadCodec` only runs in the main thread. + +When `WorkerOptions.dataConverter.payloadConverterPath` is provided, the code at that location is loaded into the main thread, the worker threads, and the webpack Workflow bundle. `Worker.create`: -*main thread* +_main thread_ - imports and validates `options.dataConverter.payloadConverterPath` - passes `payloadConverterPath` to either `ThreadedVMWorkflowCreator.create` or `VMWorkflowCreator.create` - passes `payloadConverterPath` to `WorkflowCodeBundler` `ThreadedVMWorkflowCreator.create`: -*main thread* +_main thread_ - sends `payloadConverterPath` to each worker thread - thread sends `payloadConverterPath` to VMWorkflowCreator.create `VMWorkflowCreator.create`: -*worker thread (unless in debug mode)* +_worker thread (unless in debug mode)_ - imports `payloadConverterPath` - passes `payloadConverterPath` to `VMWorkflowCreator` constructor `VMWorkflowCreator.createWorkflow`: -*worker thread (unless in debug mode)* +_worker thread (unless in debug mode)_ - passes `useCustomPayloadConverter` to `initRuntime` inside Workflow vm `worker-interface.ts#initRuntime`: -*workflow vm* +_workflow vm_ - if `useCustomPayloadConverter`, imports `__temporal_custom_payload_converter` and sets `state.payloadConverter` diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 1074214a8..8af60e392 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -2,7 +2,6 @@ import { Connection, WorkflowClient } from '@temporalio/client'; import { DataConverterError, defaultPayloadConverter, DefaultPayloadConverter, ValueError } from '@temporalio/common'; import { Core, DefaultLogger, Worker } from '@temporalio/worker'; -import { CompositePayloadConverter } from '@temporalio/workflow-common'; import { BinaryPayloadConverter, JsonPayloadConverter, @@ -19,9 +18,9 @@ import { import test from 'ava'; import { v4 as uuid4 } from 'uuid'; import root from '../protos/root'; -import { messageInstance } from './payload-converters/payload-converter'; import { RUN_INTEGRATION_TESTS } from './helpers'; import { defaultOptions } from './mock-native-worker'; +import { messageInstance } from './payload-converters/payload-converter'; import { protobufWorkflow } from './workflows'; test('UndefinedPayloadConverter converts from undefined only', async (t) => { diff --git a/packages/workflow-common/src/converter/payload-converter.ts b/packages/workflow-common/src/converter/payload-converter.ts index 1b10b5599..22ee5c306 100644 --- a/packages/workflow-common/src/converter/payload-converter.ts +++ b/packages/workflow-common/src/converter/payload-converter.ts @@ -1,14 +1,13 @@ -import { ValueError, UnsupportedTypeError } from '../errors'; -import { str, METADATA_ENCODING_KEY, Payload } from './types'; +import { UnsupportedTypeError, ValueError } from '../errors'; import { - PayloadConverterWithEncoding, - UndefinedPayloadConverter, BinaryPayloadConverter, JsonPayloadConverter, - ProtobufJsonPayloadConverter, + PayloadConverterWithEncoding, ProtobufBinaryPayloadConverter, + ProtobufJsonPayloadConverter, + UndefinedPayloadConverter, } from './payload-converters'; -import { hasOwnProperty } from '../type-helpers'; +import { METADATA_ENCODING_KEY, Payload, str } from './types'; /** * Used by the framework to serialize/deserialize parameters and return values that need to be From 9b474d11b8581a979cec84f0bb739d414ab64571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Mon, 14 Feb 2022 18:42:30 -0500 Subject: [PATCH 11/33] Use fallback instead of empty module for __temporal_custom_payload_converter --- packages/worker/src/workflow/bundler.ts | 3 +-- packages/worker/src/workflow/empty-module.ts | 0 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 packages/worker/src/workflow/empty-module.ts diff --git a/packages/worker/src/workflow/bundler.ts b/packages/worker/src/workflow/bundler.ts index b38b7ae50..e5f9df7bd 100644 --- a/packages/worker/src/workflow/bundler.ts +++ b/packages/worker/src/workflow/bundler.ts @@ -113,7 +113,7 @@ export class WorkflowCodeBundler { modules: [path.resolve(__dirname, 'module-overrides'), ...this.nodeModulesPaths], extensions: ['.ts', '.js'], // If we don't set an alias for this below, then it won't be imported, so webpack can safely ignore it - fallback: { __temporal_custom_data_converter: false }, + fallback: { __temporal_custom_payload_converter: false }, }, module: { rules: [ @@ -144,7 +144,6 @@ export class WorkflowCodeBundler { // since we can't decode without a `root` anyway. webpackConfig.resolve.alias = { 'proto3-json-serializer$': path.resolve(__dirname, 'empty-proto3-json-serializer.js'), - __temporal_custom_payload_converter$: path.resolve(__dirname, 'empty-module.js'), }; } } diff --git a/packages/worker/src/workflow/empty-module.ts b/packages/worker/src/workflow/empty-module.ts deleted file mode 100644 index e69de29bb..000000000 From 717e0962c1d0d4630e1a5c77c18ec9124264e2a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Tue, 15 Feb 2022 17:09:32 -0500 Subject: [PATCH 12/33] docs: Add docstrings to exported functions --- docs/protobuf-libraries.md | 23 +++++--- packages/common/src/codec-helpers.ts | 59 +++++++++++++------ packages/common/src/data-converter-helpers.ts | 5 ++ packages/common/src/patch-protobuf-root.ts | 15 ++++- .../payload-converters/payload-converter.ts | 2 +- packages/test/src/test-data-converter.ts | 2 +- packages/test/src/workflows/data-converter.ts | 6 -- .../src/converter/data-converter.ts | 11 ++-- .../src/converter/payload-converter.ts | 46 +++++++++++---- .../src/converter/payload-converters.ts | 6 ++ 10 files changed, 122 insertions(+), 53 deletions(-) delete mode 100644 packages/test/src/workflows/data-converter.ts diff --git a/docs/protobuf-libraries.md b/docs/protobuf-libraries.md index cafa8bca2..97d2c57a3 100644 --- a/docs/protobuf-libraries.md +++ b/docs/protobuf-libraries.md @@ -12,7 +12,7 @@ A and B, but not C. - Most popular lib (5M downloads/wk) - Fairly inactive maintainers (infrequent updates, many open PRs & issues) -- Non-standard JSON serialization +- [Non-standard](https://github.com/protobufjs/protobuf.js/issues/1304) JSON serialization - Message classes with generated types and runtime-checkable instances ### proto3-json-serializer @@ -50,7 +50,7 @@ A and B ## Current solution - Use `protobufjs` with `proto3-json-serializer` -- Have users use runtime-loaded messages (not generated classes) and `Class.create` (not `new Class()`) +- Have users use runtime-loaded messages (not generated classes) and `Class.create` (not `new Class()`, which doesn't work with runtime-loaded messages) - Patch `json-module` output (which adds `nested` attributes to lowercase namespaces [which causes a TS error](https://github.com/protobufjs/protobuf.js/issues/1014)) ```ts @@ -65,9 +65,22 @@ module.exports = patchProtobufRoot(unpatchedRoot); // root.d.ts generated with: // pbjs -t static-module *.proto | pbts -o root.d.ts - +// src/data-converter.ts +import { DefaultDataConverter } from '@temporalio/common'; +import root from '../protos/root'; + +export const dataConverter = new DefaultDataConverter({ root }); + +// src/worker.ts +import { dataConverter } from './data-converter'; + +const worker = Worker.create({ dataConverter, ... }); + // src/client.ts import { foo } from '../protos/root'; +import { dataConverter } from './data-converter'; +const client = new WorkflowClient(connection.service, { dataConverter }); await client.start(protoWorkflow, { args: [foo.bar.ProtoInput.create({ name: 'Proto', age: 1 })], // can't use `new foo.bar.ProtoInput()` taskQueue: 'tutorial', @@ -80,12 +93,6 @@ import { foo } from '../protos/root'; export async function protoWorkflow(input: foo.bar.ProtoInput): Promise { return foo.bar.ProtoResult.create({ sentence: `Name is ${input.name}` }); } - -// src/data-converter.ts -import { DefaultDataConverter } from '@temporalio/common'; -import root from '../protos/root'; - -export const dataConverter = new DefaultDataConverter({ root }); ``` We originally were thinking of this, but the namespaces in `json-module.js` get lost through `patchProtobufRoot()`: diff --git a/packages/common/src/codec-helpers.ts b/packages/common/src/codec-helpers.ts index 154c68dfd..7cc1af1e4 100644 --- a/packages/common/src/codec-helpers.ts +++ b/packages/common/src/codec-helpers.ts @@ -11,6 +11,9 @@ import { toPayloads, } from '@temporalio/workflow-common'; +/** + * Decode `payloads` and then return {@link fromPayloadsAtIndex}. + */ export async function decodeFromPayloadsAtIndex( converter: LoadedDataConverter, index: number, @@ -20,18 +23,24 @@ export async function decodeFromPayloadsAtIndex( return fromPayloadsAtIndex(payloadConverter, index, payloads ? await payloadCodec.decode(payloads) : payloads); } +/** + * Decode `payloads` and then return {@link arrayFromPayloads}`. + */ export async function decodeArrayFromPayloads( converter: LoadedDataConverter, - content?: Payload[] | null + payloads?: Payload[] | null ): Promise { const { payloadConverter, payloadCodec } = converter; - let decodedPayloads = content; - if (content) { - decodedPayloads = await payloadCodec.decode(content); + let decodedPayloads = payloads; + if (payloads) { + decodedPayloads = await payloadCodec.decode(payloads); } return arrayFromPayloads(payloadConverter, decodedPayloads); } +/** + * Run {@link decodeFailure} and then return {@link failureToError}. + */ export async function decodeOptionalFailureToOptionalError( converter: LoadedDataConverter, failure: ProtoFailure | undefined | null @@ -40,27 +49,18 @@ export async function decodeOptionalFailureToOptionalError( return failure ? failureToError(await decodeFailure(payloadCodec, failure), payloadConverter) : undefined; } -export async function encodeMapToPayloads( - converter: LoadedDataConverter, - source: Record -): Promise> { - const { payloadConverter, payloadCodec } = converter; - return Object.fromEntries( - await Promise.all( - Object.entries(source).map(async ([k, v]): Promise<[K, Payload]> => { - const [payload] = await payloadCodec.encode([payloadConverter.toPayload(v)]); - return [k as K, payload]; - }) - ) - ) as Record; -} - +/** + * Run {@link PayloadConverter.toPayload} on value, and then encode it. + */ export async function encodeToPayload(converter: LoadedDataConverter, value: unknown): Promise { const { payloadConverter, payloadCodec } = converter; const [payload] = await payloadCodec.encode([payloadConverter.toPayload(value)]); return payload; } +/** + * Run {@link PayloadConverter.toPayload} on values, and then encode them. + */ export async function encodeToPayloads( converter: LoadedDataConverter, ...values: unknown[] @@ -73,6 +73,27 @@ export async function encodeToPayloads( return payloads ? await payloadCodec.encode(payloads) : undefined; } +/** + * Run {@link PayloadConverter.toPayload} and {@link PayloadCodec.encode} on values in `map`. + */ +export async function encodeMapToPayloads( + converter: LoadedDataConverter, + map: Record +): Promise> { + const { payloadConverter, payloadCodec } = converter; + return Object.fromEntries( + await Promise.all( + Object.entries(map).map(async ([k, v]): Promise<[K, Payload]> => { + const [payload] = await payloadCodec.encode([payloadConverter.toPayload(v)]); + return [k as K, payload]; + }) + ) + ) as Record; +} + +/** + * Run {@link errorToFailure} on `error`, and then {@link encodeFailure}. + */ export async function encodeErrorToFailure(dataConverter: LoadedDataConverter, error: unknown): Promise { const { payloadConverter, payloadCodec } = dataConverter; return await encodeFailure(payloadCodec, errorToFailure(error, payloadConverter)); diff --git a/packages/common/src/data-converter-helpers.ts b/packages/common/src/data-converter-helpers.ts index 9b14dd524..9e01e11aa 100644 --- a/packages/common/src/data-converter-helpers.ts +++ b/packages/common/src/data-converter-helpers.ts @@ -40,6 +40,11 @@ function requirePayloadConverter(path: string): PayloadConverter { } } +/** + * If {@link DataConverter.payloadConverterPath} is specified, `require()` it and validate that the module has a `payloadConverter` named export. + * If not, use {@link defaultPayloadConverter}. + * If {@link DataConverter.payloadCodec} is unspecified, use {@link defaultPayloadCodec}. + */ export function loadDataConverter(dataConverter?: DataConverter): LoadedDataConverter { let payloadConverter: PayloadConverter = defaultPayloadConverter; if (dataConverter?.payloadConverterPath) { diff --git a/packages/common/src/patch-protobuf-root.ts b/packages/common/src/patch-protobuf-root.ts index e1f36b9da..3577b8c2e 100644 --- a/packages/common/src/patch-protobuf-root.ts +++ b/packages/common/src/patch-protobuf-root.ts @@ -1,6 +1,17 @@ import { isRecord } from '@temporalio/workflow-common/lib/type-helpers'; -export function patchProtobufRoot>(root: T, name?: string): T { +/** + * Create a version of `root` with non-nested namespaces to match the generated types. + * For more information, see: + * https://github.com/temporalio/sdk-typescript/blob/main/docs/protobuf-libraries.md#current-solution + * @param root Generated by `pbjs -t json-module -w commonjs -o json-module.js *.proto` + * @returns A new patched `root` + */ +export function patchProtobufRoot>(root: T): T { + return _patchProtobufRoot(root); +} + +function _patchProtobufRoot>(root: T, name?: string): T { const newRoot = new (root.constructor as any)(isNamespace(root) ? name : {}); for (const key in root) { newRoot[key] = root[key]; @@ -16,7 +27,7 @@ export function patchProtobufRoot>(root: T, na } if (isNamespace(value)) { - newRoot[typeOrNamespace] = patchProtobufRoot(value, typeOrNamespace); + newRoot[typeOrNamespace] = _patchProtobufRoot(value, typeOrNamespace); } else if (isType(value)) { newRoot[typeOrNamespace] = value; } diff --git a/packages/test/src/payload-converters/payload-converter.ts b/packages/test/src/payload-converters/payload-converter.ts index e04aab7fc..e90cf8a67 100644 --- a/packages/test/src/payload-converters/payload-converter.ts +++ b/packages/test/src/payload-converters/payload-converter.ts @@ -3,4 +3,4 @@ import root, { foo } from '../../protos/root'; export const messageInstance = foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); -export const payloadConverter = new DefaultPayloadConverter({ root }); +export const payloadConverter = new DefaultPayloadConverter({ protobufRoot: root }); diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 8af60e392..7afaa1894 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -205,7 +205,7 @@ if (RUN_INTEGRATION_TESTS) { test('DefaultPayloadConverter converts protobufs', async (t) => { const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); - const defaultPayloadConverterWithProtos = new DefaultPayloadConverter({ root }); + const defaultPayloadConverterWithProtos = new DefaultPayloadConverter({ protobufRoot: root }); t.deepEqual( defaultPayloadConverterWithProtos.toPayload(instance), // It will always use JSON because it appears before binary in the list diff --git a/packages/test/src/workflows/data-converter.ts b/packages/test/src/workflows/data-converter.ts deleted file mode 100644 index e04aab7fc..000000000 --- a/packages/test/src/workflows/data-converter.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { DefaultPayloadConverter } from '@temporalio/common'; -import root, { foo } from '../../protos/root'; - -export const messageInstance = foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); - -export const payloadConverter = new DefaultPayloadConverter({ root }); diff --git a/packages/workflow-common/src/converter/data-converter.ts b/packages/workflow-common/src/converter/data-converter.ts index 7e6249808..2e2913cef 100644 --- a/packages/workflow-common/src/converter/data-converter.ts +++ b/packages/workflow-common/src/converter/data-converter.ts @@ -5,7 +5,7 @@ import { PayloadConverter } from './payload-converter'; * When your data (arguments and return values) is sent over the wire and stored by Temporal Server, * it is encoded in binary in a {@link Payload} Protobuf message. * - * The default `DataConverter` supports `Uint8Array`, protobuf.js objects, and JSON serializables (so if `JSON.stringify(yourArg)` works, the default data converter will work). + * The default `DataConverter` supports `Uint8Array`, protobuf.js objects, and JSON serializables (so if [`JSON.stringify(yourArgOrRetval)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) works, the default data converter will work). * * Use a custom `DataConverter` to control the contents of your {@link Payload}s. * Common reasons for using a custom `DataConverter` are: @@ -14,9 +14,9 @@ import { PayloadConverter } from './payload-converter'; * - Compressing values to reduce disk or network usage. * * To use your custom `DataConverter`, provide it to the {@link WorkflowClient}, {@link Worker}, and {@link bundleWorkflowCode} (if you use it): - * - `new WorkflowClient(..., dataConverter)` - * - `Worker.create(..., dataConverter)` - * - `bundleWorkflowCode(..., payloadConverterPath)` + * - `new WorkflowClient({ ..., dataConverter })` + * - `Worker.create({ ..., dataConverter })` + * - `bundleWorkflowCode({ ..., payloadConverterPath })` */ export interface DataConverter { /** @@ -32,6 +32,9 @@ export interface DataConverter { payloadCodec?: PayloadCodec; } +/** + * A {@link DataConverter} that has been loaded via {@link loadDataConverter}. + */ export interface LoadedDataConverter { payloadConverter: PayloadConverter; payloadCodec: PayloadCodec; diff --git a/packages/workflow-common/src/converter/payload-converter.ts b/packages/workflow-common/src/converter/payload-converter.ts index 22ee5c306..242083e60 100644 --- a/packages/workflow-common/src/converter/payload-converter.ts +++ b/packages/workflow-common/src/converter/payload-converter.ts @@ -40,6 +40,11 @@ export class CompositePayloadConverter implements PayloadConverter { } } + /** + * Tries to run `.toPayload(value)` on each converter in the order provided at construction. + * Returns the first successful result. + * @throws {@link ValueError} if no converter can convert the value + */ public toPayload(value: T): Payload { for (const converter of this.converters) { try { @@ -56,6 +61,9 @@ export class CompositePayloadConverter implements PayloadConverter { throw new ValueError(`Cannot serialize ${value}`); } + /** + * Run {@link PayloadConverterWithEncoding.fromPayload} based on the {@link encodingTypes | encoding type} of the {@link Payload}. + */ public fromPayload(payload: Payload): T { if (payload.metadata === undefined || payload.metadata === null) { throw new ValueError('Missing payload metadata'); @@ -104,38 +112,52 @@ export function fromPayloadsAtIndex(converter: PayloadConverter, index: numbe return converter.fromPayload(payloads[index]); } -export function arrayFromPayloads(converter: PayloadConverter, content?: Payload[] | null): unknown[] { - if (!content) { +/** + * Run {@link PayloadConverter.fromPayload} on each value in the array. + */ +export function arrayFromPayloads(converter: PayloadConverter, payloads?: Payload[] | null): unknown[] { + if (!payloads) { return []; } - return content.map((payload: Payload) => converter.fromPayload(payload)); + return payloads.map((payload: Payload) => converter.fromPayload(payload)); } -export function mapToPayloads( - converter: PayloadConverter, - source: Record -): Record { +/** + * Run {@link PayloadConverter.toPayload} on each value in the map. + */ +export function mapToPayloads(converter: PayloadConverter, map: Record): Record { return Object.fromEntries( - Object.entries(source).map(([k, v]): [K, Payload] => [k as K, converter.toPayload(v)]) + Object.entries(map).map(([k, v]): [K, Payload] => [k as K, converter.toPayload(v)]) ) as Record; } export interface DefaultPayloadConverterOptions { - root?: Record; + /** + * The `root` provided to {@link ProtobufJsonPayloadConverter} and {@link ProtobufBinaryPayloadConverter} + */ + protobufRoot?: Record; } export class DefaultPayloadConverter extends CompositePayloadConverter { - constructor({ root }: DefaultPayloadConverterOptions = {}) { + constructor({ protobufRoot }: DefaultPayloadConverterOptions = {}) { // Match the order used in other SDKs // Go SDK: https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28 super( new UndefinedPayloadConverter(), new BinaryPayloadConverter(), - new ProtobufJsonPayloadConverter(root), - new ProtobufBinaryPayloadConverter(root), + new ProtobufJsonPayloadConverter(protobufRoot), + new ProtobufBinaryPayloadConverter(protobufRoot), new JsonPayloadConverter() ); } } +/** + * The default {@link PayloadConverter} used by the SDK. + * Supports `Uint8Array` and JSON serializables (so if [`JSON.stringify(yourArgOrRetval)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) works, the default payload converter will work). + * + * To also support Protobufs, create a custom payload converter with {@link DefaultPayloadConverter}: + * + * `const myConverter = new DefaultPayloadConverter({ protobufRoot })` + */ export const defaultPayloadConverter = new DefaultPayloadConverter(); diff --git a/packages/workflow-common/src/converter/payload-converters.ts b/packages/workflow-common/src/converter/payload-converters.ts index f63b0aea2..9a6a11bac 100644 --- a/packages/workflow-common/src/converter/payload-converters.ts +++ b/packages/workflow-common/src/converter/payload-converters.ts @@ -151,6 +151,9 @@ abstract class ProtobufPayloadConverter implements PayloadConverterWithEncoding export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter { public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF; + /** + * @param root The value returned from {@link patchProtobufRoot} + */ constructor(root?: unknown) { super(root); } @@ -178,6 +181,9 @@ export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter { export class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter { public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF_JSON; + /** + * @param root The value returned from {@link patchProtobufRoot} + */ constructor(root?: unknown) { super(root); } From 01090b7a46f5f17df4dc88c4f9769f194bc35bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Tue, 15 Feb 2022 17:59:45 -0500 Subject: [PATCH 13/33] chore: Use root .gitignore --- .gitignore | 2 ++ packages/test/.gitignore | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 packages/test/.gitignore diff --git a/.gitignore b/.gitignore index 65d7abd2f..5739dd5c8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ build/ commonjs/ es2020/ lerna*.log +packages/test/protos/json-module.js +packages/test/protos/root.d.ts \ No newline at end of file diff --git a/packages/test/.gitignore b/packages/test/.gitignore deleted file mode 100644 index 6cfeae5b1..000000000 --- a/packages/test/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -protos/json-module.js -protos/root.d.ts \ No newline at end of file From bb62659ee6bd825863865fc044dc29f4884489d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Tue, 15 Feb 2022 18:01:18 -0500 Subject: [PATCH 14/33] chore: Remove duplicate devDep --- packages/test/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/test/package.json b/packages/test/package.json index d8c17947d..0912ee7f5 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -36,7 +36,6 @@ "@temporalio/workflow": "file:../workflow", "@types/async-retry": "^1.3.3", "async-retry": "^1.3.3", - "npm-run-all": "^4.1.5", "protobufjs": "^6.11.2", "ramda": "^0.27.1" }, From 1d51cff6338f7d6eccb0c974c32924692e2f10d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Tue, 15 Feb 2022 18:50:41 -0500 Subject: [PATCH 15/33] chore: Address review comments --- packages/test/scripts/compile-proto.js | 9 ++-- packages/test/src/test-data-converter.ts | 3 +- packages/worker/src/workflow-codec-runner.ts | 11 +++-- packages/worker/src/workflow/bundler.ts | 41 ++++++++----------- .../src/workflow/module-overrides/assert.ts | 1 + packages/workflow-common/package.json | 2 +- .../src/converter/data-converter.ts | 2 +- 7 files changed, 35 insertions(+), 34 deletions(-) diff --git a/packages/test/scripts/compile-proto.js b/packages/test/scripts/compile-proto.js index a5a2d6b20..967ce58b5 100644 --- a/packages/test/scripts/compile-proto.js +++ b/packages/test/scripts/compile-proto.js @@ -48,9 +48,12 @@ async function main() { await compileProtos(protosPath, moduleOutputFile, '--target', 'json-module', '--root', 'test'); console.log(`Creating protobuf TS definitions from ${protosPath}`); - await compileProtos(protosPath, tempFile, '--target', 'static-module'); - await promisify(pbts.main)(['--out', typesOutputFile, tempFile]); - await rm(tempFile); + try { + await compileProtos(protosPath, tempFile, '--target', 'static-module'); + await promisify(pbts.main)(['--out', typesOutputFile, tempFile]); + } finally { + await rm(tempFile); + } console.log('Done'); } diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 7afaa1894..74df69149 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -191,7 +191,7 @@ if (RUN_INTEGRATION_TESTS) { const client = new WorkflowClient(connection.service, { dataConverter: { payloadConverterPath: './payload-converters/payload-converter' }, }); - worker.run(); + const runPromise = worker.run(); client.execute(protobufWorkflow, { args: [messageInstance], workflowId: uuid4(), @@ -200,6 +200,7 @@ if (RUN_INTEGRATION_TESTS) { await expectedErrorWasThrown; t.pass(); worker.shutdown(); + await runPromise; }); } diff --git a/packages/worker/src/workflow-codec-runner.ts b/packages/worker/src/workflow-codec-runner.ts index a4b8bc0a2..e5156203d 100644 --- a/packages/worker/src/workflow-codec-runner.ts +++ b/packages/worker/src/workflow-codec-runner.ts @@ -2,11 +2,14 @@ import { decodeFailure, encodeFailure, Payload } from '@temporalio/common'; import { coresdk, temporal } from '@temporalio/proto'; import { PayloadCodec, ProtoFailure } from '@temporalio/workflow-common'; +/** + * Helper class for decoding Workflow activations and encoding Workflow completions. + */ export class WorkflowCodecRunner { constructor(protected readonly codec: PayloadCodec) {} /** - * Run codec.decode on the Payloads in the Activation message + * Run codec.decode on the Payloads in the Activation message. */ public async decodeActivation(activation: coresdk.workflow_activation.IWorkflowActivation): Promise { if (activation.jobs?.length) { @@ -64,14 +67,14 @@ export class WorkflowCodecRunner { const accessibleFailureParent = failureParent as Record; if (!accessibleFailureParent.failure) return; - accessibleFailureParent['failure'] = await decodeFailure( + accessibleFailureParent.failure = await decodeFailure( this.codec, - accessibleFailureParent['failure'] as temporal.api.failure.v1.IFailure + accessibleFailureParent.failure as temporal.api.failure.v1.IFailure ); } /** - * Run codec.encode on the Payloads inside the Completion message + * Run codec.encode on the Payloads inside the Completion message. */ public async encodeCompletion(completionBytes: Uint8Array): Promise { const completion = coresdk.workflow_completion.WorkflowActivationCompletion.decode(completionBytes); diff --git a/packages/worker/src/workflow/bundler.ts b/packages/worker/src/workflow/bundler.ts index e5f9df7bd..02555e9df 100644 --- a/packages/worker/src/workflow/bundler.ts +++ b/packages/worker/src/workflow/bundler.ts @@ -59,7 +59,7 @@ export class WorkflowCodeBundler { const entrypointPath = '/src/main.js'; this.genEntrypoint(vol, entrypointPath); - await this.bundle(ufs, entrypointPath, distDir, this.payloadConverterPath); + await this.bundle(ufs, entrypointPath, distDir); return ufs.readFileSync(path.join(distDir, 'main.js'), 'utf8'); } @@ -102,18 +102,26 @@ export class WorkflowCodeBundler { /** * Run webpack */ - protected async bundle( - filesystem: typeof unionfs.ufs, - entry: string, - distDir: string, - payloadConverterPath?: string - ): Promise { - const webpackConfig: webpack.Configuration = { + protected async bundle(filesystem: typeof unionfs.ufs, entry: string, distDir: string): Promise { + const compiler = webpack({ resolve: { modules: [path.resolve(__dirname, 'module-overrides'), ...this.nodeModulesPaths], extensions: ['.ts', '.js'], // If we don't set an alias for this below, then it won't be imported, so webpack can safely ignore it fallback: { __temporal_custom_payload_converter: false }, + ...(this.payloadConverterPath + ? { + alias: { + __temporal_custom_payload_converter$: this.payloadConverterPath, + }, + } + : { + alias: { + // Save 100KB from the bundle by not including the npm module, + // since we can't decode without a `root` anyway. + 'proto3-json-serializer$': path.resolve(__dirname, 'empty-proto3-json-serializer.js'), + }, + }), }, module: { rules: [ @@ -133,22 +141,7 @@ export class WorkflowCodeBundler { filename: 'main.js', library: '__TEMPORAL__', }, - }; - if (webpackConfig.resolve) { - if (payloadConverterPath) { - webpackConfig.resolve.alias = { - __temporal_custom_payload_converter$: payloadConverterPath, - }; - } else { - // Save 100KB from the bundle by not including the npm module, - // since we can't decode without a `root` anyway. - webpackConfig.resolve.alias = { - 'proto3-json-serializer$': path.resolve(__dirname, 'empty-proto3-json-serializer.js'), - }; - } - } - - const compiler = webpack(webpackConfig); + }); // Cast to any because the type declarations are inaccurate compiler.inputFileSystem = filesystem as any; diff --git a/packages/worker/src/workflow/module-overrides/assert.ts b/packages/worker/src/workflow/module-overrides/assert.ts index 3ae95e3a5..8a1fb3e8c 100644 --- a/packages/worker/src/workflow/module-overrides/assert.ts +++ b/packages/worker/src/workflow/module-overrides/assert.ts @@ -1,2 +1,3 @@ +// proto3-json-serializer assumes it's running in Node and that `assert` is present, so we need to add it // Don't use `export default` because then `require('assert')` will be `{ default: assertFn }`. It needs to be `assertFn`. module.exports = (global as any).assert; diff --git a/packages/workflow-common/package.json b/packages/workflow-common/package.json index c5addc786..ad261db45 100644 --- a/packages/workflow-common/package.json +++ b/packages/workflow-common/package.json @@ -9,7 +9,7 @@ "workflow", "worker" ], - "author": "Roey Berman ", + "author": "Loren Sands-Ramshaw ", "license": "MIT", "dependencies": { "long": "^4.0.0", diff --git a/packages/workflow-common/src/converter/data-converter.ts b/packages/workflow-common/src/converter/data-converter.ts index 2e2913cef..6e60b93d3 100644 --- a/packages/workflow-common/src/converter/data-converter.ts +++ b/packages/workflow-common/src/converter/data-converter.ts @@ -5,7 +5,7 @@ import { PayloadConverter } from './payload-converter'; * When your data (arguments and return values) is sent over the wire and stored by Temporal Server, * it is encoded in binary in a {@link Payload} Protobuf message. * - * The default `DataConverter` supports `Uint8Array`, protobuf.js objects, and JSON serializables (so if [`JSON.stringify(yourArgOrRetval)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) works, the default data converter will work). + * The default `DataConverter` supports `Uint8Array`, and JSON serializables (so if [`JSON.stringify(yourArgOrRetval)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) works, the default data converter will work). * * Use a custom `DataConverter` to control the contents of your {@link Payload}s. * Common reasons for using a custom `DataConverter` are: From b809ccbd5fbc3a693a32eb52e553025ea8f48804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Fri, 18 Feb 2022 16:49:50 -0500 Subject: [PATCH 16/33] fix: Don't mutate Failures --- packages/common/package.json | 6 +- packages/common/src/codec-helpers.ts | 87 +++++++++++++++++++--------- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/packages/common/package.json b/packages/common/package.json index 48dee6123..1d8dd19cb 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -13,7 +13,8 @@ "author": "Roey Berman ", "license": "MIT", "dependencies": { - "@temporalio/workflow-common": "file:../workflow-common" + "@temporalio/workflow-common": "file:../workflow-common", + "lodash": "^4.17.21" }, "bugs": { "url": "https://github.com/temporalio/sdk-typescript/issues" @@ -21,5 +22,8 @@ "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/common", "publishConfig": { "access": "public" + }, + "devDependencies": { + "@types/lodash": "^4.14.178" } } diff --git a/packages/common/src/codec-helpers.ts b/packages/common/src/codec-helpers.ts index 7cc1af1e4..dbfa7a56e 100644 --- a/packages/common/src/codec-helpers.ts +++ b/packages/common/src/codec-helpers.ts @@ -10,6 +10,7 @@ import { TemporalFailure, toPayloads, } from '@temporalio/workflow-common'; +import { clone, setWith } from 'lodash'; /** * Decode `payloads` and then return {@link fromPayloadsAtIndex}. @@ -100,59 +101,89 @@ export async function encodeErrorToFailure(dataConverter: LoadedDataConverter, e } /** - * Run `codec.encode()` on the {@link Payload}s in a {@link ProtoFailure}. Mutates `failure`. + * Return a new {@link ProtoFailure} with `codec.encode()` run on all the {@link Payload}s. */ export async function encodeFailure(codec: PayloadCodec, failure: ProtoFailure): Promise { + const encodedFailure = { ...failure }; if (failure.cause) { - await encodeFailure(codec, failure.cause); + encodedFailure.cause = await encodeFailure(codec, failure.cause); } - if (failure.applicationFailureInfo?.details?.payloads?.length) { - failure.applicationFailureInfo.details.payloads = await codec.encode( - failure.applicationFailureInfo.details.payloads + if (encodedFailure.applicationFailureInfo?.details?.payloads?.length) { + setWith( + encodedFailure, + 'applicationFailureInfo.details.payloads', + await codec.encode(encodedFailure.applicationFailureInfo.details.payloads), + clone ); } - if (failure.timeoutFailureInfo?.lastHeartbeatDetails?.payloads?.length) { - failure.timeoutFailureInfo.lastHeartbeatDetails.payloads = await codec.encode( - failure.timeoutFailureInfo.lastHeartbeatDetails.payloads + if (encodedFailure.timeoutFailureInfo?.lastHeartbeatDetails?.payloads?.length) { + setWith( + encodedFailure, + 'timeoutFailureInfo.lastHeartbeatDetails.payloads', + await codec.encode(encodedFailure.timeoutFailureInfo.lastHeartbeatDetails.payloads), + clone ); } - if (failure.canceledFailureInfo?.details?.payloads?.length) { - failure.canceledFailureInfo.details.payloads = await codec.encode(failure.canceledFailureInfo.details.payloads); + if (encodedFailure.canceledFailureInfo?.details?.payloads?.length) { + setWith( + encodedFailure, + 'canceledFailureInfo.details.payloads', + await codec.encode(encodedFailure.canceledFailureInfo.details.payloads), + clone + ); } - if (failure.resetWorkflowFailureInfo?.lastHeartbeatDetails?.payloads?.length) { - failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads = await codec.encode( - failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads + if (encodedFailure.resetWorkflowFailureInfo?.lastHeartbeatDetails?.payloads?.length) { + setWith( + encodedFailure, + 'resetWorkflowFailureInfo.lastHeartbeatDetails.payloads', + await codec.encode(encodedFailure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads), + clone ); } - return failure; + return encodedFailure; } /** - * Run `codec.decode()` on the {@link Payload}s in a {@link ProtoFailure}. Mutates `failure`. + * Return a new {@link ProtoFailure} with `codec.decode()` run on all the {@link Payload}s. */ export async function decodeFailure(codec: PayloadCodec, failure: ProtoFailure): Promise { + const decodedFailure = { ...failure }; if (failure.cause) { - await decodeFailure(codec, failure.cause); + decodedFailure.cause = await decodeFailure(codec, failure.cause); } - if (failure.applicationFailureInfo?.details?.payloads?.length) { - failure.applicationFailureInfo.details.payloads = await codec.decode( - failure.applicationFailureInfo.details.payloads + if (decodedFailure.applicationFailureInfo?.details?.payloads?.length) { + setWith( + decodedFailure, + 'applicationFailureInfo.details.payloads', + await codec.decode(decodedFailure.applicationFailureInfo.details.payloads), + clone ); } - if (failure.timeoutFailureInfo?.lastHeartbeatDetails?.payloads?.length) { - failure.timeoutFailureInfo.lastHeartbeatDetails.payloads = await codec.decode( - failure.timeoutFailureInfo.lastHeartbeatDetails.payloads + if (decodedFailure.timeoutFailureInfo?.lastHeartbeatDetails?.payloads?.length) { + setWith( + decodedFailure, + 'timeoutFailureInfo.lastHeartbeatDetails.payloads', + await codec.decode(decodedFailure.timeoutFailureInfo.lastHeartbeatDetails.payloads), + clone ); } - if (failure.canceledFailureInfo?.details?.payloads?.length) { - failure.canceledFailureInfo.details.payloads = await codec.decode(failure.canceledFailureInfo.details.payloads); + if (decodedFailure.canceledFailureInfo?.details?.payloads?.length) { + setWith( + decodedFailure, + 'canceledFailureInfo.details.payloads', + await codec.decode(decodedFailure.canceledFailureInfo.details.payloads), + clone + ); } - if (failure.resetWorkflowFailureInfo?.lastHeartbeatDetails?.payloads?.length) { - failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads = await codec.decode( - failure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads + if (decodedFailure.resetWorkflowFailureInfo?.lastHeartbeatDetails?.payloads?.length) { + setWith( + decodedFailure, + 'resetWorkflowFailureInfo.lastHeartbeatDetails.payloads', + await codec.decode(decodedFailure.resetWorkflowFailureInfo.lastHeartbeatDetails.payloads), + clone ); } - return failure; + return decodedFailure; } From 9bb131c93049af3201d468893e48e5a4361b1aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Fri, 18 Feb 2022 18:53:35 -0500 Subject: [PATCH 17/33] fix: Don't mutate Activation when decoding --- packages/worker/src/worker.ts | 4 +- packages/worker/src/workflow-codec-runner.ts | 162 ++++++++++++------- 2 files changed, 106 insertions(+), 60 deletions(-) diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index 74bdae3e3..af67e1981 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -644,8 +644,8 @@ export class Worker { let isFatalError = false; try { - await this.workflowCodecRunner.decodeActivation(activation); - const unencodedCompletion = await state.workflow.activate(activation); + const decodedActivation = await this.workflowCodecRunner.decodeActivation(activation); + const unencodedCompletion = await state.workflow.activate(decodedActivation); const completion = await this.workflowCodecRunner.encodeCompletion(unencodedCompletion); this.log.debug('Completed activation', { runId: activation.runId, diff --git a/packages/worker/src/workflow-codec-runner.ts b/packages/worker/src/workflow-codec-runner.ts index e5156203d..d9648d278 100644 --- a/packages/worker/src/workflow-codec-runner.ts +++ b/packages/worker/src/workflow-codec-runner.ts @@ -1,6 +1,7 @@ import { decodeFailure, encodeFailure, Payload } from '@temporalio/common'; -import { coresdk, temporal } from '@temporalio/proto'; +import { coresdk } from '@temporalio/proto'; import { PayloadCodec, ProtoFailure } from '@temporalio/workflow-common'; +import { clone, setWith } from 'lodash'; /** * Helper class for decoding Workflow activations and encoding Workflow completions. @@ -11,66 +12,111 @@ export class WorkflowCodecRunner { /** * Run codec.decode on the Payloads in the Activation message. */ - public async decodeActivation(activation: coresdk.workflow_activation.IWorkflowActivation): Promise { + public async decodeActivation( + activation: coresdk.workflow_activation.IWorkflowActivation + ): Promise { + const decodedActivation = { ...activation }; if (activation.jobs?.length) { - await Promise.all( - activation.jobs.flatMap((job) => [ - this.decodeArray(job.startWorkflow, 'arguments'), - ...this.decodeMap(job.startWorkflow, 'headers'), - this.decodeArray(job.queryWorkflow, 'arguments'), - this.decodeArray(job.cancelWorkflow, 'details'), - this.decodeArray(job.signalWorkflow, 'input'), - this.decodeField(job.resolveActivity?.result?.completed, 'result'), - this.decodeField(job.resolveChildWorkflowExecution?.result?.completed, 'result'), - this.decodeFailure(job.resolveActivity?.result?.failed), - this.decodeFailure(job.resolveActivity?.result?.cancelled), - this.decodeFailure(job.resolveChildWorkflowExecutionStart?.cancelled), - this.decodeFailure(job.resolveSignalExternalWorkflow), - this.decodeFailure(job.resolveChildWorkflowExecution?.result?.failed), - this.decodeFailure(job.resolveChildWorkflowExecution?.result?.cancelled), - this.decodeFailure(job.resolveRequestCancelExternalWorkflow), - ]) + decodedActivation.jobs = await Promise.all( + activation.jobs.map(async (job) => { + const decodedJob = { ...job }; + if (decodedJob.startWorkflow?.arguments) { + setWith( + decodedJob, + 'startWorkflow.arguments', + await this.codec.decode(decodedJob.startWorkflow.arguments), + clone + ); + } + if (decodedJob.queryWorkflow?.arguments) { + setWith( + decodedJob, + 'queryWorkflow.arguments', + await this.codec.decode(decodedJob.queryWorkflow.arguments), + clone + ); + } + if (decodedJob.cancelWorkflow?.details) { + setWith( + decodedJob, + 'cancelWorkflow.details', + await this.codec.decode(decodedJob.cancelWorkflow.details), + clone + ); + } + if (decodedJob.signalWorkflow?.input) { + setWith( + decodedJob, + 'signalWorkflow.input', + await this.codec.decode(decodedJob.signalWorkflow.input), + clone + ); + } + if (decodedJob.startWorkflow?.headers) { + await Promise.all( + Object.entries(decodedJob.startWorkflow.headers).map(async ([header, payload]) => { + const [decodedPayload] = await this.codec.decode([payload]); + setWith(decodedJob, `startWorkflow.headers.${header}`, decodedPayload, clone); + }) + ); + } + if (decodedJob.resolveActivity?.result?.completed?.result) { + const [decodedResult] = await this.codec.decode([decodedJob.resolveActivity.result.completed.result]); + setWith(decodedJob, 'resolveActivity.result.completed.result', decodedResult, clone); + } + if (decodedJob.resolveChildWorkflowExecution?.result?.completed?.result) { + const [decodedResult] = await this.codec.decode([ + decodedJob.resolveChildWorkflowExecution.result.completed.result, + ]); + setWith(decodedJob, 'resolveChildWorkflowExecution.result.completed.result', decodedResult, clone); + } + if (decodedJob.resolveActivity?.result?.failed?.failure) { + decodedJob.resolveActivity.result.failed.failure = await decodeFailure( + this.codec, + decodedJob.resolveActivity.result.failed.failure + ); + } + if (decodedJob.resolveActivity?.result?.cancelled?.failure) { + decodedJob.resolveActivity.result.cancelled.failure = await decodeFailure( + this.codec, + decodedJob.resolveActivity.result.cancelled.failure + ); + } + if (decodedJob.resolveChildWorkflowExecutionStart?.cancelled?.failure) { + decodedJob.resolveChildWorkflowExecutionStart.cancelled.failure = await decodeFailure( + this.codec, + decodedJob.resolveChildWorkflowExecutionStart.cancelled.failure + ); + } + if (decodedJob.resolveSignalExternalWorkflow?.failure) { + decodedJob.resolveSignalExternalWorkflow.failure = await decodeFailure( + this.codec, + decodedJob.resolveSignalExternalWorkflow.failure + ); + } + if (decodedJob.resolveChildWorkflowExecution?.result?.failed?.failure) { + decodedJob.resolveChildWorkflowExecution.result.failed.failure = await decodeFailure( + this.codec, + decodedJob.resolveChildWorkflowExecution.result.failed.failure + ); + } + if (decodedJob.resolveChildWorkflowExecution?.result?.cancelled?.failure) { + decodedJob.resolveChildWorkflowExecution.result.cancelled.failure = await decodeFailure( + this.codec, + decodedJob.resolveChildWorkflowExecution.result.cancelled.failure + ); + } + if (decodedJob.resolveRequestCancelExternalWorkflow?.failure) { + decodedJob.resolveRequestCancelExternalWorkflow.failure = await decodeFailure( + this.codec, + decodedJob.resolveRequestCancelExternalWorkflow.failure + ); + } + return decodedJob; + }) ); } - } - - protected async decodeField(object: unknown, field: string): Promise { - if (!object) return; - const record = object as Record; - if (!record[field]) return; - - const [decodedPayload] = await this.codec.decode([record[field] as Payload]); - record[field] = decodedPayload; - } - - protected async decodeArray(object: unknown, field: string): Promise { - if (!object) return; - const record = object as Record; - if (!record[field]) return; - - record[field] = await this.codec.decode(record[field] as Payload[]); - } - - protected decodeMap(object: unknown, field: string): Promise[] { - if (!object) return []; - const record = object as Record; - if (!record[field]) return []; - - return Object.entries(record[field] as Record).map(async ([k, v]) => { - const [decodedPayload] = await this.codec.decode([v]); - (record[field] as Record)[k] = decodedPayload; - }); - } - - protected async decodeFailure(failureParent: unknown): Promise { - if (!failureParent) return; - const accessibleFailureParent = failureParent as Record; - if (!accessibleFailureParent.failure) return; - - accessibleFailureParent.failure = await decodeFailure( - this.codec, - accessibleFailureParent.failure as temporal.api.failure.v1.IFailure - ); + return decodedActivation; } /** From c35ed2abf16df54cafc53e7f54dd15456cff3754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Fri, 18 Feb 2022 19:44:24 -0500 Subject: [PATCH 18/33] fix: Run errorToFailure inside vm --- docs/data-converter.md | 9 ++++----- .../payload-converters/payload-converter.ts | 1 + packages/worker/src/worker.ts | 4 ++-- packages/worker/src/workflow/threaded-vm.ts | 6 +++--- packages/worker/src/workflow/vm.ts | 20 ++++++------------- .../src/workflow/workflow-worker-thread.ts | 2 +- .../workflow/workflow-worker-thread/input.ts | 2 +- packages/workflow/src/worker-interface.ts | 6 ++++++ 8 files changed, 24 insertions(+), 26 deletions(-) diff --git a/docs/data-converter.md b/docs/data-converter.md index 325e61740..6d00ba2e1 100644 --- a/docs/data-converter.md +++ b/docs/data-converter.md @@ -52,20 +52,19 @@ When `WorkerOptions.dataConverter.payloadConverterPath` is provided, the code at _main thread_ - imports and validates `options.dataConverter.payloadConverterPath` -- passes `payloadConverterPath` to either `ThreadedVMWorkflowCreator.create` or `VMWorkflowCreator.create` +- passes `useCustomPayloadConverter` to either `ThreadedVMWorkflowCreator.create` or `VMWorkflowCreator.create` - passes `payloadConverterPath` to `WorkflowCodeBundler` `ThreadedVMWorkflowCreator.create`: _main thread_ -- sends `payloadConverterPath` to each worker thread -- thread sends `payloadConverterPath` to VMWorkflowCreator.create +- sends `useCustomPayloadConverter` to each worker thread +- thread sends `useCustomPayloadConverter` to VMWorkflowCreator.create `VMWorkflowCreator.create`: _worker thread (unless in debug mode)_ -- imports `payloadConverterPath` -- passes `payloadConverterPath` to `VMWorkflowCreator` constructor +- passes `useCustomPayloadConverter` to `VMWorkflowCreator` constructor `VMWorkflowCreator.createWorkflow`: _worker thread (unless in debug mode)_ diff --git a/packages/test/src/payload-converters/payload-converter.ts b/packages/test/src/payload-converters/payload-converter.ts index e90cf8a67..d0362229a 100644 --- a/packages/test/src/payload-converters/payload-converter.ts +++ b/packages/test/src/payload-converters/payload-converter.ts @@ -1,6 +1,7 @@ import { DefaultPayloadConverter } from '@temporalio/common'; import root, { foo } from '../../protos/root'; +// Used in tests export const messageInstance = foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); export const payloadConverter = new DefaultPayloadConverter({ protobufRoot: root }); diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index af67e1981..dfa505c84 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -221,14 +221,14 @@ export class Worker { workflowCreator = await VMWorkflowCreator.create( bundle, compiledOptions.isolateExecutionTimeoutMs, - options.dataConverter?.payloadConverterPath + !!options.dataConverter?.payloadConverterPath ); } else { workflowCreator = await ThreadedVMWorkflowCreator.create({ code: bundle, threadPoolSize: compiledOptions.workflowThreadPoolSize, isolateExecutionTimeoutMs: compiledOptions.isolateExecutionTimeoutMs, - payloadConverterPath: options.dataConverter?.payloadConverterPath, + useCustomPayloadConverter: !!options.dataConverter?.payloadConverterPath, }); } } diff --git a/packages/worker/src/workflow/threaded-vm.ts b/packages/worker/src/workflow/threaded-vm.ts index 189462319..699341fe9 100644 --- a/packages/worker/src/workflow/threaded-vm.ts +++ b/packages/worker/src/workflow/threaded-vm.ts @@ -121,7 +121,7 @@ export interface ThreadedVMWorkflowCreatorOptions { code: string; threadPoolSize: number; isolateExecutionTimeoutMs: number; - payloadConverterPath?: string; + useCustomPayloadConverter: boolean; } /** @@ -139,14 +139,14 @@ export class ThreadedVMWorkflowCreator implements WorkflowCreator { threadPoolSize, code, isolateExecutionTimeoutMs, - payloadConverterPath, + useCustomPayloadConverter, }: ThreadedVMWorkflowCreatorOptions): Promise { const workerThreadClients = Array(threadPoolSize) .fill(0) .map(() => new WorkerThreadClient(new NodeWorker(require.resolve('./workflow-worker-thread')))); await Promise.all( workerThreadClients.map((client) => - client.send({ type: 'init', code, isolateExecutionTimeoutMs, payloadConverterPath }) + client.send({ type: 'init', code, isolateExecutionTimeoutMs, useCustomPayloadConverter }) ) ); return new this(workerThreadClients); diff --git a/packages/worker/src/workflow/vm.ts b/packages/worker/src/workflow/vm.ts index 9f9a6e907..08063dc08 100644 --- a/packages/worker/src/workflow/vm.ts +++ b/packages/worker/src/workflow/vm.ts @@ -2,7 +2,7 @@ import vm from 'vm'; import { coresdk } from '@temporalio/proto'; import * as internals from '@temporalio/workflow/lib/worker-interface'; import { WorkflowInfo } from '@temporalio/workflow'; -import { defaultPayloadConverter, PayloadConverter, errorToFailure, IllegalStateError } from '@temporalio/common'; +import { IllegalStateError } from '@temporalio/common'; import { partition } from '../utils'; import { Workflow, WorkflowCreator, WorkflowCreateOptions } from './interface'; import { SinkCall } from '@temporalio/workflow/lib/sinks'; @@ -18,7 +18,6 @@ export class VMWorkflowCreator implements WorkflowCreator { constructor( script: vm.Script, public readonly isolateExecutionTimeoutMs: number, - protected readonly payloadConverter: PayloadConverter, protected readonly useCustomPayloadConverter: boolean ) { this.script = script; @@ -48,7 +47,7 @@ export class VMWorkflowCreator implements WorkflowCreator { await workflowModule.initRuntime({ useCustomPayloadConverter: this.useCustomPayloadConverter, ...options }); - return new VMWorkflow(options.info, context, workflowModule, isolateExecutionTimeoutMs, this.payloadConverter); + return new VMWorkflow(options.info, context, workflowModule, isolateExecutionTimeoutMs); } protected async getContext(): Promise { @@ -84,16 +83,10 @@ export class VMWorkflowCreator implements WorkflowCreator { this: T, code: string, isolateExecutionTimeoutMs: number, - payloadConverterPath?: string + useCustomPayloadConverter = false ): Promise> { const script = new vm.Script(code, { filename: 'workflow-isolate' }); - let payloadConverter = defaultPayloadConverter; - if (payloadConverterPath) { - // payloadConverterPath was validated in Worker.create - payloadConverter = (await import(payloadConverterPath)).payloadConverter; - } - - return new this(script, isolateExecutionTimeoutMs, payloadConverter, !!payloadConverterPath) as InstanceType; + return new this(script, isolateExecutionTimeoutMs, useCustomPayloadConverter) as InstanceType; } /** @@ -116,8 +109,7 @@ export class VMWorkflow implements Workflow { public readonly info: WorkflowInfo, protected context: vm.Context | undefined, readonly workflowModule: WorkflowModule, - public readonly isolateExecutionTimeoutMs: number, - protected readonly payloadConverter: PayloadConverter + public readonly isolateExecutionTimeoutMs: number ) {} /** @@ -188,7 +180,7 @@ export class VMWorkflow implements Workflow { if (this.unhandledRejection) { return coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited({ runId: activation.runId, - failed: { failure: errorToFailure(this.unhandledRejection, this.payloadConverter) }, + failed: { failure: this.workflowModule.errorToFailure(this.unhandledRejection) }, }).finish(); } return coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited(completion).finish(); diff --git a/packages/worker/src/workflow/workflow-worker-thread.ts b/packages/worker/src/workflow/workflow-worker-thread.ts index a3319fea4..1fcb8e1df 100644 --- a/packages/worker/src/workflow/workflow-worker-thread.ts +++ b/packages/worker/src/workflow/workflow-worker-thread.ts @@ -52,7 +52,7 @@ async function handleRequest({ requestId, input }: WorkerThreadRequest): Promise workflowCreator = await VMWorkflowCreator.create( input.code, input.isolateExecutionTimeoutMs, - input.payloadConverterPath + input.useCustomPayloadConverter ); return ok(requestId); case 'destroy': diff --git a/packages/worker/src/workflow/workflow-worker-thread/input.ts b/packages/worker/src/workflow/workflow-worker-thread/input.ts index 612057b28..05bbe264e 100644 --- a/packages/worker/src/workflow/workflow-worker-thread/input.ts +++ b/packages/worker/src/workflow/workflow-worker-thread/input.ts @@ -7,7 +7,7 @@ export interface Init { type: 'init'; isolateExecutionTimeoutMs: number; code: string; - payloadConverterPath?: string; + useCustomPayloadConverter: boolean; } /** diff --git a/packages/workflow/src/worker-interface.ts b/packages/workflow/src/worker-interface.ts index d6219d068..e47f16a5e 100644 --- a/packages/workflow/src/worker-interface.ts +++ b/packages/workflow/src/worker-interface.ts @@ -11,6 +11,8 @@ import { Workflow, ApplicationFailure, errorMessage, + errorToFailure as _errorToFailure, + ProtoFailure, } from '@temporalio/workflow-common'; import type { coresdk } from '@temporalio/proto/lib/coresdk'; import { WorkflowInfo } from './interfaces'; @@ -296,3 +298,7 @@ export async function dispose(): Promise { }); await dispose({}); } + +export function errorToFailure(err: unknown): ProtoFailure { + return _errorToFailure(err, state.payloadConverter); +} From 62c48851ccf148c1551ed94bdc7e40a2ce487b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Wed, 23 Feb 2022 18:20:01 -0500 Subject: [PATCH 19/33] chore: Don't export proto payload converters from workflow-common --- docs/protobuf-libraries.md | 4 +- packages/common/src/index.ts | 6 + .../payload-converters/payload-converter.ts | 4 +- packages/test/src/test-data-converter.ts | 15 +- packages/worker/src/workflow/bundler.ts | 18 +- .../workflow/empty-proto3-json-serializer.ts | 8 - .../src/converter/payload-converter.ts | 27 +-- .../src/converter/payload-converters.ts | 164 +-------------- .../src/converter/protobufs.ts | 190 ++++++++++++++++++ 9 files changed, 224 insertions(+), 212 deletions(-) delete mode 100644 packages/worker/src/workflow/empty-proto3-json-serializer.ts create mode 100644 packages/workflow-common/src/converter/protobufs.ts diff --git a/docs/protobuf-libraries.md b/docs/protobuf-libraries.md index 97d2c57a3..05d852cf4 100644 --- a/docs/protobuf-libraries.md +++ b/docs/protobuf-libraries.md @@ -66,10 +66,10 @@ module.exports = patchProtobufRoot(unpatchedRoot); // pbjs -t static-module *.proto | pbts -o root.d.ts - // src/data-converter.ts -import { DefaultDataConverter } from '@temporalio/common'; +import { DefaultPayloadConverterWithProtobufs } from '@temporalio/common'; import root from '../protos/root'; -export const dataConverter = new DefaultDataConverter({ root }); +export const dataConverter = new DefaultPayloadConverterWithProtobufs({ protobufRoot: root }); // src/worker.ts import { dataConverter } from './data-converter'; diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index a3a7c288f..6304352b0 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -9,3 +9,9 @@ export * from './data-converter-helpers'; export * from './patch-protobuf-root'; export * from './tls-config'; export * from './utils'; +export { + ProtobufBinaryPayloadConverter, + ProtobufJsonPayloadConverter, + DefaultPayloadConverterWithProtobufs, + DefaultPayloadConverterWithProtobufsOptions, +} from '@temporalio/workflow-common/lib/converter/protobufs'; diff --git a/packages/test/src/payload-converters/payload-converter.ts b/packages/test/src/payload-converters/payload-converter.ts index d0362229a..96e8943b8 100644 --- a/packages/test/src/payload-converters/payload-converter.ts +++ b/packages/test/src/payload-converters/payload-converter.ts @@ -1,7 +1,7 @@ -import { DefaultPayloadConverter } from '@temporalio/common'; +import { DefaultPayloadConverterWithProtobufs } from '@temporalio/common'; import root, { foo } from '../../protos/root'; // Used in tests export const messageInstance = foo.bar.ProtoActivityInput.create({ name: 'Proto', age: 1 }); -export const payloadConverter = new DefaultPayloadConverter({ protobufRoot: root }); +export const payloadConverter = new DefaultPayloadConverterWithProtobufs({ protobufRoot: root }); diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 74df69149..0e56599d3 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -1,14 +1,17 @@ /* eslint @typescript-eslint/no-non-null-assertion: 0 */ import { Connection, WorkflowClient } from '@temporalio/client'; -import { DataConverterError, defaultPayloadConverter, DefaultPayloadConverter, ValueError } from '@temporalio/common'; +import { + DataConverterError, + defaultPayloadConverter, + DefaultPayloadConverterWithProtobufs, + ValueError, +} from '@temporalio/common'; import { Core, DefaultLogger, Worker } from '@temporalio/worker'; +import { BinaryPayloadConverter, JsonPayloadConverter, UndefinedPayloadConverter } from '@temporalio/workflow-common'; import { - BinaryPayloadConverter, - JsonPayloadConverter, ProtobufBinaryPayloadConverter, ProtobufJsonPayloadConverter, - UndefinedPayloadConverter, -} from '@temporalio/workflow-common/lib/converter/payload-converters'; +} from '@temporalio/workflow-common/lib/converter/protobufs'; import { encodingKeys, METADATA_ENCODING_KEY, @@ -206,7 +209,7 @@ if (RUN_INTEGRATION_TESTS) { test('DefaultPayloadConverter converts protobufs', async (t) => { const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); - const defaultPayloadConverterWithProtos = new DefaultPayloadConverter({ protobufRoot: root }); + const defaultPayloadConverterWithProtos = new DefaultPayloadConverterWithProtobufs({ protobufRoot: root }); t.deepEqual( defaultPayloadConverterWithProtos.toPayload(instance), // It will always use JSON because it appears before binary in the list diff --git a/packages/worker/src/workflow/bundler.ts b/packages/worker/src/workflow/bundler.ts index 02555e9df..5d8eb9446 100644 --- a/packages/worker/src/workflow/bundler.ts +++ b/packages/worker/src/workflow/bundler.ts @@ -109,19 +109,11 @@ export class WorkflowCodeBundler { extensions: ['.ts', '.js'], // If we don't set an alias for this below, then it won't be imported, so webpack can safely ignore it fallback: { __temporal_custom_payload_converter: false }, - ...(this.payloadConverterPath - ? { - alias: { - __temporal_custom_payload_converter$: this.payloadConverterPath, - }, - } - : { - alias: { - // Save 100KB from the bundle by not including the npm module, - // since we can't decode without a `root` anyway. - 'proto3-json-serializer$': path.resolve(__dirname, 'empty-proto3-json-serializer.js'), - }, - }), + ...(this.payloadConverterPath && { + alias: { + __temporal_custom_payload_converter$: this.payloadConverterPath, + }, + }), }, module: { rules: [ diff --git a/packages/worker/src/workflow/empty-proto3-json-serializer.ts b/packages/worker/src/workflow/empty-proto3-json-serializer.ts deleted file mode 100644 index 46a258a8f..000000000 --- a/packages/worker/src/workflow/empty-proto3-json-serializer.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { DataConverterError } from '@temporalio/workflow-common'; - -function throwError(): void { - throw new DataConverterError('In order to JSON-encode/decode protobufs, specify a `WorkerOptions.dataConverter`'); -} - -export const toProto3JSON = throwError; -export const fromProto3JSON = throwError; diff --git a/packages/workflow-common/src/converter/payload-converter.ts b/packages/workflow-common/src/converter/payload-converter.ts index 242083e60..b957da8ca 100644 --- a/packages/workflow-common/src/converter/payload-converter.ts +++ b/packages/workflow-common/src/converter/payload-converter.ts @@ -3,8 +3,6 @@ import { BinaryPayloadConverter, JsonPayloadConverter, PayloadConverterWithEncoding, - ProtobufBinaryPayloadConverter, - ProtobufJsonPayloadConverter, UndefinedPayloadConverter, } from './payload-converters'; import { METADATA_ENCODING_KEY, Payload, str } from './types'; @@ -131,24 +129,15 @@ export function mapToPayloads(converter: PayloadConverter, map ) as Record; } -export interface DefaultPayloadConverterOptions { - /** - * The `root` provided to {@link ProtobufJsonPayloadConverter} and {@link ProtobufBinaryPayloadConverter} - */ - protobufRoot?: Record; -} - export class DefaultPayloadConverter extends CompositePayloadConverter { - constructor({ protobufRoot }: DefaultPayloadConverterOptions = {}) { - // Match the order used in other SDKs - // Go SDK: https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28 - super( - new UndefinedPayloadConverter(), - new BinaryPayloadConverter(), - new ProtobufJsonPayloadConverter(protobufRoot), - new ProtobufBinaryPayloadConverter(protobufRoot), - new JsonPayloadConverter() - ); + // Match the order used in other SDKs, but exclude Protobuf converters so that the code, including + // `proto3-json-serializer`, doesn't take space in Workflow bundles that don't use Protobufs. To use Protobufs, use + // {@link DefaultPayloadConverterWithProtobufs}. + // + // Go SDK: + // https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28 + constructor() { + super(new UndefinedPayloadConverter(), new BinaryPayloadConverter(), new JsonPayloadConverter()); } } diff --git a/packages/workflow-common/src/converter/payload-converters.ts b/packages/workflow-common/src/converter/payload-converters.ts index 9a6a11bac..ff5f56421 100644 --- a/packages/workflow-common/src/converter/payload-converters.ts +++ b/packages/workflow-common/src/converter/payload-converters.ts @@ -1,19 +1,6 @@ -import { ValueError, PayloadConverterError, UnsupportedTypeError } from '../errors'; -import { - u8, - str, - Payload, - encodingTypes, - EncodingType, - encodingKeys, - METADATA_ENCODING_KEY, - METADATA_MESSAGE_TYPE_KEY, -} from './types'; -import { isRecord, hasOwnProperty, hasOwnProperties } from '../type-helpers'; -import { errorMessage } from '../errors'; -import * as protoJsonSerializer from 'proto3-json-serializer'; -import type { Root, Type, Namespace, Message } from 'protobufjs'; +import { UnsupportedTypeError, ValueError } from '../errors'; import { PayloadConverter } from './payload-converter'; +import { encodingKeys, EncodingType, encodingTypes, METADATA_ENCODING_KEY, Payload, str, u8 } from './types'; export interface PayloadConverterWithEncoding extends PayloadConverter { readonly encodingType: EncodingType; @@ -87,150 +74,3 @@ export class BinaryPayloadConverter implements PayloadConverterWithEncoding { return content.data as any; } } - -abstract class ProtobufPayloadConverter implements PayloadConverterWithEncoding { - protected readonly root: Root | undefined; - public abstract encodingType: EncodingType; - - public abstract toPayload(value: T): Payload; - public abstract fromPayload(payload: Payload): T; - - // Don't use type Root here because root.d.ts doesn't export Root, so users would have to type assert - constructor(root?: unknown) { - if (root) { - if (!isRoot(root)) { - throw new TypeError('root must be an instance of a protobufjs Root'); - } - - this.root = root; - } - } - - protected validatePayload(content: Payload): { messageType: Type; data: Uint8Array } { - if (content.data === undefined || content.data === null) { - throw new ValueError('Got payload with no data'); - } - if (!content.metadata || !(METADATA_MESSAGE_TYPE_KEY in content.metadata)) { - throw new ValueError(`Got protobuf payload without metadata.${METADATA_MESSAGE_TYPE_KEY}`); - } - if (!this.root) { - throw new PayloadConverterError('Unable to deserialize protobuf message without `root` being provided'); - } - - const messageTypeName = str(content.metadata[METADATA_MESSAGE_TYPE_KEY]); - let messageType; - try { - messageType = this.root.lookupType(messageTypeName); - } catch (e) { - if (errorMessage(e)?.includes('no such type')) { - throw new PayloadConverterError( - `Got a \`${messageTypeName}\` protobuf message but cannot find corresponding message class in \`root\`` - ); - } - - throw e; - } - - return { messageType, data: content.data }; - } - - protected constructPayload({ messageTypeName, message }: { messageTypeName: string; message: Uint8Array }): Payload { - return { - metadata: { - [METADATA_ENCODING_KEY]: u8(this.encodingType), - [METADATA_MESSAGE_TYPE_KEY]: u8(messageTypeName), - }, - data: message, - }; - } -} - -/** - * Converts between protobufjs Message instances and serialized Protobuf Payload - */ -export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter { - public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF; - - /** - * @param root The value returned from {@link patchProtobufRoot} - */ - constructor(root?: unknown) { - super(root); - } - - public toPayload(value: unknown): Payload { - if (!isProtobufMessage(value)) { - throw new UnsupportedTypeError(); - } - - return this.constructPayload({ - messageTypeName: getNamespacedTypeName(value.$type), - message: value.$type.encode(value).finish(), - }); - } - - public fromPayload(content: Payload): T { - const { messageType, data } = this.validatePayload(content); - return messageType.decode(data) as unknown as T; - } -} - -/** - * Converts between protobufjs Message instances and serialized JSON Payload - */ -export class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter { - public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF_JSON; - - /** - * @param root The value returned from {@link patchProtobufRoot} - */ - constructor(root?: unknown) { - super(root); - } - - public toPayload(value: unknown): Payload { - if (!isProtobufMessage(value)) { - throw new UnsupportedTypeError(); - } - - const jsonValue = protoJsonSerializer.toProto3JSON(value); - - return this.constructPayload({ - messageTypeName: getNamespacedTypeName(value.$type), - message: u8(JSON.stringify(jsonValue)), - }); - } - - public fromPayload(content: Payload): T { - const { messageType, data } = this.validatePayload(content); - return protoJsonSerializer.fromProto3JSON(messageType, JSON.parse(str(data))) as unknown as T; - } -} - -function isProtobufType(type: unknown): type is Type { - return ( - isRecord(type) && - type.constructor.name === 'Type' && - hasOwnProperties(type, ['parent', 'name', 'create', 'encode', 'decode']) && - typeof type.name === 'string' && - typeof type.create === 'function' && - typeof type.encode === 'function' && - typeof type.decode === 'function' - ); -} - -function isProtobufMessage(value: unknown): value is Message { - return isRecord(value) && hasOwnProperty(value, '$type') && isProtobufType(value.$type); -} - -function getNamespacedTypeName(node: Type | Namespace): string { - if (node.parent && !isRoot(node.parent)) { - return getNamespacedTypeName(node.parent) + '.' + node.name; - } else { - return node.name; - } -} - -function isRoot(root: unknown): root is Root { - return isRecord(root) && root.constructor.name === 'Root'; -} diff --git a/packages/workflow-common/src/converter/protobufs.ts b/packages/workflow-common/src/converter/protobufs.ts new file mode 100644 index 000000000..902b8c141 --- /dev/null +++ b/packages/workflow-common/src/converter/protobufs.ts @@ -0,0 +1,190 @@ +import * as protoJsonSerializer from 'proto3-json-serializer'; +import type { Message, Namespace, Root, Type } from 'protobufjs'; +import { errorMessage, PayloadConverterError, UnsupportedTypeError, ValueError } from '../errors'; +import { hasOwnProperties, hasOwnProperty, isRecord } from '../type-helpers'; +import { + EncodingType, + encodingTypes, + METADATA_ENCODING_KEY, + METADATA_MESSAGE_TYPE_KEY, + Payload, + str, + u8, +} from './types'; +import { + BinaryPayloadConverter, + JsonPayloadConverter, + PayloadConverterWithEncoding, + UndefinedPayloadConverter, +} from './payload-converters'; +import { CompositePayloadConverter } from './payload-converter'; + +abstract class ProtobufPayloadConverter implements PayloadConverterWithEncoding { + protected readonly root: Root | undefined; + public abstract encodingType: EncodingType; + + public abstract toPayload(value: T): Payload; + public abstract fromPayload(payload: Payload): T; + + // Don't use type Root here because root.d.ts doesn't export Root, so users would have to type assert + constructor(root?: unknown) { + if (root) { + if (!isRoot(root)) { + throw new TypeError('root must be an instance of a protobufjs Root'); + } + + this.root = root; + } + } + + protected validatePayload(content: Payload): { messageType: Type; data: Uint8Array } { + if (content.data === undefined || content.data === null) { + throw new ValueError('Got payload with no data'); + } + if (!content.metadata || !(METADATA_MESSAGE_TYPE_KEY in content.metadata)) { + throw new ValueError(`Got protobuf payload without metadata.${METADATA_MESSAGE_TYPE_KEY}`); + } + if (!this.root) { + throw new PayloadConverterError('Unable to deserialize protobuf message without `root` being provided'); + } + + const messageTypeName = str(content.metadata[METADATA_MESSAGE_TYPE_KEY]); + let messageType; + try { + messageType = this.root.lookupType(messageTypeName); + } catch (e) { + if (errorMessage(e)?.includes('no such type')) { + throw new PayloadConverterError( + `Got a \`${messageTypeName}\` protobuf message but cannot find corresponding message class in \`root\`` + ); + } + + throw e; + } + + return { messageType, data: content.data }; + } + + protected constructPayload({ messageTypeName, message }: { messageTypeName: string; message: Uint8Array }): Payload { + return { + metadata: { + [METADATA_ENCODING_KEY]: u8(this.encodingType), + [METADATA_MESSAGE_TYPE_KEY]: u8(messageTypeName), + }, + data: message, + }; + } +} + +/** + * Converts between protobufjs Message instances and serialized Protobuf Payload + */ +export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter { + public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF; + + /** + * @param root The value returned from {@link patchProtobufRoot} + */ + constructor(root?: unknown) { + super(root); + } + + public toPayload(value: unknown): Payload { + if (!isProtobufMessage(value)) { + throw new UnsupportedTypeError(); + } + + return this.constructPayload({ + messageTypeName: getNamespacedTypeName(value.$type), + message: value.$type.encode(value).finish(), + }); + } + + public fromPayload(content: Payload): T { + const { messageType, data } = this.validatePayload(content); + return messageType.decode(data) as unknown as T; + } +} + +/** + * Converts between protobufjs Message instances and serialized JSON Payload + */ +export class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter { + public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF_JSON; + + /** + * @param root The value returned from {@link patchProtobufRoot} + */ + constructor(root?: unknown) { + super(root); + } + + public toPayload(value: unknown): Payload { + if (!isProtobufMessage(value)) { + throw new UnsupportedTypeError(); + } + + const jsonValue = protoJsonSerializer.toProto3JSON(value); + + return this.constructPayload({ + messageTypeName: getNamespacedTypeName(value.$type), + message: u8(JSON.stringify(jsonValue)), + }); + } + + public fromPayload(content: Payload): T { + const { messageType, data } = this.validatePayload(content); + return protoJsonSerializer.fromProto3JSON(messageType, JSON.parse(str(data))) as unknown as T; + } +} + +function isProtobufType(type: unknown): type is Type { + return ( + isRecord(type) && + type.constructor.name === 'Type' && + hasOwnProperties(type, ['parent', 'name', 'create', 'encode', 'decode']) && + typeof type.name === 'string' && + typeof type.create === 'function' && + typeof type.encode === 'function' && + typeof type.decode === 'function' + ); +} + +function isProtobufMessage(value: unknown): value is Message { + return isRecord(value) && hasOwnProperty(value, '$type') && isProtobufType(value.$type); +} + +function getNamespacedTypeName(node: Type | Namespace): string { + if (node.parent && !isRoot(node.parent)) { + return getNamespacedTypeName(node.parent) + '.' + node.name; + } else { + return node.name; + } +} + +function isRoot(root: unknown): root is Root { + return isRecord(root) && root.constructor.name === 'Root'; +} + +export interface DefaultPayloadConverterWithProtobufsOptions { + /** + * The `root` provided to {@link ProtobufJsonPayloadConverter} and {@link ProtobufBinaryPayloadConverter} + */ + protobufRoot: Record; +} + +export class DefaultPayloadConverterWithProtobufs extends CompositePayloadConverter { + // Match the order used in other SDKs. + // + // Go SDK: + // https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28 + constructor({ protobufRoot }: DefaultPayloadConverterWithProtobufsOptions) { + super( + new UndefinedPayloadConverter(), + new BinaryPayloadConverter(), + new ProtobufJsonPayloadConverter(protobufRoot), + new ProtobufBinaryPayloadConverter(protobufRoot), + new JsonPayloadConverter() + ); + } +} From 6cb9540b3e9ccc20afcb2f747724e21405da4d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Wed, 23 Feb 2022 18:21:30 -0500 Subject: [PATCH 20/33] fix: Update tryToContinueAfterCompletion --- packages/test/src/test-workflows.ts | 7 ++----- .../test/src/workflows/try-to-continue-after-completion.ts | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/test/src/test-workflows.ts b/packages/test/src/test-workflows.ts index 878d14dd7..ba07e8a97 100644 --- a/packages/test/src/test-workflows.ts +++ b/packages/test/src/test-workflows.ts @@ -1652,13 +1652,10 @@ test('patchedTopLevel', async (t) => { t.deepEqual(logs, [[['Patches cannot be used before Workflow starts']]]); }); -test.only('tryToContinueAfterCompletion', async (t) => { +test('tryToContinueAfterCompletion', async (t) => { const { workflowType } = t.context; { - const failure = await activate(t, makeStartWorkflow(workflowType)); - console.log('failure:', failure); - - const completion = cleanWorkflowFailureStackTrace(failure); + const completion = cleanWorkflowFailureStackTrace(await activate(t, makeStartWorkflow(workflowType))); compareCompletion( t, completion, diff --git a/packages/test/src/workflows/try-to-continue-after-completion.ts b/packages/test/src/workflows/try-to-continue-after-completion.ts index 84a2dfc4a..f98034cb9 100644 --- a/packages/test/src/workflows/try-to-continue-after-completion.ts +++ b/packages/test/src/workflows/try-to-continue-after-completion.ts @@ -6,7 +6,7 @@ import { continueAsNew, ApplicationFailure } from '@temporalio/workflow'; export async function tryToContinueAfterCompletion(): Promise { await Promise.race([ // Note that continueAsNew only throws after microtasks and as a result, looses the race - continueAsNew(), + Promise.resolve().then(() => continueAsNew()), Promise.reject(ApplicationFailure.nonRetryable('fail before continue')), ]); } From 0bc2fda0747bb0cc6c995eb26ab4aa4359dac230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Thu, 24 Feb 2022 11:46:23 -0500 Subject: [PATCH 21/33] chore: Move common packages --- docs/protobuf-libraries.md | 2 +- .../README.md | 0 .../package.json | 0 .../src/codec-helpers.ts | 0 .../src/data-converter-helpers.ts | 0 .../src/index.ts | 13 ++++++------- .../src/otel.ts | 0 .../src/tls-config.ts | 0 .../src/utils.ts | 0 .../tsconfig.json | 0 .../README.md | 0 .../package.json | 0 .../src/activity-options.ts | 0 .../src/converter/data-converter.ts | 0 .../src/converter/encoding.ts | 0 .../src/converter}/patch-protobuf-root.ts | 0 .../src/converter/payload-codec.ts | 0 .../src/converter/payload-converter.ts | 0 .../src/converter/payload-converters.ts | 0 .../src/converter/protobufs.ts | 0 .../src/converter/types.ts | 0 .../src/errors.ts | 0 .../src/failure.ts | 0 .../src/index.ts | 1 + .../src/interceptors.ts | 0 .../src/interfaces.ts | 0 .../src/retry-policy.ts | 0 .../src/time.ts | 0 .../src/type-helpers.ts | 0 .../src/workflow-handle.ts | 0 .../src/workflow-options.ts | 0 .../tsconfig.json | 0 32 files changed, 8 insertions(+), 8 deletions(-) rename packages/{common => internal-non-workflow-common}/README.md (100%) rename packages/{common => internal-non-workflow-common}/package.json (100%) rename packages/{common => internal-non-workflow-common}/src/codec-helpers.ts (100%) rename packages/{common => internal-non-workflow-common}/src/data-converter-helpers.ts (100%) rename packages/{common => internal-non-workflow-common}/src/index.ts (92%) rename packages/{common => internal-non-workflow-common}/src/otel.ts (100%) rename packages/{common => internal-non-workflow-common}/src/tls-config.ts (100%) rename packages/{common => internal-non-workflow-common}/src/utils.ts (100%) rename packages/{common => internal-non-workflow-common}/tsconfig.json (100%) rename packages/{workflow-common => internal-workflow-common}/README.md (100%) rename packages/{workflow-common => internal-workflow-common}/package.json (100%) rename packages/{workflow-common => internal-workflow-common}/src/activity-options.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/converter/data-converter.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/converter/encoding.ts (100%) rename packages/{common/src => internal-workflow-common/src/converter}/patch-protobuf-root.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/converter/payload-codec.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/converter/payload-converter.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/converter/payload-converters.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/converter/protobufs.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/converter/types.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/errors.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/failure.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/index.ts (92%) rename packages/{workflow-common => internal-workflow-common}/src/interceptors.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/interfaces.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/retry-policy.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/time.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/type-helpers.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/workflow-handle.ts (100%) rename packages/{workflow-common => internal-workflow-common}/src/workflow-options.ts (100%) rename packages/{workflow-common => internal-workflow-common}/tsconfig.json (100%) diff --git a/docs/protobuf-libraries.md b/docs/protobuf-libraries.md index 05d852cf4..f1877106b 100644 --- a/docs/protobuf-libraries.md +++ b/docs/protobuf-libraries.md @@ -66,7 +66,7 @@ module.exports = patchProtobufRoot(unpatchedRoot); // pbjs -t static-module *.proto | pbts -o root.d.ts - // src/data-converter.ts -import { DefaultPayloadConverterWithProtobufs } from '@temporalio/common'; +import { DefaultPayloadConverterWithProtobufs } from '@temporalio/common/lib/converter/protobufs'; import root from '../protos/root'; export const dataConverter = new DefaultPayloadConverterWithProtobufs({ protobufRoot: root }); diff --git a/packages/common/README.md b/packages/internal-non-workflow-common/README.md similarity index 100% rename from packages/common/README.md rename to packages/internal-non-workflow-common/README.md diff --git a/packages/common/package.json b/packages/internal-non-workflow-common/package.json similarity index 100% rename from packages/common/package.json rename to packages/internal-non-workflow-common/package.json diff --git a/packages/common/src/codec-helpers.ts b/packages/internal-non-workflow-common/src/codec-helpers.ts similarity index 100% rename from packages/common/src/codec-helpers.ts rename to packages/internal-non-workflow-common/src/codec-helpers.ts diff --git a/packages/common/src/data-converter-helpers.ts b/packages/internal-non-workflow-common/src/data-converter-helpers.ts similarity index 100% rename from packages/common/src/data-converter-helpers.ts rename to packages/internal-non-workflow-common/src/data-converter-helpers.ts diff --git a/packages/common/src/index.ts b/packages/internal-non-workflow-common/src/index.ts similarity index 92% rename from packages/common/src/index.ts rename to packages/internal-non-workflow-common/src/index.ts index 6304352b0..6610159eb 100644 --- a/packages/common/src/index.ts +++ b/packages/internal-non-workflow-common/src/index.ts @@ -4,14 +4,13 @@ * @module */ export * from '@temporalio/workflow-common'; -export * from './codec-helpers'; -export * from './data-converter-helpers'; -export * from './patch-protobuf-root'; -export * from './tls-config'; -export * from './utils'; export { - ProtobufBinaryPayloadConverter, - ProtobufJsonPayloadConverter, DefaultPayloadConverterWithProtobufs, DefaultPayloadConverterWithProtobufsOptions, + ProtobufBinaryPayloadConverter, + ProtobufJsonPayloadConverter, } from '@temporalio/workflow-common/lib/converter/protobufs'; +export * from './codec-helpers'; +export * from './data-converter-helpers'; +export * from './tls-config'; +export * from './utils'; diff --git a/packages/common/src/otel.ts b/packages/internal-non-workflow-common/src/otel.ts similarity index 100% rename from packages/common/src/otel.ts rename to packages/internal-non-workflow-common/src/otel.ts diff --git a/packages/common/src/tls-config.ts b/packages/internal-non-workflow-common/src/tls-config.ts similarity index 100% rename from packages/common/src/tls-config.ts rename to packages/internal-non-workflow-common/src/tls-config.ts diff --git a/packages/common/src/utils.ts b/packages/internal-non-workflow-common/src/utils.ts similarity index 100% rename from packages/common/src/utils.ts rename to packages/internal-non-workflow-common/src/utils.ts diff --git a/packages/common/tsconfig.json b/packages/internal-non-workflow-common/tsconfig.json similarity index 100% rename from packages/common/tsconfig.json rename to packages/internal-non-workflow-common/tsconfig.json diff --git a/packages/workflow-common/README.md b/packages/internal-workflow-common/README.md similarity index 100% rename from packages/workflow-common/README.md rename to packages/internal-workflow-common/README.md diff --git a/packages/workflow-common/package.json b/packages/internal-workflow-common/package.json similarity index 100% rename from packages/workflow-common/package.json rename to packages/internal-workflow-common/package.json diff --git a/packages/workflow-common/src/activity-options.ts b/packages/internal-workflow-common/src/activity-options.ts similarity index 100% rename from packages/workflow-common/src/activity-options.ts rename to packages/internal-workflow-common/src/activity-options.ts diff --git a/packages/workflow-common/src/converter/data-converter.ts b/packages/internal-workflow-common/src/converter/data-converter.ts similarity index 100% rename from packages/workflow-common/src/converter/data-converter.ts rename to packages/internal-workflow-common/src/converter/data-converter.ts diff --git a/packages/workflow-common/src/converter/encoding.ts b/packages/internal-workflow-common/src/converter/encoding.ts similarity index 100% rename from packages/workflow-common/src/converter/encoding.ts rename to packages/internal-workflow-common/src/converter/encoding.ts diff --git a/packages/common/src/patch-protobuf-root.ts b/packages/internal-workflow-common/src/converter/patch-protobuf-root.ts similarity index 100% rename from packages/common/src/patch-protobuf-root.ts rename to packages/internal-workflow-common/src/converter/patch-protobuf-root.ts diff --git a/packages/workflow-common/src/converter/payload-codec.ts b/packages/internal-workflow-common/src/converter/payload-codec.ts similarity index 100% rename from packages/workflow-common/src/converter/payload-codec.ts rename to packages/internal-workflow-common/src/converter/payload-codec.ts diff --git a/packages/workflow-common/src/converter/payload-converter.ts b/packages/internal-workflow-common/src/converter/payload-converter.ts similarity index 100% rename from packages/workflow-common/src/converter/payload-converter.ts rename to packages/internal-workflow-common/src/converter/payload-converter.ts diff --git a/packages/workflow-common/src/converter/payload-converters.ts b/packages/internal-workflow-common/src/converter/payload-converters.ts similarity index 100% rename from packages/workflow-common/src/converter/payload-converters.ts rename to packages/internal-workflow-common/src/converter/payload-converters.ts diff --git a/packages/workflow-common/src/converter/protobufs.ts b/packages/internal-workflow-common/src/converter/protobufs.ts similarity index 100% rename from packages/workflow-common/src/converter/protobufs.ts rename to packages/internal-workflow-common/src/converter/protobufs.ts diff --git a/packages/workflow-common/src/converter/types.ts b/packages/internal-workflow-common/src/converter/types.ts similarity index 100% rename from packages/workflow-common/src/converter/types.ts rename to packages/internal-workflow-common/src/converter/types.ts diff --git a/packages/workflow-common/src/errors.ts b/packages/internal-workflow-common/src/errors.ts similarity index 100% rename from packages/workflow-common/src/errors.ts rename to packages/internal-workflow-common/src/errors.ts diff --git a/packages/workflow-common/src/failure.ts b/packages/internal-workflow-common/src/failure.ts similarity index 100% rename from packages/workflow-common/src/failure.ts rename to packages/internal-workflow-common/src/failure.ts diff --git a/packages/workflow-common/src/index.ts b/packages/internal-workflow-common/src/index.ts similarity index 92% rename from packages/workflow-common/src/index.ts rename to packages/internal-workflow-common/src/index.ts index 2294136b7..4d13838c5 100644 --- a/packages/workflow-common/src/index.ts +++ b/packages/internal-workflow-common/src/index.ts @@ -5,6 +5,7 @@ */ export * from './activity-options'; export * from './converter/data-converter'; +export * from './converter/patch-protobuf-root'; export * from './converter/payload-codec'; export * from './converter/payload-converter'; export * from './converter/payload-converters'; diff --git a/packages/workflow-common/src/interceptors.ts b/packages/internal-workflow-common/src/interceptors.ts similarity index 100% rename from packages/workflow-common/src/interceptors.ts rename to packages/internal-workflow-common/src/interceptors.ts diff --git a/packages/workflow-common/src/interfaces.ts b/packages/internal-workflow-common/src/interfaces.ts similarity index 100% rename from packages/workflow-common/src/interfaces.ts rename to packages/internal-workflow-common/src/interfaces.ts diff --git a/packages/workflow-common/src/retry-policy.ts b/packages/internal-workflow-common/src/retry-policy.ts similarity index 100% rename from packages/workflow-common/src/retry-policy.ts rename to packages/internal-workflow-common/src/retry-policy.ts diff --git a/packages/workflow-common/src/time.ts b/packages/internal-workflow-common/src/time.ts similarity index 100% rename from packages/workflow-common/src/time.ts rename to packages/internal-workflow-common/src/time.ts diff --git a/packages/workflow-common/src/type-helpers.ts b/packages/internal-workflow-common/src/type-helpers.ts similarity index 100% rename from packages/workflow-common/src/type-helpers.ts rename to packages/internal-workflow-common/src/type-helpers.ts diff --git a/packages/workflow-common/src/workflow-handle.ts b/packages/internal-workflow-common/src/workflow-handle.ts similarity index 100% rename from packages/workflow-common/src/workflow-handle.ts rename to packages/internal-workflow-common/src/workflow-handle.ts diff --git a/packages/workflow-common/src/workflow-options.ts b/packages/internal-workflow-common/src/workflow-options.ts similarity index 100% rename from packages/workflow-common/src/workflow-options.ts rename to packages/internal-workflow-common/src/workflow-options.ts diff --git a/packages/workflow-common/tsconfig.json b/packages/internal-workflow-common/tsconfig.json similarity index 100% rename from packages/workflow-common/tsconfig.json rename to packages/internal-workflow-common/tsconfig.json From 1b265a1ce699e467565f4ac0a21693868629ab65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Thu, 24 Feb 2022 17:38:30 -0500 Subject: [PATCH 22/33] feat!: Add common package and update references --- docs/protobuf-libraries.md | 2 +- package-lock.json | 1637 +++++++++-------- package.json | 3 +- packages/activity/package.json | 1 + packages/activity/src/index.ts | 8 +- packages/activity/tsconfig.json | 6 +- packages/client/package.json | 2 + .../client/src/async-completion-client.ts | 6 +- packages/client/src/connection.ts | 2 +- packages/client/src/index.ts | 24 +- packages/client/src/interceptors.ts | 2 +- packages/client/src/workflow-client.ts | 22 +- packages/client/src/workflow-options.ts | 8 +- packages/client/tsconfig.json | 6 +- packages/common/README.md | 11 + packages/common/package.json | 29 + .../src/converter/data-converter.ts | 0 .../src/converter/encoding.ts | 0 .../src/converter/patch-protobuf-root.ts | 2 +- .../src/converter/payload-codec.ts | 0 .../src/converter/payload-converter.ts | 2 +- .../src/converter/payload-converters.ts | 2 +- .../converter/protobuf-payload-converters.ts} | 25 +- .../src/converter/types.ts | 0 .../src/failure.ts | 2 +- packages/common/src/index.ts | 12 + packages/common/src/protobufs.ts | 5 + packages/common/tsconfig.json | 9 + .../src/client/index.ts | 2 +- .../src/worker/index.ts | 2 +- .../src/workflow/index.ts | 2 +- .../internal-non-workflow-common/README.md | 4 +- .../internal-non-workflow-common/package.json | 9 +- .../src/codec-helpers.ts | 2 +- .../src/data-converter-helpers.ts | 6 +- .../internal-non-workflow-common/src/index.ts | 9 +- .../internal-non-workflow-common/src/otel.ts | 4 +- .../tsconfig.json | 1 + packages/internal-workflow-common/README.md | 4 +- .../internal-workflow-common/package.json | 12 +- .../internal-workflow-common/src/index.ts | 9 +- .../src/retry-policy.ts | 2 +- packages/internal-workflow-common/src/time.ts | 2 +- .../src/workflow-options.ts | 5 +- packages/meta/tsconfig.json | 10 +- packages/test/package.json | 3 +- packages/test/protos/root.js | 2 +- packages/test/src/activities/index.ts | 7 +- packages/test/src/load/setup.ts | 2 +- packages/test/src/mock-native-worker.ts | 5 +- .../payload-converters/payload-converter.ts | 2 +- .../test/src/test-custom-data-converter.ts | 4 +- packages/test/src/test-data-converter.ts | 12 +- packages/test/src/test-integration.ts | 32 +- packages/test/src/test-patch-root.ts | 2 +- packages/test/src/test-retry-policy.ts | 3 +- packages/test/src/test-server-options.ts | 2 +- packages/test/src/test-time.ts | 2 +- packages/test/src/test-worker-activities.ts | 13 +- .../test/src/test-workflow-cancellation.ts | 10 +- packages/test/src/test-workflows.ts | 24 +- packages/test/tsconfig.json | 3 +- packages/worker/package.json | 2 + packages/worker/src/activity.ts | 13 +- packages/worker/src/core.ts | 17 +- packages/worker/src/interceptors.ts | 2 +- packages/worker/src/worker-options.ts | 7 +- packages/worker/src/worker.ts | 18 +- packages/worker/src/workflow-codec-runner.ts | 4 +- packages/worker/tsconfig.json | 9 +- packages/workflow/package.json | 2 +- packages/workflow/src/cancellation-scope.ts | 2 +- packages/workflow/src/errors.ts | 2 +- packages/workflow/src/index.ts | 40 +- packages/workflow/src/interceptors.ts | 3 +- packages/workflow/src/interfaces.ts | 4 +- packages/workflow/src/internals.ts | 26 +- packages/workflow/src/worker-interface.ts | 18 +- packages/workflow/src/workflow-handle.ts | 2 +- packages/workflow/src/workflow.ts | 25 +- packages/workflow/tsconfig.json | 6 +- 81 files changed, 1251 insertions(+), 992 deletions(-) create mode 100644 packages/common/README.md create mode 100644 packages/common/package.json rename packages/{internal-workflow-common => common}/src/converter/data-converter.ts (100%) rename packages/{internal-workflow-common => common}/src/converter/encoding.ts (100%) rename packages/{internal-workflow-common => common}/src/converter/patch-protobuf-root.ts (95%) rename packages/{internal-workflow-common => common}/src/converter/payload-codec.ts (100%) rename packages/{internal-workflow-common => common}/src/converter/payload-converter.ts (98%) rename packages/{internal-workflow-common => common}/src/converter/payload-converters.ts (96%) rename packages/{internal-workflow-common/src/converter/protobufs.ts => common/src/converter/protobuf-payload-converters.ts} (96%) rename packages/{internal-workflow-common => common}/src/converter/types.ts (100%) rename packages/{internal-workflow-common => common}/src/failure.ts (99%) create mode 100644 packages/common/src/index.ts create mode 100644 packages/common/src/protobufs.ts create mode 100644 packages/common/tsconfig.json diff --git a/docs/protobuf-libraries.md b/docs/protobuf-libraries.md index f1877106b..bc38aed2d 100644 --- a/docs/protobuf-libraries.md +++ b/docs/protobuf-libraries.md @@ -66,7 +66,7 @@ module.exports = patchProtobufRoot(unpatchedRoot); // pbjs -t static-module *.proto | pbts -o root.d.ts - // src/data-converter.ts -import { DefaultPayloadConverterWithProtobufs } from '@temporalio/common/lib/converter/protobufs'; +import { DefaultPayloadConverterWithProtobufs } from '@temporalio/common/lib/protobufs'; import root from '../protos/root'; export const dataConverter = new DefaultPayloadConverterWithProtobufs({ protobufRoot: root }); diff --git a/package-lock.json b/package-lock.json index 986ef0d86..9fab39948 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,8 @@ "@temporalio/common": "file:packages/common", "@temporalio/create": "file:packages/create-project", "@temporalio/interceptors-opentelemetry": "file:packages/interceptors-opentelemetry", + "@temporalio/internal-non-workflow-common": "file:packages/internal-non-workflow-common", + "@temporalio/internal-workflow-common": "file:packages/internal-workflow-common", "@temporalio/proto": "file:packages/proto", "@temporalio/test": "file:packages/test", "@temporalio/worker": "file:packages/worker", @@ -68,21 +70,21 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.15.7", + "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -435,15 +437,15 @@ } }, "node_modules/@gar/promisify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", - "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, "node_modules/@grpc/grpc-js": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.5.tgz", - "integrity": "sha512-A6cOzSu7dqXZ7rzvh/9JZf+Jg/MOpLEMP0IdT8pT8hrWJZ6TB4ydN/MRuqOtAugInJe/VQ9F8BPricUpYZSaZA==", + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.7.tgz", + "integrity": "sha512-RAlSbZ9LXo0wNoHKeUlwP9dtGgVBDUbnBKFpfAv5iSqMG4qWz9um2yLH215+Wow1I48etIa1QMS+WAGmsE/7HQ==", "dependencies": { "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" @@ -453,15 +455,15 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.7.tgz", - "integrity": "sha512-QzTPIyJxU0u+r2qGe8VMl3j/W2ryhEvBv7hc42OjYfthSj370fUrb7na65rG6w3YLZS/fb8p89iTBobfWGDgdw==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.9.tgz", + "integrity": "sha512-UlcCS8VbsU9d3XTXGiEVFonN7hXk+oMXZtoHHG2oSA1/GcDP1q6OUgs20PzHDGizzyi8ufGSUDlk3O2NyY7leg==", "dependencies": { "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", "long": "^4.0.0", "protobufjs": "^6.10.0", - "yargs": "^16.1.1" + "yargs": "^16.2.0" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" @@ -1802,16 +1804,13 @@ "dev": true }, "node_modules/@npmcli/fs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz", - "integrity": "sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", "dev": true, "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" } }, "node_modules/@npmcli/git": { @@ -2035,16 +2034,16 @@ } }, "node_modules/@octokit/request": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", - "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", "dev": true, "dependencies": { "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.1.0", "@octokit/types": "^6.16.1", "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" } }, @@ -2081,9 +2080,9 @@ } }, "node_modules/@opentelemetry/api": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.3.tgz", - "integrity": "sha512-puWxACExDe9nxbBB3lOymQFrLYml2dVOrd7USiVRnSbgXE+KwBu+HxFvxrzfqsiSda9IWsXJG1ef7C1O2/GmKQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.4.tgz", + "integrity": "sha512-BuJuXRSJNQ3QoKA6GWWDyuLpOUck+9hAXNMCnrloc1aWVoy6Xq6t9PUV08aBZ4Lutqq2LEHM486bpZqoViScog==", "engines": { "node": ">=8.0.0" } @@ -2130,6 +2129,7 @@ "version": "0.25.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-collector/-/exporter-collector-0.25.0.tgz", "integrity": "sha512-xZYstLt4hz1aTloJaepWdjMMf9305MqwqbUWjcU/X9pOxvgFWRlchO6x/HQTw7ow0i/S+ShzC+greKnb+1WvLA==", + "deprecated": "Please use trace and metric specific exporters @opentelemetry/exporter-trace-otlp-http and @opentelemetry/exporter-metrics-otlp-http", "dependencies": { "@opentelemetry/api-metrics": "0.25.0", "@opentelemetry/core": "0.25.0", @@ -2148,6 +2148,7 @@ "version": "0.25.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-collector-grpc/-/exporter-collector-grpc-0.25.0.tgz", "integrity": "sha512-Mqkdh89pC1NxX5BngxHmDqMQ6WVCFuMr1PvwRZmJBBR2MXaStO5qIxELHuHgkDZEXpIFJbqNC7JAfDklXm8o1w==", + "deprecated": "Please use trace and metric specific exporters @opentelemetry/exporter-trace-otlp-grpc and @opentelemetry/exporter-metrics-otlp-grpc", "dependencies": { "@grpc/grpc-js": "^1.3.7", "@grpc/proto-loader": "^0.6.4", @@ -2526,6 +2527,14 @@ "resolved": "packages/interceptors-opentelemetry", "link": true }, + "node_modules/@temporalio/internal-non-workflow-common": { + "resolved": "packages/internal-non-workflow-common", + "link": true + }, + "node_modules/@temporalio/internal-workflow-common": { + "resolved": "packages/internal-workflow-common", + "link": true + }, "node_modules/@temporalio/proto": { "resolved": "packages/proto", "link": true @@ -2552,9 +2561,9 @@ } }, "node_modules/@ts-morph/common": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.12.2.tgz", - "integrity": "sha512-m5KjptpIf1K0t0QL38uE+ol1n+aNn9MgRq++G3Zym1FlqfN+rThsXlp3cAgib14pIeXF7jk3UtJQOviwawFyYg==", + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.12.3.tgz", + "integrity": "sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w==", "dev": true, "dependencies": { "fast-glob": "^3.2.7", @@ -2604,27 +2613,27 @@ "dev": true }, "node_modules/@types/eslint": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz", - "integrity": "sha512-UP9rzNn/XyGwb5RQ2fok+DzcIRIYwc16qTXse5+Smsy8MOIccCChT15KAwnsgQx4PzJkaMq4myFyZ4CL5TjhIQ==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "node_modules/@types/eslint-scope": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.2.tgz", - "integrity": "sha512-TzgYCWoPiTeRg6RQYgtuW7iODtVoKu3RVL72k3WohqhjfaOLK5Mg2T4Tg1o2bSfu0vPkoI48wdQFv5b/Xe04wQ==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", "dependencies": { "@types/eslint": "*", "@types/estree": "*" } }, "node_modules/@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==" + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" }, "node_modules/@types/fs-extra": { "version": "9.0.13", @@ -2674,6 +2683,12 @@ "@types/node": "*" } }, + "node_modules/@types/lodash": { + "version": "4.14.178", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", + "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==", + "dev": true + }, "node_modules/@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", @@ -2692,9 +2707,9 @@ "dev": true }, "node_modules/@types/minipass": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-3.1.0.tgz", - "integrity": "sha512-b2yPKwCrB8x9SB65kcCistMoe3wrYnxxt5rJSZ1kprw0uOXvhuKi9kTQ746Y+Pbqoh+9C0N4zt0ztmTnG9yg7A==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-3.1.2.tgz", + "integrity": "sha512-foLGjgrJkUjLG/o2t2ymlZGEoBNBa/TfoUZ7oCTkOjP1T43UGBJspovJou/l3ZuHvye2ewR5cZNtp2zyWgILMA==", "dev": true, "dependencies": { "@types/node": "*" @@ -2707,14 +2722,14 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.14.tgz", - "integrity": "sha512-mK6BKLpL0bG6v2CxHbm0ed6RcZrAtTHBTd/ZpnlVPVa3HkumsqLE4BC4u6TQ8D7pnrRbOU0am6epuALs+Ncnzw==" + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==" }, "node_modules/@types/node-fetch": { - "version": "2.5.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz", - "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", "dev": true, "dependencies": { "@types/node": "*", @@ -2748,9 +2763,9 @@ "dev": true }, "node_modules/@types/pidusage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/pidusage/-/pidusage-2.0.1.tgz", - "integrity": "sha512-tYYcz/+5v/EGYT83C0pIXrJGOiVBLksQvxgJboG4nGqx/gZTvq0Ro4SkAjECqMk7L4Ww58VWB4j48qeYh4/YJg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/pidusage/-/pidusage-2.0.2.tgz", + "integrity": "sha512-lHgpGZjXDfjggZDLkgp4zQTYkvXq4S7RxjBjrDcPe1MBU72hESWxubutx8+AM4QkJdRxAhrQyxSA6pzHKJKlsQ==", "dev": true }, "node_modules/@types/prompts": { @@ -2760,9 +2775,9 @@ "dev": true }, "node_modules/@types/ramda": { - "version": "0.27.60", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.60.tgz", - "integrity": "sha512-6ie74xhtl2ducVG8cC8mnYwpvIUWHBoLHbmvEsl5qPqJkqVP9ce5yZW10WgheKd2ua1yPIwd0miMWBsG19UmiQ==", + "version": "0.27.65", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.65.tgz", + "integrity": "sha512-JVX1qgW9xxKUraEbiftgrREtf8A2m+2IAl32oh9pHCrNj0nwPRnOhuvghn1ELTCjTq66slejsePafMXjbHpPAA==", "dev": true, "dependencies": { "ts-toolbelt": "^6.15.1" @@ -2808,9 +2823,9 @@ "dev": true }, "node_modules/@types/uuid": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz", - "integrity": "sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, "node_modules/@types/validate-npm-package-name": { @@ -2959,6 +2974,113 @@ } } }, + "node_modules/@typescript-eslint/utils": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.12.1.tgz", + "integrity": "sha512-Qq9FIuU0EVEsi8fS6pG+uurbhNTtoYr4fq8tKjBupsK5Bgbk2I32UGm0Sh+WOyjOPgo/5URbxxSNV6HYsxV4MQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.12.1", + "@typescript-eslint/types": "5.12.1", + "@typescript-eslint/typescript-estree": "5.12.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.1.tgz", + "integrity": "sha512-J0Wrh5xS6XNkd4TkOosxdpObzlYfXjAFIm9QxYLCPOcHVv1FyyFCPom66uIh8uBr0sZCrtS+n19tzufhwab8ZQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.12.1", + "@typescript-eslint/visitor-keys": "5.12.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.1.tgz", + "integrity": "sha512-hfcbq4qVOHV1YRdhkDldhV9NpmmAu2vp6wuFODL71Y0Ixak+FLeEU4rnPxgmZMnGreGEghlEucs9UZn5KOfHJA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.1.tgz", + "integrity": "sha512-ahOdkIY9Mgbza7L9sIi205Pe1inCkZWAHE1TV1bpxlU4RZNPtXaDZfiiFWcL9jdxvW1hDYZJXrFm+vlMkXRbBw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.12.1", + "@typescript-eslint/visitor-keys": "5.12.1", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.1.tgz", + "integrity": "sha512-l1KSLfupuwrXx6wc0AuOmC7Ko5g14ZOQ86wJJqRbdLbXLK02pK/DPiDDqCc7BqqiiA04/eAA6ayL0bgOrAkH7A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.12.1", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", @@ -3135,9 +3257,9 @@ } }, "node_modules/acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "bin": { "acorn": "bin/acorn" }, @@ -3190,9 +3312,9 @@ } }, "node_modules/agentkeepalive": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", - "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", "dev": true, "dependencies": { "debug": "^4.1.0", @@ -3679,9 +3801,9 @@ } }, "node_modules/boxen/node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { "node": ">=10" @@ -3711,14 +3833,14 @@ } }, "node_modules/browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz", + "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==", "dependencies": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", + "caniuse-lite": "^1.0.30001312", + "electron-to-chromium": "^1.4.71", "escalade": "^3.1.1", - "node-releases": "^2.0.1", + "node-releases": "^2.0.2", "picocolors": "^1.0.0" }, "bin": { @@ -3877,6 +3999,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -3921,9 +4044,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001289", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001289.tgz", - "integrity": "sha512-hV6x4IfrYViN8cJbGFVbjD7KCrhS/O7wfDgvevYRanJ/IN+hhxpTcXXqaxy3CzPNFe5rlqdimdEB/k7H0YzxHg==", + "version": "1.0.30001312", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz", + "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==", "funding": { "type": "opencollective", "url": "https://opencollective.com/browserslist" @@ -4009,10 +4132,16 @@ "dev": true }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -4260,34 +4389,16 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "node_modules/columnify": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz", - "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", "dev": true, "dependencies": { - "strip-ansi": "^3.0.0", + "strip-ansi": "^6.0.1", "wcwidth": "^1.0.0" - } - }, - "node_modules/columnify/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/columnify/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0.0" } }, "node_modules/combined-stream": { @@ -4412,9 +4523,9 @@ } }, "node_modules/conventional-changelog-conventionalcommits": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.1.tgz", - "integrity": "sha512-lzWJpPZhbM1R0PIzkwzGBCnAkH5RKJzJfFQZcl/D+2lsJxAwGnDKBqn/F4C1RD31GJNn8NuKWQzAZDAVXPp2Mw==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz", + "integrity": "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==", "dev": true, "dependencies": { "compare-func": "^2.0.0", @@ -4559,14 +4670,14 @@ } }, "node_modules/conventional-changelog-writer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.0.tgz", - "integrity": "sha512-HnDh9QHLNWfL6E1uHz6krZEQOgm8hN7z/m7tT16xwd802fwgMN0Wqd7AQYVkhpsjDUx/99oo+nGgvKF657XP5g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", + "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", "dev": true, "dependencies": { "conventional-commits-filter": "^2.0.7", "dateformat": "^3.0.0", - "handlebars": "^4.7.6", + "handlebars": "^4.7.7", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.15", "meow": "^8.0.0", @@ -4604,9 +4715,9 @@ } }, "node_modules/conventional-commits-parser": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.3.tgz", - "integrity": "sha512-YyRDR7On9H07ICFpRm/igcdjIqebXbvf4Cff+Pf0BrBys1i1EOzx9iFXNlAbdrLAR8jf7bkUYkDAr8pEy0q4Pw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", + "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", "dev": true, "dependencies": { "is-text-path": "^1.0.1", @@ -4891,6 +5002,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, "dependencies": { "object-keys": "^1.0.12" }, @@ -5036,9 +5148,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.24", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz", - "integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg==" + "version": "1.4.72", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.72.tgz", + "integrity": "sha512-9LkRQwjW6/wnSfevR21a3k8sOJ+XWSH7kkzs9/EUenKmuDkndP3W9y1yCZpOxufwGbX3JV8glZZSDb4o95zwXQ==" }, "node_modules/emittery": { "version": "0.8.1", @@ -5089,9 +5201,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", + "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -5152,6 +5264,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -5160,6 +5273,7 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -5198,6 +5312,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -5297,9 +5412,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.4.0.tgz", + "integrity": "sha512-CFotdUcMY18nGRo5KGsnNxpznzhkopOcOo0InID+sgQssPrzjvsyKZPvOgymTFeHrFuC3Tzdf2YndhXtULK9Iw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -5324,17 +5439,12 @@ } }, "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/experimental-utils": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", - "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.12.1.tgz", + "integrity": "sha512-4bEa8WrS5DdzJq43smPH12ys4AOoCxVu2xjYGXQR4DnNyM8pqNzCr28zodf38Jc4bxWdniSEKKC1bQaccXGq5Q==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.7.0", - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/typescript-estree": "5.7.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@typescript-eslint/utils": "5.12.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -5344,90 +5454,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/scope-manager": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", - "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/visitor-keys": "5.7.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/types": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", - "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", - "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/visitor-keys": "5.7.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", - "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.7.0", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-deprecation/node_modules/eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/eslint-plugin-tsdoc": { @@ -5703,9 +5730,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -5715,7 +5742,7 @@ "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -5824,9 +5851,9 @@ } }, "node_modules/flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "node_modules/forever-agent": { @@ -5853,9 +5880,9 @@ } }, "node_modules/fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -5904,7 +5931,8 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "node_modules/functional-red-black-tree": { "version": "1.0.1", @@ -6022,6 +6050,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -6129,6 +6158,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -6150,9 +6180,9 @@ } }, "node_modules/git-raw-commits": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz", - "integrity": "sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", "dev": true, "dependencies": { "dargs": "^7.0.0", @@ -6292,9 +6322,9 @@ } }, "node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -6307,16 +6337,16 @@ } }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -6380,9 +6410,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, "node_modules/handlebars": { "version": "4.7.7", @@ -6441,6 +6471,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -6452,6 +6483,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6468,6 +6500,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -6479,6 +6512,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -6513,9 +6547,9 @@ } }, "node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6622,9 +6656,9 @@ ] }, "node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" @@ -6683,9 +6717,9 @@ } }, "node_modules/import-local": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", - "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "dependencies": { "pkg-dir": "^4.2.0", @@ -6696,6 +6730,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/imurmurhash": { @@ -6820,6 +6857,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, "dependencies": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -6847,12 +6885,14 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -6876,6 +6916,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -6891,6 +6932,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -6911,9 +6953,10 @@ } }, "node_modules/is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -6925,6 +6968,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7029,6 +7073,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -7060,6 +7105,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7125,6 +7171,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -7140,6 +7187,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7169,6 +7217,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7183,6 +7232,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -7227,6 +7277,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -7267,9 +7318,9 @@ "dev": true }, "node_modules/jest-worker": { - "version": "27.4.5", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", - "integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==", + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -7448,9 +7499,9 @@ } }, "node_modules/keyv": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", - "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", + "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", "dependencies": { "json-buffer": "3.0.1" } @@ -7732,8 +7783,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", @@ -7949,9 +7999,9 @@ } }, "node_modules/memfs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.0.tgz", - "integrity": "sha512-o/RfP0J1d03YwsAxyHxAYs2kyJp55AFkMazlFAZFR2I2IXkxiUTXRabJ6RmNNCQ83LAD2jy52Khj0m3OffpNdA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", "dependencies": { "fs-monkey": "1.0.3" }, @@ -7963,6 +8013,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true, "engines": { "node": ">= 0.10.0" } @@ -8157,9 +8208,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8378,9 +8429,9 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, "engines": { "node": ">= 0.6" @@ -8394,18 +8445,27 @@ "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node_modules/node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, "engines": { "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, "node_modules/node-fetch/node_modules/tr46": { @@ -8501,9 +8561,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" }, "node_modules/nopt": { "version": "4.0.3", @@ -8684,6 +8744,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "chalk": "^2.4.1", @@ -8708,6 +8769,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -8719,6 +8781,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -8732,6 +8795,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -8747,6 +8811,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -8755,6 +8820,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, "engines": { "node": ">=4" } @@ -8762,12 +8828,14 @@ "node_modules/npm-run-all/node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true }, "node_modules/npm-run-all/node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -8782,6 +8850,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -8793,6 +8862,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -8805,6 +8875,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, "engines": { "node": ">=4" } @@ -8813,6 +8884,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, "dependencies": { "pify": "^3.0.0" }, @@ -8824,6 +8896,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, "engines": { "node": ">=4" } @@ -8832,6 +8905,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, "dependencies": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", @@ -8845,6 +8919,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, "bin": { "semver": "bin/semver" } @@ -8853,6 +8928,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "dependencies": { "shebang-regex": "^1.0.0" }, @@ -8864,6 +8940,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8872,6 +8949,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, "engines": { "node": ">=4" } @@ -8880,6 +8958,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -8891,6 +8970,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -8950,9 +9030,10 @@ } }, "node_modules/object-inspect": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", - "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8961,6 +9042,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "engines": { "node": ">= 0.4" } @@ -8969,6 +9051,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", @@ -9702,7 +9785,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-type": { "version": "4.0.0", @@ -9725,9 +9809,9 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "engines": { "node": ">=8.6" }, @@ -9739,6 +9823,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, "bin": { "pidtree": "bin/pidtree.js" }, @@ -10115,9 +10200,9 @@ "dev": true }, "node_modules/proto3-json-serializer": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.6.tgz", - "integrity": "sha512-tGbV6m6Kad8NqxMh5hw87euPS0YoZSAOIfvR01zYkQV8Gpx1V/8yU/0gCKCvfCkhAJsjvzzhnnsdQxA1w7PSog==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.8.tgz", + "integrity": "sha512-ACilkB6s1U1gWnl5jtICpnDai4VCxmI9GFxuEaYdxtDG2oVI3sVFIUsvUZcQbJgtPM6p+zqKbjTKQZp6Y4FpQw==", "dependencies": { "protobufjs": "^6.11.2" } @@ -10199,9 +10284,9 @@ } }, "node_modules/qs": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz", - "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -10261,9 +10346,9 @@ } }, "node_modules/ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz", + "integrity": "sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==" }, "node_modules/randombytes": { "version": "2.1.0", @@ -10345,6 +10430,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", + "deprecated": "The functionality that this package provided is now in @npmcli/arborist", "dev": true, "dependencies": { "read-package-json": "^2.0.0", @@ -10751,9 +10837,9 @@ } }, "node_modules/request/node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true, "engines": { "node": ">=0.6" @@ -10791,6 +10877,7 @@ "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, "dependencies": { "is-core-module": "^2.1.0", "path-parse": "^1.0.6" @@ -10919,18 +11006,13 @@ } }, "node_modules/rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", + "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", "dependencies": { - "tslib": "~2.1.0" + "tslib": "^2.1.0" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -11071,7 +11153,8 @@ "node_modules/shell-quote": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true }, "node_modules/shimmer": { "version": "1.2.1", @@ -11083,6 +11166,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -11093,9 +11177,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "node_modules/sisteransi": { @@ -11179,13 +11263,13 @@ } }, "node_modules/socks": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", - "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", "dev": true, "dependencies": { "ip": "^1.1.5", - "smart-buffer": "^4.1.0" + "smart-buffer": "^4.2.0" }, "engines": { "node": ">= 10.13.0", @@ -11251,6 +11335,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -11259,12 +11344,14 @@ "node_modules/spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -11273,7 +11360,8 @@ "node_modules/spdx-license-ids": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==" + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true }, "node_modules/split": { "version": "1.0.1", @@ -11312,9 +11400,9 @@ "dev": true }, "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "dependencies": { "asn1": "~0.2.3", @@ -11424,6 +11512,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -11440,6 +11529,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -11452,6 +11542,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -11558,9 +11649,9 @@ } }, "node_modules/table": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.5.tgz", - "integrity": "sha512-LFNeryOqiQHqCVKzhkymKwt6ozeRhlm8IL1mE8rNUurkir4heF6PzMyRgaTa4tlyPTGGgXuvVOF/OLWiH09Lqw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -11574,9 +11665,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", + "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -11783,10 +11874,11 @@ "link": true }, "node_modules/terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.11.0.tgz", + "integrity": "sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==", "dependencies": { + "acorn": "^8.5.0", "commander": "^2.20.0", "source-map": "~0.7.2", "source-map-support": "~0.5.20" @@ -11796,22 +11888,14 @@ }, "engines": { "node": ">=10" - }, - "peerDependencies": { - "acorn": "^8.5.0" - }, - "peerDependenciesMeta": { - "acorn": { - "optional": true - } } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", - "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", + "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", "dependencies": { - "jest-worker": "^27.4.1", + "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", "source-map": "^0.6.1", @@ -11993,12 +12077,12 @@ } }, "node_modules/ts-morph": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-13.0.2.tgz", - "integrity": "sha512-SjeeHaRf/mFsNeR3KTJnx39JyEOzT4e+DX28gQx5zjzEOuFs2eGrqeN2PLKs/+AibSxPmzV7RD8nJVKmFJqtLA==", + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-13.0.3.tgz", + "integrity": "sha512-pSOfUMx8Ld/WUreoSzvMFQG5i9uEiWIsBYjpU9+TTASOeUa89j5HykomeqVULm1oqWtBdleI3KEFRLrlA3zGIw==", "dev": true, "dependencies": { - "@ts-morph/common": "~0.12.2", + "@ts-morph/common": "~0.12.3", "code-block-writer": "^11.0.0" } }, @@ -12069,8 +12153,7 @@ "node_modules/tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -12151,9 +12234,9 @@ } }, "node_modules/typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12163,9 +12246,9 @@ } }, "node_modules/uglify-js": { - "version": "3.14.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", - "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.1.tgz", + "integrity": "sha512-FAGKF12fWdkpvNJZENacOH0e/83eG6JyVQyanIJaBXCN1J11TUQv1T1/z8S+Z0CG0ZPk1nPcreF/c7lrTd0TEQ==", "dev": true, "optional": true, "bin": { @@ -12194,6 +12277,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, "dependencies": { "function-bind": "^1.1.1", "has-bigints": "^1.0.1", @@ -12357,6 +12441,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -12415,12 +12500,12 @@ } }, "node_modules/webpack": { - "version": "5.65.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", - "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", + "version": "5.69.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz", + "integrity": "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==", "dependencies": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", @@ -12433,7 +12518,7 @@ "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", + "graceful-fs": "^4.2.9", "json-parse-better-errors": "^1.0.2", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", @@ -12442,7 +12527,7 @@ "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", "watchpack": "^2.3.1", - "webpack-sources": "^3.2.2" + "webpack-sources": "^3.2.3" }, "bin": { "webpack": "bin/webpack.js" @@ -12461,9 +12546,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz", - "integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "engines": { "node": ">=10.13.0" } @@ -12509,6 +12594,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -12792,9 +12878,9 @@ } }, "node_modules/yargs": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", - "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", "dev": true, "dependencies": { "cliui": "^7.0.2", @@ -12848,8 +12934,7 @@ } }, "packages/activity": { - "name": "@temporalio/activity", - "version": "0.16.4", + "version": "0.17.2", "license": "MIT", "dependencies": { "@temporalio/common": "file:../common", @@ -12857,8 +12942,7 @@ } }, "packages/client": { - "name": "@temporalio/client", - "version": "0.16.4", + "version": "0.17.2", "license": "MIT", "dependencies": { "@grpc/grpc-js": "^1.3.7", @@ -12878,18 +12962,19 @@ } }, "packages/common": { - "name": "@temporalio/common", - "version": "0.16.4", + "version": "0.17.2", "license": "MIT", "dependencies": { - "@temporalio/proto": "file:../proto", - "ms": "^2.1.3", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", "proto3-json-serializer": "^0.1.6" + }, + "devDependencies": { + "@temporalio/proto": "file:../proto", + "protobufjs": "^6.11.2" } }, "packages/core-bridge": { - "name": "@temporalio/core-bridge", - "version": "0.16.4", + "version": "0.17.2", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -12901,8 +12986,7 @@ } }, "packages/create-project": { - "name": "@temporalio/create", - "version": "0.16.3", + "version": "0.17.0", "license": "MIT", "dependencies": { "async-retry": "^1.3.3", @@ -12986,8 +13070,7 @@ } }, "packages/interceptors-opentelemetry": { - "name": "@temporalio/interceptors-opentelemetry", - "version": "0.16.4", + "version": "0.17.2", "license": "MIT", "dependencies": { "@opentelemetry/api": "^1.0.3", @@ -13053,9 +13136,30 @@ "node": ">=8.0.0" } }, + "packages/internal-non-workflow-common": { + "version": "0.17.2", + "license": "MIT", + "dependencies": { + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", + "lodash": "^4.17.21" + }, + "devDependencies": { + "@types/lodash": "^4.14.178" + } + }, + "packages/internal-workflow-common": { + "version": "0.17.2", + "license": "MIT", + "dependencies": { + "long": "^4.0.0", + "ms": "^2.1.3" + }, + "devDependencies": { + "@temporalio/proto": "file:../proto" + } + }, "packages/meta": { - "name": "temporalio", - "version": "0.16.4", + "version": "0.17.2", "license": "MIT", "dependencies": { "@temporalio/activity": "file:../activity", @@ -13070,8 +13174,7 @@ } }, "packages/proto": { - "name": "@temporalio/proto", - "version": "0.16.0", + "version": "0.17.2", "license": "MIT", "dependencies": { "@types/long": "^4.0.1", @@ -13083,8 +13186,7 @@ } }, "packages/test": { - "name": "@temporalio/test", - "version": "0.16.4", + "version": "0.17.2", "license": "MIT", "dependencies": { "@opentelemetry/exporter-collector-grpc": "^0.25.0", @@ -13092,19 +13194,19 @@ "@temporalio/client": "file:../client", "@temporalio/common": "file:../common", "@temporalio/interceptors-opentelemetry": "file:../interceptors-opentelemetry", + "@temporalio/internal-non-workflow-common": "file:../internal-non-workflow-common", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", "@temporalio/proto": "file:../proto", "@temporalio/worker": "file:../worker", "@temporalio/workflow": "file:../workflow", "@types/async-retry": "^1.3.3", "async-retry": "^1.3.3", - "npm-run-all": "^4.1.5", "protobufjs": "^6.11.2", "ramda": "^0.27.1" } }, "packages/worker": { - "name": "@temporalio/worker", - "version": "0.16.4", + "version": "0.17.2", "license": "MIT", "dependencies": { "@opentelemetry/api": "^1.0.3", @@ -13138,11 +13240,13 @@ } }, "packages/workflow": { - "name": "@temporalio/workflow", - "version": "0.16.4", + "version": "0.17.2", "license": "MIT", "dependencies": { "@temporalio/common": "file:../common", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common" + }, + "devDependencies": { "@temporalio/proto": "file:../proto" } } @@ -13158,18 +13262,18 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", "dev": true }, "@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.15.7", + "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -13441,30 +13545,30 @@ } }, "@gar/promisify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", - "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, "@grpc/grpc-js": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.5.tgz", - "integrity": "sha512-A6cOzSu7dqXZ7rzvh/9JZf+Jg/MOpLEMP0IdT8pT8hrWJZ6TB4ydN/MRuqOtAugInJe/VQ9F8BPricUpYZSaZA==", + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.7.tgz", + "integrity": "sha512-RAlSbZ9LXo0wNoHKeUlwP9dtGgVBDUbnBKFpfAv5iSqMG4qWz9um2yLH215+Wow1I48etIa1QMS+WAGmsE/7HQ==", "requires": { "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" } }, "@grpc/proto-loader": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.7.tgz", - "integrity": "sha512-QzTPIyJxU0u+r2qGe8VMl3j/W2ryhEvBv7hc42OjYfthSj370fUrb7na65rG6w3YLZS/fb8p89iTBobfWGDgdw==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.9.tgz", + "integrity": "sha512-UlcCS8VbsU9d3XTXGiEVFonN7hXk+oMXZtoHHG2oSA1/GcDP1q6OUgs20PzHDGizzyi8ufGSUDlk3O2NyY7leg==", "requires": { "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", "long": "^4.0.0", "protobufjs": "^6.10.0", - "yargs": "^16.1.1" + "yargs": "^16.2.0" }, "dependencies": { "yargs": { @@ -14584,9 +14688,9 @@ "dev": true }, "@npmcli/fs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz", - "integrity": "sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", "dev": true, "requires": { "@gar/promisify": "^1.0.1", @@ -14784,16 +14888,16 @@ } }, "@octokit/request": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", - "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", "dev": true, "requires": { "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.1.0", "@octokit/types": "^6.16.1", "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" } }, @@ -14830,9 +14934,9 @@ } }, "@opentelemetry/api": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.3.tgz", - "integrity": "sha512-puWxACExDe9nxbBB3lOymQFrLYml2dVOrd7USiVRnSbgXE+KwBu+HxFvxrzfqsiSda9IWsXJG1ef7C1O2/GmKQ==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.4.tgz", + "integrity": "sha512-BuJuXRSJNQ3QoKA6GWWDyuLpOUck+9hAXNMCnrloc1aWVoy6Xq6t9PUV08aBZ4Lutqq2LEHM486bpZqoViScog==" }, "@opentelemetry/api-metrics": { "version": "0.25.0", @@ -15153,9 +15257,10 @@ "@temporalio/common": { "version": "file:packages/common", "requires": { + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", "@temporalio/proto": "file:../proto", - "ms": "^2.1.3", - "proto3-json-serializer": "^0.1.6" + "proto3-json-serializer": "^0.1.6", + "protobufjs": "^6.11.2" } }, "@temporalio/core-bridge": { @@ -15277,6 +15382,22 @@ } } }, + "@temporalio/internal-non-workflow-common": { + "version": "file:packages/internal-non-workflow-common", + "requires": { + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", + "@types/lodash": "^4.14.178", + "lodash": "^4.17.21" + } + }, + "@temporalio/internal-workflow-common": { + "version": "file:packages/internal-workflow-common", + "requires": { + "@temporalio/proto": "file:../proto", + "long": "^4.0.0", + "ms": "^2.1.3" + } + }, "@temporalio/proto": { "version": "file:packages/proto", "requires": { @@ -15294,12 +15415,13 @@ "@temporalio/client": "file:../client", "@temporalio/common": "file:../common", "@temporalio/interceptors-opentelemetry": "file:../interceptors-opentelemetry", + "@temporalio/internal-non-workflow-common": "file:../internal-non-workflow-common", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", "@temporalio/proto": "file:../proto", "@temporalio/worker": "file:../worker", "@temporalio/workflow": "file:../workflow", "@types/async-retry": "^1.3.3", "async-retry": "^1.3.3", - "npm-run-all": "^4.1.5", "protobufjs": "^6.11.2", "ramda": "^0.27.1" } @@ -15340,6 +15462,7 @@ "version": "file:packages/workflow", "requires": { "@temporalio/common": "file:../common", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", "@temporalio/proto": "file:../proto" } }, @@ -15350,9 +15473,9 @@ "dev": true }, "@ts-morph/common": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.12.2.tgz", - "integrity": "sha512-m5KjptpIf1K0t0QL38uE+ol1n+aNn9MgRq++G3Zym1FlqfN+rThsXlp3cAgib14pIeXF7jk3UtJQOviwawFyYg==", + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.12.3.tgz", + "integrity": "sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w==", "dev": true, "requires": { "fast-glob": "^3.2.7", @@ -15402,27 +15525,27 @@ "dev": true }, "@types/eslint": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz", - "integrity": "sha512-UP9rzNn/XyGwb5RQ2fok+DzcIRIYwc16qTXse5+Smsy8MOIccCChT15KAwnsgQx4PzJkaMq4myFyZ4CL5TjhIQ==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", "requires": { "@types/estree": "*", "@types/json-schema": "*" } }, "@types/eslint-scope": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.2.tgz", - "integrity": "sha512-TzgYCWoPiTeRg6RQYgtuW7iODtVoKu3RVL72k3WohqhjfaOLK5Mg2T4Tg1o2bSfu0vPkoI48wdQFv5b/Xe04wQ==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", "requires": { "@types/eslint": "*", "@types/estree": "*" } }, "@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==" + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" }, "@types/fs-extra": { "version": "9.0.13", @@ -15472,6 +15595,12 @@ "@types/node": "*" } }, + "@types/lodash": { + "version": "4.14.178", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", + "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==", + "dev": true + }, "@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", @@ -15490,9 +15619,9 @@ "dev": true }, "@types/minipass": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-3.1.0.tgz", - "integrity": "sha512-b2yPKwCrB8x9SB65kcCistMoe3wrYnxxt5rJSZ1kprw0uOXvhuKi9kTQ746Y+Pbqoh+9C0N4zt0ztmTnG9yg7A==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-3.1.2.tgz", + "integrity": "sha512-foLGjgrJkUjLG/o2t2ymlZGEoBNBa/TfoUZ7oCTkOjP1T43UGBJspovJou/l3ZuHvye2ewR5cZNtp2zyWgILMA==", "dev": true, "requires": { "@types/node": "*" @@ -15505,14 +15634,14 @@ "dev": true }, "@types/node": { - "version": "16.11.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.14.tgz", - "integrity": "sha512-mK6BKLpL0bG6v2CxHbm0ed6RcZrAtTHBTd/ZpnlVPVa3HkumsqLE4BC4u6TQ8D7pnrRbOU0am6epuALs+Ncnzw==" + "version": "16.11.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", + "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==" }, "@types/node-fetch": { - "version": "2.5.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz", - "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", "dev": true, "requires": { "@types/node": "*", @@ -15545,9 +15674,9 @@ "dev": true }, "@types/pidusage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/pidusage/-/pidusage-2.0.1.tgz", - "integrity": "sha512-tYYcz/+5v/EGYT83C0pIXrJGOiVBLksQvxgJboG4nGqx/gZTvq0Ro4SkAjECqMk7L4Ww58VWB4j48qeYh4/YJg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/pidusage/-/pidusage-2.0.2.tgz", + "integrity": "sha512-lHgpGZjXDfjggZDLkgp4zQTYkvXq4S7RxjBjrDcPe1MBU72hESWxubutx8+AM4QkJdRxAhrQyxSA6pzHKJKlsQ==", "dev": true }, "@types/prompts": { @@ -15557,9 +15686,9 @@ "dev": true }, "@types/ramda": { - "version": "0.27.60", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.60.tgz", - "integrity": "sha512-6ie74xhtl2ducVG8cC8mnYwpvIUWHBoLHbmvEsl5qPqJkqVP9ce5yZW10WgheKd2ua1yPIwd0miMWBsG19UmiQ==", + "version": "0.27.65", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.65.tgz", + "integrity": "sha512-JVX1qgW9xxKUraEbiftgrREtf8A2m+2IAl32oh9pHCrNj0nwPRnOhuvghn1ELTCjTq66slejsePafMXjbHpPAA==", "dev": true, "requires": { "ts-toolbelt": "^6.15.1" @@ -15605,9 +15734,9 @@ "dev": true }, "@types/uuid": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz", - "integrity": "sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, "@types/validate-npm-package-name": { @@ -15689,6 +15818,69 @@ "tsutils": "^3.21.0" } }, + "@typescript-eslint/utils": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.12.1.tgz", + "integrity": "sha512-Qq9FIuU0EVEsi8fS6pG+uurbhNTtoYr4fq8tKjBupsK5Bgbk2I32UGm0Sh+WOyjOPgo/5URbxxSNV6HYsxV4MQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.12.1", + "@typescript-eslint/types": "5.12.1", + "@typescript-eslint/typescript-estree": "5.12.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.12.1.tgz", + "integrity": "sha512-J0Wrh5xS6XNkd4TkOosxdpObzlYfXjAFIm9QxYLCPOcHVv1FyyFCPom66uIh8uBr0sZCrtS+n19tzufhwab8ZQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.1", + "@typescript-eslint/visitor-keys": "5.12.1" + } + }, + "@typescript-eslint/types": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.12.1.tgz", + "integrity": "sha512-hfcbq4qVOHV1YRdhkDldhV9NpmmAu2vp6wuFODL71Y0Ixak+FLeEU4rnPxgmZMnGreGEghlEucs9UZn5KOfHJA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.1.tgz", + "integrity": "sha512-ahOdkIY9Mgbza7L9sIi205Pe1inCkZWAHE1TV1bpxlU4RZNPtXaDZfiiFWcL9jdxvW1hDYZJXrFm+vlMkXRbBw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.1", + "@typescript-eslint/visitor-keys": "5.12.1", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.1.tgz", + "integrity": "sha512-l1KSLfupuwrXx6wc0AuOmC7Ko5g14ZOQ86wJJqRbdLbXLK02pK/DPiDDqCc7BqqiiA04/eAA6ayL0bgOrAkH7A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.12.1", + "eslint-visitor-keys": "^3.0.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + } + } + }, "@typescript-eslint/visitor-keys": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", @@ -15855,9 +16047,9 @@ } }, "acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==" + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" }, "acorn-import-assertions": { "version": "1.8.0", @@ -15894,9 +16086,9 @@ } }, "agentkeepalive": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", - "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", "dev": true, "requires": { "debug": "^4.1.0", @@ -16288,9 +16480,9 @@ }, "dependencies": { "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true } } @@ -16313,14 +16505,14 @@ } }, "browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz", + "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==", "requires": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", + "caniuse-lite": "^1.0.30001312", + "electron-to-chromium": "^1.4.71", "escalade": "^3.1.1", - "node-releases": "^2.0.1", + "node-releases": "^2.0.2", "picocolors": "^1.0.0" } }, @@ -16435,6 +16627,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -16464,9 +16657,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001289", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001289.tgz", - "integrity": "sha512-hV6x4IfrYViN8cJbGFVbjD7KCrhS/O7wfDgvevYRanJ/IN+hhxpTcXXqaxy3CzPNFe5rlqdimdEB/k7H0YzxHg==" + "version": "1.0.30001312", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz", + "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==" }, "cargo-cp-artifact": { "version": "0.1.6", @@ -16526,9 +16719,9 @@ "dev": true }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -16719,30 +16912,13 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "columnify": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz", - "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", "dev": true, "requires": { - "strip-ansi": "^3.0.0", + "strip-ansi": "^6.0.1", "wcwidth": "^1.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } } }, "combined-stream": { @@ -16849,9 +17025,9 @@ } }, "conventional-changelog-conventionalcommits": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.1.tgz", - "integrity": "sha512-lzWJpPZhbM1R0PIzkwzGBCnAkH5RKJzJfFQZcl/D+2lsJxAwGnDKBqn/F4C1RD31GJNn8NuKWQzAZDAVXPp2Mw==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz", + "integrity": "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==", "dev": true, "requires": { "compare-func": "^2.0.0", @@ -16970,14 +17146,14 @@ "dev": true }, "conventional-changelog-writer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.0.tgz", - "integrity": "sha512-HnDh9QHLNWfL6E1uHz6krZEQOgm8hN7z/m7tT16xwd802fwgMN0Wqd7AQYVkhpsjDUx/99oo+nGgvKF657XP5g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", + "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", "dev": true, "requires": { "conventional-commits-filter": "^2.0.7", "dateformat": "^3.0.0", - "handlebars": "^4.7.6", + "handlebars": "^4.7.7", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.15", "meow": "^8.0.0", @@ -17005,9 +17181,9 @@ } }, "conventional-commits-parser": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.3.tgz", - "integrity": "sha512-YyRDR7On9H07ICFpRm/igcdjIqebXbvf4Cff+Pf0BrBys1i1EOzx9iFXNlAbdrLAR8jf7bkUYkDAr8pEy0q4Pw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", + "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", "dev": true, "requires": { "is-text-path": "^1.0.1", @@ -17225,6 +17401,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -17340,9 +17517,9 @@ } }, "electron-to-chromium": { - "version": "1.4.24", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.24.tgz", - "integrity": "sha512-erwx5r69B/WFfFuF2jcNN0817BfDBdC4765kQ6WltOMuwsimlQo3JTEq0Cle+wpHralwdeX3OfAtw/mHxPK0Wg==" + "version": "1.4.72", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.72.tgz", + "integrity": "sha512-9LkRQwjW6/wnSfevR21a3k8sOJ+XWSH7kkzs9/EUenKmuDkndP3W9y1yCZpOxufwGbX3JV8glZZSDb4o95zwXQ==" }, "emittery": { "version": "0.8.1", @@ -17386,9 +17563,9 @@ } }, "enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", + "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -17431,6 +17608,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -17439,6 +17617,7 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -17471,6 +17650,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -17568,9 +17748,9 @@ } }, "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.4.0.tgz", + "integrity": "sha512-CFotdUcMY18nGRo5KGsnNxpznzhkopOcOo0InID+sgQssPrzjvsyKZPvOgymTFeHrFuC3Tzdf2YndhXtULK9Iw==", "dev": true, "requires": {} }, @@ -17586,65 +17766,13 @@ }, "dependencies": { "@typescript-eslint/experimental-utils": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz", - "integrity": "sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.7.0", - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/typescript-estree": "5.7.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz", - "integrity": "sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/visitor-keys": "5.7.0" - } - }, - "@typescript-eslint/types": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.7.0.tgz", - "integrity": "sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz", - "integrity": "sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.7.0", - "@typescript-eslint/visitor-keys": "5.7.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz", - "integrity": "sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg==", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.12.1.tgz", + "integrity": "sha512-4bEa8WrS5DdzJq43smPH12ys4AOoCxVu2xjYGXQR4DnNyM8pqNzCr28zodf38Jc4bxWdniSEKKC1bQaccXGq5Q==", "dev": true, "requires": { - "@typescript-eslint/types": "5.7.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/utils": "5.12.1" } - }, - "eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", - "dev": true } } }, @@ -17824,9 +17952,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -17917,9 +18045,9 @@ } }, "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "forever-agent": { @@ -17940,9 +18068,9 @@ } }, "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -17978,7 +18106,8 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -18077,6 +18206,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -18162,6 +18292,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, "requires": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -18177,9 +18308,9 @@ } }, "git-raw-commits": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.10.tgz", - "integrity": "sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", "dev": true, "requires": { "dargs": "^7.0.0", @@ -18290,25 +18421,25 @@ } }, "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" } }, @@ -18350,9 +18481,9 @@ } }, "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" }, "handlebars": { "version": "4.7.7", @@ -18393,6 +18524,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -18400,7 +18532,8 @@ "has-bigints": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true }, "has-flag": { "version": "4.0.0", @@ -18410,12 +18543,14 @@ "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true }, "has-tostringtag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -18438,9 +18573,9 @@ "integrity": "sha512-xQxyJg7VcgveAZtY0eAu7iCn+2VCqLBkQoz7G4RnOIEsmP82J/LnRdr0I2Mi/pThkP5tviL8yFq5utE3yOPYpQ==" }, "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -18514,9 +18649,9 @@ "dev": true }, "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "ignore-by-default": { @@ -18559,9 +18694,9 @@ "dev": true }, "import-local": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", - "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "requires": { "pkg-dir": "^4.2.0", @@ -18676,6 +18811,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, "requires": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -18697,12 +18833,14 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, "requires": { "has-bigints": "^1.0.1" } @@ -18720,6 +18858,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -18728,7 +18867,8 @@ "is-callable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true }, "is-ci": { "version": "2.0.0", @@ -18740,9 +18880,10 @@ } }, "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, "requires": { "has": "^1.0.3" } @@ -18751,6 +18892,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -18823,7 +18965,8 @@ "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true }, "is-npm": { "version": "5.0.0", @@ -18840,6 +18983,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -18884,6 +19028,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -18892,7 +19037,8 @@ "is-shared-array-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true }, "is-ssh": { "version": "1.3.3", @@ -18913,6 +19059,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -18921,6 +19068,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -18950,6 +19098,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, "requires": { "call-bind": "^1.0.2" } @@ -18984,9 +19133,9 @@ "dev": true }, "jest-worker": { - "version": "27.4.5", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", - "integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==", + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "requires": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -19132,9 +19281,9 @@ } }, "keyv": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", - "integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz", + "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==", "requires": { "json-buffer": "3.0.1" } @@ -19365,8 +19514,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash._reinterpolate": { "version": "3.0.0", @@ -19539,9 +19687,9 @@ } }, "memfs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.0.tgz", - "integrity": "sha512-o/RfP0J1d03YwsAxyHxAYs2kyJp55AFkMazlFAZFR2I2IXkxiUTXRabJ6RmNNCQ83LAD2jy52Khj0m3OffpNdA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", "requires": { "fs-monkey": "1.0.3" } @@ -19549,7 +19697,8 @@ "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true }, "meow": { "version": "8.1.2", @@ -19685,9 +19834,9 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } @@ -19858,9 +20007,9 @@ "dev": true }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, "neo-async": { @@ -19871,12 +20020,13 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dev": true, "requires": { "whatwg-url": "^5.0.0" @@ -19961,9 +20111,9 @@ } }, "node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" }, "nopt": { "version": "4.0.3", @@ -20110,6 +20260,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "chalk": "^2.4.1", @@ -20126,6 +20277,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -20134,6 +20286,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -20144,6 +20297,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "requires": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -20155,22 +20309,26 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -20182,6 +20340,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -20193,6 +20352,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, "requires": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -20201,12 +20361,14 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, "requires": { "pify": "^3.0.0" } @@ -20214,12 +20376,14 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, "requires": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", @@ -20229,12 +20393,14 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -20242,17 +20408,20 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -20261,6 +20430,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -20307,19 +20477,22 @@ "dev": true }, "object-inspect": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", - "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==" + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true }, "object.assign": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", @@ -20873,7 +21046,8 @@ "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "path-type": { "version": "4.0.0", @@ -20893,14 +21067,15 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "pidtree": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==" + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true }, "pidusage": { "version": "2.0.21", @@ -21166,9 +21341,9 @@ "dev": true }, "proto3-json-serializer": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.6.tgz", - "integrity": "sha512-tGbV6m6Kad8NqxMh5hw87euPS0YoZSAOIfvR01zYkQV8Gpx1V/8yU/0gCKCvfCkhAJsjvzzhnnsdQxA1w7PSog==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.8.tgz", + "integrity": "sha512-ACilkB6s1U1gWnl5jtICpnDai4VCxmI9GFxuEaYdxtDG2oVI3sVFIUsvUZcQbJgtPM6p+zqKbjTKQZp6Y4FpQw==", "requires": { "protobufjs": "^6.11.2" } @@ -21235,9 +21410,9 @@ "dev": true }, "qs": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.2.tgz", - "integrity": "sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", "dev": true, "requires": { "side-channel": "^1.0.4" @@ -21268,9 +21443,9 @@ "dev": true }, "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz", + "integrity": "sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==" }, "randombytes": { "version": "2.1.0", @@ -21672,9 +21847,9 @@ } }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true } } @@ -21705,6 +21880,7 @@ "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, "requires": { "is-core-module": "^2.1.0", "path-parse": "^1.0.6" @@ -21788,18 +21964,11 @@ } }, "rxjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", - "integrity": "sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", + "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", "requires": { - "tslib": "~2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - } + "tslib": "^2.1.0" } }, "safe-buffer": { @@ -21906,7 +22075,8 @@ "shell-quote": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", - "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true }, "shimmer": { "version": "1.2.1", @@ -21918,6 +22088,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -21925,9 +22096,9 @@ } }, "signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "sisteransi": { @@ -21991,13 +22162,13 @@ "dev": true }, "socks": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", - "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", "dev": true, "requires": { "ip": "^1.1.5", - "smart-buffer": "^4.1.0" + "smart-buffer": "^4.2.0" } }, "socks-proxy-agent": { @@ -22046,6 +22217,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -22054,12 +22226,14 @@ "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -22068,7 +22242,8 @@ "spdx-license-ids": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==" + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true }, "split": { "version": "1.0.1", @@ -22101,9 +22276,9 @@ "dev": true }, "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -22180,6 +22355,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", @@ -22190,6 +22366,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -22199,6 +22376,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -22272,9 +22450,9 @@ } }, "table": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.5.tgz", - "integrity": "sha512-LFNeryOqiQHqCVKzhkymKwt6ozeRhlm8IL1mE8rNUurkir4heF6PzMyRgaTa4tlyPTGGgXuvVOF/OLWiH09Lqw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -22285,9 +22463,9 @@ }, "dependencies": { "ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", + "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -22453,10 +22631,11 @@ } }, "terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.11.0.tgz", + "integrity": "sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==", "requires": { + "acorn": "^8.5.0", "commander": "^2.20.0", "source-map": "~0.7.2", "source-map-support": "~0.5.20" @@ -22475,11 +22654,11 @@ } }, "terser-webpack-plugin": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", - "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", + "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", "requires": { - "jest-worker": "^27.4.1", + "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", "source-map": "^0.6.1", @@ -22590,12 +22769,12 @@ } }, "ts-morph": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-13.0.2.tgz", - "integrity": "sha512-SjeeHaRf/mFsNeR3KTJnx39JyEOzT4e+DX28gQx5zjzEOuFs2eGrqeN2PLKs/+AibSxPmzV7RD8nJVKmFJqtLA==", + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-13.0.3.tgz", + "integrity": "sha512-pSOfUMx8Ld/WUreoSzvMFQG5i9uEiWIsBYjpU9+TTASOeUa89j5HykomeqVULm1oqWtBdleI3KEFRLrlA3zGIw==", "dev": true, "requires": { - "@ts-morph/common": "~0.12.2", + "@ts-morph/common": "~0.12.3", "code-block-writer": "^11.0.0" } }, @@ -22652,8 +22831,7 @@ "tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "tsutils": { "version": "3.21.0", @@ -22718,14 +22896,14 @@ } }, "typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==" + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==" }, "uglify-js": { - "version": "3.14.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.5.tgz", - "integrity": "sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.1.tgz", + "integrity": "sha512-FAGKF12fWdkpvNJZENacOH0e/83eG6JyVQyanIJaBXCN1J11TUQv1T1/z8S+Z0CG0ZPk1nPcreF/c7lrTd0TEQ==", "dev": true, "optional": true }, @@ -22745,6 +22923,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, "requires": { "function-bind": "^1.1.1", "has-bigints": "^1.0.1", @@ -22882,6 +23061,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -22931,12 +23111,12 @@ "dev": true }, "webpack": { - "version": "5.65.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", - "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", + "version": "5.69.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz", + "integrity": "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==", "requires": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", "@webassemblyjs/ast": "1.11.1", "@webassemblyjs/wasm-edit": "1.11.1", "@webassemblyjs/wasm-parser": "1.11.1", @@ -22949,7 +23129,7 @@ "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", + "graceful-fs": "^4.2.9", "json-parse-better-errors": "^1.0.2", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", @@ -22958,13 +23138,13 @@ "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", "watchpack": "^2.3.1", - "webpack-sources": "^3.2.2" + "webpack-sources": "^3.2.3" } }, "webpack-sources": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.2.tgz", - "integrity": "sha512-cp5qdmHnu5T8wRg2G3vZZHoJPN14aqQ89SyQ11NpGH5zEMDCclt49rzo+MaRazk7/UeILhAI+/sEtcM+7Fr0nw==" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" }, "well-known-symbols": { "version": "2.0.0", @@ -22995,6 +23175,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, "requires": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -23215,9 +23396,9 @@ "dev": true }, "yargs": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", - "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", "dev": true, "requires": { "cliui": "^7.0.2", diff --git a/package.json b/package.json index aa35e668e..a40f2b2c4 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,8 @@ "dependencies": { "@temporalio/client": "file:packages/client", "@temporalio/common": "file:packages/common", - "@temporalio/workflow-common": "file:packages/workflow-common", + "@temporalio/internal-workflow-common": "file:packages/internal-workflow-common", + "@temporalio/internal-non-workflow-common": "file:packages/internal-non-workflow-common", "@temporalio/create": "file:packages/create-project", "@temporalio/interceptors-opentelemetry": "file:packages/interceptors-opentelemetry", "@temporalio/proto": "file:packages/proto", diff --git a/packages/activity/package.json b/packages/activity/package.json index 4a9bd7e13..794c797f7 100644 --- a/packages/activity/package.json +++ b/packages/activity/package.json @@ -14,6 +14,7 @@ "license": "MIT", "dependencies": { "@temporalio/common": "file:../common", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", "abort-controller": "^3.0.0" }, "bugs": { diff --git a/packages/activity/src/index.ts b/packages/activity/src/index.ts index 5b8b6b75e..2852f365e 100644 --- a/packages/activity/src/index.ts +++ b/packages/activity/src/index.ts @@ -26,11 +26,11 @@ * @module */ -import { AsyncLocalStorage } from 'async_hooks'; +import { msToNumber } from '@temporalio/internal-workflow-common'; import { AbortSignal } from 'abort-controller'; -import { msToNumber } from '@temporalio/common'; - -export { ActivityFunction, ActivityInterface, CancelledFailure } from '@temporalio/common'; +import { AsyncLocalStorage } from 'async_hooks'; +export { CancelledFailure } from '@temporalio/common'; +export { ActivityFunction, ActivityInterface } from '@temporalio/internal-workflow-common'; /** * Throw this error from an Activity in order to make the Worker diff --git a/packages/activity/tsconfig.json b/packages/activity/tsconfig.json index 1a63b5edd..071c9877b 100644 --- a/packages/activity/tsconfig.json +++ b/packages/activity/tsconfig.json @@ -4,10 +4,6 @@ "outDir": "./lib", "rootDir": "./src" }, - "references": [ - { - "path": "../common/tsconfig.json" - } - ], + "references": [{ "path": "../common" }, { "path": "../internal-workflow-common" }], "include": ["./src/**/*.ts"] } diff --git a/packages/client/package.json b/packages/client/package.json index 16605744c..acb949a37 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -15,6 +15,8 @@ "dependencies": { "@grpc/grpc-js": "^1.3.7", "@temporalio/common": "file:../common", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", + "@temporalio/internal-non-workflow-common": "file:../internal-non-workflow-common", "@temporalio/proto": "file:../proto", "ms": "^2.1.3", "protobufjs": "^6.11.2", diff --git a/packages/client/src/async-completion-client.ts b/packages/client/src/async-completion-client.ts index b4ebe8174..dc9f8cd19 100644 --- a/packages/client/src/async-completion-client.ts +++ b/packages/client/src/async-completion-client.ts @@ -1,14 +1,12 @@ import { ServerErrorResponse } from '@grpc/grpc-js'; import { Status } from '@grpc/grpc-js/build/src/constants'; +import { DataConverter, ensureTemporalFailure, LoadedDataConverter } from '@temporalio/common'; import { - DataConverter, encodeErrorToFailure, encodeToPayloads, - ensureTemporalFailure, filterNullAndUndefined, loadDataConverter, - LoadedDataConverter, -} from '@temporalio/common'; +} from '@temporalio/internal-non-workflow-common'; import os from 'os'; import { Connection, WorkflowService } from './connection'; diff --git a/packages/client/src/connection.ts b/packages/client/src/connection.ts index acf6613c0..0236d7b5b 100644 --- a/packages/client/src/connection.ts +++ b/packages/client/src/connection.ts @@ -1,6 +1,6 @@ import * as grpc from '@grpc/grpc-js'; import { temporal } from '@temporalio/proto'; -import { TLSConfig, normalizeTlsConfig } from '@temporalio/common'; +import { TLSConfig, normalizeTlsConfig } from '@temporalio/internal-non-workflow-common'; import { defaultGrpcRetryOptions, makeGrpcRetryInterceptor } from './grpc-retry'; export type WorkflowService = temporal.api.workflowservice.v1.WorkflowService; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 076f3e17b..730b0dce7 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -9,24 +9,24 @@ * @module */ -export * from './workflow-client'; -export * from './async-completion-client'; -export * from './connection'; -export * from './types'; -export * from './errors'; -export * from './workflow-options'; -export * from './interceptors'; export { - DataConverter, - defaultPayloadConverter, ActivityFailure, ApplicationFailure, - ChildWorkflowFailure, CancelledFailure, + ChildWorkflowFailure, + DataConverter, + defaultPayloadConverter, + ProtoFailure, ServerFailure, TemporalFailure, TerminatedFailure, TimeoutFailure, - ProtoFailure, - RetryPolicy, } from '@temporalio/common'; +export { RetryPolicy } from '@temporalio/internal-workflow-common'; +export * from './async-completion-client'; +export * from './connection'; +export * from './errors'; +export * from './interceptors'; +export * from './types'; +export * from './workflow-client'; +export * from './workflow-options'; diff --git a/packages/client/src/interceptors.ts b/packages/client/src/interceptors.ts index ce34d3067..687dd3b83 100644 --- a/packages/client/src/interceptors.ts +++ b/packages/client/src/interceptors.ts @@ -4,7 +4,7 @@ * @module */ -import { Next, Headers } from '@temporalio/common'; +import { Next, Headers } from '@temporalio/internal-workflow-common'; import { temporal } from '@temporalio/proto'; import { CompiledWorkflowOptions } from './workflow-options'; import { RequestCancelWorkflowExecutionResponse, TerminateWorkflowExecutionResponse, WorkflowExecution } from './types'; diff --git a/packages/client/src/workflow-client.ts b/packages/client/src/workflow-client.ts index 4fe8da827..ab261c40d 100644 --- a/packages/client/src/workflow-client.ts +++ b/packages/client/src/workflow-client.ts @@ -1,26 +1,30 @@ import { - BaseWorkflowHandle, CancelledFailure, - compileRetryPolicy, - composeInterceptors, DataConverter, + LoadedDataConverter, + RetryState, + TerminatedFailure, + TimeoutFailure, + TimeoutType, +} from '@temporalio/common'; +import { decodeArrayFromPayloads, decodeFromPayloadsAtIndex, decodeOptionalFailureToOptionalError, encodeMapToPayloads, encodeToPayloads, loadDataConverter, - LoadedDataConverter, +} from '@temporalio/internal-non-workflow-common'; +import { + BaseWorkflowHandle, + compileRetryPolicy, + composeInterceptors, QueryDefinition, - RetryState, SignalDefinition, - TerminatedFailure, - TimeoutFailure, - TimeoutType, WithWorkflowArgs, Workflow, WorkflowResultType, -} from '@temporalio/common'; +} from '@temporalio/internal-workflow-common'; import { temporal } from '@temporalio/proto'; import os from 'os'; import { v4 as uuid4 } from 'uuid'; diff --git a/packages/client/src/workflow-options.ts b/packages/client/src/workflow-options.ts index 4f6456c68..129aec62e 100644 --- a/packages/client/src/workflow-options.ts +++ b/packages/client/src/workflow-options.ts @@ -1,6 +1,10 @@ -import { WithCompiledWorkflowDurationOptions, CommonWorkflowOptions, SignalDefinition } from '@temporalio/common'; +import { + CommonWorkflowOptions, + SignalDefinition, + WithCompiledWorkflowDurationOptions, +} from '@temporalio/internal-workflow-common'; -export { WithCompiledWorkflowDurationOptions, compileWorkflowOptions } from '@temporalio/common'; +export { compileWorkflowOptions, WithCompiledWorkflowDurationOptions } from '@temporalio/internal-workflow-common'; export interface CompiledWorkflowOptions extends WithCompiledWorkflowDurationOptions { args: unknown[]; diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index bc49e15b5..6c5efafac 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -4,6 +4,10 @@ "outDir": "./lib", "rootDir": "./src" }, - "references": [{ "path": "../common/tsconfig.json" }], + "references": [ + { "path": "../common" }, + { "path": "../internal-workflow-common" }, + { "path": "../internal-non-workflow-common" } + ], "include": ["./src/**/*.ts"] } diff --git a/packages/common/README.md b/packages/common/README.md new file mode 100644 index 000000000..8f431185f --- /dev/null +++ b/packages/common/README.md @@ -0,0 +1,11 @@ +# `@temporalio/common` + +[![NPM](https://img.shields.io/npm/v/@temporalio/common?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/common) + +Part of [Temporal](https://temporal.io)'s TypeScript SDK (see [docs](https://docs.temporal.io/docs/typescript/introduction/) and [samples](https://github.com/temporalio/samples-typescript)). + +Common library for code that's used across the Client, Worker, and/or Workflow: + +- [DataConverter docs](https://docs.temporal.io/docs/typescript/data-converters) +- [Failure docs](https://docs.temporal.io/docs/typescript/handling-failure) +- [API reference](https://typescript.temporal.io/api/namespaces/common) diff --git a/packages/common/package.json b/packages/common/package.json new file mode 100644 index 000000000..8415f853f --- /dev/null +++ b/packages/common/package.json @@ -0,0 +1,29 @@ +{ + "name": "@temporalio/common", + "version": "0.17.2", + "description": "Common library for code that's used across the Client, Worker, and/or Workflow", + "main": "lib/index.js", + "types": "./lib/index.d.ts", + "keywords": [ + "temporal", + "workflow", + "worker" + ], + "author": "Loren Sands-Ramshaw ", + "license": "MIT", + "dependencies": { + "proto3-json-serializer": "^0.1.6", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common" + }, + "bugs": { + "url": "https://github.com/temporalio/sdk-typescript/issues" + }, + "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/common", + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@temporalio/proto": "file:../proto", + "protobufjs": "^6.11.2" + } +} diff --git a/packages/internal-workflow-common/src/converter/data-converter.ts b/packages/common/src/converter/data-converter.ts similarity index 100% rename from packages/internal-workflow-common/src/converter/data-converter.ts rename to packages/common/src/converter/data-converter.ts diff --git a/packages/internal-workflow-common/src/converter/encoding.ts b/packages/common/src/converter/encoding.ts similarity index 100% rename from packages/internal-workflow-common/src/converter/encoding.ts rename to packages/common/src/converter/encoding.ts diff --git a/packages/internal-workflow-common/src/converter/patch-protobuf-root.ts b/packages/common/src/converter/patch-protobuf-root.ts similarity index 95% rename from packages/internal-workflow-common/src/converter/patch-protobuf-root.ts rename to packages/common/src/converter/patch-protobuf-root.ts index 3577b8c2e..f3a1a2a39 100644 --- a/packages/internal-workflow-common/src/converter/patch-protobuf-root.ts +++ b/packages/common/src/converter/patch-protobuf-root.ts @@ -1,4 +1,4 @@ -import { isRecord } from '@temporalio/workflow-common/lib/type-helpers'; +import { isRecord } from '@temporalio/internal-workflow-common'; /** * Create a version of `root` with non-nested namespaces to match the generated types. diff --git a/packages/internal-workflow-common/src/converter/payload-codec.ts b/packages/common/src/converter/payload-codec.ts similarity index 100% rename from packages/internal-workflow-common/src/converter/payload-codec.ts rename to packages/common/src/converter/payload-codec.ts diff --git a/packages/internal-workflow-common/src/converter/payload-converter.ts b/packages/common/src/converter/payload-converter.ts similarity index 98% rename from packages/internal-workflow-common/src/converter/payload-converter.ts rename to packages/common/src/converter/payload-converter.ts index b957da8ca..6eb300c54 100644 --- a/packages/internal-workflow-common/src/converter/payload-converter.ts +++ b/packages/common/src/converter/payload-converter.ts @@ -1,4 +1,4 @@ -import { UnsupportedTypeError, ValueError } from '../errors'; +import { UnsupportedTypeError, ValueError } from '@temporalio/internal-workflow-common'; import { BinaryPayloadConverter, JsonPayloadConverter, diff --git a/packages/internal-workflow-common/src/converter/payload-converters.ts b/packages/common/src/converter/payload-converters.ts similarity index 96% rename from packages/internal-workflow-common/src/converter/payload-converters.ts rename to packages/common/src/converter/payload-converters.ts index ff5f56421..9ee8871f8 100644 --- a/packages/internal-workflow-common/src/converter/payload-converters.ts +++ b/packages/common/src/converter/payload-converters.ts @@ -1,4 +1,4 @@ -import { UnsupportedTypeError, ValueError } from '../errors'; +import { UnsupportedTypeError, ValueError } from '@temporalio/internal-workflow-common'; import { PayloadConverter } from './payload-converter'; import { encodingKeys, EncodingType, encodingTypes, METADATA_ENCODING_KEY, Payload, str, u8 } from './types'; diff --git a/packages/internal-workflow-common/src/converter/protobufs.ts b/packages/common/src/converter/protobuf-payload-converters.ts similarity index 96% rename from packages/internal-workflow-common/src/converter/protobufs.ts rename to packages/common/src/converter/protobuf-payload-converters.ts index 902b8c141..085fbf5e6 100644 --- a/packages/internal-workflow-common/src/converter/protobufs.ts +++ b/packages/common/src/converter/protobuf-payload-converters.ts @@ -1,7 +1,21 @@ +import { + errorMessage, + hasOwnProperties, + hasOwnProperty, + isRecord, + PayloadConverterError, + UnsupportedTypeError, + ValueError, +} from '@temporalio/internal-workflow-common'; import * as protoJsonSerializer from 'proto3-json-serializer'; import type { Message, Namespace, Root, Type } from 'protobufjs'; -import { errorMessage, PayloadConverterError, UnsupportedTypeError, ValueError } from '../errors'; -import { hasOwnProperties, hasOwnProperty, isRecord } from '../type-helpers'; +import { CompositePayloadConverter } from './payload-converter'; +import { + BinaryPayloadConverter, + JsonPayloadConverter, + PayloadConverterWithEncoding, + UndefinedPayloadConverter, +} from './payload-converters'; import { EncodingType, encodingTypes, @@ -11,13 +25,6 @@ import { str, u8, } from './types'; -import { - BinaryPayloadConverter, - JsonPayloadConverter, - PayloadConverterWithEncoding, - UndefinedPayloadConverter, -} from './payload-converters'; -import { CompositePayloadConverter } from './payload-converter'; abstract class ProtobufPayloadConverter implements PayloadConverterWithEncoding { protected readonly root: Root | undefined; diff --git a/packages/internal-workflow-common/src/converter/types.ts b/packages/common/src/converter/types.ts similarity index 100% rename from packages/internal-workflow-common/src/converter/types.ts rename to packages/common/src/converter/types.ts diff --git a/packages/internal-workflow-common/src/failure.ts b/packages/common/src/failure.ts similarity index 99% rename from packages/internal-workflow-common/src/failure.ts rename to packages/common/src/failure.ts index e79eecd1f..524a6b58b 100644 --- a/packages/internal-workflow-common/src/failure.ts +++ b/packages/common/src/failure.ts @@ -1,6 +1,6 @@ +import { checkExtends } from '@temporalio/internal-workflow-common'; import type { temporal } from '@temporalio/proto/lib/coresdk'; import { arrayFromPayloads, fromPayloadsAtIndex, PayloadConverter, toPayloads } from './converter/payload-converter'; -import { checkExtends } from './type-helpers'; export const FAILURE_SOURCE = 'TypeScriptSDK'; export type ProtoFailure = temporal.api.failure.v1.IFailure; diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts new file mode 100644 index 000000000..03a53f7cd --- /dev/null +++ b/packages/common/src/index.ts @@ -0,0 +1,12 @@ +/** + * Common library for code that's used across the Client, Worker, and/or Workflow + * + * @module + */ +export * from '@temporalio/internal-workflow-common/lib/errors'; +export * from './converter/data-converter'; +export * from './converter/payload-codec'; +export * from './converter/payload-converter'; +export * from './converter/payload-converters'; +export * from './converter/types'; +export * from './failure'; diff --git a/packages/common/src/protobufs.ts b/packages/common/src/protobufs.ts new file mode 100644 index 000000000..edfc09ffe --- /dev/null +++ b/packages/common/src/protobufs.ts @@ -0,0 +1,5 @@ +// Don't export from index to save space in Workflow bundle for users who don't use Protobufs +// Import with: +// import { patchProtobufRoot } from '@temporalio/common/lib/protobufs'; +export * from './converter/protobuf-payload-converters'; +export * from './converter/patch-protobuf-root'; diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json new file mode 100644 index 000000000..0656e2193 --- /dev/null +++ b/packages/common/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src" + }, + "references": [{ "path": "../internal-workflow-common" }], + "include": ["./src/**/*.ts"] +} diff --git a/packages/interceptors-opentelemetry/src/client/index.ts b/packages/interceptors-opentelemetry/src/client/index.ts index 83ffbcceb..7dca8a4af 100644 --- a/packages/interceptors-opentelemetry/src/client/index.ts +++ b/packages/interceptors-opentelemetry/src/client/index.ts @@ -1,6 +1,6 @@ import * as otel from '@opentelemetry/api'; import { Next, WorkflowClientCallsInterceptor, WorkflowStartInput } from '@temporalio/client'; -import { headersWithContext, RUN_ID_ATTR_KEY } from '@temporalio/common/lib/otel'; +import { headersWithContext, RUN_ID_ATTR_KEY } from '@temporalio/internal-non-workflow-common/lib/otel'; import { instrument } from '../instrumentation'; import { SpanName, SPAN_DELIMITER } from '../workflow'; diff --git a/packages/interceptors-opentelemetry/src/worker/index.ts b/packages/interceptors-opentelemetry/src/worker/index.ts index 5d29083ba..5fcb3cb66 100644 --- a/packages/interceptors-opentelemetry/src/worker/index.ts +++ b/packages/interceptors-opentelemetry/src/worker/index.ts @@ -2,7 +2,7 @@ import * as otel from '@opentelemetry/api'; import { Resource } from '@opentelemetry/resources'; import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; import { Context as ActivityContext } from '@temporalio/activity'; -import { extractContextFromHeaders } from '@temporalio/common/lib/otel'; +import { extractContextFromHeaders } from '@temporalio/internal-non-workflow-common/lib/otel'; import { ActivityExecuteInput, ActivityInboundCallsInterceptor, InjectedSink, Next } from '@temporalio/worker'; import { instrument } from '../instrumentation'; import { OpenTelemetryWorkflowExporter, SerializableSpan, SpanName, SPAN_DELIMITER } from '../workflow'; diff --git a/packages/interceptors-opentelemetry/src/workflow/index.ts b/packages/interceptors-opentelemetry/src/workflow/index.ts index ddd50be9c..17b0e38d2 100644 --- a/packages/interceptors-opentelemetry/src/workflow/index.ts +++ b/packages/interceptors-opentelemetry/src/workflow/index.ts @@ -12,7 +12,7 @@ import { ContinueAsNewInput, WorkflowInternalsInterceptor, } from '@temporalio/workflow'; -import { extractContextFromHeaders, headersWithContext } from '@temporalio/common/lib/otel'; +import { extractContextFromHeaders, headersWithContext } from '@temporalio/internal-non-workflow-common/lib/otel'; import { ContextManager } from './context-manager'; import { SpanExporter } from './span-exporter'; import { SpanName, SPAN_DELIMITER } from './definitions'; diff --git a/packages/internal-non-workflow-common/README.md b/packages/internal-non-workflow-common/README.md index a18b14fd1..40d606f4f 100644 --- a/packages/internal-non-workflow-common/README.md +++ b/packages/internal-non-workflow-common/README.md @@ -1,6 +1,6 @@ -# `@temporalio/common` +# `@temporalio/internal-non-workflow-common` -[![NPM](https://img.shields.io/npm/v/@temporalio/common?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/common) +[![NPM](https://img.shields.io/npm/v/@temporalio/internal-non-workflow-common?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/internal-non-workflow-common) Part of [Temporal](https://temporal.io)'s TypeScript SDK (see [docs](https://docs.temporal.io/docs/typescript/introduction/) and [samples](https://github.com/temporalio/samples-typescript)). diff --git a/packages/internal-non-workflow-common/package.json b/packages/internal-non-workflow-common/package.json index 1d8dd19cb..de8c2143f 100644 --- a/packages/internal-non-workflow-common/package.json +++ b/packages/internal-non-workflow-common/package.json @@ -1,7 +1,7 @@ { - "name": "@temporalio/common", + "name": "@temporalio/internal-non-workflow-common", "version": "0.17.2", - "description": "Temporal.io SDK common library for use in Workflow and normal code", + "description": "Internal SDK library: users should usually use other packages instead. Not included in Workflow bundle.", "main": "lib/index.js", "types": "./lib/index.d.ts", "keywords": [ @@ -13,13 +13,14 @@ "author": "Roey Berman ", "license": "MIT", "dependencies": { - "@temporalio/workflow-common": "file:../workflow-common", + "@temporalio/common": "file:../common", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", "lodash": "^4.17.21" }, "bugs": { "url": "https://github.com/temporalio/sdk-typescript/issues" }, - "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/common", + "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/internal-non-workflow-common", "publishConfig": { "access": "public" }, diff --git a/packages/internal-non-workflow-common/src/codec-helpers.ts b/packages/internal-non-workflow-common/src/codec-helpers.ts index dbfa7a56e..6206480cf 100644 --- a/packages/internal-non-workflow-common/src/codec-helpers.ts +++ b/packages/internal-non-workflow-common/src/codec-helpers.ts @@ -9,7 +9,7 @@ import { ProtoFailure, TemporalFailure, toPayloads, -} from '@temporalio/workflow-common'; +} from '@temporalio/common'; import { clone, setWith } from 'lodash'; /** diff --git a/packages/internal-non-workflow-common/src/data-converter-helpers.ts b/packages/internal-non-workflow-common/src/data-converter-helpers.ts index 9e01e11aa..b293e0230 100644 --- a/packages/internal-non-workflow-common/src/data-converter-helpers.ts +++ b/packages/internal-non-workflow-common/src/data-converter-helpers.ts @@ -2,12 +2,10 @@ import { DataConverter, defaultPayloadCodec, defaultPayloadConverter, - errorCode, - hasOwnProperty, LoadedDataConverter, PayloadConverter, - ValueError, -} from '@temporalio/workflow-common'; +} from '@temporalio/common'; +import { errorCode, hasOwnProperty, ValueError } from '@temporalio/internal-workflow-common'; const isValidPayloadConverter = (PayloadConverter: unknown): PayloadConverter is PayloadConverter => typeof PayloadConverter === 'object' && diff --git a/packages/internal-non-workflow-common/src/index.ts b/packages/internal-non-workflow-common/src/index.ts index 6610159eb..c016411b0 100644 --- a/packages/internal-non-workflow-common/src/index.ts +++ b/packages/internal-non-workflow-common/src/index.ts @@ -1,15 +1,8 @@ /** - * Common library for both isolated Workflow and normal non-Workflow code + * Internal SDK library: users should usually use other packages instead. Not included in Workflow bundle. * * @module */ -export * from '@temporalio/workflow-common'; -export { - DefaultPayloadConverterWithProtobufs, - DefaultPayloadConverterWithProtobufsOptions, - ProtobufBinaryPayloadConverter, - ProtobufJsonPayloadConverter, -} from '@temporalio/workflow-common/lib/converter/protobufs'; export * from './codec-helpers'; export * from './data-converter-helpers'; export * from './tls-config'; diff --git a/packages/internal-non-workflow-common/src/otel.ts b/packages/internal-non-workflow-common/src/otel.ts index b185fd44c..ace0d40cc 100644 --- a/packages/internal-non-workflow-common/src/otel.ts +++ b/packages/internal-non-workflow-common/src/otel.ts @@ -1,6 +1,6 @@ import * as otel from '@opentelemetry/api'; -import { defaultPayloadConverter } from '@temporalio/workflow-common'; -import { Headers } from '@temporalio/workflow-common'; +import { defaultPayloadConverter } from '@temporalio/common'; +import { Headers } from '@temporalio/internal-workflow-common'; /** Default trace header for opentelemetry interceptors */ export const TRACE_HEADER = '_tracer-data'; diff --git a/packages/internal-non-workflow-common/tsconfig.json b/packages/internal-non-workflow-common/tsconfig.json index 1e09513a4..071c9877b 100644 --- a/packages/internal-non-workflow-common/tsconfig.json +++ b/packages/internal-non-workflow-common/tsconfig.json @@ -4,5 +4,6 @@ "outDir": "./lib", "rootDir": "./src" }, + "references": [{ "path": "../common" }, { "path": "../internal-workflow-common" }], "include": ["./src/**/*.ts"] } diff --git a/packages/internal-workflow-common/README.md b/packages/internal-workflow-common/README.md index fcfcddc18..f3b71160e 100644 --- a/packages/internal-workflow-common/README.md +++ b/packages/internal-workflow-common/README.md @@ -1,6 +1,6 @@ -# `@temporalio/workflow-common` +# `@temporalio/internal-workflow-common` -[![NPM](https://img.shields.io/npm/v/@temporalio/workflow-common?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/workflow-common) +[![NPM](https://img.shields.io/npm/v/@temporalio/internal-workflow-common?style=for-the-badge)](https://www.npmjs.com/package/@temporalio/internal-workflow-common) Part of [Temporal](https://temporal.io)'s TypeScript SDK (see [docs](https://docs.temporal.io/docs/typescript/introduction/) and [samples](https://github.com/temporalio/samples-typescript)). diff --git a/packages/internal-workflow-common/package.json b/packages/internal-workflow-common/package.json index ad261db45..94b2341a2 100644 --- a/packages/internal-workflow-common/package.json +++ b/packages/internal-workflow-common/package.json @@ -1,7 +1,7 @@ { - "name": "@temporalio/workflow-common", + "name": "@temporalio/internal-workflow-common", "version": "0.17.2", - "description": "Internal SDK library: users should usually use `@temporalio/common` instead", + "description": "Internal SDK library: users should usually use other packages instead. Included in Workflow bundle.", "main": "lib/index.js", "types": "./lib/index.d.ts", "keywords": [ @@ -13,18 +13,16 @@ "license": "MIT", "dependencies": { "long": "^4.0.0", - "ms": "^2.1.3", - "proto3-json-serializer": "^0.1.6" + "ms": "^2.1.3" }, "bugs": { "url": "https://github.com/temporalio/sdk-typescript/issues" }, - "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/common", + "homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/internal-workflow-common", "publishConfig": { "access": "public" }, "devDependencies": { - "@temporalio/proto": "file:../proto", - "protobufjs": "^6.11.2" + "@temporalio/proto": "file:../proto" } } diff --git a/packages/internal-workflow-common/src/index.ts b/packages/internal-workflow-common/src/index.ts index 4d13838c5..0a0ae8569 100644 --- a/packages/internal-workflow-common/src/index.ts +++ b/packages/internal-workflow-common/src/index.ts @@ -1,17 +1,10 @@ /** - * Common library for both isolated Workflow and normal non-Workflow code + * Internal SDK library: users should usually use other packages instead. Included in Workflow bundle. * * @module */ export * from './activity-options'; -export * from './converter/data-converter'; -export * from './converter/patch-protobuf-root'; -export * from './converter/payload-codec'; -export * from './converter/payload-converter'; -export * from './converter/payload-converters'; -export * from './converter/types'; export * from './errors'; -export * from './failure'; export * from './interceptors'; export * from './interfaces'; export * from './retry-policy'; diff --git a/packages/internal-workflow-common/src/retry-policy.ts b/packages/internal-workflow-common/src/retry-policy.ts index b6d8e87cb..40f3e2b16 100644 --- a/packages/internal-workflow-common/src/retry-policy.ts +++ b/packages/internal-workflow-common/src/retry-policy.ts @@ -1,5 +1,5 @@ import type { temporal } from '@temporalio/proto'; -import { ValueError } from '.'; +import { ValueError } from './errors'; import { msOptionalToNumber, msOptionalToTs, msToNumber, msToTs } from './time'; /** diff --git a/packages/internal-workflow-common/src/time.ts b/packages/internal-workflow-common/src/time.ts index 778cd3b13..82f3eafe4 100644 --- a/packages/internal-workflow-common/src/time.ts +++ b/packages/internal-workflow-common/src/time.ts @@ -1,6 +1,6 @@ +import type * as iface from '@temporalio/proto/lib/coresdk'; import Long from 'long'; import ms from 'ms'; -import type * as iface from '@temporalio/proto/lib/coresdk'; import { ValueError } from './errors'; // NOTE: these are the same interface in JS diff --git a/packages/internal-workflow-common/src/workflow-options.ts b/packages/internal-workflow-common/src/workflow-options.ts index f80656519..b69c22d9e 100644 --- a/packages/internal-workflow-common/src/workflow-options.ts +++ b/packages/internal-workflow-common/src/workflow-options.ts @@ -1,9 +1,8 @@ import type { coresdk, google } from '@temporalio/proto/lib/coresdk'; import { Workflow } from './interfaces'; -import { falsyMsToTs } from './time'; -import { Replace } from './type-helpers'; import { RetryPolicy } from './retry-policy'; -import { checkExtends } from './type-helpers'; +import { falsyMsToTs } from './time'; +import { checkExtends, Replace } from './type-helpers'; // Avoid importing the proto implementation to reduce workflow bundle size // Copied from coresdk.common.WorkflowIdReusePolicy diff --git a/packages/meta/tsconfig.json b/packages/meta/tsconfig.json index 8cdb53cef..43757dd46 100644 --- a/packages/meta/tsconfig.json +++ b/packages/meta/tsconfig.json @@ -6,11 +6,11 @@ "rootDir": "./src" }, "references": [ - { "path": "../activity/tsconfig.json" }, - { "path": "../client/tsconfig.json" }, - { "path": "../common/tsconfig.json" }, - { "path": "../worker/tsconfig.json" }, - { "path": "../workflow/tsconfig.json" } + { "path": "../activity" }, + { "path": "../client" }, + { "path": "../common" }, + { "path": "../worker" }, + { "path": "../workflow" } ], "include": ["./src/**/*.ts"] } diff --git a/packages/test/package.json b/packages/test/package.json index 0912ee7f5..f69b18418 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -29,7 +29,8 @@ "@temporalio/activity": "file:../activity", "@temporalio/client": "file:../client", "@temporalio/common": "file:../common", - "@temporalio/workflow-common": "file:../workflow-common", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", + "@temporalio/internal-non-workflow-common": "file:../internal-non-workflow-common", "@temporalio/interceptors-opentelemetry": "file:../interceptors-opentelemetry", "@temporalio/proto": "file:../proto", "@temporalio/worker": "file:../worker", diff --git a/packages/test/protos/root.js b/packages/test/protos/root.js index efb3689e3..b7385c0ac 100644 --- a/packages/test/protos/root.js +++ b/packages/test/protos/root.js @@ -1,3 +1,3 @@ -const { patchProtobufRoot } = require('@temporalio/common'); +const { patchProtobufRoot } = require('@temporalio/common/lib/protobufs'); const unpatchedRoot = require('./json-module'); module.exports = patchProtobufRoot(unpatchedRoot); diff --git a/packages/test/src/activities/index.ts b/packages/test/src/activities/index.ts index a08550068..42d121c97 100644 --- a/packages/test/src/activities/index.ts +++ b/packages/test/src/activities/index.ts @@ -2,12 +2,13 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { Context } from '@temporalio/activity'; import { Connection, LOCAL_DOCKER_TARGET, WorkflowClient } from '@temporalio/client'; -import { fakeProgress as fakeProgressInner } from './fake-progress'; +import { ApplicationFailure } from '@temporalio/common'; +import { QueryDefinition } from '@temporalio/internal-workflow-common'; +import { ProtoActivityInput, ProtoActivityResult } from '../../protos/root'; import { cancellableFetch as cancellableFetchInner } from './cancellable-fetch'; -import { ApplicationFailure, QueryDefinition } from '@temporalio/common'; +import { fakeProgress as fakeProgressInner } from './fake-progress'; export { throwSpecificError } from './failure-tester'; -import { ProtoActivityInput, ProtoActivityResult } from '../../protos/root'; // TODO: Get rid of this by providing client via activity context function getTestConnection(): Connection { diff --git a/packages/test/src/load/setup.ts b/packages/test/src/load/setup.ts index 87ed615a4..2ecfd7add 100644 --- a/packages/test/src/load/setup.ts +++ b/packages/test/src/load/setup.ts @@ -1,6 +1,6 @@ import arg from 'arg'; import { Connection } from '@temporalio/client'; -import { msToTs } from '@temporalio/common'; +import { msToTs } from '@temporalio/internal-workflow-common'; import { SetupArgSpec, setupArgSpec, getRequired } from './args'; async function createNamespace(connection: Connection, namespace: string, maxAttempts = 100, retryIntervalSecs = 1) { diff --git a/packages/test/src/mock-native-worker.ts b/packages/test/src/mock-native-worker.ts index 37b64a94e..223ec1c59 100644 --- a/packages/test/src/mock-native-worker.ts +++ b/packages/test/src/mock-native-worker.ts @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { SpanContext } from '@opentelemetry/api'; -import { defaultPayloadConverter, loadDataConverter, msToTs } from '@temporalio/common'; +import { defaultPayloadConverter, fromPayloadsAtIndex, LoadedDataConverter } from '@temporalio/common'; +import { loadDataConverter } from '@temporalio/internal-non-workflow-common'; +import { msToTs } from '@temporalio/internal-workflow-common'; import { coresdk } from '@temporalio/proto'; import { DefaultLogger } from '@temporalio/worker'; import { errors, NativeWorkerLike, Worker as RealWorker } from '@temporalio/worker/lib/worker'; @@ -11,7 +13,6 @@ import { WorkerOptions, } from '@temporalio/worker/lib/worker-options'; import { WorkflowCreator } from '@temporalio/worker/src/workflow/interface'; -import { fromPayloadsAtIndex, LoadedDataConverter } from '@temporalio/workflow-common'; import { lastValueFrom } from 'rxjs'; import * as activities from './activities'; diff --git a/packages/test/src/payload-converters/payload-converter.ts b/packages/test/src/payload-converters/payload-converter.ts index 96e8943b8..9e85cd45e 100644 --- a/packages/test/src/payload-converters/payload-converter.ts +++ b/packages/test/src/payload-converters/payload-converter.ts @@ -1,4 +1,4 @@ -import { DefaultPayloadConverterWithProtobufs } from '@temporalio/common'; +import { DefaultPayloadConverterWithProtobufs } from '@temporalio/common/lib/protobufs'; import root, { foo } from '../../protos/root'; // Used in tests diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-data-converter.ts index f4d359e96..0f28b4d3d 100644 --- a/packages/test/src/test-custom-data-converter.ts +++ b/packages/test/src/test-custom-data-converter.ts @@ -1,14 +1,14 @@ import { Connection, WorkflowClient } from '@temporalio/client'; +import { toPayloads } from '@temporalio/common'; import { coresdk } from '@temporalio/proto'; import { Worker } from '@temporalio/worker'; import test, { ExecutionContext } from 'ava'; import { v4 as uuid4 } from 'uuid'; import { protoActivity } from './activities'; -import { payloadConverter, messageInstance } from './payload-converters/payload-converter'; import { cleanStackTrace, RUN_INTEGRATION_TESTS } from './helpers'; import { defaultOptions, isolateFreeWorker } from './mock-native-worker'; +import { messageInstance, payloadConverter } from './payload-converters/payload-converter'; import { protobufWorkflow } from './workflows'; -import { toPayloads } from '@temporalio/workflow-common'; export async function runWorker(worker: Worker, fn: () => Promise): Promise { const promise = worker.run(); diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 0e56599d3..79489e08f 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -1,23 +1,25 @@ /* eslint @typescript-eslint/no-non-null-assertion: 0 */ import { Connection, WorkflowClient } from '@temporalio/client'; import { + BinaryPayloadConverter, DataConverterError, defaultPayloadConverter, - DefaultPayloadConverterWithProtobufs, + JsonPayloadConverter, + UndefinedPayloadConverter, ValueError, } from '@temporalio/common'; -import { Core, DefaultLogger, Worker } from '@temporalio/worker'; -import { BinaryPayloadConverter, JsonPayloadConverter, UndefinedPayloadConverter } from '@temporalio/workflow-common'; import { + DefaultPayloadConverterWithProtobufs, ProtobufBinaryPayloadConverter, ProtobufJsonPayloadConverter, -} from '@temporalio/workflow-common/lib/converter/protobufs'; +} from '@temporalio/common/lib/protobufs'; import { encodingKeys, METADATA_ENCODING_KEY, METADATA_MESSAGE_TYPE_KEY, u8, -} from '@temporalio/workflow-common/lib/converter/types'; +} from '@temporalio/common/lib/converter/types'; +import { Core, DefaultLogger, Worker } from '@temporalio/worker'; import test from 'ava'; import { v4 as uuid4 } from 'uuid'; import root from '../protos/root'; diff --git a/packages/test/src/test-integration.ts b/packages/test/src/test-integration.ts index f7da9a58a..8252609f7 100644 --- a/packages/test/src/test-integration.ts +++ b/packages/test/src/test-integration.ts @@ -1,33 +1,33 @@ /* eslint @typescript-eslint/no-non-null-assertion: 0 */ -import anyTest, { TestInterface } from 'ava'; -import ms from 'ms'; -import { v4 as uuid4 } from 'uuid'; -import dedent from 'dedent'; -import { WorkflowClient } from '@temporalio/client'; +import { + ActivityFailure, + ApplicationFailure, + WorkflowClient, + WorkflowContinuedAsNewError, + WorkflowFailedError, +} from '@temporalio/client'; import { ChildWorkflowFailure, defaultPayloadConverter, + fromPayloadsAtIndex, RetryState, TerminatedFailure, TimeoutFailure, TimeoutType, - tsToMs, WorkflowExecution, } from '@temporalio/common'; -import { Worker, DefaultLogger, Core } from '@temporalio/worker'; +import { tsToMs } from '@temporalio/internal-workflow-common'; import * as iface from '@temporalio/proto'; -import { - WorkflowContinuedAsNewError, - WorkflowFailedError, - ActivityFailure, - ApplicationFailure, -} from '@temporalio/client'; +import { Core, DefaultLogger, Worker } from '@temporalio/worker'; +import asyncRetry from 'async-retry'; +import anyTest, { TestInterface } from 'ava'; +import dedent from 'dedent'; +import ms from 'ms'; +import { v4 as uuid4 } from 'uuid'; import * as activities from './activities'; +import { cleanStackTrace, RUN_INTEGRATION_TESTS, u8 } from './helpers'; import * as workflows from './workflows'; -import { u8, RUN_INTEGRATION_TESTS, cleanStackTrace } from './helpers'; import { withZeroesHTTPServer } from './zeroes-http-server'; -import asyncRetry from 'async-retry'; -import { fromPayloadsAtIndex } from '@temporalio/workflow-common'; const { EVENT_TYPE_TIMER_STARTED, EVENT_TYPE_TIMER_FIRED, EVENT_TYPE_TIMER_CANCELED } = iface.temporal.api.enums.v1.EventType; diff --git a/packages/test/src/test-patch-root.ts b/packages/test/src/test-patch-root.ts index 360d121c1..5ed55c340 100644 --- a/packages/test/src/test-patch-root.ts +++ b/packages/test/src/test-patch-root.ts @@ -1,5 +1,5 @@ import test from 'ava'; -import { patchProtobufRoot } from '@temporalio/common'; +import { patchProtobufRoot } from '@temporalio/common/lib/protobufs'; test('patchRoot', (t) => { const type = new Type(); diff --git a/packages/test/src/test-retry-policy.ts b/packages/test/src/test-retry-policy.ts index 9cbc98bbe..5c0b6d566 100644 --- a/packages/test/src/test-retry-policy.ts +++ b/packages/test/src/test-retry-policy.ts @@ -1,5 +1,6 @@ import test from 'ava'; -import { compileRetryPolicy, msToTs, ValueError } from '@temporalio/common'; +import { ValueError } from '@temporalio/common'; +import { compileRetryPolicy, msToTs } from '@temporalio/internal-workflow-common'; test('compileRetryPolicy validates intervals are not 0', (t) => { t.throws(() => compileRetryPolicy({ initialInterval: 0 }), { diff --git a/packages/test/src/test-server-options.ts b/packages/test/src/test-server-options.ts index 045b1882e..1be1794b6 100644 --- a/packages/test/src/test-server-options.ts +++ b/packages/test/src/test-server-options.ts @@ -1,5 +1,5 @@ import test from 'ava'; -import { normalizeTlsConfig } from '@temporalio/common'; +import { normalizeTlsConfig } from '@temporalio/internal-non-workflow-common'; test('normalizeTlsConfig turns null to undefined', (t) => { t.is(normalizeTlsConfig(null), undefined); diff --git a/packages/test/src/test-time.ts b/packages/test/src/test-time.ts index cb509641b..af78d1c8c 100644 --- a/packages/test/src/test-time.ts +++ b/packages/test/src/test-time.ts @@ -1,6 +1,6 @@ import test from 'ava'; import Long from 'long'; -import { msToTs } from '@temporalio/common'; +import { msToTs } from '@temporalio/internal-workflow-common'; test('msToTs converts to Timestamp', (t) => { t.deepEqual({ seconds: Long.fromInt(600), nanos: 0 }, msToTs('10 minutes')); diff --git a/packages/test/src/test-worker-activities.ts b/packages/test/src/test-worker-activities.ts index 913656a8f..ea14aa904 100644 --- a/packages/test/src/test-worker-activities.ts +++ b/packages/test/src/test-worker-activities.ts @@ -1,14 +1,13 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import anyTest, { TestInterface, ExecutionContext } from 'ava'; -import { v4 as uuid4 } from 'uuid'; -import dedent from 'dedent'; +import { defaultPayloadConverter, toPayloads } from '@temporalio/common'; import { coresdk } from '@temporalio/proto'; -import { defaultPayloadConverter } from '@temporalio/common'; +import anyTest, { ExecutionContext, TestInterface } from 'ava'; +import dedent from 'dedent'; +import { v4 as uuid4 } from 'uuid'; import { httpGet } from './activities'; -import { Worker, isolateFreeWorker, defaultOptions } from './mock-native-worker'; -import { withZeroesHTTPServer } from './zeroes-http-server'; import { cleanStackTrace } from './helpers'; -import { toPayloads } from '@temporalio/workflow-common'; +import { defaultOptions, isolateFreeWorker, Worker } from './mock-native-worker'; +import { withZeroesHTTPServer } from './zeroes-http-server'; export interface Context { worker: Worker; diff --git a/packages/test/src/test-workflow-cancellation.ts b/packages/test/src/test-workflow-cancellation.ts index eaa7460b5..7123a3bd6 100644 --- a/packages/test/src/test-workflow-cancellation.ts +++ b/packages/test/src/test-workflow-cancellation.ts @@ -1,13 +1,13 @@ -import anyTest, { Constructor, Macro, TestInterface } from 'ava'; -import { v4 as uuid4 } from 'uuid'; import { WorkflowClient, WorkflowFailedError } from '@temporalio/client'; -import { Worker } from '@temporalio/worker'; import { ApplicationFailure, CancelledFailure } from '@temporalio/common'; -import { RUN_INTEGRATION_TESTS } from './helpers'; +import { Worker } from '@temporalio/worker'; +import anyTest, { Constructor, Macro, TestInterface } from 'ava'; +import { v4 as uuid4 } from 'uuid'; import * as activities from './activities'; +import { RUN_INTEGRATION_TESTS } from './helpers'; import { - workflowCancellationScenarios, WorkflowCancellationScenarioOutcome, + workflowCancellationScenarios, WorkflowCancellationScenarioTiming, } from './workflows'; diff --git a/packages/test/src/test-workflows.ts b/packages/test/src/test-workflows.ts index ba07e8a97..4d73e0067 100644 --- a/packages/test/src/test-workflows.ts +++ b/packages/test/src/test-workflows.ts @@ -1,17 +1,23 @@ -import anyTest, { ExecutionContext, TestInterface } from 'ava'; -import vm from 'vm'; -import path from 'path'; -import Long from 'long'; -import dedent from 'dedent'; +import { + ApplicationFailure, + defaultPayloadConverter, + errorToFailure, + RetryState, + toPayloads, +} from '@temporalio/common'; +import { msToTs } from '@temporalio/internal-workflow-common'; import { coresdk } from '@temporalio/proto'; -import { ApplicationFailure, defaultPayloadConverter, errorToFailure, msToTs, RetryState } from '@temporalio/common'; +import { DefaultLogger } from '@temporalio/worker/lib/logger'; import { WorkflowCodeBundler } from '@temporalio/worker/lib/workflow/bundler'; import { VMWorkflow, VMWorkflowCreator } from '@temporalio/worker/lib/workflow/vm'; -import { DefaultLogger } from '@temporalio/worker/lib/logger'; +import { WorkflowInfo } from '@temporalio/workflow'; +import anyTest, { ExecutionContext, TestInterface } from 'ava'; +import dedent from 'dedent'; +import Long from 'long'; +import path from 'path'; +import vm from 'vm'; import * as activityFunctions from './activities'; import { u8 } from './helpers'; -import { WorkflowInfo } from '@temporalio/workflow'; -import { toPayloads } from '@temporalio/workflow-common'; export interface Context { workflow: VMWorkflow; diff --git a/packages/test/tsconfig.json b/packages/test/tsconfig.json index 01dbcd3d9..3116b7df1 100644 --- a/packages/test/tsconfig.json +++ b/packages/test/tsconfig.json @@ -5,7 +5,8 @@ "rootDir": "./src" }, "references": [ - { "path": "../workflow-common" }, + { "path": "../internal-workflow-common" }, + { "path": "../internal-non-workflow-common" }, { "path": "../activity" }, { "path": "../client" }, { "path": "../common" }, diff --git a/packages/worker/package.json b/packages/worker/package.json index 1c91d2542..a8d626699 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -16,6 +16,8 @@ "@opentelemetry/api": "^1.0.3", "@temporalio/activity": "file:../activity", "@temporalio/common": "file:../common", + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", + "@temporalio/internal-non-workflow-common": "file:../internal-non-workflow-common", "@temporalio/core-bridge": "file:../core-bridge", "@temporalio/proto": "file:../proto", "@temporalio/workflow": "file:../workflow", diff --git a/packages/worker/src/activity.ts b/packages/worker/src/activity.ts index 87c459532..7559c2b85 100644 --- a/packages/worker/src/activity.ts +++ b/packages/worker/src/activity.ts @@ -1,15 +1,8 @@ import { asyncLocalStorage, Context, Info } from '@temporalio/activity'; -import { - ActivityFunction, - CancelledFailure, - composeInterceptors, - encodeErrorToFailure, - encodeToPayload, - ensureTemporalFailure, - FAILURE_SOURCE, -} from '@temporalio/common'; +import { CancelledFailure, ensureTemporalFailure, FAILURE_SOURCE, LoadedDataConverter } from '@temporalio/common'; +import { encodeErrorToFailure, encodeToPayload } from '@temporalio/internal-non-workflow-common'; +import { ActivityFunction, composeInterceptors } from '@temporalio/internal-workflow-common'; import { coresdk } from '@temporalio/proto'; -import { LoadedDataConverter } from '@temporalio/workflow-common'; import { AbortController } from 'abort-controller'; import { ActivityExecuteInput, diff --git a/packages/worker/src/core.ts b/packages/worker/src/core.ts index d2453a767..450cffa33 100644 --- a/packages/worker/src/core.ts +++ b/packages/worker/src/core.ts @@ -1,8 +1,4 @@ -import { promisify } from 'util'; -import Heap from 'heap-js'; -import { BehaviorSubject, lastValueFrom, of } from 'rxjs'; -import { concatMap, delay, map, repeat } from 'rxjs/operators'; -import { IllegalStateError, normalizeTlsConfig, filterNullAndUndefined } from '@temporalio/common'; +import { IllegalStateError } from '@temporalio/common'; import * as native from '@temporalio/core-bridge'; import { corePollLogs, @@ -10,10 +6,15 @@ import { newCore, TelemetryOptions as RequiredTelemetryOptions, } from '@temporalio/core-bridge'; -import { compileServerOptions, getDefaultServerOptions, RequiredServerOptions, ServerOptions } from './server-options'; -import { DefaultLogger, Logger, LogEntry, LogTimestamp, timeOfDayToBigint } from './logger'; +import { filterNullAndUndefined, normalizeTlsConfig } from '@temporalio/internal-non-workflow-common'; +import { MakeOptional } from '@temporalio/internal-workflow-common'; +import Heap from 'heap-js'; +import { BehaviorSubject, lastValueFrom, of } from 'rxjs'; +import { concatMap, delay, map, repeat } from 'rxjs/operators'; +import { promisify } from 'util'; import * as errors from './errors'; -import { MakeOptional } from '@temporalio/workflow-common/src/type-helpers'; +import { DefaultLogger, LogEntry, Logger, LogTimestamp, timeOfDayToBigint } from './logger'; +import { compileServerOptions, getDefaultServerOptions, RequiredServerOptions, ServerOptions } from './server-options'; export type TelemetryOptions = MakeOptional; diff --git a/packages/worker/src/interceptors.ts b/packages/worker/src/interceptors.ts index 1a1723fba..028f66fe7 100644 --- a/packages/worker/src/interceptors.ts +++ b/packages/worker/src/interceptors.ts @@ -6,8 +6,8 @@ * @module */ -import { Next, Headers } from '@temporalio/common'; import { Context as ActivityContext } from '@temporalio/activity'; +import { Headers, Next } from '@temporalio/internal-workflow-common'; export { Next, Headers }; diff --git a/packages/worker/src/worker-options.ts b/packages/worker/src/worker-options.ts index 1ddac27ff..fd5e2e156 100644 --- a/packages/worker/src/worker-options.ts +++ b/packages/worker/src/worker-options.ts @@ -1,7 +1,8 @@ -import os from 'os'; +import { DataConverter } from '@temporalio/common'; +import { ActivityInterface, msToNumber } from '@temporalio/internal-workflow-common'; import fs from 'fs'; -import { resolve, dirname } from 'path'; -import { ActivityInterface, msToNumber, DataConverter } from '@temporalio/common'; +import os from 'os'; +import { dirname, resolve } from 'path'; import { WorkerInterceptors } from './interceptors'; import { InjectedSinks } from './sinks'; import { GiB } from './utils'; diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index dfa505c84..fdcfe6588 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -3,27 +3,29 @@ import { SpanContext } from '@opentelemetry/api'; import { Info as ActivityInfo } from '@temporalio/activity'; import { DataConverter, + defaultPayloadConverter, + errorMessage, + IllegalStateError, + LoadedDataConverter, +} from '@temporalio/common'; +import * as native from '@temporalio/core-bridge'; +import { decodeArrayFromPayloads, decodeFromPayloadsAtIndex, - defaultPayloadConverter, encodeErrorToFailure, encodeToPayload, - errorMessage, - IllegalStateError, loadDataConverter, - tsToMs, -} from '@temporalio/common'; +} from '@temporalio/internal-non-workflow-common'; import { extractSpanContextFromHeaders, linkSpans, NUM_JOBS_ATTR_KEY, RUN_ID_ATTR_KEY, TASK_TOKEN_ATTR_KEY, -} from '@temporalio/common/lib/otel'; -import * as native from '@temporalio/core-bridge'; +} from '@temporalio/internal-non-workflow-common/lib/otel'; +import { tsToMs } from '@temporalio/internal-workflow-common'; import { coresdk } from '@temporalio/proto'; import { SinkCall, WorkflowInfo } from '@temporalio/workflow'; -import { LoadedDataConverter } from '@temporalio/workflow-common'; import fs from 'fs/promises'; import { BehaviorSubject, diff --git a/packages/worker/src/workflow-codec-runner.ts b/packages/worker/src/workflow-codec-runner.ts index d9648d278..6dec52657 100644 --- a/packages/worker/src/workflow-codec-runner.ts +++ b/packages/worker/src/workflow-codec-runner.ts @@ -1,6 +1,6 @@ -import { decodeFailure, encodeFailure, Payload } from '@temporalio/common'; +import { Payload, PayloadCodec, ProtoFailure } from '@temporalio/common'; +import { decodeFailure, encodeFailure } from '@temporalio/internal-non-workflow-common'; import { coresdk } from '@temporalio/proto'; -import { PayloadCodec, ProtoFailure } from '@temporalio/workflow-common'; import { clone, setWith } from 'lodash'; /** diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index 9dda6fa0c..b594f3f36 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -6,9 +6,12 @@ "rootDir": "./src" }, "references": [ - { "path": "../client/tsconfig.json" }, - { "path": "../workflow/tsconfig.json" }, - { "path": "../activity/tsconfig.json" } + { "path": "../client" }, + { "path": "../workflow" }, + { "path": "../activity" }, + { "path": "../common" }, + { "path": "../internal-workflow-common" }, + { "path": "../internal-non-workflow-common" } ], "include": ["./src/**/*.ts"] } diff --git a/packages/workflow/package.json b/packages/workflow/package.json index eb91e9f7e..771ba2603 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -20,7 +20,7 @@ ], "scripts": {}, "dependencies": { - "@temporalio/workflow-common": "file:../workflow-common" + "@temporalio/internal-workflow-common": "file:../internal-workflow-common" }, "devDependencies": { "@temporalio/proto": "file:../proto" diff --git a/packages/workflow/src/cancellation-scope.ts b/packages/workflow/src/cancellation-scope.ts index 5beda2800..437070f69 100644 --- a/packages/workflow/src/cancellation-scope.ts +++ b/packages/workflow/src/cancellation-scope.ts @@ -1,4 +1,4 @@ -import { CancelledFailure, IllegalStateError } from '@temporalio/workflow-common'; +import { CancelledFailure, IllegalStateError } from '@temporalio/common'; import type { AsyncLocalStorage as ALS } from 'async_hooks'; // AsyncLocalStorage is injected via vm module into global scope. diff --git a/packages/workflow/src/errors.ts b/packages/workflow/src/errors.ts index 621f26dc8..90bc017ab 100644 --- a/packages/workflow/src/errors.ts +++ b/packages/workflow/src/errors.ts @@ -1,4 +1,4 @@ -import { CancelledFailure, ActivityFailure, ChildWorkflowFailure } from '@temporalio/workflow-common'; +import { CancelledFailure, ActivityFailure, ChildWorkflowFailure } from '@temporalio/common'; /** * Base class for all workflow errors diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts index a27db128b..06e9e89ad 100644 --- a/packages/workflow/src/index.ts +++ b/packages/workflow/src/index.ts @@ -51,39 +51,41 @@ */ export { - Workflow, - WorkflowResultType, - ActivityCancellationType, - ActivityFunction, - ActivityInterface, - ActivityOptions, - RetryPolicy, - rootCause, - IllegalStateError, - defaultPayloadConverter, - PayloadConverter, - WorkflowIdReusePolicy, ActivityFailure, ApplicationFailure, CancelledFailure, ChildWorkflowFailure, + defaultPayloadConverter, + PayloadConverter, + rootCause, ServerFailure, TemporalFailure, TerminatedFailure, TimeoutFailure, +} from '@temporalio/common'; +export { + ActivityCancellationType, + ActivityFunction, + ActivityInterface, + ActivityOptions, + IllegalStateError, + RetryPolicy, ValueError, -} from '@temporalio/workflow-common'; + Workflow, + WorkflowIdReusePolicy, + WorkflowResultType, +} from '@temporalio/internal-workflow-common'; +export { AsyncLocalStorage, CancellationScope, CancellationScopeOptions, ROOT_SCOPE } from './cancellation-scope'; +export * from './errors'; +export * from './interceptors'; export { - ChildWorkflowOptions, ChildWorkflowCancellationType, + ChildWorkflowOptions, ContinueAsNewOptions, ParentClosePolicy, WorkflowInfo, } from './interfaces'; -export * from './errors'; -export * from './workflow'; -export * from './interceptors'; -export { AsyncLocalStorage, ROOT_SCOPE, CancellationScope, CancellationScopeOptions } from './cancellation-scope'; +export { Sink, SinkCall, SinkFunction, Sinks } from './sinks'; export { Trigger } from './trigger'; -export { SinkFunction, Sink, Sinks, SinkCall } from './sinks'; +export * from './workflow'; export { ChildWorkflowHandle, ExternalWorkflowHandle } from './workflow-handle'; diff --git a/packages/workflow/src/interceptors.ts b/packages/workflow/src/interceptors.ts index 18a9d413a..7b621f46f 100644 --- a/packages/workflow/src/interceptors.ts +++ b/packages/workflow/src/interceptors.ts @@ -6,7 +6,8 @@ * @module */ -import { ActivityOptions, WorkflowExecution, Headers, Next } from '@temporalio/workflow-common'; +import { WorkflowExecution } from '@temporalio/common'; +import { ActivityOptions, Headers, Next } from '@temporalio/internal-workflow-common'; import type { coresdk } from '@temporalio/proto/lib/coresdk'; import { ChildWorkflowOptionsWithDefaults, ContinueAsNewOptions } from './interfaces'; diff --git a/packages/workflow/src/interfaces.ts b/packages/workflow/src/interfaces.ts index 44aa027cd..67171782a 100644 --- a/packages/workflow/src/interfaces.ts +++ b/packages/workflow/src/interfaces.ts @@ -1,6 +1,6 @@ import type { coresdk } from '@temporalio/proto/lib/coresdk'; -import { CommonWorkflowOptions } from '@temporalio/workflow-common'; -import { checkExtends } from '@temporalio/workflow-common/lib/type-helpers'; +import { CommonWorkflowOptions } from '@temporalio/internal-workflow-common'; +import { checkExtends } from '@temporalio/internal-workflow-common'; /** * Workflow execution information diff --git a/packages/workflow/src/internals.ts b/packages/workflow/src/internals.ts index 3f6feb926..93dd384d9 100644 --- a/packages/workflow/src/internals.ts +++ b/packages/workflow/src/internals.ts @@ -1,22 +1,25 @@ import { - composeInterceptors, - errorToFailure, + arrayFromPayloads, + defaultPayloadConverter, ensureTemporalFailure, + errorToFailure, failureToError, optionalFailureToOptionalError, - IllegalStateError, - WorkflowSignalType, PayloadConverter, - defaultPayloadConverter, - arrayFromPayloads, + TemporalFailure, +} from '@temporalio/common'; +import { + checkExtends, + composeInterceptors, + IllegalStateError, Workflow, WorkflowQueryType, - TemporalFailure, -} from '@temporalio/workflow-common'; -import { checkExtends } from '@temporalio/workflow-common/lib/type-helpers'; + WorkflowSignalType, +} from '@temporalio/internal-workflow-common'; import type { coresdk } from '@temporalio/proto/lib/coresdk'; import { alea, RNG } from './alea'; -import { ContinueAsNew, WorkflowInfo } from './interfaces'; +import { ROOT_SCOPE } from './cancellation-scope'; +import { DeterminismViolationError, isCancellation, WorkflowExecutionAlreadyStartedError } from './errors'; import { QueryInput, SignalInput, @@ -24,9 +27,8 @@ import { WorkflowInterceptors, WorkflowInterceptorsFactory, } from './interceptors'; -import { DeterminismViolationError, WorkflowExecutionAlreadyStartedError, isCancellation } from './errors'; +import { ContinueAsNew, WorkflowInfo } from './interfaces'; import { SinkCall } from './sinks'; -import { ROOT_SCOPE } from './cancellation-scope'; enum StartChildWorkflowExecutionFailedCause { START_CHILD_WORKFLOW_EXECUTION_FAILED_CAUSE_UNSPECIFIED = 0, diff --git a/packages/workflow/src/worker-interface.ts b/packages/workflow/src/worker-interface.ts index e47f16a5e..ff986f9c2 100644 --- a/packages/workflow/src/worker-interface.ts +++ b/packages/workflow/src/worker-interface.ts @@ -3,25 +3,23 @@ * * @module */ +import { ApplicationFailure, errorToFailure as _errorToFailure, ProtoFailure } from '@temporalio/common'; import { + composeInterceptors, + errorMessage, IllegalStateError, msToTs, tsToMs, - composeInterceptors, Workflow, - ApplicationFailure, - errorMessage, - errorToFailure as _errorToFailure, - ProtoFailure, -} from '@temporalio/workflow-common'; +} from '@temporalio/internal-workflow-common'; import type { coresdk } from '@temporalio/proto/lib/coresdk'; -import { WorkflowInfo } from './interfaces'; -import { handleWorkflowFailure, InterceptorsImportFunc, state, WorkflowsImportFunc } from './internals'; -import { storage } from './cancellation-scope'; import { alea } from './alea'; +import { storage } from './cancellation-scope'; import { DeterminismViolationError } from './errors'; -import { SinkCall } from './sinks'; import { WorkflowInterceptorsFactory } from './interceptors'; +import { WorkflowInfo } from './interfaces'; +import { handleWorkflowFailure, InterceptorsImportFunc, state, WorkflowsImportFunc } from './internals'; +import { SinkCall } from './sinks'; export interface WorkflowCreateOptions { info: WorkflowInfo; diff --git a/packages/workflow/src/workflow-handle.ts b/packages/workflow/src/workflow-handle.ts index 0678532c5..c207a9915 100644 --- a/packages/workflow/src/workflow-handle.ts +++ b/packages/workflow/src/workflow-handle.ts @@ -1,4 +1,4 @@ -import { BaseWorkflowHandle, SignalDefinition, Workflow } from '@temporalio/workflow-common'; +import { BaseWorkflowHandle, SignalDefinition, Workflow } from '@temporalio/internal-workflow-common'; /** * Handle representing an external Workflow execution diff --git a/packages/workflow/src/workflow.ts b/packages/workflow/src/workflow.ts index 42a09d6ae..713a72919 100644 --- a/packages/workflow/src/workflow.ts +++ b/packages/workflow/src/workflow.ts @@ -1,22 +1,23 @@ +import { mapToPayloads, toPayloads } from '@temporalio/common'; import { ActivityFunction, + ActivityInterface, ActivityOptions, + compileRetryPolicy, + composeInterceptors, IllegalStateError, + msOptionalToTs, msToNumber, msToTs, - msOptionalToTs, - Workflow, - composeInterceptors, - mapToPayloads, - WorkflowResultType, - SignalDefinition, QueryDefinition, + SignalDefinition, WithWorkflowArgs, + Workflow, + WorkflowResultType, WorkflowReturnType, - compileRetryPolicy, - ActivityInterface, - toPayloads, -} from '@temporalio/workflow-common'; +} from '@temporalio/internal-workflow-common'; +import { CancellationScope, registerSleepImplementation } from './cancellation-scope'; +import { ActivityInput, SignalWorkflowInput, StartChildWorkflowExecutionInput, TimerInput } from './interceptors'; import { ChildWorkflowCancellationType, ChildWorkflowOptions, @@ -26,10 +27,8 @@ import { WorkflowInfo, } from './interfaces'; import { state } from './internals'; -import { ActivityInput, StartChildWorkflowExecutionInput, SignalWorkflowInput, TimerInput } from './interceptors'; import { Sinks } from './sinks'; -import { CancellationScope, registerSleepImplementation } from './cancellation-scope'; -import { ExternalWorkflowHandle, ChildWorkflowHandle } from './workflow-handle'; +import { ChildWorkflowHandle, ExternalWorkflowHandle } from './workflow-handle'; // Avoid a circular dependency registerSleepImplementation(sleep); diff --git a/packages/workflow/tsconfig.json b/packages/workflow/tsconfig.json index 840091b7c..d7a513019 100644 --- a/packages/workflow/tsconfig.json +++ b/packages/workflow/tsconfig.json @@ -4,10 +4,6 @@ "rootDir": "./src", "outDir": "./lib" }, - "references": [ - { - "path": "../workflow-common/tsconfig.json" - } - ], + "references": [{ "path": "../internal-workflow-common" }], "include": ["./src/**/*.ts"] } From f37057ce4eec28aadc9de3aa589894a42f1bc414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Mon, 28 Feb 2022 10:53:45 -0500 Subject: [PATCH 23/33] fix: UndefinedPayloadConverter test --- .../common/src/converter/payload-codec.ts | 3 ++- .../src/converter/payload-converters.ts | 2 +- packages/common/src/protobufs.ts | 16 ++++++++--- packages/meta/src/index.ts | 1 + packages/test/src/test-data-converter.ts | 27 +++++++++++++++---- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/common/src/converter/payload-codec.ts b/packages/common/src/converter/payload-codec.ts index 40cc02e1b..a0fa2231d 100644 --- a/packages/common/src/converter/payload-codec.ts +++ b/packages/common/src/converter/payload-codec.ts @@ -2,7 +2,8 @@ import { Payload } from './types'; /** * `PayloadCodec` is an optional step that happens between the wire and the {@link PayloadConverter}: - * Temporal Server ↔️ Wire ↔️ PayloadCodec ↔️ PayloadConverter ↔️ User code + * + * Temporal Server ↔️ Wire ↔️ `PayloadCodec` ↔️ `PayloadConverter` ↔️ User code * * Implement this to transform an array of {@link Payload}s to/from the format sent over the wire and stored by Temporal Server. * Common transformations are encryption and compression. diff --git a/packages/common/src/converter/payload-converters.ts b/packages/common/src/converter/payload-converters.ts index 9ee8871f8..9fb632e0c 100644 --- a/packages/common/src/converter/payload-converters.ts +++ b/packages/common/src/converter/payload-converters.ts @@ -13,7 +13,7 @@ export class UndefinedPayloadConverter implements PayloadConverterWithEncoding { public encodingType = encodingTypes.METADATA_ENCODING_NULL; public toPayload(value: unknown): Payload { - if (value !== undefined) throw new UnsupportedTypeError(); // Can't encode + if (value !== undefined) throw new UnsupportedTypeError('Can only encode undefined'); // Can't encode return { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL, diff --git a/packages/common/src/protobufs.ts b/packages/common/src/protobufs.ts index edfc09ffe..666d65f50 100644 --- a/packages/common/src/protobufs.ts +++ b/packages/common/src/protobufs.ts @@ -1,5 +1,15 @@ -// Don't export from index to save space in Workflow bundle for users who don't use Protobufs -// Import with: -// import { patchProtobufRoot } from '@temporalio/common/lib/protobufs'; +/** + * Entry point for classes and utilities related to using + * [Protobufs](https://docs.temporal.io/docs/typescript/data-converters#protobufs) for serialization. + * + * Import from `@temporalio/common/lib/protobufs`, for example: + * + * ``` + * import { patchProtobufRoot } from '@temporalio/common/lib/protobufs'; + * ``` + * @module + */ + +// Don't export from index, so we save space in Workflow bundles of users who don't use Protobufs export * from './converter/protobuf-payload-converters'; export * from './converter/patch-protobuf-root'; diff --git a/packages/meta/src/index.ts b/packages/meta/src/index.ts index f277167ff..a513e356c 100644 --- a/packages/meta/src/index.ts +++ b/packages/meta/src/index.ts @@ -2,5 +2,6 @@ export * as worker from '@temporalio/worker'; export * as client from '@temporalio/client'; export * as proto from '@temporalio/proto'; export * as common from '@temporalio/common'; +export * as protobufs from '@temporalio/common/lib/protobufs'; export * as workflow from '@temporalio/workflow'; export * as activity from '@temporalio/activity'; diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 79489e08f..761c7b2c8 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -6,6 +6,7 @@ import { defaultPayloadConverter, JsonPayloadConverter, UndefinedPayloadConverter, + UnsupportedTypeError, ValueError, } from '@temporalio/common'; import { @@ -30,11 +31,27 @@ import { protobufWorkflow } from './workflows'; test('UndefinedPayloadConverter converts from undefined only', async (t) => { const converter = new UndefinedPayloadConverter(); - t.is(await converter.toPayload(null), undefined); - t.is(await converter.toPayload({}), undefined); - t.is(await converter.toPayload(1), undefined); - t.is(await converter.toPayload(0), undefined); - t.is(await converter.toPayload('abc'), undefined); + await t.throwsAsync(async () => await converter.toPayload(null), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode undefined', + }); + await t.throwsAsync(async () => await converter.toPayload({}), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode undefined', + }); + await t.throwsAsync(async () => await converter.toPayload(1), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode undefined', + }); + await t.throwsAsync(async () => await converter.toPayload(0), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode undefined', + }); + + await t.throwsAsync(async () => await converter.toPayload('abc'), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode undefined', + }); t.deepEqual(await converter.toPayload(undefined), { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL }, }); From cd1a310d8dfc4a0343e73a1725746f81d6af143b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Mon, 28 Feb 2022 12:56:05 -0500 Subject: [PATCH 24/33] chore: Fix tests --- .../common/src/converter/payload-converter.ts | 2 +- .../src/converter/payload-converters.ts | 27 +++++- packages/docs/docs/index.md | 2 +- .../internal-workflow-common/src/errors.ts | 10 ++- .../test/src/test-custom-data-converter.ts | 5 +- packages/test/src/test-data-converter.ts | 82 +++++++++++++------ 6 files changed, 93 insertions(+), 35 deletions(-) diff --git a/packages/common/src/converter/payload-converter.ts b/packages/common/src/converter/payload-converter.ts index 6eb300c54..a73b50b95 100644 --- a/packages/common/src/converter/payload-converter.ts +++ b/packages/common/src/converter/payload-converter.ts @@ -56,7 +56,7 @@ export class CompositePayloadConverter implements PayloadConverter { } } } - throw new ValueError(`Cannot serialize ${value}`); + throw new ValueError(`Cannot serialize ${value} of type ${typeof value}`); } /** diff --git a/packages/common/src/converter/payload-converters.ts b/packages/common/src/converter/payload-converters.ts index 9fb632e0c..db16dec88 100644 --- a/packages/common/src/converter/payload-converters.ts +++ b/packages/common/src/converter/payload-converters.ts @@ -1,4 +1,9 @@ -import { UnsupportedTypeError, ValueError } from '@temporalio/internal-workflow-common'; +import { + errorMessage, + UnsupportedJsonTypeError, + UnsupportedTypeError, + ValueError, +} from '@temporalio/internal-workflow-common'; import { PayloadConverter } from './payload-converter'; import { encodingKeys, EncodingType, encodingTypes, METADATA_ENCODING_KEY, Payload, str, u8 } from './types'; @@ -33,12 +38,26 @@ export class JsonPayloadConverter implements PayloadConverterWithEncoding { public encodingType = encodingTypes.METADATA_ENCODING_JSON; public toPayload(value: unknown): Payload { - if (value === undefined) throw new UnsupportedTypeError(); // Should be encoded with the UndefinedPayloadConverter + if (value === undefined) + throw new UnsupportedTypeError("Can't encode undefined. Use UndefinedPayloadConverter instead."); + + let json; + try { + json = JSON.stringify(value); + } catch (e) { + throw new UnsupportedJsonTypeError( + `Can't run JSON.stringify on this value: ${value}. Either convert it (or its properties) to JSON-serializable values (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description ), or use a custom data converter: https://docs.temporal.io/docs/typescript/data-converters . JSON.stringify error message: ${errorMessage( + e + )}`, + e as Error + ); + } + return { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_JSON, }, - data: u8(JSON.stringify(value)), + data: u8(json), }; } @@ -59,7 +78,7 @@ export class BinaryPayloadConverter implements PayloadConverterWithEncoding { public toPayload(value: unknown): Payload { // TODO: support any DataView or ArrayBuffer? if (!(value instanceof Uint8Array)) { - throw new UnsupportedTypeError(); + throw new UnsupportedTypeError('Can only encode Uint8Array'); } return { metadata: { diff --git a/packages/docs/docs/index.md b/packages/docs/docs/index.md index e11010cf6..8678eda78 100644 --- a/packages/docs/docs/index.md +++ b/packages/docs/docs/index.md @@ -14,5 +14,5 @@ slug: / | [`@temporalio/workflow`](./api/namespaces/workflow) | Workflow runtime library | | [`@temporalio/activity`](./api/namespaces/activity) | Access to current activity context | | [`@temporalio/client`](./api/namespaces/client) | Communicate with the Temporal service for things like administration and scheduling workflows | -| [`@temporalio/common`](./api/namespaces/common) | Utility functions and class which can be used in both Workflow and non Workflow code | +| [`@temporalio/common`](./api/namespaces/common) | Common library for code that's used across the Client, Worker, and/or Workflow | | [`@temporalio/proto`](./api/namespaces/proto) | Compiled protobuf definitions | diff --git a/packages/internal-workflow-common/src/errors.ts b/packages/internal-workflow-common/src/errors.ts index 1f9d905b8..0eee3f20e 100644 --- a/packages/internal-workflow-common/src/errors.ts +++ b/packages/internal-workflow-common/src/errors.ts @@ -6,7 +6,7 @@ export class DataConverterError extends Error { public readonly name: string = 'DataConverterError'; } -export class PayloadConverterError extends Error { +export class PayloadConverterError extends DataConverterError { public readonly name: string = 'PayloadConverterError'; } @@ -14,6 +14,14 @@ export class UnsupportedTypeError extends PayloadConverterError { public readonly name: string = 'UnsupportedTypeError'; } +export class UnsupportedJsonTypeError extends UnsupportedTypeError { + public readonly name: string = 'UnsupportedJsonTypeError'; + + constructor(message: string | undefined, public readonly cause?: Error) { + super(message ?? undefined); + } +} + /** * Used in different parts of the project to signal that something unexpected has happened */ diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-data-converter.ts index 0f28b4d3d..5a48acd13 100644 --- a/packages/test/src/test-custom-data-converter.ts +++ b/packages/test/src/test-custom-data-converter.ts @@ -73,7 +73,7 @@ test('Worker throws on invalid payloadConverterPath', async (t) => { dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter-no-export') }, }), { - message: /The module at payloadConverterPath .* does not have a `dataConverter` named export./, + message: /Module .* does not have a `payloadConverter` named export/, } ); @@ -83,8 +83,7 @@ test('Worker throws on invalid payloadConverterPath', async (t) => { dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter-bad-export') }, }), { - message: - /The `dataConverter` named export at payloadConverterPath .* should be an instance of a class that implements the DataConverter interface./, + message: /payloadConverter export at .* must be an object with toPayload and fromPayload methods/, } ); }); diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-data-converter.ts index 761c7b2c8..a75d4ae33 100644 --- a/packages/test/src/test-data-converter.ts +++ b/packages/test/src/test-data-converter.ts @@ -2,24 +2,25 @@ import { Connection, WorkflowClient } from '@temporalio/client'; import { BinaryPayloadConverter, - DataConverterError, defaultPayloadConverter, JsonPayloadConverter, + PayloadConverterError, UndefinedPayloadConverter, + UnsupportedJsonTypeError, UnsupportedTypeError, ValueError, } from '@temporalio/common'; -import { - DefaultPayloadConverterWithProtobufs, - ProtobufBinaryPayloadConverter, - ProtobufJsonPayloadConverter, -} from '@temporalio/common/lib/protobufs'; import { encodingKeys, METADATA_ENCODING_KEY, METADATA_MESSAGE_TYPE_KEY, u8, } from '@temporalio/common/lib/converter/types'; +import { + DefaultPayloadConverterWithProtobufs, + ProtobufBinaryPayloadConverter, + ProtobufJsonPayloadConverter, +} from '@temporalio/common/lib/protobufs'; import { Core, DefaultLogger, Worker } from '@temporalio/worker'; import test from 'ava'; import { v4 as uuid4 } from 'uuid'; @@ -47,11 +48,11 @@ test('UndefinedPayloadConverter converts from undefined only', async (t) => { instanceOf: UnsupportedTypeError, message: 'Can only encode undefined', }); - await t.throwsAsync(async () => await converter.toPayload('abc'), { instanceOf: UnsupportedTypeError, message: 'Can only encode undefined', }); + t.deepEqual(await converter.toPayload(undefined), { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL }, }); @@ -64,11 +65,27 @@ test('UndefinedPayloadConverter converts to undefined', async (t) => { test('BinaryPayloadConverter converts from Uint8Array', async (t) => { const converter = new BinaryPayloadConverter(); - t.is(await converter.toPayload(null), undefined); - t.is(await converter.toPayload({}), undefined); - t.is(await converter.toPayload(1), undefined); - t.is(await converter.toPayload(0), undefined); - t.is(await converter.toPayload('abc'), undefined); + await t.throwsAsync(async () => await converter.toPayload(null), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode Uint8Array', + }); + await t.throwsAsync(async () => await converter.toPayload({}), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode Uint8Array', + }); + await t.throwsAsync(async () => await converter.toPayload(1), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode Uint8Array', + }); + await t.throwsAsync(async () => await converter.toPayload(0), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode Uint8Array', + }); + await t.throwsAsync(async () => await converter.toPayload('abc'), { + instanceOf: UnsupportedTypeError, + message: 'Can only encode Uint8Array', + }); + t.deepEqual(await converter.toPayload(u8('abc')), { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_RAW }, data: u8('abc'), @@ -91,7 +108,15 @@ test('JsonPayloadConverter converts from non undefined', async (t) => { t.deepEqual(await converter.toPayload(1), payload(1)); t.deepEqual(await converter.toPayload(0), payload(0)); t.deepEqual(await converter.toPayload('abc'), payload('abc')); - t.is(await converter.toPayload(undefined), undefined); + + await t.throwsAsync(async () => await converter.toPayload(undefined), { + instanceOf: UnsupportedTypeError, + message: /Can't encode undefined/, + }); + await t.throwsAsync(async () => await converter.toPayload(0n), { + instanceOf: UnsupportedJsonTypeError, + message: /Can't run JSON.stringify/, + }); }); test('JsonPayloadConverter converts to object', async (t) => { @@ -141,7 +166,7 @@ test('ProtobufBinaryPayloadConverter throws detailed errors', async (t) => { data: root.ProtoActivityInput.encode(instance).finish(), }), { - instanceOf: DataConverterError, + instanceOf: PayloadConverterError, message: 'Got a `NonExistentMessageClass` protobuf message but cannot find corresponding message class in `root`', } ); @@ -236,19 +261,26 @@ test('DefaultPayloadConverter converts protobufs', async (t) => { ); }); -test('defaultPayloadConverter converts to payload by trying each converter in order', async (t) => { +test('DefaultPayloadConverter converts to payload by trying each converter in order', async (t) => { + const defaultPayloadConverterWithProtos = new DefaultPayloadConverterWithProtobufs({ protobufRoot: root }); const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); t.deepEqual( - defaultPayloadConverter.toPayload(instance), + defaultPayloadConverterWithProtos.toPayload(instance), await new ProtobufJsonPayloadConverter().toPayload(instance) ); - t.deepEqual(defaultPayloadConverter.toPayload('abc'), await new JsonPayloadConverter().toPayload('abc')); - t.deepEqual(defaultPayloadConverter.toPayload(undefined), await new UndefinedPayloadConverter().toPayload(undefined)); - t.deepEqual(defaultPayloadConverter.toPayload(u8('abc')), await new BinaryPayloadConverter().toPayload(u8('abc'))); - await t.throws(() => defaultPayloadConverter.toPayload(0n), { - instanceOf: TypeError, - message: 'Do not know how to serialize a BigInt', + t.deepEqual(defaultPayloadConverterWithProtos.toPayload('abc'), await new JsonPayloadConverter().toPayload('abc')); + t.deepEqual( + defaultPayloadConverterWithProtos.toPayload(undefined), + await new UndefinedPayloadConverter().toPayload(undefined) + ); + t.deepEqual( + defaultPayloadConverterWithProtos.toPayload(u8('abc')), + await new BinaryPayloadConverter().toPayload(u8('abc')) + ); + await t.throws(() => defaultPayloadConverterWithProtos.toPayload(0n), { + instanceOf: ValueError, + message: 'Cannot serialize 0 of type bigint', }); }); @@ -263,15 +295,15 @@ test('defaultPayloadConverter converts from payload by payload type', async (t) await t.throwsAsync( async () => defaultPayloadConverter.fromPayload({ - metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_PROTOBUF }, + metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_JSON }, }), { instanceOf: ValueError, message: 'Got payload with no data' } ); const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); const protoError = { - instanceOf: DataConverterError, - message: 'Unable to deserialize protobuf message without `root` being provided', + instanceOf: ValueError, + message: /Unknown encoding: .*protobuf/, }; await t.throwsAsync( async () => defaultPayloadConverter.fromPayload(new ProtobufBinaryPayloadConverter(root).toPayload(instance)!), From 663ffbb94b3c0dd0032ceb2d6aa650bdbef077d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Mon, 28 Feb 2022 15:49:02 -0500 Subject: [PATCH 25/33] chore: Fix integration test and add codec tests --- .../src/codec-helpers.ts | 2 +- .../src/activities/create-concat-activity.ts | 9 + packages/test/src/helpers.ts | 5 + .../test/src/test-custom-payload-codec.ts | 181 ++++++++++++++++++ ...er.ts => test-custom-payload-converter.ts} | 6 +- ...converter.ts => test-payload-converter.ts} | 0 packages/test/src/workflows/index.ts | 1 + packages/test/src/workflows/protobufs.ts | 10 +- packages/test/src/workflows/two-strings.ts | 23 +++ 9 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 packages/test/src/activities/create-concat-activity.ts create mode 100644 packages/test/src/test-custom-payload-codec.ts rename packages/test/src/{test-custom-data-converter.ts => test-custom-payload-converter.ts} (94%) rename packages/test/src/{test-data-converter.ts => test-payload-converter.ts} (100%) create mode 100644 packages/test/src/workflows/two-strings.ts diff --git a/packages/internal-non-workflow-common/src/codec-helpers.ts b/packages/internal-non-workflow-common/src/codec-helpers.ts index 6206480cf..d244a663d 100644 --- a/packages/internal-non-workflow-common/src/codec-helpers.ts +++ b/packages/internal-non-workflow-common/src/codec-helpers.ts @@ -70,7 +70,7 @@ export async function encodeToPayloads( if (values.length === 0) { return undefined; } - const payloads = toPayloads(payloadConverter, values); + const payloads = toPayloads(payloadConverter, ...values); return payloads ? await payloadCodec.encode(payloads) : undefined; } diff --git a/packages/test/src/activities/create-concat-activity.ts b/packages/test/src/activities/create-concat-activity.ts new file mode 100644 index 000000000..e04623530 --- /dev/null +++ b/packages/test/src/activities/create-concat-activity.ts @@ -0,0 +1,9 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +export function createConcatActivity(logs: string[]) { + return { + async concat(string1: string, string2: string): Promise { + logs.push(`Activity${string1}${string2}`); + return string1 + string2; + }, + }; +} diff --git a/packages/test/src/helpers.ts b/packages/test/src/helpers.ts index cc9af5b69..be5badafa 100644 --- a/packages/test/src/helpers.ts +++ b/packages/test/src/helpers.ts @@ -20,3 +20,8 @@ export async function sleep(ms: number): Promise { export function cleanStackTrace(stackTrace: string | undefined | null): string | undefined { return stackTrace?.replace(/ \([^)]+\)|-isolate:\d+:\d+/g, ''); } + +export function containsMatching(strings: string[], regex: RegExp): boolean { + console.log('strings:', strings); + return strings.some((str) => regex.test(str)); +} diff --git a/packages/test/src/test-custom-payload-codec.ts b/packages/test/src/test-custom-payload-codec.ts new file mode 100644 index 000000000..34fb45d66 --- /dev/null +++ b/packages/test/src/test-custom-payload-codec.ts @@ -0,0 +1,181 @@ +import { Connection, WorkflowClient } from '@temporalio/client'; +import { Payload, PayloadCodec } from '@temporalio/common'; +import { InjectedSinks, Worker } from '@temporalio/worker'; +import test from 'ava'; +import { v4 as uuid4 } from 'uuid'; +import { createConcatActivity } from './activities/create-concat-activity'; +import { RUN_INTEGRATION_TESTS, u8 } from './helpers'; +import { defaultOptions } from './mock-native-worker'; +import { LogSinks, twoStrings, twoStringsActivity } from './workflows'; + +class TestEncodeCodec implements PayloadCodec { + async encode(payloads: Payload[]): Promise { + return payloads.map((payload) => { + payload.data = u8('"encoded"'); + return payload; + }); + } + + async decode(payloads: Payload[]): Promise { + return payloads; + } +} + +class TestDecodeCodec implements PayloadCodec { + async encode(payloads: Payload[]): Promise { + return payloads; + } + + async decode(payloads: Payload[]): Promise { + return payloads.map((payload) => { + payload.data = u8('"decoded"'); + return payload; + }); + } +} + +if (RUN_INTEGRATION_TESTS) { + test('Workflow arguments and retvals are encoded', async (t) => { + const logs: string[] = []; + const sinks: InjectedSinks = { + logger: { + log: { + fn(_, message) { + logs.push(message); + }, + }, + }, + }; + + const dataConverter = { payloadCodec: new TestEncodeCodec() }; + const taskQueue = 'test-workflow-encoded'; + const worker = await Worker.create({ + ...defaultOptions, + taskQueue, + dataConverter, // TODO { payloadCodec: defaultPayloadCodec } + sinks, + }); + const client = new WorkflowClient(new Connection().service, { dataConverter }); + const runAndShutdown = async () => { + const result = await client.execute(twoStrings, { + args: ['arg1', 'arg2'], + workflowId: uuid4(), + taskQueue, + }); + + t.is(result, 'encoded'); + worker.shutdown(); + }; + await Promise.all([worker.run(), runAndShutdown()]); + t.is(logs[0], 'encodedencoded'); + }); + + test('Workflow arguments and retvals are decoded', async (t) => { + const logs: string[] = []; + const sinks: InjectedSinks = { + logger: { + log: { + fn(_, message) { + logs.push(message); + }, + }, + }, + }; + + const dataConverter = { payloadCodec: new TestDecodeCodec() }; + const taskQueue = 'test-workflow-decoded'; + const worker = await Worker.create({ + ...defaultOptions, + taskQueue, + dataConverter, // TODO { payloadCodec: defaultPayloadCodec } + sinks, + }); + const client = new WorkflowClient(new Connection().service, { dataConverter }); + const runAndShutdown = async () => { + const result = await client.execute(twoStrings, { + args: ['arg1', 'arg2'], + workflowId: uuid4(), + taskQueue, + }); + + t.is(result, 'decoded'); + worker.shutdown(); + }; + await Promise.all([worker.run(), runAndShutdown()]); + t.is(logs[0], 'decodeddecoded'); + }); + + test('Activity arguments and retvals are encoded', async (t) => { + const workflowLogs: string[] = []; + const sinks: InjectedSinks = { + logger: { + log: { + fn(_, message) { + workflowLogs.push(message); + }, + }, + }, + }; + const activityLogs: string[] = []; + + const dataConverter = { payloadCodec: new TestEncodeCodec() }; + const taskQueue = 'test-activity-encoded'; + const worker = await Worker.create({ + ...defaultOptions, + activities: createConcatActivity(activityLogs), + taskQueue, + dataConverter, + sinks, + }); + const client = new WorkflowClient(new Connection().service, { dataConverter }); + const runAndShutdown = async () => { + const result = await client.execute(twoStringsActivity, { + workflowId: uuid4(), + taskQueue, + }); + + t.is(result, 'encoded'); + worker.shutdown(); + }; + await Promise.all([worker.run(), runAndShutdown()]); + t.is(workflowLogs[0], 'encoded'); + t.is(activityLogs[0], 'Activityencodedencoded'); + }); + + test('Activity arguments and retvals are decoded', async (t) => { + const workflowLogs: string[] = []; + const sinks: InjectedSinks = { + logger: { + log: { + fn(_, message) { + workflowLogs.push(message); + }, + }, + }, + }; + const activityLogs: string[] = []; + + const dataConverter = { payloadCodec: new TestDecodeCodec() }; + const taskQueue = 'test-activity-decoded'; + const worker = await Worker.create({ + ...defaultOptions, + activities: createConcatActivity(activityLogs), + taskQueue, + dataConverter, + sinks, + }); + const client = new WorkflowClient(new Connection().service, { dataConverter }); + const runAndShutdown = async () => { + const result = await client.execute(twoStringsActivity, { + workflowId: uuid4(), + taskQueue, + }); + + t.is(result, 'decoded'); + worker.shutdown(); + }; + await Promise.all([worker.run(), runAndShutdown()]); + t.is(workflowLogs[0], 'decoded'); + t.is(activityLogs[0], 'Activitydecodeddecoded'); + }); +} diff --git a/packages/test/src/test-custom-data-converter.ts b/packages/test/src/test-custom-payload-converter.ts similarity index 94% rename from packages/test/src/test-custom-data-converter.ts rename to packages/test/src/test-custom-payload-converter.ts index 5a48acd13..25d7f12cc 100644 --- a/packages/test/src/test-custom-data-converter.ts +++ b/packages/test/src/test-custom-payload-converter.ts @@ -4,6 +4,7 @@ import { coresdk } from '@temporalio/proto'; import { Worker } from '@temporalio/worker'; import test, { ExecutionContext } from 'ava'; import { v4 as uuid4 } from 'uuid'; +import { ProtoActivityResult } from '../protos/root'; import { protoActivity } from './activities'; import { cleanStackTrace, RUN_INTEGRATION_TESTS } from './helpers'; import { defaultOptions, isolateFreeWorker } from './mock-native-worker'; @@ -35,7 +36,7 @@ function compareCompletion( if (RUN_INTEGRATION_TESTS) { test('Client and Worker work with provided dataConverter', async (t) => { const dataConverter = { payloadConverterPath: require.resolve('./payload-converters/payload-converter') }; - const taskQueue = 'test-custom-data-converter'; + const taskQueue = 'test-custom-payload-converter'; const worker = await Worker.create({ ...defaultOptions, taskQueue, @@ -49,7 +50,8 @@ if (RUN_INTEGRATION_TESTS) { workflowId: uuid4(), taskQueue, }); - t.deepEqual(result, messageInstance); + + t.deepEqual(result, ProtoActivityResult.create({ sentence: `Proto is 1 years old.` })); worker.shutdown(); }; await Promise.all([worker.run(), runAndShutdown()]); diff --git a/packages/test/src/test-data-converter.ts b/packages/test/src/test-payload-converter.ts similarity index 100% rename from packages/test/src/test-data-converter.ts rename to packages/test/src/test-payload-converter.ts diff --git a/packages/test/src/workflows/index.ts b/packages/test/src/workflows/index.ts index 0c9dab3c7..7b1775f7d 100644 --- a/packages/test/src/workflows/index.ts +++ b/packages/test/src/workflows/index.ts @@ -77,3 +77,4 @@ export * from './unhandled-rejection'; export * from './protobufs'; export { interceptorExample } from './interceptor-example'; export { internalsInterceptorExample } from './internals-interceptor-example'; +export * from './two-strings'; diff --git a/packages/test/src/workflows/protobufs.ts b/packages/test/src/workflows/protobufs.ts index 66a64a5ad..5456e814c 100644 --- a/packages/test/src/workflows/protobufs.ts +++ b/packages/test/src/workflows/protobufs.ts @@ -1,5 +1,9 @@ -import type { ProtoActivityInput } from '../../protos/root'; +import { proxyActivities } from '@temporalio/workflow'; +import type { ProtoActivityInput, ProtoActivityResult } from '../../protos/root'; +import type * as activities from '../activities'; -export async function protobufWorkflow(args: ProtoActivityInput): Promise { - return args; +const { protoActivity } = proxyActivities({ startToCloseTimeout: '1s' }); + +export async function protobufWorkflow(args: ProtoActivityInput): Promise { + return await protoActivity(args); } diff --git a/packages/test/src/workflows/two-strings.ts b/packages/test/src/workflows/two-strings.ts new file mode 100644 index 000000000..3b1565bad --- /dev/null +++ b/packages/test/src/workflows/two-strings.ts @@ -0,0 +1,23 @@ +import { proxyActivities, proxySinks, Sinks } from '@temporalio/workflow'; +import type { createConcatActivity } from '../activities/create-concat-activity'; + +export interface LogSinks extends Sinks { + logger: { + log(message: string): void; + }; +} + +const { logger } = proxySinks(); + +const { concat } = proxyActivities>({ startToCloseTimeout: '1s' }); + +export async function twoStrings(string1: string, string2: string): Promise { + logger.log(string1 + string2); + return string1 + string2; +} + +export async function twoStringsActivity(): Promise { + const result = await concat('str1', 'str2'); + logger.log(result); + return result; +} From a29118b911a053a7a2e5378dcfa4afeb1b6fe626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Mon, 28 Feb 2022 19:56:52 -0500 Subject: [PATCH 26/33] chore: Disable header encoding for now --- packages/worker/src/workflow-codec-runner.ts | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/worker/src/workflow-codec-runner.ts b/packages/worker/src/workflow-codec-runner.ts index 6dec52657..25db3a240 100644 --- a/packages/worker/src/workflow-codec-runner.ts +++ b/packages/worker/src/workflow-codec-runner.ts @@ -52,14 +52,14 @@ export class WorkflowCodecRunner { clone ); } - if (decodedJob.startWorkflow?.headers) { - await Promise.all( - Object.entries(decodedJob.startWorkflow.headers).map(async ([header, payload]) => { - const [decodedPayload] = await this.codec.decode([payload]); - setWith(decodedJob, `startWorkflow.headers.${header}`, decodedPayload, clone); - }) - ); - } + // if (decodedJob.startWorkflow?.headers) { + // await Promise.all( + // Object.entries(decodedJob.startWorkflow.headers).map(async ([header, payload]) => { + // const [decodedPayload] = await this.codec.decode([payload]); + // setWith(decodedJob, `startWorkflow.headers.${header}`, decodedPayload, clone); + // }) + // ); + // } if (decodedJob.resolveActivity?.result?.completed?.result) { const [decodedResult] = await this.codec.decode([decodedJob.resolveActivity.result.completed.result]); setWith(decodedJob, 'resolveActivity.result.completed.result', decodedResult, clone); @@ -123,13 +123,13 @@ export class WorkflowCodecRunner { * Run codec.encode on the Payloads inside the Completion message. */ public async encodeCompletion(completionBytes: Uint8Array): Promise { - const completion = coresdk.workflow_completion.WorkflowActivationCompletion.decode(completionBytes); + const completion = coresdk.workflow_completion.WorkflowActivationCompletion.decodeDelimited(completionBytes); await Promise.all([ ...(completion.successful?.commands?.flatMap((command) => command ? [ - ...this.encodeMap(command.scheduleActivity, 'headerFields'), + // ...this.encodeMap(command.scheduleActivity, 'headerFields'), this.encodeArray(command.scheduleActivity, 'arguments'), this.encodeField(command.respondToQuery?.succeeded, 'response'), this.encodeFailure(command.respondToQuery, 'failed'), @@ -137,11 +137,11 @@ export class WorkflowCodecRunner { this.encodeFailure(command.failWorkflowExecution, 'failure'), this.encodeArray(command.continueAsNewWorkflowExecution, 'arguments'), ...this.encodeMap(command.continueAsNewWorkflowExecution, 'memo'), - ...this.encodeMap(command.continueAsNewWorkflowExecution, 'header'), + // ...this.encodeMap(command.continueAsNewWorkflowExecution, 'header'), ...this.encodeMap(command.continueAsNewWorkflowExecution, 'searchAttributes'), this.encodeArray(command.startChildWorkflowExecution, 'input'), ...this.encodeMap(command.startChildWorkflowExecution, 'memo'), - ...this.encodeMap(command.startChildWorkflowExecution, 'header'), + // ...this.encodeMap(command.startChildWorkflowExecution, 'header'), ...this.encodeMap(command.startChildWorkflowExecution, 'searchAttributes'), this.encodeArray(command.signalExternalWorkflowExecution, 'args'), ] From 4a931b7ba63c1c28d69cd33e8ffdadf4720a5db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Mon, 28 Feb 2022 20:20:40 -0500 Subject: [PATCH 27/33] chore: Fix rest of integration tests --- packages/test/src/test-integration.ts | 2 +- packages/test/src/test-payload-converter.ts | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/test/src/test-integration.ts b/packages/test/src/test-integration.ts index 8252609f7..3e0ee72ad 100644 --- a/packages/test/src/test-integration.ts +++ b/packages/test/src/test-integration.ts @@ -729,7 +729,7 @@ if (RUN_INTEGRATION_TESTS) { if (wftFailedEvent === undefined) { throw new Error('No WFT failed event'); } - t.is(wftFailedEvent.workflowTaskFailedEventAttributes?.failure?.message, 'Error: unhandled rejection'); + t.is(wftFailedEvent.workflowTaskFailedEventAttributes?.failure?.message, 'unhandled rejection'); }, { minTimeout: 300, factor: 1, retries: 100 } ); diff --git a/packages/test/src/test-payload-converter.ts b/packages/test/src/test-payload-converter.ts index a75d4ae33..7c3780e1b 100644 --- a/packages/test/src/test-payload-converter.ts +++ b/packages/test/src/test-payload-converter.ts @@ -218,11 +218,7 @@ if (RUN_INTEGRATION_TESTS) { markErrorThrown = resolve; }); const logger = new DefaultLogger('ERROR', (entry) => { - if ( - entry.meta?.error.stack.includes( - 'DataConverterError: Unable to deserialize protobuf message without `root` being provided' - ) - ) { + if (entry.meta?.error.stack.includes('ValueError: Unknown encoding: json/protobuf')) { markErrorThrown(); } }); @@ -236,7 +232,7 @@ if (RUN_INTEGRATION_TESTS) { }); const connection = new Connection(); const client = new WorkflowClient(connection.service, { - dataConverter: { payloadConverterPath: './payload-converters/payload-converter' }, + dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter') }, }); const runPromise = worker.run(); client.execute(protobufWorkflow, { From 29ac6e6033761f4b1a5c31055c7722a30328eb10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Mon, 28 Feb 2022 21:52:22 -0500 Subject: [PATCH 28/33] test: Run custom codec on integration tests --- packages/test/src/integration-tests.ts | 791 ++++++++++++++++++++ packages/test/src/test-integration-codec.ts | 27 + packages/test/src/test-integration.ts | 778 +------------------ 3 files changed, 821 insertions(+), 775 deletions(-) create mode 100644 packages/test/src/integration-tests.ts create mode 100644 packages/test/src/test-integration-codec.ts diff --git a/packages/test/src/integration-tests.ts b/packages/test/src/integration-tests.ts new file mode 100644 index 000000000..530ecdab1 --- /dev/null +++ b/packages/test/src/integration-tests.ts @@ -0,0 +1,791 @@ +/* eslint @typescript-eslint/no-non-null-assertion: 0 */ +import { + ActivityFailure, + ApplicationFailure, + Connection, + WorkflowClient, + WorkflowContinuedAsNewError, + WorkflowFailedError, +} from '@temporalio/client'; +import { + ChildWorkflowFailure, + defaultPayloadCodec, + defaultPayloadConverter, + Payload, + PayloadCodec, + RetryState, + TerminatedFailure, + TimeoutFailure, + TimeoutType, + WorkflowExecution, +} from '@temporalio/common'; +import { decodeFromPayloadsAtIndex } from '@temporalio/internal-non-workflow-common'; +import { tsToMs } from '@temporalio/internal-workflow-common'; +import * as iface from '@temporalio/proto'; +import { Core, DefaultLogger, Worker } from '@temporalio/worker'; +import asyncRetry from 'async-retry'; +import anyTest, { Implementation, TestInterface } from 'ava'; +import dedent from 'dedent'; +import ms from 'ms'; +import { v4 as uuid4 } from 'uuid'; +import * as activities from './activities'; +import { cleanStackTrace, u8 } from './helpers'; +import * as workflows from './workflows'; +import { withZeroesHTTPServer } from './zeroes-http-server'; + +const { EVENT_TYPE_TIMER_STARTED, EVENT_TYPE_TIMER_FIRED, EVENT_TYPE_TIMER_CANCELED } = + iface.temporal.api.enums.v1.EventType; + +const timerEventTypes = new Set([EVENT_TYPE_TIMER_STARTED, EVENT_TYPE_TIMER_FIRED, EVENT_TYPE_TIMER_CANCELED]); +const CHANGE_MARKER_NAME = 'core_patch'; + +export interface Context { + worker: Worker; + client: WorkflowClient; + runPromise: Promise; +} + +const _test = anyTest as TestInterface; +const namespace = 'default'; + +export function runIntegrationTests(codec?: PayloadCodec): void { + const test = (name: string, fn: Implementation) => _test(codec ? 'With codec—' + name : name, fn); + const dataConverter = { payloadCodec: codec ?? defaultPayloadCodec }; + const loadedDataConverter = { payloadConverter: defaultPayloadConverter, payloadCodec: codec ?? defaultPayloadCodec }; + async function fromPayload(payload: Payload) { + const [decodedPayload] = await dataConverter.payloadCodec.decode([payload]); + return defaultPayloadConverter.fromPayload(decodedPayload); + } + + // export function runIntegrationTests(PayloadCodec): void { + _test.before(async (t) => { + const logger = new DefaultLogger('DEBUG'); + // Use forwarded logging from core + await Core.install({ logger, telemetryOptions: { logForwardingLevel: 'INFO' } }); + const worker = await Worker.create({ + workflowsPath: require.resolve('./workflows'), + activities, + taskQueue: 'test', + dataConverter, + }); + + const runPromise = worker.run(); + // Catch the error here to avoid unhandled rejection + runPromise.catch((err) => { + console.error('Caught error while worker was running', err); + }); + const connection = new Connection(); + t.context = { + worker, + runPromise, + client: new WorkflowClient(connection.service, { dataConverter }), + }; + + // The initialization of the custom search attributes is slooooow. Wait for it to finish + await asyncRetry( + async () => { + try { + const handle = await t.context.client.start(workflows.sleeper, { + workflowId: uuid4(), + taskQueue: 'no_one_cares_pointless_queue', + workflowExecutionTimeout: 1000, + searchAttributes: { CustomIntField: 1 }, + }); + await handle.terminate(); + } catch (e: any) { + // We don't stop until we see an error that *isn't* the error about the field not being + // valid + if (!e.details.includes('CustomIntField')) { + return; + } + throw e; + } + }, + { + retries: 60, + maxTimeout: 1000, + } + ); + }); + + _test.after.always(async (t) => { + t.context.worker.shutdown(); + await t.context.runPromise; + }); + + test('Workflow not found results in failure', async (t) => { + const { client } = t.context; + const promise = client.execute('not-found', { + taskQueue: 'test', + workflowId: uuid4(), + }); + const err: WorkflowFailedError = await t.throwsAsync(() => promise, { + instanceOf: WorkflowFailedError, + }); + if (!(err.cause instanceof ApplicationFailure)) { + t.fail('Expected err.cause to be an instance of ApplicationFailure'); + return; + } + t.is(err.cause.type, 'ReferenceError'); + t.is(err.cause.message, "'not-found' is not a function"); + t.true(err.cause.nonRetryable); + t.is(err.cause.stack, "ApplicationFailure: 'not-found' is not a function"); + }); + + test('args-and-return', async (t) => { + const { client } = t.context; + const res = await client.execute(workflows.argsAndReturn, { + taskQueue: 'test', + workflowId: uuid4(), + args: ['Hello', undefined, u8('world!')], + }); + t.is(res, 'Hello, world!'); + }); + + test('cancel-fake-progress', async (t) => { + const { client } = t.context; + await client.execute(workflows.cancelFakeProgress, { + taskQueue: 'test', + workflowId: uuid4(), + }); + t.pass(); + }); + + test('cancel-http-request', async (t) => { + const { client } = t.context; + await withZeroesHTTPServer(async (port) => { + const url = `http://127.0.0.1:${port}`; + await client.execute(workflows.cancellableHTTPRequest, { + taskQueue: 'test', + workflowId: uuid4(), + args: [url], + }); + }); + t.pass(); + }); + + test('activity-failure with Error', async (t) => { + const { client } = t.context; + const err: WorkflowFailedError = await t.throwsAsync( + client.execute(workflows.activityFailure, { + taskQueue: 'test', + workflowId: uuid4(), + args: [{ useApplicationFailure: false }], + }), + { + instanceOf: WorkflowFailedError, + } + ); + t.is(err.message, 'Workflow execution failed'); + if (!(err.cause instanceof ActivityFailure)) { + t.fail('Expected err.cause to be an instance of ActivityFailure'); + return; + } + if (!(err.cause.cause instanceof ApplicationFailure)) { + t.fail('Expected err.cause.cause to be an instance of ApplicationFailure'); + return; + } + t.is(err.cause.cause.message, 'Fail me'); + t.is( + cleanStackTrace(err.cause.cause.stack), + dedent` + Error: Fail me + at Activity.throwAnError [as fn] + ` + ); + }); + + test('activity-failure with ApplicationFailure', async (t) => { + const { client } = t.context; + const err: WorkflowFailedError = await t.throwsAsync( + client.execute(workflows.activityFailure, { + taskQueue: 'test', + workflowId: uuid4(), + args: [{ useApplicationFailure: true }], + }), + { + instanceOf: WorkflowFailedError, + } + ); + t.is(err.message, 'Workflow execution failed'); + if (!(err.cause instanceof ActivityFailure)) { + t.fail('Expected err.cause to be an instance of ActivityFailure'); + return; + } + if (!(err.cause.cause instanceof ApplicationFailure)) { + t.fail('Expected err.cause.cause to be an instance of ApplicationFailure'); + return; + } + t.is(err.cause.cause.message, 'Fail me'); + t.is(err.cause.cause.type, 'Error'); + t.deepEqual(err.cause.cause.details, ['details', 123, false]); + t.is( + cleanStackTrace(err.cause.cause.stack), + dedent` + ApplicationFailure: Fail me + at Function.nonRetryable + at Activity.throwAnError [as fn] + ` + ); + }); + + test('child-workflow-invoke', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.childWorkflowInvoke, { + taskQueue: 'test', + workflowId: uuid4(), + }); + const { workflowId, runId, execResult, result } = await workflow.result(); + t.is(execResult, 'success'); + t.is(result, 'success'); + const child = client.getHandle(workflowId, runId); + t.is(await child.result(), 'success'); + }); + + test('child-workflow-failure', async (t) => { + const { client } = t.context; + const err: WorkflowFailedError = await t.throwsAsync( + client.execute(workflows.childWorkflowFailure, { + taskQueue: 'test', + workflowId: uuid4(), + }), + { + instanceOf: WorkflowFailedError, + } + ); + if (!(err.cause instanceof ChildWorkflowFailure)) { + return t.fail('Expected err.cause to be an instance of ChildWorkflowFailure'); + } + if (!(err.cause.cause instanceof ApplicationFailure)) { + return t.fail('Expected err.cause.cause to be an instance of ApplicationFailure'); + } + t.is(err.cause.cause.message, 'failure'); + t.is( + cleanStackTrace(err.cause.cause.stack), + dedent` + ApplicationFailure: failure + at Function.nonRetryable + at throwAsync + ` + ); + }); + + test('child-workflow-termination', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.childWorkflowTermination, { + taskQueue: 'test', + workflowId: uuid4(), + }); + + let childExecution: WorkflowExecution | undefined = undefined; + + while (childExecution === undefined) { + childExecution = await workflow.query(workflows.childExecutionQuery); + } + const child = client.getHandle(childExecution.workflowId!, childExecution.runId!); + await child.terminate(); + const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { + instanceOf: WorkflowFailedError, + }); + if (!(err.cause instanceof ChildWorkflowFailure)) { + return t.fail('Expected err.cause to be an instance of ChildWorkflowFailure'); + } + t.is(err.cause.retryState, RetryState.RETRY_STATE_NON_RETRYABLE_FAILURE); + if (!(err.cause.cause instanceof TerminatedFailure)) { + return t.fail('Expected err.cause.cause to be an instance of TerminatedFailure'); + } + }); + + test('child-workflow-timeout', async (t) => { + const { client } = t.context; + const err: WorkflowFailedError = await t.throwsAsync( + client.execute(workflows.childWorkflowTimeout, { + taskQueue: 'test', + workflowId: uuid4(), + }), + { + instanceOf: WorkflowFailedError, + } + ); + if (!(err.cause instanceof ChildWorkflowFailure)) { + return t.fail('Expected err.cause to be an instance of ChildWorkflowFailure'); + } + t.is(err.cause.retryState, RetryState.RETRY_STATE_TIMEOUT); + if (!(err.cause.cause instanceof TimeoutFailure)) { + return t.fail('Expected err.cause.cause to be an instance of TimeoutFailure'); + } + t.is(err.cause.cause.timeoutType, TimeoutType.TIMEOUT_TYPE_START_TO_CLOSE); + }); + + test('child-workflow-start-fail', async (t) => { + const { client } = t.context; + await client.execute(workflows.childWorkflowStartFail, { + taskQueue: 'test', + workflowId: uuid4(), + }); + // Assertions in workflow code + t.pass(); + }); + + test('child-workflow-cancel', async (t) => { + const { client } = t.context; + await client.execute(workflows.childWorkflowCancel, { + taskQueue: 'test', + workflowId: uuid4(), + }); + // Assertions in workflow code + t.pass(); + }); + + test('child-workflow-signals', async (t) => { + const { client } = t.context; + await client.execute(workflows.childWorkflowSignals, { + taskQueue: 'test', + workflowId: uuid4(), + }); + // Assertions in workflow code + t.pass(); + }); + + test('query and unblock', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.unblockOrCancel, { + taskQueue: 'test', + workflowId: uuid4(), + }); + t.true(await workflow.query(workflows.isBlockedQuery)); + await workflow.signal(workflows.unblockSignal); + await workflow.result(); + t.false(await workflow.query(workflows.isBlockedQuery)); + }); + + test('interrupt-signal', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.interruptableWorkflow, { + taskQueue: 'test', + workflowId: uuid4(), + }); + await workflow.signal(workflows.interruptSignal, 'just because'); + const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { + instanceOf: WorkflowFailedError, + }); + if (!(err.cause instanceof ApplicationFailure)) { + return t.fail('Expected err.cause to be an instance of ApplicationFailure'); + } + t.is(err.cause.message, 'just because'); + }); + + test('fail-signal', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.failSignalWorkflow, { + taskQueue: 'test', + workflowId: uuid4(), + }); + await workflow.signal(workflows.failSignal); + const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { + instanceOf: WorkflowFailedError, + }); + if (!(err.cause instanceof ApplicationFailure)) { + return t.fail('Expected err.cause to be an instance of ApplicationFailure'); + } + t.is(err.cause.message, 'Signal failed'); + }); + + test('async-fail-signal', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.asyncFailSignalWorkflow, { + taskQueue: 'test', + workflowId: uuid4(), + }); + await workflow.signal(workflows.failSignal); + const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { + instanceOf: WorkflowFailedError, + }); + if (!(err.cause instanceof ApplicationFailure)) { + return t.fail('Expected err.cause to be an instance of ApplicationFailure'); + } + t.is(err.cause.message, 'Signal failed'); + }); + + test('http', async (t) => { + const { client } = t.context; + const res = await client.execute(workflows.http, { + taskQueue: 'test', + workflowId: uuid4(), + }); + t.deepEqual(res, await activities.httpGet('https://temporal.io')); + }); + + test('sleep', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.sleeper, { + taskQueue: 'test', + workflowId: uuid4(), + }); + const res = await workflow.result(); + t.is(res, undefined); + const execution = await client.service.getWorkflowExecutionHistory({ + namespace, + execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, + }); + const timerEvents = execution.history!.events!.filter(({ eventType }) => timerEventTypes.has(eventType!)); + t.is(timerEvents.length, 2); + t.is(timerEvents[0].timerStartedEventAttributes!.timerId, '1'); + t.is(tsToMs(timerEvents[0].timerStartedEventAttributes!.startToFireTimeout), 100); + t.is(timerEvents[1].timerFiredEventAttributes!.timerId, '1'); + }); + + test('cancel-timer-immediately', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.cancelTimer, { + taskQueue: 'test', + workflowId: uuid4(), + }); + const res = await workflow.result(); + t.is(res, undefined); + const execution = await client.service.getWorkflowExecutionHistory({ + namespace, + execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, + }); + const timerEvents = execution.history!.events!.filter(({ eventType }) => timerEventTypes.has(eventType!)); + // Timer is cancelled before it is scheduled + t.is(timerEvents.length, 0); + }); + + test('cancel-timer-with-delay', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.cancelTimerWithDelay, { + taskQueue: 'test', + workflowId: uuid4(), + }); + const res = await workflow.result(); + t.is(res, undefined); + const execution = await client.service.getWorkflowExecutionHistory({ + namespace, + execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, + }); + const timerEvents = execution.history!.events!.filter(({ eventType }) => timerEventTypes.has(eventType!)); + t.is(timerEvents.length, 4); + t.is(timerEvents[0].timerStartedEventAttributes!.timerId, '1'); + t.is(tsToMs(timerEvents[0].timerStartedEventAttributes!.startToFireTimeout), 10000); + t.is(timerEvents[1].timerStartedEventAttributes!.timerId, '2'); + t.is(tsToMs(timerEvents[1].timerStartedEventAttributes!.startToFireTimeout), 1); + t.is(timerEvents[2].timerFiredEventAttributes!.timerId, '2'); + t.is(timerEvents[3].timerCanceledEventAttributes!.timerId, '1'); + }); + + test('patched', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.patchedWorkflow, { + taskQueue: 'test', + workflowId: uuid4(), + }); + const res = await workflow.result(); + t.is(res, undefined); + const execution = await client.service.getWorkflowExecutionHistory({ + namespace, + execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, + }); + const hasChangeEvents = execution.history!.events!.filter( + ({ eventType }) => eventType === iface.temporal.api.enums.v1.EventType.EVENT_TYPE_MARKER_RECORDED + ); + // There will only be one marker despite there being 2 hasChange calls because they have the + // same ID and core will only record one marker per id. + t.is(hasChangeEvents.length, 1); + t.is(hasChangeEvents[0].markerRecordedEventAttributes!.markerName, CHANGE_MARKER_NAME); + }); + + test('deprecate-patch', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.deprecatePatchWorkflow, { + taskQueue: 'test', + workflowId: uuid4(), + }); + const res = await workflow.result(); + t.is(res, undefined); + const execution = await client.service.getWorkflowExecutionHistory({ + namespace, + execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, + }); + const hasChangeEvents = execution.history!.events!.filter( + ({ eventType }) => eventType === iface.temporal.api.enums.v1.EventType.EVENT_TYPE_MARKER_RECORDED + ); + t.is(hasChangeEvents.length, 1); + t.is(hasChangeEvents[0].markerRecordedEventAttributes!.markerName, CHANGE_MARKER_NAME); + }); + + test('Worker default ServerOptions are generated correctly', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.argsAndReturn, { + args: ['hey', undefined, Buffer.from('abc')], + taskQueue: 'test', + workflowId: uuid4(), + }); + await workflow.result(); + const execution = await client.service.getWorkflowExecutionHistory({ + namespace, + execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, + }); + const events = execution.history!.events!.filter( + ({ eventType }) => eventType === iface.temporal.api.enums.v1.EventType.EVENT_TYPE_WORKFLOW_TASK_COMPLETED + ); + t.is(events.length, 1); + const [event] = events; + t.regex(event.workflowTaskCompletedEventAttributes!.identity!, /\d+@.+/); + t.regex(event.workflowTaskCompletedEventAttributes!.binaryChecksum!, /@temporalio\/worker@\d+\.\d+\.\d+/); + }); + + test('WorkflowOptions are passed correctly with defaults', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.argsAndReturn, { + args: ['hey', undefined, Buffer.from('def')], + taskQueue: 'test', + workflowId: uuid4(), + }); + await workflow.result(); + const execution = await workflow.describe(); + t.deepEqual( + execution.workflowExecutionInfo?.type, + new iface.temporal.api.common.v1.WorkflowType({ name: 'argsAndReturn' }) + ); + t.deepEqual(execution.workflowExecutionInfo?.memo, new iface.temporal.api.common.v1.Memo({ fields: {} })); + t.deepEqual(Object.keys(execution.workflowExecutionInfo!.searchAttributes!.indexedFields!), ['BinaryChecksums']); + + const checksums = defaultPayloadConverter.fromPayload( + execution.workflowExecutionInfo!.searchAttributes!.indexedFields!.BinaryChecksums! + ); + t.true(checksums instanceof Array && checksums.length === 1); + t.regex((checksums as string[])[0], /@temporalio\/worker@\d+\.\d+\.\d+/); + t.is(execution.executionConfig?.taskQueue?.name, 'test'); + t.is(execution.executionConfig?.taskQueue?.kind, iface.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL); + t.is(execution.executionConfig?.workflowRunTimeout, null); + t.is(execution.executionConfig?.workflowExecutionTimeout, null); + }); + + test('WorkflowOptions are passed correctly', async (t) => { + const { client } = t.context; + const options = { + taskQueue: 'test2', + memo: { a: 'b' }, + searchAttributes: { CustomIntField: 3 }, + workflowId: uuid4(), + workflowRunTimeout: '2s', + workflowExecutionTimeout: '3s', + workflowTaskTimeout: '1s', + }; + const workflow = await client.start(workflows.sleeper, options); + // Throws because we use a different task queue + await t.throwsAsync(() => workflow.result(), { + instanceOf: WorkflowFailedError, + message: 'Workflow execution timed out', + }); + const execution = await workflow.describe(); + t.deepEqual( + execution.workflowExecutionInfo?.type, + new iface.temporal.api.common.v1.WorkflowType({ name: 'sleeper' }) + ); + t.deepEqual(await fromPayload(execution.workflowExecutionInfo!.memo!.fields!.a!), 'b'); + t.deepEqual( + await fromPayload(execution.workflowExecutionInfo!.searchAttributes!.indexedFields!.CustomIntField!), + 3 + ); + t.is(execution.executionConfig?.taskQueue?.name, 'test2'); + t.is(execution.executionConfig?.taskQueue?.kind, iface.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL); + + t.is(tsToMs(execution.executionConfig!.workflowRunTimeout!), ms(options.workflowRunTimeout)); + t.is(tsToMs(execution.executionConfig!.workflowExecutionTimeout!), ms(options.workflowExecutionTimeout)); + t.is(tsToMs(execution.executionConfig!.defaultWorkflowTaskTimeout!), ms(options.workflowTaskTimeout)); + }); + + test('WorkflowHandle.result() throws if terminated', async (t) => { + const { client } = t.context; + const workflow = await client.start(workflows.sleeper, { + taskQueue: 'test', + workflowId: uuid4(), + args: [1000000], + }); + await workflow.terminate('hasta la vista baby'); + await t.throwsAsync(workflow.result(), { + instanceOf: WorkflowFailedError, + message: 'hasta la vista baby', + }); + }); + + test('WorkflowHandle.result() throws if continued as new', async (t) => { + const { client } = t.context; + const ogWF = await client.start(workflows.continueAsNewSameWorkflow, { + taskQueue: 'test', + workflowId: uuid4(), + followRuns: false, + }); + let err = await t.throwsAsync(ogWF.result(), { instanceOf: WorkflowContinuedAsNewError }); + if (!(err instanceof WorkflowContinuedAsNewError)) return; // Type assertion + let workflow = client.getHandle( + ogWF.workflowId, + err.newExecutionRunId, + { + followRuns: false, + } + ); + + await workflow.signal(workflows.continueAsNewSignal); + err = await t.throwsAsync(workflow.result(), { + instanceOf: WorkflowContinuedAsNewError, + }); + if (!(err instanceof WorkflowContinuedAsNewError)) return; // Type assertion + + workflow = client.getHandle(workflow.workflowId, err.newExecutionRunId); + await workflow.result(); + }); + + test('WorkflowHandle.result() follows chain of execution', async (t) => { + const { client } = t.context; + await client.execute(workflows.continueAsNewSameWorkflow, { + taskQueue: 'test', + workflowId: uuid4(), + args: ['execute', 'none'], + }); + t.pass(); + }); + + test('continue-as-new-to-different-workflow', async (t) => { + const { client } = t.context; + const ogWF = await client.start(workflows.continueAsNewToDifferentWorkflow, { + taskQueue: 'test', + workflowId: uuid4(), + followRuns: false, + }); + const err = await t.throwsAsync(ogWF.result(), { instanceOf: WorkflowContinuedAsNewError }); + if (!(err instanceof WorkflowContinuedAsNewError)) return; // Type assertion + const workflow = client.getHandle(ogWF.workflowId, err.newExecutionRunId, { + followRuns: false, + }); + await workflow.result(); + const info = await workflow.describe(); + t.is(info.workflowExecutionInfo?.type?.name, 'sleeper'); + const { history } = await client.service.getWorkflowExecutionHistory({ + namespace, + execution: { workflowId: workflow.workflowId, runId: err.newExecutionRunId }, + }); + const timeSlept = await decodeFromPayloadsAtIndex( + loadedDataConverter, + 0, + history?.events?.[0].workflowExecutionStartedEventAttributes?.input?.payloads + ); + t.is(timeSlept, 1); + }); + + test('signalWithStart works as intended and returns correct runId', async (t) => { + const { client } = t.context; + const ogWF = await client.signalWithStart(workflows.interruptableWorkflow, { + taskQueue: 'test', + workflowId: uuid4(), + signal: workflows.interruptSignal, + signalArgs: ['interrupted from signalWithStart'], + }); + { + const err: WorkflowFailedError = await t.throwsAsync(ogWF.result(), { + instanceOf: WorkflowFailedError, + }); + if (!(err.cause instanceof ApplicationFailure)) { + return t.fail('Expected err.cause to be an instance of ApplicationFailure'); + } + t.is(err.cause.message, 'interrupted from signalWithStart'); + } + // Test returned runId + const workflow = client.getHandle(ogWF.workflowId, ogWF.originalRunId); + { + const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { + instanceOf: WorkflowFailedError, + }); + if (!(err.cause instanceof ApplicationFailure)) { + return t.fail('Expected err.cause to be an instance of ApplicationFailure'); + } + t.is(err.cause.message, 'interrupted from signalWithStart'); + } + }); + + test('activity-failures', async (t) => { + const { client } = t.context; + await client.execute(workflows.activityFailures, { + taskQueue: 'test', + workflowId: uuid4(), + }); + t.pass(); + }); + + test('sleepInvalidDuration is caught in Workflow runtime', async (t) => { + const { client } = t.context; + await client.execute(workflows.sleepInvalidDuration, { + taskQueue: 'test', + workflowId: uuid4(), + }); + t.pass(); + }); + + test('unhandledRejection causes WFT to fail', async (t) => { + const { client } = t.context; + const workflowId = uuid4(); + const handle = await client.start(workflows.throwUnhandledRejection, { + taskQueue: 'test', + workflowId, + // throw an exception that our worker can associate with an running workflow + args: [{ crashWorker: false }], + }); + await asyncRetry( + async () => { + const history = await client.service.getWorkflowExecutionHistory({ + namespace: 'default', + execution: { workflowId }, + }); + const wftFailedEvent = history.history?.events?.find((ev) => ev.workflowTaskFailedEventAttributes); + if (wftFailedEvent === undefined) { + throw new Error('No WFT failed event'); + } + t.is(wftFailedEvent.workflowTaskFailedEventAttributes?.failure?.message, 'unhandled rejection'); + }, + { minTimeout: 300, factor: 1, retries: 100 } + ); + await handle.terminate(); + }); + + test('Workflow RetryPolicy kicks in with retryable failure', async (t) => { + const { client } = t.context; + const workflowId = uuid4(); + const handle = await client.start(workflows.throwAsync, { + taskQueue: 'test', + workflowId, + args: ['retryable'], + retry: { + initialInterval: 1, + maximumInterval: 1, + maximumAttempts: 2, + }, + }); + await t.throwsAsync(handle.result()); + const handleForSecondAttempt = client.getHandle(workflowId); + const { workflowExecutionInfo } = await handleForSecondAttempt.describe(); + t.not(workflowExecutionInfo?.execution?.runId, handle.originalRunId); + }); + + test('Workflow RetryPolicy ignored with nonRetryable failure', async (t) => { + const { client } = t.context; + const workflowId = uuid4(); + const handle = await client.start(workflows.throwAsync, { + taskQueue: 'test', + workflowId, + args: ['nonRetryable'], + retry: { + initialInterval: 1, + maximumInterval: 1, + maximumAttempts: 2, + }, + }); + await t.throwsAsync(handle.result()); + const res = await handle.describe(); + t.is( + res.workflowExecutionInfo?.status, + iface.temporal.api.enums.v1.WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_FAILED + ); + }); +} diff --git a/packages/test/src/test-integration-codec.ts b/packages/test/src/test-integration-codec.ts new file mode 100644 index 000000000..0b19574fb --- /dev/null +++ b/packages/test/src/test-integration-codec.ts @@ -0,0 +1,27 @@ +import { Payload, PayloadCodec } from '@temporalio/common'; +import { RUN_INTEGRATION_TESTS } from './helpers'; +import { runIntegrationTests } from './integration-tests'; + +class TestPayloadCodec implements PayloadCodec { + async encode(payloads: Payload[]): Promise { + return payloads.map((payload) => { + if (payload.data) { + payload.data = payload.data.map((byte) => byte + 1); + } + return payload; + }); + } + + async decode(payloads: Payload[]): Promise { + return payloads.map((payload) => { + if (payload.data) { + payload.data = payload.data.map((byte) => byte - 1); + } + return payload; + }); + } +} + +if (RUN_INTEGRATION_TESTS) { + runIntegrationTests(new TestPayloadCodec()); +} diff --git a/packages/test/src/test-integration.ts b/packages/test/src/test-integration.ts index 3e0ee72ad..f3a471f2d 100644 --- a/packages/test/src/test-integration.ts +++ b/packages/test/src/test-integration.ts @@ -1,778 +1,6 @@ -/* eslint @typescript-eslint/no-non-null-assertion: 0 */ -import { - ActivityFailure, - ApplicationFailure, - WorkflowClient, - WorkflowContinuedAsNewError, - WorkflowFailedError, -} from '@temporalio/client'; -import { - ChildWorkflowFailure, - defaultPayloadConverter, - fromPayloadsAtIndex, - RetryState, - TerminatedFailure, - TimeoutFailure, - TimeoutType, - WorkflowExecution, -} from '@temporalio/common'; -import { tsToMs } from '@temporalio/internal-workflow-common'; -import * as iface from '@temporalio/proto'; -import { Core, DefaultLogger, Worker } from '@temporalio/worker'; -import asyncRetry from 'async-retry'; -import anyTest, { TestInterface } from 'ava'; -import dedent from 'dedent'; -import ms from 'ms'; -import { v4 as uuid4 } from 'uuid'; -import * as activities from './activities'; -import { cleanStackTrace, RUN_INTEGRATION_TESTS, u8 } from './helpers'; -import * as workflows from './workflows'; -import { withZeroesHTTPServer } from './zeroes-http-server'; - -const { EVENT_TYPE_TIMER_STARTED, EVENT_TYPE_TIMER_FIRED, EVENT_TYPE_TIMER_CANCELED } = - iface.temporal.api.enums.v1.EventType; - -const timerEventTypes = new Set([EVENT_TYPE_TIMER_STARTED, EVENT_TYPE_TIMER_FIRED, EVENT_TYPE_TIMER_CANCELED]); -const CHANGE_MARKER_NAME = 'core_patch'; - -export interface Context { - worker: Worker; - client: WorkflowClient; - runPromise: Promise; -} - -const test = anyTest as TestInterface; -const namespace = 'default'; +import { RUN_INTEGRATION_TESTS } from './helpers'; +import { runIntegrationTests } from './integration-tests'; if (RUN_INTEGRATION_TESTS) { - test.before(async (t) => { - const logger = new DefaultLogger('DEBUG'); - // Use forwarded logging from core - await Core.install({ logger, telemetryOptions: { logForwardingLevel: 'INFO' } }); - const worker = await Worker.create({ - workflowsPath: require.resolve('./workflows'), - activities, - taskQueue: 'test', - }); - - const runPromise = worker.run(); - // Catch the error here to avoid unhandled rejection - runPromise.catch((err) => { - console.error('Caught error while worker was running', err); - }); - t.context = { - worker, - runPromise, - client: new WorkflowClient(), - }; - - // The initialization of the custom search attributes is slooooow. Wait for it to finish - await asyncRetry( - async () => { - try { - const handle = await t.context.client.start(workflows.sleeper, { - workflowId: uuid4(), - taskQueue: 'no_one_cares_pointless_queue', - workflowExecutionTimeout: 1000, - searchAttributes: { CustomIntField: 1 }, - }); - await handle.terminate(); - } catch (e: any) { - // We don't stop until we see an error that *isn't* the error about the field not being - // valid - if (!e.details.includes('CustomIntField')) { - return; - } - throw e; - } - }, - { - retries: 60, - maxTimeout: 1000, - } - ); - }); - - test.after.always(async (t) => { - t.context.worker.shutdown(); - await t.context.runPromise; - }); - - test('Workflow not found results in failure', async (t) => { - const { client } = t.context; - const promise = client.execute('not-found', { - taskQueue: 'test', - workflowId: uuid4(), - }); - const err: WorkflowFailedError = await t.throwsAsync(() => promise, { - instanceOf: WorkflowFailedError, - }); - if (!(err.cause instanceof ApplicationFailure)) { - t.fail('Expected err.cause to be an instance of ApplicationFailure'); - return; - } - t.is(err.cause.type, 'ReferenceError'); - t.is(err.cause.message, "'not-found' is not a function"); - t.true(err.cause.nonRetryable); - t.is(err.cause.stack, "ApplicationFailure: 'not-found' is not a function"); - }); - - test('args-and-return', async (t) => { - const { client } = t.context; - const res = await client.execute(workflows.argsAndReturn, { - taskQueue: 'test', - workflowId: uuid4(), - args: ['Hello', undefined, u8('world!')], - }); - t.is(res, 'Hello, world!'); - }); - - test('cancel-fake-progress', async (t) => { - const { client } = t.context; - await client.execute(workflows.cancelFakeProgress, { - taskQueue: 'test', - workflowId: uuid4(), - }); - t.pass(); - }); - - test('cancel-http-request', async (t) => { - const { client } = t.context; - await withZeroesHTTPServer(async (port) => { - const url = `http://127.0.0.1:${port}`; - await client.execute(workflows.cancellableHTTPRequest, { - taskQueue: 'test', - workflowId: uuid4(), - args: [url], - }); - }); - t.pass(); - }); - - test('activity-failure with Error', async (t) => { - const { client } = t.context; - const err: WorkflowFailedError = await t.throwsAsync( - client.execute(workflows.activityFailure, { - taskQueue: 'test', - workflowId: uuid4(), - args: [{ useApplicationFailure: false }], - }), - { - instanceOf: WorkflowFailedError, - } - ); - t.is(err.message, 'Workflow execution failed'); - if (!(err.cause instanceof ActivityFailure)) { - t.fail('Expected err.cause to be an instance of ActivityFailure'); - return; - } - if (!(err.cause.cause instanceof ApplicationFailure)) { - t.fail('Expected err.cause.cause to be an instance of ApplicationFailure'); - return; - } - t.is(err.cause.cause.message, 'Fail me'); - t.is( - cleanStackTrace(err.cause.cause.stack), - dedent` - Error: Fail me - at Activity.throwAnError [as fn] - ` - ); - }); - - test('activity-failure with ApplicationFailure', async (t) => { - const { client } = t.context; - const err: WorkflowFailedError = await t.throwsAsync( - client.execute(workflows.activityFailure, { - taskQueue: 'test', - workflowId: uuid4(), - args: [{ useApplicationFailure: true }], - }), - { - instanceOf: WorkflowFailedError, - } - ); - t.is(err.message, 'Workflow execution failed'); - if (!(err.cause instanceof ActivityFailure)) { - t.fail('Expected err.cause to be an instance of ActivityFailure'); - return; - } - if (!(err.cause.cause instanceof ApplicationFailure)) { - t.fail('Expected err.cause.cause to be an instance of ApplicationFailure'); - return; - } - t.is(err.cause.cause.message, 'Fail me'); - t.is(err.cause.cause.type, 'Error'); - t.deepEqual(err.cause.cause.details, ['details', 123, false]); - t.is( - cleanStackTrace(err.cause.cause.stack), - dedent` - ApplicationFailure: Fail me - at Function.nonRetryable - at Activity.throwAnError [as fn] - ` - ); - }); - - test('child-workflow-invoke', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.childWorkflowInvoke, { - taskQueue: 'test', - workflowId: uuid4(), - }); - const { workflowId, runId, execResult, result } = await workflow.result(); - t.is(execResult, 'success'); - t.is(result, 'success'); - const child = client.getHandle(workflowId, runId); - t.is(await child.result(), 'success'); - }); - - test('child-workflow-failure', async (t) => { - const { client } = t.context; - const err: WorkflowFailedError = await t.throwsAsync( - client.execute(workflows.childWorkflowFailure, { - taskQueue: 'test', - workflowId: uuid4(), - }), - { - instanceOf: WorkflowFailedError, - } - ); - if (!(err.cause instanceof ChildWorkflowFailure)) { - return t.fail('Expected err.cause to be an instance of ChildWorkflowFailure'); - } - if (!(err.cause.cause instanceof ApplicationFailure)) { - return t.fail('Expected err.cause.cause to be an instance of ApplicationFailure'); - } - t.is(err.cause.cause.message, 'failure'); - t.is( - cleanStackTrace(err.cause.cause.stack), - dedent` - ApplicationFailure: failure - at Function.nonRetryable - at throwAsync - ` - ); - }); - - test('child-workflow-termination', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.childWorkflowTermination, { - taskQueue: 'test', - workflowId: uuid4(), - }); - - let childExecution: WorkflowExecution | undefined = undefined; - - while (childExecution === undefined) { - childExecution = await workflow.query(workflows.childExecutionQuery); - } - const child = client.getHandle(childExecution.workflowId!, childExecution.runId!); - await child.terminate(); - const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { - instanceOf: WorkflowFailedError, - }); - if (!(err.cause instanceof ChildWorkflowFailure)) { - return t.fail('Expected err.cause to be an instance of ChildWorkflowFailure'); - } - t.is(err.cause.retryState, RetryState.RETRY_STATE_NON_RETRYABLE_FAILURE); - if (!(err.cause.cause instanceof TerminatedFailure)) { - return t.fail('Expected err.cause.cause to be an instance of TerminatedFailure'); - } - }); - - test('child-workflow-timeout', async (t) => { - const { client } = t.context; - const err: WorkflowFailedError = await t.throwsAsync( - client.execute(workflows.childWorkflowTimeout, { - taskQueue: 'test', - workflowId: uuid4(), - }), - { - instanceOf: WorkflowFailedError, - } - ); - if (!(err.cause instanceof ChildWorkflowFailure)) { - return t.fail('Expected err.cause to be an instance of ChildWorkflowFailure'); - } - t.is(err.cause.retryState, RetryState.RETRY_STATE_TIMEOUT); - if (!(err.cause.cause instanceof TimeoutFailure)) { - return t.fail('Expected err.cause.cause to be an instance of TimeoutFailure'); - } - t.is(err.cause.cause.timeoutType, TimeoutType.TIMEOUT_TYPE_START_TO_CLOSE); - }); - - test('child-workflow-start-fail', async (t) => { - const { client } = t.context; - await client.execute(workflows.childWorkflowStartFail, { - taskQueue: 'test', - workflowId: uuid4(), - }); - // Assertions in workflow code - t.pass(); - }); - - test('child-workflow-cancel', async (t) => { - const { client } = t.context; - await client.execute(workflows.childWorkflowCancel, { - taskQueue: 'test', - workflowId: uuid4(), - }); - // Assertions in workflow code - t.pass(); - }); - - test('child-workflow-signals', async (t) => { - const { client } = t.context; - await client.execute(workflows.childWorkflowSignals, { - taskQueue: 'test', - workflowId: uuid4(), - }); - // Assertions in workflow code - t.pass(); - }); - - test('query and unblock', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.unblockOrCancel, { - taskQueue: 'test', - workflowId: uuid4(), - }); - t.true(await workflow.query(workflows.isBlockedQuery)); - await workflow.signal(workflows.unblockSignal); - await workflow.result(); - t.false(await workflow.query(workflows.isBlockedQuery)); - }); - - test('interrupt-signal', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.interruptableWorkflow, { - taskQueue: 'test', - workflowId: uuid4(), - }); - await workflow.signal(workflows.interruptSignal, 'just because'); - const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { - instanceOf: WorkflowFailedError, - }); - if (!(err.cause instanceof ApplicationFailure)) { - return t.fail('Expected err.cause to be an instance of ApplicationFailure'); - } - t.is(err.cause.message, 'just because'); - }); - - test('fail-signal', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.failSignalWorkflow, { - taskQueue: 'test', - workflowId: uuid4(), - }); - await workflow.signal(workflows.failSignal); - const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { - instanceOf: WorkflowFailedError, - }); - if (!(err.cause instanceof ApplicationFailure)) { - return t.fail('Expected err.cause to be an instance of ApplicationFailure'); - } - t.is(err.cause.message, 'Signal failed'); - }); - - test('async-fail-signal', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.asyncFailSignalWorkflow, { - taskQueue: 'test', - workflowId: uuid4(), - }); - await workflow.signal(workflows.failSignal); - const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { - instanceOf: WorkflowFailedError, - }); - if (!(err.cause instanceof ApplicationFailure)) { - return t.fail('Expected err.cause to be an instance of ApplicationFailure'); - } - t.is(err.cause.message, 'Signal failed'); - }); - - test('http', async (t) => { - const { client } = t.context; - const res = await client.execute(workflows.http, { - taskQueue: 'test', - workflowId: uuid4(), - }); - t.deepEqual(res, await activities.httpGet('https://temporal.io')); - }); - - test('sleep', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.sleeper, { - taskQueue: 'test', - workflowId: uuid4(), - }); - const res = await workflow.result(); - t.is(res, undefined); - const execution = await client.service.getWorkflowExecutionHistory({ - namespace, - execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, - }); - const timerEvents = execution.history!.events!.filter(({ eventType }) => timerEventTypes.has(eventType!)); - t.is(timerEvents.length, 2); - t.is(timerEvents[0].timerStartedEventAttributes!.timerId, '1'); - t.is(tsToMs(timerEvents[0].timerStartedEventAttributes!.startToFireTimeout), 100); - t.is(timerEvents[1].timerFiredEventAttributes!.timerId, '1'); - }); - - test('cancel-timer-immediately', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.cancelTimer, { - taskQueue: 'test', - workflowId: uuid4(), - }); - const res = await workflow.result(); - t.is(res, undefined); - const execution = await client.service.getWorkflowExecutionHistory({ - namespace, - execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, - }); - const timerEvents = execution.history!.events!.filter(({ eventType }) => timerEventTypes.has(eventType!)); - // Timer is cancelled before it is scheduled - t.is(timerEvents.length, 0); - }); - - test('cancel-timer-with-delay', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.cancelTimerWithDelay, { - taskQueue: 'test', - workflowId: uuid4(), - }); - const res = await workflow.result(); - t.is(res, undefined); - const execution = await client.service.getWorkflowExecutionHistory({ - namespace, - execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, - }); - const timerEvents = execution.history!.events!.filter(({ eventType }) => timerEventTypes.has(eventType!)); - t.is(timerEvents.length, 4); - t.is(timerEvents[0].timerStartedEventAttributes!.timerId, '1'); - t.is(tsToMs(timerEvents[0].timerStartedEventAttributes!.startToFireTimeout), 10000); - t.is(timerEvents[1].timerStartedEventAttributes!.timerId, '2'); - t.is(tsToMs(timerEvents[1].timerStartedEventAttributes!.startToFireTimeout), 1); - t.is(timerEvents[2].timerFiredEventAttributes!.timerId, '2'); - t.is(timerEvents[3].timerCanceledEventAttributes!.timerId, '1'); - }); - - test('patched', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.patchedWorkflow, { - taskQueue: 'test', - workflowId: uuid4(), - }); - const res = await workflow.result(); - t.is(res, undefined); - const execution = await client.service.getWorkflowExecutionHistory({ - namespace, - execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, - }); - const hasChangeEvents = execution.history!.events!.filter( - ({ eventType }) => eventType === iface.temporal.api.enums.v1.EventType.EVENT_TYPE_MARKER_RECORDED - ); - // There will only be one marker despite there being 2 hasChange calls because they have the - // same ID and core will only record one marker per id. - t.is(hasChangeEvents.length, 1); - t.is(hasChangeEvents[0].markerRecordedEventAttributes!.markerName, CHANGE_MARKER_NAME); - }); - - test('deprecate-patch', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.deprecatePatchWorkflow, { - taskQueue: 'test', - workflowId: uuid4(), - }); - const res = await workflow.result(); - t.is(res, undefined); - const execution = await client.service.getWorkflowExecutionHistory({ - namespace, - execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, - }); - const hasChangeEvents = execution.history!.events!.filter( - ({ eventType }) => eventType === iface.temporal.api.enums.v1.EventType.EVENT_TYPE_MARKER_RECORDED - ); - t.is(hasChangeEvents.length, 1); - t.is(hasChangeEvents[0].markerRecordedEventAttributes!.markerName, CHANGE_MARKER_NAME); - }); - - test('Worker default ServerOptions are generated correctly', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.argsAndReturn, { - args: ['hey', undefined, Buffer.from('abc')], - taskQueue: 'test', - workflowId: uuid4(), - }); - await workflow.result(); - const execution = await client.service.getWorkflowExecutionHistory({ - namespace, - execution: { workflowId: workflow.workflowId, runId: workflow.originalRunId }, - }); - const events = execution.history!.events!.filter( - ({ eventType }) => eventType === iface.temporal.api.enums.v1.EventType.EVENT_TYPE_WORKFLOW_TASK_COMPLETED - ); - t.is(events.length, 1); - const [event] = events; - t.regex(event.workflowTaskCompletedEventAttributes!.identity!, /\d+@.+/); - t.regex(event.workflowTaskCompletedEventAttributes!.binaryChecksum!, /@temporalio\/worker@\d+\.\d+\.\d+/); - }); - - test('WorkflowOptions are passed correctly with defaults', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.argsAndReturn, { - args: ['hey', undefined, Buffer.from('def')], - taskQueue: 'test', - workflowId: uuid4(), - }); - await workflow.result(); - const execution = await workflow.describe(); - t.deepEqual( - execution.workflowExecutionInfo?.type, - new iface.temporal.api.common.v1.WorkflowType({ name: 'argsAndReturn' }) - ); - t.deepEqual(execution.workflowExecutionInfo?.memo, new iface.temporal.api.common.v1.Memo({ fields: {} })); - t.deepEqual(Object.keys(execution.workflowExecutionInfo!.searchAttributes!.indexedFields!), ['BinaryChecksums']); - - const checksums = defaultPayloadConverter.fromPayload( - execution.workflowExecutionInfo!.searchAttributes!.indexedFields!.BinaryChecksums! - ); - t.true(checksums instanceof Array && checksums.length === 1); - t.regex((checksums as string[])[0], /@temporalio\/worker@\d+\.\d+\.\d+/); - t.is(execution.executionConfig?.taskQueue?.name, 'test'); - t.is(execution.executionConfig?.taskQueue?.kind, iface.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL); - t.is(execution.executionConfig?.workflowRunTimeout, null); - t.is(execution.executionConfig?.workflowExecutionTimeout, null); - }); - - test('WorkflowOptions are passed correctly', async (t) => { - const { client } = t.context; - const options = { - taskQueue: 'test2', - memo: { a: 'b' }, - searchAttributes: { CustomIntField: 3 }, - workflowId: uuid4(), - workflowRunTimeout: '2s', - workflowExecutionTimeout: '3s', - workflowTaskTimeout: '1s', - }; - const workflow = await client.start(workflows.sleeper, options); - // Throws because we use a different task queue - await t.throwsAsync(() => workflow.result(), { - instanceOf: WorkflowFailedError, - message: 'Workflow execution timed out', - }); - const execution = await workflow.describe(); - t.deepEqual( - execution.workflowExecutionInfo?.type, - new iface.temporal.api.common.v1.WorkflowType({ name: 'sleeper' }) - ); - t.deepEqual(defaultPayloadConverter.fromPayload(execution.workflowExecutionInfo!.memo!.fields!.a!), 'b'); - t.deepEqual( - defaultPayloadConverter.fromPayload( - execution.workflowExecutionInfo!.searchAttributes!.indexedFields!.CustomIntField! - ), - 3 - ); - t.is(execution.executionConfig?.taskQueue?.name, 'test2'); - t.is(execution.executionConfig?.taskQueue?.kind, iface.temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_NORMAL); - - t.is(tsToMs(execution.executionConfig!.workflowRunTimeout!), ms(options.workflowRunTimeout)); - t.is(tsToMs(execution.executionConfig!.workflowExecutionTimeout!), ms(options.workflowExecutionTimeout)); - t.is(tsToMs(execution.executionConfig!.defaultWorkflowTaskTimeout!), ms(options.workflowTaskTimeout)); - }); - - test('WorkflowHandle.result() throws if terminated', async (t) => { - const { client } = t.context; - const workflow = await client.start(workflows.sleeper, { - taskQueue: 'test', - workflowId: uuid4(), - args: [1000000], - }); - await workflow.terminate('hasta la vista baby'); - await t.throwsAsync(workflow.result(), { - instanceOf: WorkflowFailedError, - message: 'hasta la vista baby', - }); - }); - - test('WorkflowHandle.result() throws if continued as new', async (t) => { - const { client } = t.context; - const ogWF = await client.start(workflows.continueAsNewSameWorkflow, { - taskQueue: 'test', - workflowId: uuid4(), - followRuns: false, - }); - let err = await t.throwsAsync(ogWF.result(), { instanceOf: WorkflowContinuedAsNewError }); - if (!(err instanceof WorkflowContinuedAsNewError)) return; // Type assertion - let workflow = client.getHandle( - ogWF.workflowId, - err.newExecutionRunId, - { - followRuns: false, - } - ); - - await workflow.signal(workflows.continueAsNewSignal); - err = await t.throwsAsync(workflow.result(), { - instanceOf: WorkflowContinuedAsNewError, - }); - if (!(err instanceof WorkflowContinuedAsNewError)) return; // Type assertion - - workflow = client.getHandle(workflow.workflowId, err.newExecutionRunId); - await workflow.result(); - }); - - test('WorkflowHandle.result() follows chain of execution', async (t) => { - const { client } = t.context; - await client.execute(workflows.continueAsNewSameWorkflow, { - taskQueue: 'test', - workflowId: uuid4(), - args: ['execute', 'none'], - }); - t.pass(); - }); - - test('continue-as-new-to-different-workflow', async (t) => { - const { client } = t.context; - const ogWF = await client.start(workflows.continueAsNewToDifferentWorkflow, { - taskQueue: 'test', - workflowId: uuid4(), - followRuns: false, - }); - const err = await t.throwsAsync(ogWF.result(), { instanceOf: WorkflowContinuedAsNewError }); - if (!(err instanceof WorkflowContinuedAsNewError)) return; // Type assertion - const workflow = client.getHandle(ogWF.workflowId, err.newExecutionRunId, { - followRuns: false, - }); - await workflow.result(); - const info = await workflow.describe(); - t.is(info.workflowExecutionInfo?.type?.name, 'sleeper'); - const { history } = await client.service.getWorkflowExecutionHistory({ - namespace, - execution: { workflowId: workflow.workflowId, runId: err.newExecutionRunId }, - }); - const timeSlept = fromPayloadsAtIndex( - defaultPayloadConverter, - 0, - history?.events?.[0].workflowExecutionStartedEventAttributes?.input?.payloads - ); - t.is(timeSlept, 1); - }); - - test('signalWithStart works as intended and returns correct runId', async (t) => { - const { client } = t.context; - const ogWF = await client.signalWithStart(workflows.interruptableWorkflow, { - taskQueue: 'test', - workflowId: uuid4(), - signal: workflows.interruptSignal, - signalArgs: ['interrupted from signalWithStart'], - }); - { - const err: WorkflowFailedError = await t.throwsAsync(ogWF.result(), { - instanceOf: WorkflowFailedError, - }); - if (!(err.cause instanceof ApplicationFailure)) { - return t.fail('Expected err.cause to be an instance of ApplicationFailure'); - } - t.is(err.cause.message, 'interrupted from signalWithStart'); - } - // Test returned runId - const workflow = client.getHandle(ogWF.workflowId, ogWF.originalRunId); - { - const err: WorkflowFailedError = await t.throwsAsync(workflow.result(), { - instanceOf: WorkflowFailedError, - }); - if (!(err.cause instanceof ApplicationFailure)) { - return t.fail('Expected err.cause to be an instance of ApplicationFailure'); - } - t.is(err.cause.message, 'interrupted from signalWithStart'); - } - }); - - test('activity-failures', async (t) => { - const { client } = t.context; - await client.execute(workflows.activityFailures, { - taskQueue: 'test', - workflowId: uuid4(), - }); - t.pass(); - }); - - test('sleepInvalidDuration is caught in Workflow runtime', async (t) => { - const { client } = t.context; - await client.execute(workflows.sleepInvalidDuration, { - taskQueue: 'test', - workflowId: uuid4(), - }); - t.pass(); - }); - - test('unhandledRejection causes WFT to fail', async (t) => { - const { client } = t.context; - const workflowId = uuid4(); - const handle = await client.start(workflows.throwUnhandledRejection, { - taskQueue: 'test', - workflowId, - // throw an exception that our worker can associate with an running workflow - args: [{ crashWorker: false }], - }); - await asyncRetry( - async () => { - const history = await client.service.getWorkflowExecutionHistory({ - namespace: 'default', - execution: { workflowId }, - }); - const wftFailedEvent = history.history?.events?.find((ev) => ev.workflowTaskFailedEventAttributes); - if (wftFailedEvent === undefined) { - throw new Error('No WFT failed event'); - } - t.is(wftFailedEvent.workflowTaskFailedEventAttributes?.failure?.message, 'unhandled rejection'); - }, - { minTimeout: 300, factor: 1, retries: 100 } - ); - await handle.terminate(); - }); - - test('Workflow RetryPolicy kicks in with retryable failure', async (t) => { - const { client } = t.context; - const workflowId = uuid4(); - const handle = await client.start(workflows.throwAsync, { - taskQueue: 'test', - workflowId, - args: ['retryable'], - retry: { - initialInterval: 1, - maximumInterval: 1, - maximumAttempts: 2, - }, - }); - await t.throwsAsync(handle.result()); - const handleForSecondAttempt = client.getHandle(workflowId); - const { workflowExecutionInfo } = await handleForSecondAttempt.describe(); - t.not(workflowExecutionInfo?.execution?.runId, handle.originalRunId); - }); - - test('Workflow RetryPolicy ignored with nonRetryable failure', async (t) => { - const { client } = t.context; - const workflowId = uuid4(); - const handle = await client.start(workflows.throwAsync, { - taskQueue: 'test', - workflowId, - args: ['nonRetryable'], - retry: { - initialInterval: 1, - maximumInterval: 1, - maximumAttempts: 2, - }, - }); - await t.throwsAsync(handle.result()); - const res = await handle.describe(); - t.is( - res.workflowExecutionInfo?.status, - iface.temporal.api.enums.v1.WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_FAILED - ); - }); + runIntegrationTests(); } From a68bc36179f79f9433d9211dbcc292660cc5a08d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Tue, 1 Mar 2022 15:58:39 -0500 Subject: [PATCH 29/33] Remove unnecessary useCustomPayloadConverter --- docs/data-converter.md | 22 +++++++++---------- packages/worker/src/worker.ts | 7 +----- packages/worker/src/workflow/threaded-vm.ts | 8 ++----- packages/worker/src/workflow/vm.ts | 13 ++++------- .../src/workflow/workflow-worker-thread.ts | 6 +---- .../workflow/workflow-worker-thread/input.ts | 1 - packages/workflow/src/worker-interface.ts | 22 +++++++------------ 7 files changed, 26 insertions(+), 53 deletions(-) diff --git a/docs/data-converter.md b/docs/data-converter.md index 6d00ba2e1..f7acf1a51 100644 --- a/docs/data-converter.md +++ b/docs/data-converter.md @@ -52,26 +52,24 @@ When `WorkerOptions.dataConverter.payloadConverterPath` is provided, the code at _main thread_ - imports and validates `options.dataConverter.payloadConverterPath` -- passes `useCustomPayloadConverter` to either `ThreadedVMWorkflowCreator.create` or `VMWorkflowCreator.create` - passes `payloadConverterPath` to `WorkflowCodeBundler` -`ThreadedVMWorkflowCreator.create`: -_main thread_ +Execution goes to either: -- sends `useCustomPayloadConverter` to each worker thread -- thread sends `useCustomPayloadConverter` to VMWorkflowCreator.create +- `ThreadedVMWorkflowCreator.create` + _main thread_ -`VMWorkflowCreator.create`: -_worker thread (unless in debug mode)_ +- `VMWorkflowCreator.create` + _worker thread (unless in debug mode)_ -- passes `useCustomPayloadConverter` to `VMWorkflowCreator` constructor +And then to: -`VMWorkflowCreator.createWorkflow`: -_worker thread (unless in debug mode)_ +- `VMWorkflowCreator.createWorkflow` + _worker thread (unless in debug mode)_ -- passes `useCustomPayloadConverter` to `initRuntime` inside Workflow vm +And then to: `worker-interface.ts#initRuntime`: _workflow vm_ -- if `useCustomPayloadConverter`, imports `__temporal_custom_payload_converter` and sets `state.payloadConverter` +- Imports `__temporal_custom_payload_converter`, which will either be the code bundled from `payloadConverterPath` or `undefined`. If it's defined, sets `state.payloadConverter`. diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index 48a7964e5..98f07f67a 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -293,17 +293,12 @@ export class Worker { } if (bundle) { if (compiledOptions.debugMode) { - workflowCreator = await VMWorkflowCreator.create( - bundle, - compiledOptions.isolateExecutionTimeoutMs, - !!compiledOptions.dataConverter?.payloadConverterPath - ); + workflowCreator = await VMWorkflowCreator.create(bundle, compiledOptions.isolateExecutionTimeoutMs); } else { workflowCreator = await ThreadedVMWorkflowCreator.create({ code: bundle, threadPoolSize: compiledOptions.workflowThreadPoolSize, isolateExecutionTimeoutMs: compiledOptions.isolateExecutionTimeoutMs, - useCustomPayloadConverter: !!compiledOptions.dataConverter?.payloadConverterPath, }); } } diff --git a/packages/worker/src/workflow/threaded-vm.ts b/packages/worker/src/workflow/threaded-vm.ts index 699341fe9..9023d0574 100644 --- a/packages/worker/src/workflow/threaded-vm.ts +++ b/packages/worker/src/workflow/threaded-vm.ts @@ -13,7 +13,7 @@ import { coresdk } from '@temporalio/proto'; import { IllegalStateError, SinkCall } from '@temporalio/workflow'; import { Worker as NodeWorker } from 'worker_threads'; import { UnexpectedError } from '../errors'; -import { Workflow, WorkflowCreator, WorkflowCreateOptions } from './interface'; +import { Workflow, WorkflowCreateOptions, WorkflowCreator } from './interface'; import { WorkerThreadInput, WorkerThreadRequest } from './workflow-worker-thread/input'; import { WorkerThreadOutput, WorkerThreadResponse } from './workflow-worker-thread/output'; @@ -121,7 +121,6 @@ export interface ThreadedVMWorkflowCreatorOptions { code: string; threadPoolSize: number; isolateExecutionTimeoutMs: number; - useCustomPayloadConverter: boolean; } /** @@ -139,15 +138,12 @@ export class ThreadedVMWorkflowCreator implements WorkflowCreator { threadPoolSize, code, isolateExecutionTimeoutMs, - useCustomPayloadConverter, }: ThreadedVMWorkflowCreatorOptions): Promise { const workerThreadClients = Array(threadPoolSize) .fill(0) .map(() => new WorkerThreadClient(new NodeWorker(require.resolve('./workflow-worker-thread')))); await Promise.all( - workerThreadClients.map((client) => - client.send({ type: 'init', code, isolateExecutionTimeoutMs, useCustomPayloadConverter }) - ) + workerThreadClients.map((client) => client.send({ type: 'init', code, isolateExecutionTimeoutMs })) ); return new this(workerThreadClients); } diff --git a/packages/worker/src/workflow/vm.ts b/packages/worker/src/workflow/vm.ts index dc62b51f9..5eef4e8e8 100644 --- a/packages/worker/src/workflow/vm.ts +++ b/packages/worker/src/workflow/vm.ts @@ -40,11 +40,7 @@ export class VMWorkflowCreator implements WorkflowCreator { script?: vm.Script; - constructor( - script: vm.Script, - public readonly isolateExecutionTimeoutMs: number, - protected readonly useCustomPayloadConverter: boolean - ) { + constructor(script: vm.Script, public readonly isolateExecutionTimeoutMs: number) { if (!VMWorkflowCreator.unhandledRejectionHandlerHasBeenSet) { setUnhandledRejectionHandler(); VMWorkflowCreator.unhandledRejectionHandlerHasBeenSet = true; @@ -75,7 +71,7 @@ export class VMWorkflowCreator implements WorkflowCreator { } ) as any; - await workflowModule.initRuntime({ useCustomPayloadConverter: this.useCustomPayloadConverter, ...options }); + await workflowModule.initRuntime(options); const newVM = new VMWorkflow(options.info, context, workflowModule, isolateExecutionTimeoutMs); VMWorkflowCreator.workflowByRunId.set(options.info.runId, newVM); @@ -114,11 +110,10 @@ export class VMWorkflowCreator implements WorkflowCreator { public static async create( this: T, code: string, - isolateExecutionTimeoutMs: number, - useCustomPayloadConverter = false + isolateExecutionTimeoutMs: number ): Promise> { const script = new vm.Script(code, { filename: 'workflow-isolate' }); - return new this(script, isolateExecutionTimeoutMs, useCustomPayloadConverter) as InstanceType; + return new this(script, isolateExecutionTimeoutMs) as InstanceType; } /** diff --git a/packages/worker/src/workflow/workflow-worker-thread.ts b/packages/worker/src/workflow/workflow-worker-thread.ts index 905d47c8c..11ab3c6c3 100644 --- a/packages/worker/src/workflow/workflow-worker-thread.ts +++ b/packages/worker/src/workflow/workflow-worker-thread.ts @@ -30,11 +30,7 @@ setUnhandledRejectionHandler(); async function handleRequest({ requestId, input }: WorkerThreadRequest): Promise { switch (input.type) { case 'init': - workflowCreator = await VMWorkflowCreator.create( - input.code, - input.isolateExecutionTimeoutMs, - input.useCustomPayloadConverter - ); + workflowCreator = await VMWorkflowCreator.create(input.code, input.isolateExecutionTimeoutMs); return ok(requestId); case 'destroy': await workflowCreator?.destroy(); diff --git a/packages/worker/src/workflow/workflow-worker-thread/input.ts b/packages/worker/src/workflow/workflow-worker-thread/input.ts index 05bbe264e..c2a677eb8 100644 --- a/packages/worker/src/workflow/workflow-worker-thread/input.ts +++ b/packages/worker/src/workflow/workflow-worker-thread/input.ts @@ -7,7 +7,6 @@ export interface Init { type: 'init'; isolateExecutionTimeoutMs: number; code: string; - useCustomPayloadConverter: boolean; } /** diff --git a/packages/workflow/src/worker-interface.ts b/packages/workflow/src/worker-interface.ts index ff986f9c2..a2d28e99b 100644 --- a/packages/workflow/src/worker-interface.ts +++ b/packages/workflow/src/worker-interface.ts @@ -26,7 +26,6 @@ export interface WorkflowCreateOptions { randomnessSeed: number[]; now: number; patches: string[]; - useCustomPayloadConverter?: boolean; } export interface ImportFunctions { @@ -111,13 +110,7 @@ export function overrideGlobals(): void { * * Sets required internal state and instantiates the workflow and interceptors. */ -export async function initRuntime({ - info, - randomnessSeed, - now, - patches, - useCustomPayloadConverter, -}: WorkflowCreateOptions): Promise { +export async function initRuntime({ info, randomnessSeed, now, patches }: WorkflowCreateOptions): Promise { // Set the runId globally on the context so it can be retrieved in the case // of an unhandled promise rejection. (globalThis as any).__TEMPORAL__.runId = info.runId; @@ -155,12 +148,13 @@ export async function initRuntime({ } } - if (useCustomPayloadConverter) { - // webpack doesn't know what to bundle given a dynamic import expression, so we can't do: - // state.payloadConverter = (await import(payloadConverterPath)).payloadConverter; - // @ts-expect-error this is a webpack alias to payloadConverterPath - state.payloadConverter = (await import('__temporal_custom_payload_converter')).payloadConverter; - // The `payloadConverter` export is validated in the Worker + // webpack doesn't know what to bundle given a dynamic import expression, so we can't do: + // state.payloadConverter = (await import(payloadConverterPath)).payloadConverter; + // @ts-expect-error this is a webpack alias to payloadConverterPath + const customPayloadConverter = (await import('__temporal_custom_payload_converter')).payloadConverter; + // The `payloadConverter` export is validated in the Worker + if (customPayloadConverter !== undefined) { + state.payloadConverter = customPayloadConverter; } let workflow: Workflow; From c25e9abd75090e006bc8216082bbb00dff094f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Tue, 1 Mar 2022 16:34:40 -0500 Subject: [PATCH 30/33] Guard against non-Record module --- .../src/data-converter-helpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/internal-non-workflow-common/src/data-converter-helpers.ts b/packages/internal-non-workflow-common/src/data-converter-helpers.ts index b293e0230..11546fd05 100644 --- a/packages/internal-non-workflow-common/src/data-converter-helpers.ts +++ b/packages/internal-non-workflow-common/src/data-converter-helpers.ts @@ -5,7 +5,7 @@ import { LoadedDataConverter, PayloadConverter, } from '@temporalio/common'; -import { errorCode, hasOwnProperty, ValueError } from '@temporalio/internal-workflow-common'; +import { errorCode, hasOwnProperty, isRecord, ValueError } from '@temporalio/internal-workflow-common'; const isValidPayloadConverter = (PayloadConverter: unknown): PayloadConverter is PayloadConverter => typeof PayloadConverter === 'object' && @@ -25,7 +25,7 @@ function requirePayloadConverter(path: string): PayloadConverter { throw error; } - if (hasOwnProperty(module, 'payloadConverter')) { + if (isRecord(module) && hasOwnProperty(module, 'payloadConverter')) { if (isValidPayloadConverter(module.payloadConverter)) { return module.payloadConverter; } else { From ffcef1cdf99064762fa0cb58360690c544e727c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Wed, 2 Mar 2022 11:27:54 -0500 Subject: [PATCH 31/33] Make PayloadConverter.toPayload return undefined instead of throwing --- docs/data-converter.md | 2 +- .../common/src/converter/payload-codec.ts | 2 +- .../common/src/converter/payload-converter.ts | 62 +++++++++------ .../src/converter/payload-converters.ts | 22 ++---- .../converter/protobuf-payload-converters.ts | 15 ++-- .../src/codec-helpers.ts | 12 ++- .../internal-non-workflow-common/src/otel.ts | 4 +- .../internal-workflow-common/src/errors.ts | 6 +- packages/test/src/test-interceptors.ts | 18 ++--- packages/test/src/test-payload-converter.ts | 75 +++++-------------- packages/test/src/test-workflows.ts | 53 +++++++------ .../test/src/workflows/interceptor-example.ts | 15 ++-- 12 files changed, 129 insertions(+), 157 deletions(-) diff --git a/docs/data-converter.md b/docs/data-converter.md index f7acf1a51..ef7bd8b42 100644 --- a/docs/data-converter.md +++ b/docs/data-converter.md @@ -32,7 +32,7 @@ export interface DataConverter { } export interface PayloadConverter { - toPayload(value: T): Payload; + toPayload(value: T): Payload | undefined; fromPayload(payload: Payload): T; } diff --git a/packages/common/src/converter/payload-codec.ts b/packages/common/src/converter/payload-codec.ts index a0fa2231d..9e3812122 100644 --- a/packages/common/src/converter/payload-codec.ts +++ b/packages/common/src/converter/payload-codec.ts @@ -3,7 +3,7 @@ import { Payload } from './types'; /** * `PayloadCodec` is an optional step that happens between the wire and the {@link PayloadConverter}: * - * Temporal Server ↔️ Wire ↔️ `PayloadCodec` ↔️ `PayloadConverter` ↔️ User code + * Temporal Server <--> Wire <--> `PayloadCodec` <--> `PayloadConverter` <--> User code * * Implement this to transform an array of {@link Payload}s to/from the format sent over the wire and stored by Temporal Server. * Common transformations are encryption and compression. diff --git a/packages/common/src/converter/payload-converter.ts b/packages/common/src/converter/payload-converter.ts index a73b50b95..49b687836 100644 --- a/packages/common/src/converter/payload-converter.ts +++ b/packages/common/src/converter/payload-converter.ts @@ -1,4 +1,4 @@ -import { UnsupportedTypeError, ValueError } from '@temporalio/internal-workflow-common'; +import { PayloadConverterError, ValueError } from '@temporalio/internal-workflow-common'; import { BinaryPayloadConverter, JsonPayloadConverter, @@ -8,8 +8,7 @@ import { import { METADATA_ENCODING_KEY, Payload, str } from './types'; /** - * Used by the framework to serialize/deserialize parameters and return values that need to be - * sent over the wire. + * Used by the framework to serialize/deserialize parameters and return values. * * This is called inside the [Workflow isolate](https://docs.temporal.io/docs/typescript/determinism). * To write async code or use Node APIs (or use packages that use Node APIs), use a {@link PayloadCodec}. @@ -19,7 +18,7 @@ export interface PayloadConverter { * Converts a value to a {@link Payload}. * @param value The value to convert. Example values include the Workflow args sent by the client and the values returned by a Workflow or Activity. */ - toPayload(value: T): Payload; + toPayload(value: T): Payload | undefined; /** * Converts a {@link Payload} back to a value. @@ -40,23 +39,24 @@ export class CompositePayloadConverter implements PayloadConverter { /** * Tries to run `.toPayload(value)` on each converter in the order provided at construction. - * Returns the first successful result. - * @throws {@link ValueError} if no converter can convert the value + * Returns the first successful result, or `undefined` if there is no converter that can handle the value. */ - public toPayload(value: T): Payload { + public toPayload(value: T): Payload | undefined { for (const converter of this.converters) { try { const result = converter.toPayload(value); - return result; + if (result !== undefined) { + return result; + } } catch (e: unknown) { - if (e instanceof UnsupportedTypeError) { + if (e instanceof PayloadConverterError) { continue; } else { throw e; } } } - throw new ValueError(`Cannot serialize ${value} of type ${typeof value}`); + return undefined; } /** @@ -75,12 +75,25 @@ export class CompositePayloadConverter implements PayloadConverter { } } +/** + * Tries to convert `value` to a {@link Payload}. Throws if conversion fails. + * + * @throws {@link PayloadConverterError} + */ +export function toPayload(converter: PayloadConverter, value: unknown): Payload { + const payload = converter.toPayload(value); + if (payload === undefined) { + throw new PayloadConverterError(`Failed to convert value: ${value}`); + } + return payload; +} + /** * Implements conversion of a list of values. * * @param converter - * @param values JS values to convert to Payloads. - * @return converted value + * @param values JS values to convert to Payloads + * @return converted values * @throws PayloadConverterError if conversion of the value passed as parameter failed for any * reason. */ @@ -88,7 +101,19 @@ export function toPayloads(converter: PayloadConverter, ...values: unknown[]): P if (values.length === 0) { return undefined; } - return values.map((value) => converter.toPayload(value)); + + return values.map((value) => toPayload(converter, value)); +} + +/** + * Run {@link PayloadConverter.toPayload} on each value in the map. + * + * @throws {@link PayloadConverterError} if conversion of any value in the map fails + */ +export function mapToPayloads(converter: PayloadConverter, map: Record): Record { + return Object.fromEntries( + Object.entries(map).map(([k, v]): [K, Payload] => [k as K, toPayload(converter, v)]) + ) as Record; } /** @@ -99,7 +124,7 @@ export function toPayloads(converter: PayloadConverter, ...values: unknown[]): P * @param index index of the value in the payloads * @param payloads serialized value to convert to JS values. * @return converted JS value - * @throws PayloadConverterError if conversion of the data passed as parameter failed for any + * @throws {@link PayloadConverterError} if conversion of the data passed as parameter failed for any * reason. */ export function fromPayloadsAtIndex(converter: PayloadConverter, index: number, payloads?: Payload[] | null): T { @@ -120,15 +145,6 @@ export function arrayFromPayloads(converter: PayloadConverter, payloads?: Payloa return payloads.map((payload: Payload) => converter.fromPayload(payload)); } -/** - * Run {@link PayloadConverter.toPayload} on each value in the map. - */ -export function mapToPayloads(converter: PayloadConverter, map: Record): Record { - return Object.fromEntries( - Object.entries(map).map(([k, v]): [K, Payload] => [k as K, converter.toPayload(v)]) - ) as Record; -} - export class DefaultPayloadConverter extends CompositePayloadConverter { // Match the order used in other SDKs, but exclude Protobuf converters so that the code, including // `proto3-json-serializer`, doesn't take space in Workflow bundles that don't use Protobufs. To use Protobufs, use diff --git a/packages/common/src/converter/payload-converters.ts b/packages/common/src/converter/payload-converters.ts index db16dec88..917fed208 100644 --- a/packages/common/src/converter/payload-converters.ts +++ b/packages/common/src/converter/payload-converters.ts @@ -1,9 +1,4 @@ -import { - errorMessage, - UnsupportedJsonTypeError, - UnsupportedTypeError, - ValueError, -} from '@temporalio/internal-workflow-common'; +import { errorMessage, UnsupportedJsonTypeError, ValueError } from '@temporalio/internal-workflow-common'; import { PayloadConverter } from './payload-converter'; import { encodingKeys, EncodingType, encodingTypes, METADATA_ENCODING_KEY, Payload, str, u8 } from './types'; @@ -17,8 +12,8 @@ export interface PayloadConverterWithEncoding extends PayloadConverter { export class UndefinedPayloadConverter implements PayloadConverterWithEncoding { public encodingType = encodingTypes.METADATA_ENCODING_NULL; - public toPayload(value: unknown): Payload { - if (value !== undefined) throw new UnsupportedTypeError('Can only encode undefined'); // Can't encode + public toPayload(value: unknown): Payload | undefined { + if (value !== undefined) return undefined; // Can't encode return { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL, @@ -37,9 +32,8 @@ export class UndefinedPayloadConverter implements PayloadConverterWithEncoding { export class JsonPayloadConverter implements PayloadConverterWithEncoding { public encodingType = encodingTypes.METADATA_ENCODING_JSON; - public toPayload(value: unknown): Payload { - if (value === undefined) - throw new UnsupportedTypeError("Can't encode undefined. Use UndefinedPayloadConverter instead."); + public toPayload(value: unknown): Payload | undefined { + if (value === undefined) return undefined; let json; try { @@ -75,11 +69,9 @@ export class JsonPayloadConverter implements PayloadConverterWithEncoding { export class BinaryPayloadConverter implements PayloadConverterWithEncoding { public encodingType = encodingTypes.METADATA_ENCODING_RAW; - public toPayload(value: unknown): Payload { + public toPayload(value: unknown): Payload | undefined { // TODO: support any DataView or ArrayBuffer? - if (!(value instanceof Uint8Array)) { - throw new UnsupportedTypeError('Can only encode Uint8Array'); - } + if (!(value instanceof Uint8Array)) return undefined; return { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_RAW, diff --git a/packages/common/src/converter/protobuf-payload-converters.ts b/packages/common/src/converter/protobuf-payload-converters.ts index 085fbf5e6..a61f62df8 100644 --- a/packages/common/src/converter/protobuf-payload-converters.ts +++ b/packages/common/src/converter/protobuf-payload-converters.ts @@ -4,7 +4,6 @@ import { hasOwnProperty, isRecord, PayloadConverterError, - UnsupportedTypeError, ValueError, } from '@temporalio/internal-workflow-common'; import * as protoJsonSerializer from 'proto3-json-serializer'; @@ -30,7 +29,7 @@ abstract class ProtobufPayloadConverter implements PayloadConverterWithEncoding protected readonly root: Root | undefined; public abstract encodingType: EncodingType; - public abstract toPayload(value: T): Payload; + public abstract toPayload(value: T): Payload | undefined; public abstract fromPayload(payload: Payload): T; // Don't use type Root here because root.d.ts doesn't export Root, so users would have to type assert @@ -96,10 +95,8 @@ export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter { super(root); } - public toPayload(value: unknown): Payload { - if (!isProtobufMessage(value)) { - throw new UnsupportedTypeError(); - } + public toPayload(value: unknown): Payload | undefined { + if (!isProtobufMessage(value)) return undefined; return this.constructPayload({ messageTypeName: getNamespacedTypeName(value.$type), @@ -126,10 +123,8 @@ export class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter { super(root); } - public toPayload(value: unknown): Payload { - if (!isProtobufMessage(value)) { - throw new UnsupportedTypeError(); - } + public toPayload(value: unknown): Payload | undefined { + if (!isProtobufMessage(value)) return undefined; const jsonValue = protoJsonSerializer.toProto3JSON(value); diff --git a/packages/internal-non-workflow-common/src/codec-helpers.ts b/packages/internal-non-workflow-common/src/codec-helpers.ts index d244a663d..8b6b11d46 100644 --- a/packages/internal-non-workflow-common/src/codec-helpers.ts +++ b/packages/internal-non-workflow-common/src/codec-helpers.ts @@ -6,8 +6,10 @@ import { LoadedDataConverter, Payload, PayloadCodec, + PayloadConverterError, ProtoFailure, TemporalFailure, + toPayload, toPayloads, } from '@temporalio/common'; import { clone, setWith } from 'lodash'; @@ -55,8 +57,8 @@ export async function decodeOptionalFailureToOptionalError( */ export async function encodeToPayload(converter: LoadedDataConverter, value: unknown): Promise { const { payloadConverter, payloadCodec } = converter; - const [payload] = await payloadCodec.encode([payloadConverter.toPayload(value)]); - return payload; + const [encoded] = await payloadCodec.encode([toPayload(payloadConverter, value)]); + return encoded; } /** @@ -85,8 +87,10 @@ export async function encodeMapToPayloads( return Object.fromEntries( await Promise.all( Object.entries(map).map(async ([k, v]): Promise<[K, Payload]> => { - const [payload] = await payloadCodec.encode([payloadConverter.toPayload(v)]); - return [k as K, payload]; + const payload = payloadConverter.toPayload(v); + if (payload === undefined) throw new PayloadConverterError(`Failed to encode entry: ${k}: ${v}`); + const [encodedPayload] = await payloadCodec.encode([payload]); + return [k as K, encodedPayload]; }) ) ) as Record; diff --git a/packages/internal-non-workflow-common/src/otel.ts b/packages/internal-non-workflow-common/src/otel.ts index ace0d40cc..533e83282 100644 --- a/packages/internal-non-workflow-common/src/otel.ts +++ b/packages/internal-non-workflow-common/src/otel.ts @@ -1,5 +1,5 @@ import * as otel from '@opentelemetry/api'; -import { defaultPayloadConverter } from '@temporalio/common'; +import { defaultPayloadConverter, toPayload } from '@temporalio/common'; import { Headers } from '@temporalio/internal-workflow-common'; /** Default trace header for opentelemetry interceptors */ @@ -41,7 +41,7 @@ export async function extractSpanContextFromHeaders(headers: Headers): Promise { const carrier = {}; otel.propagation.inject(otel.context.active(), carrier, otel.defaultTextMapSetter); - return { ...headers, [TRACE_HEADER]: defaultPayloadConverter.toPayload(carrier) }; + return { ...headers, [TRACE_HEADER]: toPayload(defaultPayloadConverter, carrier) }; } /** diff --git a/packages/internal-workflow-common/src/errors.ts b/packages/internal-workflow-common/src/errors.ts index 66c51a01d..051ea4d1b 100644 --- a/packages/internal-workflow-common/src/errors.ts +++ b/packages/internal-workflow-common/src/errors.ts @@ -10,11 +10,7 @@ export class PayloadConverterError extends DataConverterError { public readonly name: string = 'PayloadConverterError'; } -export class UnsupportedTypeError extends PayloadConverterError { - public readonly name: string = 'UnsupportedTypeError'; -} - -export class UnsupportedJsonTypeError extends UnsupportedTypeError { +export class UnsupportedJsonTypeError extends PayloadConverterError { public readonly name: string = 'UnsupportedJsonTypeError'; constructor(message: string | undefined, public readonly cause?: Error) { diff --git a/packages/test/src/test-interceptors.ts b/packages/test/src/test-interceptors.ts index 51a2e318d..cd074b500 100644 --- a/packages/test/src/test-interceptors.ts +++ b/packages/test/src/test-interceptors.ts @@ -5,19 +5,19 @@ * @module */ -import test from 'ava'; -import { v4 as uuid4 } from 'uuid'; -import dedent from 'dedent'; -import { Core, DefaultLogger, Worker } from '@temporalio/worker'; -import { ApplicationFailure, TerminatedFailure } from '@temporalio/common'; import { Connection, WorkflowClient, WorkflowFailedError } from '@temporalio/client'; +import { ApplicationFailure, TerminatedFailure, toPayload } from '@temporalio/common'; +import { Core, DefaultLogger, Worker } from '@temporalio/worker'; import { defaultPayloadConverter, WorkflowInfo } from '@temporalio/workflow'; -import { defaultOptions } from './mock-native-worker'; +import test from 'ava'; +import dedent from 'dedent'; +import { v4 as uuid4 } from 'uuid'; import { cleanStackTrace, RUN_INTEGRATION_TESTS } from './helpers'; +import { defaultOptions } from './mock-native-worker'; import { + continueAsNewToDifferentWorkflow, interceptorExample, internalsInterceptorExample, - continueAsNewToDifferentWorkflow, unblockOrCancel, } from './workflows'; import { getSecretQuery, unblockWithSecretSignal } from './workflows/interceptor-example'; @@ -58,7 +58,7 @@ if (RUN_INTEGRATION_TESTS) { ...input, headers: { ...input.headers, - message: defaultPayloadConverter.toPayload(message), + message: toPayload(defaultPayloadConverter, message), }, }); }, @@ -75,7 +75,7 @@ if (RUN_INTEGRATION_TESTS) { signalArgs: [encoded], headers: { ...input.headers, - message: defaultPayloadConverter.toPayload(message), + message: toPayload(defaultPayloadConverter, message), }, }); }, diff --git a/packages/test/src/test-payload-converter.ts b/packages/test/src/test-payload-converter.ts index 7c3780e1b..467a7ab33 100644 --- a/packages/test/src/test-payload-converter.ts +++ b/packages/test/src/test-payload-converter.ts @@ -7,7 +7,6 @@ import { PayloadConverterError, UndefinedPayloadConverter, UnsupportedJsonTypeError, - UnsupportedTypeError, ValueError, } from '@temporalio/common'; import { @@ -32,26 +31,11 @@ import { protobufWorkflow } from './workflows'; test('UndefinedPayloadConverter converts from undefined only', async (t) => { const converter = new UndefinedPayloadConverter(); - await t.throwsAsync(async () => await converter.toPayload(null), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode undefined', - }); - await t.throwsAsync(async () => await converter.toPayload({}), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode undefined', - }); - await t.throwsAsync(async () => await converter.toPayload(1), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode undefined', - }); - await t.throwsAsync(async () => await converter.toPayload(0), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode undefined', - }); - await t.throwsAsync(async () => await converter.toPayload('abc'), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode undefined', - }); + t.is(await converter.toPayload(null), undefined); + t.is(await converter.toPayload({}), undefined); + t.is(await converter.toPayload(1), undefined); + t.is(await converter.toPayload(0), undefined); + t.is(await converter.toPayload('abc'), undefined); t.deepEqual(await converter.toPayload(undefined), { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL }, @@ -65,26 +49,11 @@ test('UndefinedPayloadConverter converts to undefined', async (t) => { test('BinaryPayloadConverter converts from Uint8Array', async (t) => { const converter = new BinaryPayloadConverter(); - await t.throwsAsync(async () => await converter.toPayload(null), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode Uint8Array', - }); - await t.throwsAsync(async () => await converter.toPayload({}), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode Uint8Array', - }); - await t.throwsAsync(async () => await converter.toPayload(1), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode Uint8Array', - }); - await t.throwsAsync(async () => await converter.toPayload(0), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode Uint8Array', - }); - await t.throwsAsync(async () => await converter.toPayload('abc'), { - instanceOf: UnsupportedTypeError, - message: 'Can only encode Uint8Array', - }); + t.is(await converter.toPayload(null), undefined); + t.is(await converter.toPayload({}), undefined); + t.is(await converter.toPayload(1), undefined); + t.is(await converter.toPayload(0), undefined); + t.is(await converter.toPayload('abc'), undefined); t.deepEqual(await converter.toPayload(u8('abc')), { metadata: { [METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_RAW }, @@ -109,10 +78,7 @@ test('JsonPayloadConverter converts from non undefined', async (t) => { t.deepEqual(await converter.toPayload(0), payload(0)); t.deepEqual(await converter.toPayload('abc'), payload('abc')); - await t.throwsAsync(async () => await converter.toPayload(undefined), { - instanceOf: UnsupportedTypeError, - message: /Can't encode undefined/, - }); + t.is(await converter.toPayload(undefined), undefined); await t.throwsAsync(async () => await converter.toPayload(0n), { instanceOf: UnsupportedJsonTypeError, message: /Can't run JSON.stringify/, @@ -247,37 +213,34 @@ if (RUN_INTEGRATION_TESTS) { }); } -test('DefaultPayloadConverter converts protobufs', async (t) => { +test('DefaultPayloadConverter converts protobufs', (t) => { const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); const defaultPayloadConverterWithProtos = new DefaultPayloadConverterWithProtobufs({ protobufRoot: root }); t.deepEqual( defaultPayloadConverterWithProtos.toPayload(instance), // It will always use JSON because it appears before binary in the list - await new ProtobufJsonPayloadConverter(root).toPayload(instance) + new ProtobufJsonPayloadConverter(root).toPayload(instance) ); }); -test('DefaultPayloadConverter converts to payload by trying each converter in order', async (t) => { +test('DefaultPayloadConverter converts to payload by trying each converter in order', (t) => { const defaultPayloadConverterWithProtos = new DefaultPayloadConverterWithProtobufs({ protobufRoot: root }); const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); t.deepEqual( defaultPayloadConverterWithProtos.toPayload(instance), - await new ProtobufJsonPayloadConverter().toPayload(instance) + new ProtobufJsonPayloadConverter().toPayload(instance) ); - t.deepEqual(defaultPayloadConverterWithProtos.toPayload('abc'), await new JsonPayloadConverter().toPayload('abc')); + t.deepEqual(defaultPayloadConverterWithProtos.toPayload('abc'), new JsonPayloadConverter().toPayload('abc')); t.deepEqual( defaultPayloadConverterWithProtos.toPayload(undefined), - await new UndefinedPayloadConverter().toPayload(undefined) + new UndefinedPayloadConverter().toPayload(undefined) ); t.deepEqual( defaultPayloadConverterWithProtos.toPayload(u8('abc')), - await new BinaryPayloadConverter().toPayload(u8('abc')) + new BinaryPayloadConverter().toPayload(u8('abc')) ); - await t.throws(() => defaultPayloadConverterWithProtos.toPayload(0n), { - instanceOf: ValueError, - message: 'Cannot serialize 0 of type bigint', - }); + t.is(defaultPayloadConverterWithProtos.toPayload(0n), undefined); }); test('defaultPayloadConverter converts from payload by payload type', async (t) => { diff --git a/packages/test/src/test-workflows.ts b/packages/test/src/test-workflows.ts index 1abc4d23e..8b961cfbb 100644 --- a/packages/test/src/test-workflows.ts +++ b/packages/test/src/test-workflows.ts @@ -3,6 +3,7 @@ import { defaultPayloadConverter, errorToFailure, RetryState, + toPayload, toPayloads, } from '@temporalio/common'; import { msToTs } from '@temporalio/internal-workflow-common'; @@ -325,7 +326,11 @@ test('random', async (t) => { test('successString', async (t) => { const { workflowType } = t.context; const req = await activate(t, makeStartWorkflow(workflowType)); - compareCompletion(t, req, makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload('success'))])); + compareCompletion( + t, + req, + makeSuccess([makeCompleteWorkflowExecution(toPayload(defaultPayloadConverter, 'success'))]) + ); }); /** @@ -388,7 +393,7 @@ test('date', async (t) => { test('asyncWorkflow', async (t) => { const { workflowType } = t.context; const req = await activate(t, makeStartWorkflow(workflowType)); - compareCompletion(t, req, makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload('async'))])); + compareCompletion(t, req, makeSuccess([makeCompleteWorkflowExecution(toPayload(defaultPayloadConverter, 'async'))])); }); test('deferredResolve', async (t) => { @@ -414,7 +419,7 @@ test('sleeper', async (t) => { test('with ms string - sleeper', async (t) => { const { logs, workflowType } = t.context; { - const req = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayload('10s')])); + const req = await activate(t, makeStartWorkflow(workflowType, [toPayload(defaultPayloadConverter, '10s')])); compareCompletion(t, req, makeSuccess([makeStartTimerCommand({ seq: 1, startToFireTimeout: msToTs('10s') })])); } { @@ -493,7 +498,7 @@ test('trailingTimer', async (t) => { completion, makeSuccess([ makeStartTimerCommand({ seq: 3, startToFireTimeout: msToTs(1) }), - makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload('first')), + makeCompleteWorkflowExecution(toPayload(defaultPayloadConverter, 'first')), ]) ); } @@ -793,7 +798,7 @@ test('cancelWorkflow', async (t) => { ]) ); } - const result = defaultPayloadConverter.toPayload(await activityFunctions.httpGet(url)); + const result = toPayload(defaultPayloadConverter, await activityFunctions.httpGet(url)); { const req = await activate( t, @@ -835,7 +840,7 @@ test('unblock - unblockOrCancel', async (t) => { makeSuccess([ makeRespondToQueryCommand({ queryId: '1', - succeeded: { response: defaultPayloadConverter.toPayload(true) }, + succeeded: { response: toPayload(defaultPayloadConverter, true) }, }), ]) ); @@ -852,7 +857,7 @@ test('unblock - unblockOrCancel', async (t) => { makeSuccess([ makeRespondToQueryCommand({ queryId: '2', - succeeded: { response: defaultPayloadConverter.toPayload(false) }, + succeeded: { response: toPayload(defaultPayloadConverter, false) }, }), ]) ); @@ -892,9 +897,9 @@ test('cancelTimerAltImpl', async (t) => { test('nonCancellable', async (t) => { const { workflowType } = t.context; const url = 'https://temporal.io'; - const result = defaultPayloadConverter.toPayload({ test: true }); + const result = toPayload(defaultPayloadConverter, { test: true }); { - const completion = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayload(url)])); + const completion = await activate(t, makeStartWorkflow(workflowType, [toPayload(defaultPayloadConverter, url)])); compareCompletion( t, completion, @@ -919,9 +924,9 @@ test('nonCancellable', async (t) => { test('resumeAfterCancellation', async (t) => { const { workflowType } = t.context; const url = 'https://temporal.io'; - const result = defaultPayloadConverter.toPayload({ test: true }); + const result = toPayload(defaultPayloadConverter, { test: true }); { - const completion = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayload(url)])); + const completion = await activate(t, makeStartWorkflow(workflowType, [toPayload(defaultPayloadConverter, url)])); compareCompletion( t, completion, @@ -1000,7 +1005,7 @@ test('handleExternalWorkflowCancellationWhileActivityRunning', async (t) => { { const completion = await activate( t, - makeResolveActivity(2, { completed: { result: defaultPayloadConverter.toPayload(undefined) } }) + makeResolveActivity(2, { completed: { result: toPayload(defaultPayloadConverter, undefined) } }) ); compareCompletion(t, completion, makeSuccess([{ cancelWorkflowExecution: {} }])); } @@ -1010,7 +1015,7 @@ test('nestedCancellation', async (t) => { const { workflowType } = t.context; const url = 'https://temporal.io'; { - const completion = await activate(t, makeStartWorkflow(workflowType, [defaultPayloadConverter.toPayload(url)])); + const completion = await activate(t, makeStartWorkflow(workflowType, [toPayload(defaultPayloadConverter, url)])); compareCompletion( t, @@ -1029,7 +1034,7 @@ test('nestedCancellation', async (t) => { { const completion = await activate( t, - makeResolveActivity(1, { completed: { result: defaultPayloadConverter.toPayload(undefined) } }) + makeResolveActivity(1, { completed: { result: toPayload(defaultPayloadConverter, undefined) } }) ); compareCompletion( @@ -1073,7 +1078,7 @@ test('nestedCancellation', async (t) => { { const completion = await activate( t, - makeResolveActivity(3, { completed: { result: defaultPayloadConverter.toPayload(undefined) } }) + makeResolveActivity(3, { completed: { result: toPayload(defaultPayloadConverter, undefined) } }) ); compareCompletion( t, @@ -1114,12 +1119,12 @@ test('sharedScopes', async (t) => { { const completion = await activate( t, - makeResolveActivity(2, { completed: { result: defaultPayloadConverter.toPayload(result) } }) + makeResolveActivity(2, { completed: { result: toPayload(defaultPayloadConverter, result) } }) ); compareCompletion( t, completion, - makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload(result))]) + makeSuccess([makeCompleteWorkflowExecution(toPayload(defaultPayloadConverter, result))]) ); } }); @@ -1152,12 +1157,12 @@ test('shieldAwaitedInRootScope', async (t) => { { const completion = await activate( t, - makeResolveActivity(1, { completed: { result: defaultPayloadConverter.toPayload(result) } }) + makeResolveActivity(1, { completed: { result: toPayload(defaultPayloadConverter, result) } }) ); compareCompletion( t, completion, - makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload(result))]) + makeSuccess([makeCompleteWorkflowExecution(toPayload(defaultPayloadConverter, result))]) ); } }); @@ -1340,7 +1345,7 @@ test('cancelActivityAfterFirstCompletion', async (t) => { { const req = await activate( t, - makeResolveActivity(1, { completed: { result: defaultPayloadConverter.toPayload('response1') } }) + makeResolveActivity(1, { completed: { result: toPayload(defaultPayloadConverter, 'response1') } }) ); compareCompletion( t, @@ -1364,12 +1369,12 @@ test('cancelActivityAfterFirstCompletion', async (t) => { { const req = await activate( t, - makeResolveActivity(2, { completed: { result: defaultPayloadConverter.toPayload('response2') } }) + makeResolveActivity(2, { completed: { result: toPayload(defaultPayloadConverter, 'response2') } }) ); compareCompletion( t, req, - makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload(['response1', 'response2']))]) + makeSuccess([makeCompleteWorkflowExecution(toPayload(defaultPayloadConverter, ['response1', 'response2']))]) ); } t.deepEqual(logs, [['Workflow cancelled while waiting on non cancellable scope']]); @@ -1445,13 +1450,13 @@ test('resolve activity with result - http', async (t) => { { const completion = await activate( t, - makeResolveActivity(1, { completed: { result: defaultPayloadConverter.toPayload(result) } }) + makeResolveActivity(1, { completed: { result: toPayload(defaultPayloadConverter, result) } }) ); compareCompletion( t, completion, - makeSuccess([makeCompleteWorkflowExecution(defaultPayloadConverter.toPayload(result))]) + makeSuccess([makeCompleteWorkflowExecution(toPayload(defaultPayloadConverter, result))]) ); } }); diff --git a/packages/test/src/workflows/interceptor-example.ts b/packages/test/src/workflows/interceptor-example.ts index aaed91e76..3d4b0d5c5 100644 --- a/packages/test/src/workflows/interceptor-example.ts +++ b/packages/test/src/workflows/interceptor-example.ts @@ -1,13 +1,14 @@ +import { toPayload } from '@temporalio/common'; import { - executeChild, - WorkflowInterceptors, - defaultPayloadConverter, - sleep, + ApplicationFailure, condition, - defineSignal, + defaultPayloadConverter, defineQuery, + defineSignal, + executeChild, setHandler, - ApplicationFailure, + sleep, + WorkflowInterceptors, } from '@temporalio/workflow'; import { echo } from './configured-activities'; @@ -71,7 +72,7 @@ export const interceptors = (): WorkflowInterceptors => ({ async scheduleActivity(input, next) { return next({ ...input, - headers: { ...input.headers, message: defaultPayloadConverter.toPayload(receivedMessage) }, + headers: { ...input.headers, message: toPayload(defaultPayloadConverter, receivedMessage) }, }); }, async startTimer(input, next) { From 5b4ded86f49f658323c7e636b98f78f80b4cd344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Wed, 2 Mar 2022 12:34:13 -0500 Subject: [PATCH 32/33] Address review comments --- docs/data-converter.md | 19 +------- packages/client/src/workflow-client.ts | 47 ++++++++++++------- .../common/src/converter/data-converter.ts | 23 +++++---- packages/test/src/mock-native-worker.ts | 15 ++---- .../test/src/test-custom-payload-codec.ts | 28 +++++------ packages/worker/src/worker-options.ts | 5 +- packages/worker/src/worker.ts | 19 ++++---- packages/workflow/package.json | 4 +- packages/workflow/src/worker-interface.ts | 18 +++---- 9 files changed, 84 insertions(+), 94 deletions(-) diff --git a/docs/data-converter.md b/docs/data-converter.md index ef7bd8b42..99bd74c8f 100644 --- a/docs/data-converter.md +++ b/docs/data-converter.md @@ -23,7 +23,7 @@ function workflowInclusiveInstanceOf(instance: unknown, type: Function): boolean ## Decision -Given the possibility of switching or adding other isolation methods in future, we opted to convert to/from Payloads inside the vm (`PayloadConverter`). We also added another transformer layer called `PayloadCodec` that runs outside the vm, can use node modules and Promises, and operates on Payloads. A `DataConverter` is a `PayloadConverter` and a `PayloadCodec`: +Given the possibility of switching or adding other isolation methods in future, we opted to convert to/from Payloads inside the vm (`PayloadConverter`). We also added another transformer layer called `PayloadCodec` that runs outside the vm, can use node APIs and Promises, and operates on Payloads. A `DataConverter` is a `PayloadConverter` and a `PayloadCodec`: ```ts export interface DataConverter { @@ -46,7 +46,7 @@ export interface PayloadCodec { `PayloadCodec` only runs in the main thread. -When `WorkerOptions.dataConverter.payloadConverterPath` is provided, the code at that location is loaded into the main thread, the worker threads, and the webpack Workflow bundle. +When `WorkerOptions.dataConverter.payloadConverterPath` is provided, the code at that location is loaded into the main thread and the webpack Workflow bundle. `Worker.create`: _main thread_ @@ -54,21 +54,6 @@ _main thread_ - imports and validates `options.dataConverter.payloadConverterPath` - passes `payloadConverterPath` to `WorkflowCodeBundler` -Execution goes to either: - -- `ThreadedVMWorkflowCreator.create` - _main thread_ - -- `VMWorkflowCreator.create` - _worker thread (unless in debug mode)_ - -And then to: - -- `VMWorkflowCreator.createWorkflow` - _worker thread (unless in debug mode)_ - -And then to: - `worker-interface.ts#initRuntime`: _workflow vm_ diff --git a/packages/client/src/workflow-client.ts b/packages/client/src/workflow-client.ts index 3f700481c..43b704760 100644 --- a/packages/client/src/workflow-client.ts +++ b/packages/client/src/workflow-client.ts @@ -166,6 +166,9 @@ export interface WorkflowClientOptions { } export type WorkflowClientOptionsWithDefaults = Required; +export type LoadedWorkflowClientOptions = WorkflowClientOptionsWithDefaults & { + loadedDataConverter: LoadedDataConverter; +}; export function defaultWorkflowClientOptions(): WorkflowClientOptionsWithDefaults { return { @@ -243,12 +246,14 @@ export type WorkflowStartOptions = WithWorkflowAr * Client for starting Workflow executions and creating Workflow handles */ export class WorkflowClient { - public readonly options: WorkflowClientOptionsWithDefaults; - protected readonly dataConverter: LoadedDataConverter; + public readonly options: LoadedWorkflowClientOptions; constructor(public readonly service: WorkflowService = new Connection().service, options?: WorkflowClientOptions) { - this.dataConverter = loadDataConverter(options?.dataConverter); - this.options = { ...defaultWorkflowClientOptions(), ...options }; + this.options = { + ...defaultWorkflowClientOptions(), + ...options, + loadedDataConverter: loadDataConverter(options?.dataConverter), + }; } /** @@ -429,7 +434,7 @@ export class WorkflowClient { // Note that we can only return one value from our workflow function in JS. // Ignore any other payloads in result const [result] = await decodeArrayFromPayloads( - this.dataConverter, + this.options.loadedDataConverter, ev.workflowExecutionCompletedEventAttributes.result?.payloads ); return result as any; @@ -442,14 +447,14 @@ export class WorkflowClient { const { failure, retryState } = ev.workflowExecutionFailedEventAttributes; throw new WorkflowFailedError( 'Workflow execution failed', - await decodeOptionalFailureToOptionalError(this.dataConverter, failure), + await decodeOptionalFailureToOptionalError(this.options.loadedDataConverter, failure), retryState ?? RetryState.RETRY_STATE_UNSPECIFIED ); } else if (ev.workflowExecutionCanceledEventAttributes) { const failure = new CancelledFailure( 'Workflow canceled', await decodeArrayFromPayloads( - this.dataConverter, + this.options.loadedDataConverter, ev.workflowExecutionCanceledEventAttributes.details?.payloads ) ); @@ -529,7 +534,7 @@ export class WorkflowClient { execution: input.workflowExecution, query: { queryType: input.queryType, - queryArgs: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) }, + queryArgs: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...input.args) }, }, }); } catch (err) { @@ -545,7 +550,7 @@ export class WorkflowClient { throw new TypeError('Invalid response from server'); } // We ignore anything but the first result - return await decodeFromPayloadsAtIndex(this.dataConverter, 0, response.queryResult?.payloads); + return await decodeFromPayloadsAtIndex(this.options.loadedDataConverter, 0, response.queryResult?.payloads); } /** @@ -562,7 +567,7 @@ export class WorkflowClient { requestId: uuid4(), // control is unused, signalName: input.signalName, - input: { payloads: await encodeToPayloads(this.dataConverter, ...input.args) }, + input: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...input.args) }, }); } catch (err) { this.rethrowGrpcError(err, input.workflowExecution, 'Failed to signal Workflow'); @@ -585,9 +590,9 @@ export class WorkflowClient { workflowId: options.workflowId, workflowIdReusePolicy: options.workflowIdReusePolicy, workflowType: { name: workflowType }, - input: { payloads: await encodeToPayloads(this.dataConverter, ...options.args) }, + input: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...options.args) }, signalName, - signalInput: { payloads: await encodeToPayloads(this.dataConverter, ...signalArgs) }, + signalInput: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...signalArgs) }, taskQueue: { kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED, name: options.taskQueue, @@ -596,10 +601,12 @@ export class WorkflowClient { workflowRunTimeout: options.workflowRunTimeout, workflowTaskTimeout: options.workflowTaskTimeout, retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined, - memo: options.memo ? { fields: await encodeMapToPayloads(this.dataConverter, options.memo) } : undefined, + memo: options.memo + ? { fields: await encodeMapToPayloads(this.options.loadedDataConverter, options.memo) } + : undefined, searchAttributes: options.searchAttributes ? { - indexedFields: await encodeMapToPayloads(this.dataConverter, options.searchAttributes), + indexedFields: await encodeMapToPayloads(this.options.loadedDataConverter, options.searchAttributes), } : undefined, cronSchedule: options.cronSchedule, @@ -626,7 +633,7 @@ export class WorkflowClient { workflowId: opts.workflowId, workflowIdReusePolicy: opts.workflowIdReusePolicy, workflowType: { name: workflowType }, - input: { payloads: await encodeToPayloads(this.dataConverter, ...opts.args) }, + input: { payloads: await encodeToPayloads(this.options.loadedDataConverter, ...opts.args) }, taskQueue: { kind: temporal.api.enums.v1.TaskQueueKind.TASK_QUEUE_KIND_UNSPECIFIED, name: opts.taskQueue, @@ -635,10 +642,10 @@ export class WorkflowClient { workflowRunTimeout: opts.workflowRunTimeout, workflowTaskTimeout: opts.workflowTaskTimeout, retryPolicy: opts.retry ? compileRetryPolicy(opts.retry) : undefined, - memo: opts.memo ? { fields: await encodeMapToPayloads(this.dataConverter, opts.memo) } : undefined, + memo: opts.memo ? { fields: await encodeMapToPayloads(this.options.loadedDataConverter, opts.memo) } : undefined, searchAttributes: opts.searchAttributes ? { - indexedFields: await encodeMapToPayloads(this.dataConverter, opts.searchAttributes), + indexedFields: await encodeMapToPayloads(this.options.loadedDataConverter, opts.searchAttributes), } : undefined, cronSchedule: opts.cronSchedule, @@ -672,7 +679,11 @@ export class WorkflowClient { namespace: this.options.namespace, identity: this.options.identity, ...input, - details: { payloads: input.details ? await encodeToPayloads(this.dataConverter, ...input.details) : undefined }, + details: { + payloads: input.details + ? await encodeToPayloads(this.options.loadedDataConverter, ...input.details) + : undefined, + }, firstExecutionRunId: input.firstExecutionRunId, }); } catch (err) { diff --git a/packages/common/src/converter/data-converter.ts b/packages/common/src/converter/data-converter.ts index 6e60b93d3..b0909443a 100644 --- a/packages/common/src/converter/data-converter.ts +++ b/packages/common/src/converter/data-converter.ts @@ -2,18 +2,25 @@ import { PayloadCodec } from './payload-codec'; import { PayloadConverter } from './payload-converter'; /** - * When your data (arguments and return values) is sent over the wire and stored by Temporal Server, - * it is encoded in binary in a {@link Payload} Protobuf message. + * When your data (arguments and return values) is sent over the wire and stored by Temporal Server, it is encoded in + * binary in a {@link Payload} Protobuf message. * - * The default `DataConverter` supports `Uint8Array`, and JSON serializables (so if [`JSON.stringify(yourArgOrRetval)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) works, the default data converter will work). + * The default `DataConverter` supports `Uint8Array`, and JSON serializables (so if + * [`JSON.stringify(yourArgOrRetval)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) + * works, the default data converter will work). Protobufs are supported via [this + * API](https://docs.temporal.io/docs/typescript/data-converters#protobufs). * - * Use a custom `DataConverter` to control the contents of your {@link Payload}s. - * Common reasons for using a custom `DataConverter` are: - * - Converting values that are not supported by the default `DataConverter` (for example, `JSON.stringify()` doesn't handle `BigInt`s, so if you want to return `{ total: 1000n }` from a Workflow, Signal, or Activity, you need your own `DataConverter`). - * - Encrypting values that may contain private information that you don't want stored in plaintext in Temporal Server's database. + * Use a custom `DataConverter` to control the contents of your {@link Payload}s. Common reasons for using a custom + * `DataConverter` are: + * - Converting values that are not supported by the default `DataConverter` (for example, `JSON.stringify()` doesn't + * handle `BigInt`s, so if you want to return `{ total: 1000n }` from a Workflow, Signal, or Activity, you need your + * own `DataConverter`). + * - Encrypting values that may contain private information that you don't want stored in plaintext in Temporal Server's + * database. * - Compressing values to reduce disk or network usage. * - * To use your custom `DataConverter`, provide it to the {@link WorkflowClient}, {@link Worker}, and {@link bundleWorkflowCode} (if you use it): + * To use your custom `DataConverter`, provide it to the {@link WorkflowClient}, {@link Worker}, and + * {@link bundleWorkflowCode} (if you use it): * - `new WorkflowClient({ ..., dataConverter })` * - `Worker.create({ ..., dataConverter })` * - `bundleWorkflowCode({ ..., payloadConverterPath })` diff --git a/packages/test/src/mock-native-worker.ts b/packages/test/src/mock-native-worker.ts index ebda82b6f..f158d3ad2 100644 --- a/packages/test/src/mock-native-worker.ts +++ b/packages/test/src/mock-native-worker.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { SpanContext } from '@opentelemetry/api'; -import { defaultPayloadConverter, fromPayloadsAtIndex, LoadedDataConverter } from '@temporalio/common'; -import { loadDataConverter } from '@temporalio/internal-non-workflow-common'; +import { defaultPayloadConverter, fromPayloadsAtIndex } from '@temporalio/common'; import { msToTs } from '@temporalio/internal-workflow-common'; import { coresdk } from '@temporalio/proto'; import { DefaultLogger } from '@temporalio/worker'; @@ -160,13 +159,9 @@ export class Worker extends RealWorker { return this.nativeWorker as MockNativeWorker; } - public constructor( - workflowCreator: WorkflowCreator, - opts: CompiledWorkerOptions, - dataConverter: LoadedDataConverter - ) { + public constructor(workflowCreator: WorkflowCreator, opts: CompiledWorkerOptions) { const nativeWorker = new MockNativeWorker(); - super(nativeWorker, workflowCreator, opts, dataConverter); + super(nativeWorker, workflowCreator, opts); } public runWorkflows(...args: Parameters): Promise { @@ -182,7 +177,6 @@ export const defaultOptions: WorkerOptions = { }; export async function isolateFreeWorker(options: WorkerOptions = defaultOptions): Promise { - const dataConverter = await loadDataConverter(options.dataConverter); return new Worker( { async createWorkflow() { @@ -192,7 +186,6 @@ export async function isolateFreeWorker(options: WorkerOptions = defaultOptions) /* Nothing to destroy */ }, }, - compileWorkerOptions(addDefaultWorkerOptions(options)), - dataConverter + compileWorkerOptions(addDefaultWorkerOptions(options)) ); } diff --git a/packages/test/src/test-custom-payload-codec.ts b/packages/test/src/test-custom-payload-codec.ts index 34fb45d66..7469bf0b5 100644 --- a/packages/test/src/test-custom-payload-codec.ts +++ b/packages/test/src/test-custom-payload-codec.ts @@ -52,7 +52,7 @@ if (RUN_INTEGRATION_TESTS) { const worker = await Worker.create({ ...defaultOptions, taskQueue, - dataConverter, // TODO { payloadCodec: defaultPayloadCodec } + dataConverter, sinks, }); const client = new WorkflowClient(new Connection().service, { dataConverter }); @@ -63,11 +63,11 @@ if (RUN_INTEGRATION_TESTS) { taskQueue, }); - t.is(result, 'encoded'); + t.is(result, 'encoded'); // workflow retval encoded by worker worker.shutdown(); }; await Promise.all([worker.run(), runAndShutdown()]); - t.is(logs[0], 'encodedencoded'); + t.is(logs[0], 'encodedencoded'); // workflow args encoded by client }); test('Workflow arguments and retvals are decoded', async (t) => { @@ -87,7 +87,7 @@ if (RUN_INTEGRATION_TESTS) { const worker = await Worker.create({ ...defaultOptions, taskQueue, - dataConverter, // TODO { payloadCodec: defaultPayloadCodec } + dataConverter, sinks, }); const client = new WorkflowClient(new Connection().service, { dataConverter }); @@ -98,11 +98,11 @@ if (RUN_INTEGRATION_TESTS) { taskQueue, }); - t.is(result, 'decoded'); + t.is(result, 'decoded'); // workflow retval decoded by client worker.shutdown(); }; await Promise.all([worker.run(), runAndShutdown()]); - t.is(logs[0], 'decodeddecoded'); + t.is(logs[0], 'decodeddecoded'); // workflow args decoded by worker }); test('Activity arguments and retvals are encoded', async (t) => { @@ -129,17 +129,15 @@ if (RUN_INTEGRATION_TESTS) { }); const client = new WorkflowClient(new Connection().service, { dataConverter }); const runAndShutdown = async () => { - const result = await client.execute(twoStringsActivity, { + await client.execute(twoStringsActivity, { workflowId: uuid4(), taskQueue, }); - - t.is(result, 'encoded'); worker.shutdown(); }; await Promise.all([worker.run(), runAndShutdown()]); - t.is(workflowLogs[0], 'encoded'); - t.is(activityLogs[0], 'Activityencodedencoded'); + t.is(workflowLogs[0], 'encoded'); // activity retval encoded by worker + t.is(activityLogs[0], 'Activityencodedencoded'); // activity args encoded by worker }); test('Activity arguments and retvals are decoded', async (t) => { @@ -166,16 +164,14 @@ if (RUN_INTEGRATION_TESTS) { }); const client = new WorkflowClient(new Connection().service, { dataConverter }); const runAndShutdown = async () => { - const result = await client.execute(twoStringsActivity, { + await client.execute(twoStringsActivity, { workflowId: uuid4(), taskQueue, }); - - t.is(result, 'decoded'); worker.shutdown(); }; await Promise.all([worker.run(), runAndShutdown()]); - t.is(workflowLogs[0], 'decoded'); - t.is(activityLogs[0], 'Activitydecodeddecoded'); + t.is(workflowLogs[0], 'decoded'); // activity retval decoded by worker + t.is(activityLogs[0], 'Activitydecodeddecoded'); // activity args decoded by worker }); } diff --git a/packages/worker/src/worker-options.ts b/packages/worker/src/worker-options.ts index 97c07feb2..df325f4e0 100644 --- a/packages/worker/src/worker-options.ts +++ b/packages/worker/src/worker-options.ts @@ -1,4 +1,5 @@ -import { DataConverter } from '@temporalio/common'; +import { DataConverter, LoadedDataConverter } from '@temporalio/common'; +import { loadDataConverter } from '@temporalio/internal-non-workflow-common'; import { ActivityInterface, msToNumber } from '@temporalio/internal-workflow-common'; import os from 'os'; import { WorkerInterceptors } from './interceptors'; @@ -243,6 +244,7 @@ export interface CompiledWorkerOptions extends Omit { try { - const dataConverter = loadDataConverter(compiledOptions.dataConverter); let bundle: string | undefined = undefined; let workflowCreator: WorkflowCreator | undefined = undefined; if (compiledOptions.workflowsPath) { @@ -302,7 +300,7 @@ export class Worker { }); } } - return new this(nativeWorker, workflowCreator, compiledOptions, dataConverter); + return new this(nativeWorker, workflowCreator, compiledOptions); } catch (err) { // Deregister our worker in case Worker creation (Webpack) failed await nativeWorker.completeShutdown(); @@ -319,11 +317,10 @@ export class Worker { * Optional WorkflowCreator - if not provided, Worker will not poll on Workflows */ protected readonly workflowCreator: WorkflowCreator | undefined, - public readonly options: CompiledWorkerOptions, - protected readonly dataConverter: LoadedDataConverter + public readonly options: CompiledWorkerOptions ) { this.tracer = getTracer(options.enableSDKTracing); - this.workflowCodecRunner = new WorkflowCodecRunner(dataConverter.payloadCodec); + this.workflowCodecRunner = new WorkflowCodecRunner(options.loadedDataConverter.payloadCodec); } /** @@ -466,7 +463,7 @@ export class Worker { const info = await extractActivityInfo( task, false, - this.dataConverter, + this.options.loadedDataConverter, this.nativeWorker.namespace ); const { activityType } = info; @@ -490,7 +487,7 @@ export class Worker { } let args: unknown[]; try { - args = await decodeArrayFromPayloads(this.dataConverter, task.start?.input); + args = await decodeArrayFromPayloads(this.options.loadedDataConverter, task.start?.input); } catch (err) { output = { type: 'result', @@ -522,7 +519,7 @@ export class Worker { activity = new Activity( info, fn, - this.dataConverter, + this.options.loadedDataConverter, (details) => this.activityHeartbeatSubject.next({ taskToken, @@ -759,7 +756,7 @@ export class Worker { const completion = coresdk.workflow_completion.WorkflowActivationCompletion.encodeDelimited({ runId: activation.runId, failed: { - failure: await encodeErrorToFailure(this.dataConverter, error), + failure: await encodeErrorToFailure(this.options.loadedDataConverter, error), }, }).finish(); // We do not dispose of the Workflow yet, wait to be evicted from Core. @@ -832,7 +829,7 @@ export class Worker { complete: () => this.log.debug('Heartbeats complete'), }), mergeMap(async ({ taskToken, details }) => { - const payload = await encodeToPayload(this.dataConverter, details); + const payload = await encodeToPayload(this.options.loadedDataConverter, details); const arr = coresdk.ActivityHeartbeat.encodeDelimited({ taskToken, details: [payload], diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 8c0377135..edf3829e7 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -20,9 +20,7 @@ ], "scripts": {}, "dependencies": { - "@temporalio/internal-workflow-common": "file:../internal-workflow-common" - }, - "devDependencies": { + "@temporalio/internal-workflow-common": "file:../internal-workflow-common", "@temporalio/proto": "file:../proto" }, "publishConfig": { diff --git a/packages/workflow/src/worker-interface.ts b/packages/workflow/src/worker-interface.ts index a2d28e99b..da61151e5 100644 --- a/packages/workflow/src/worker-interface.ts +++ b/packages/workflow/src/worker-interface.ts @@ -129,6 +129,15 @@ export async function initRuntime({ info, randomnessSeed, now, patches }: Workfl } } + // webpack doesn't know what to bundle given a dynamic import expression, so we can't do: + // state.payloadConverter = (await import(payloadConverterPath)).payloadConverter; + // @ts-expect-error this is a webpack alias to payloadConverterPath + const customPayloadConverter = (await import('__temporal_custom_payload_converter')).payloadConverter; + // The `payloadConverter` export is validated in the Worker + if (customPayloadConverter !== undefined) { + state.payloadConverter = customPayloadConverter; + } + const { importWorkflows, importInterceptors } = state; if (importWorkflows === undefined || importInterceptors === undefined) { throw new IllegalStateError('Workflow has not been initialized'); @@ -148,15 +157,6 @@ export async function initRuntime({ info, randomnessSeed, now, patches }: Workfl } } - // webpack doesn't know what to bundle given a dynamic import expression, so we can't do: - // state.payloadConverter = (await import(payloadConverterPath)).payloadConverter; - // @ts-expect-error this is a webpack alias to payloadConverterPath - const customPayloadConverter = (await import('__temporal_custom_payload_converter')).payloadConverter; - // The `payloadConverter` export is validated in the Worker - if (customPayloadConverter !== undefined) { - state.payloadConverter = customPayloadConverter; - } - let workflow: Workflow; try { const mod = await importWorkflows(); From 243f9346cb6397486743b008d9624d9886853b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loren=20=F0=9F=A4=93?= Date: Wed, 2 Mar 2022 13:38:01 -0500 Subject: [PATCH 33/33] Address comments --- docs/data-converter.md | 2 +- .../common/src/converter/payload-converter.ts | 16 +++------ .../src/converter/payload-converters.ts | 2 ++ .../internal-workflow-common/src/errors.ts | 6 +--- packages/test/src/mock-native-worker.ts | 2 +- .../test/src/test-custom-payload-converter.ts | 33 ++++++++++--------- packages/test/src/test-payload-converter.ts | 6 ++-- 7 files changed, 32 insertions(+), 35 deletions(-) diff --git a/docs/data-converter.md b/docs/data-converter.md index 99bd74c8f..a17739858 100644 --- a/docs/data-converter.md +++ b/docs/data-converter.md @@ -23,7 +23,7 @@ function workflowInclusiveInstanceOf(instance: unknown, type: Function): boolean ## Decision -Given the possibility of switching or adding other isolation methods in future, we opted to convert to/from Payloads inside the vm (`PayloadConverter`). We also added another transformer layer called `PayloadCodec` that runs outside the vm, can use node APIs and Promises, and operates on Payloads. A `DataConverter` is a `PayloadConverter` and a `PayloadCodec`: +Given the possibility of switching or adding other isolation methods in future, we opted to convert to/from Payloads inside the vm (`PayloadConverter`). We also added another transformer layer called `PayloadCodec` that runs outside the vm, can use node async APIs (like `zlib.gzip` for compression or `crypto.scrypt` for encryption), and operates on Payloads. A `DataConverter` is a `PayloadConverter` and a `PayloadCodec`: ```ts export interface DataConverter { diff --git a/packages/common/src/converter/payload-converter.ts b/packages/common/src/converter/payload-converter.ts index 49b687836..27ca7657a 100644 --- a/packages/common/src/converter/payload-converter.ts +++ b/packages/common/src/converter/payload-converter.ts @@ -40,20 +40,14 @@ export class CompositePayloadConverter implements PayloadConverter { /** * Tries to run `.toPayload(value)` on each converter in the order provided at construction. * Returns the first successful result, or `undefined` if there is no converter that can handle the value. + * + * @throws UnsupportedJsonTypeError */ public toPayload(value: T): Payload | undefined { for (const converter of this.converters) { - try { - const result = converter.toPayload(value); - if (result !== undefined) { - return result; - } - } catch (e: unknown) { - if (e instanceof PayloadConverterError) { - continue; - } else { - throw e; - } + const result = converter.toPayload(value); + if (result !== undefined) { + return result; } } return undefined; diff --git a/packages/common/src/converter/payload-converters.ts b/packages/common/src/converter/payload-converters.ts index 917fed208..f24702a7b 100644 --- a/packages/common/src/converter/payload-converters.ts +++ b/packages/common/src/converter/payload-converters.ts @@ -28,6 +28,8 @@ export class UndefinedPayloadConverter implements PayloadConverterWithEncoding { /** * Converts between non-undefined values and serialized JSON Payload + * + * @throws UnsupportedJsonTypeError */ export class JsonPayloadConverter implements PayloadConverterWithEncoding { public encodingType = encodingTypes.METADATA_ENCODING_JSON; diff --git a/packages/internal-workflow-common/src/errors.ts b/packages/internal-workflow-common/src/errors.ts index 051ea4d1b..abba20f6b 100644 --- a/packages/internal-workflow-common/src/errors.ts +++ b/packages/internal-workflow-common/src/errors.ts @@ -2,11 +2,7 @@ export class ValueError extends Error { public readonly name: string = 'ValueError'; } -export class DataConverterError extends Error { - public readonly name: string = 'DataConverterError'; -} - -export class PayloadConverterError extends DataConverterError { +export class PayloadConverterError extends ValueError { public readonly name: string = 'PayloadConverterError'; } diff --git a/packages/test/src/mock-native-worker.ts b/packages/test/src/mock-native-worker.ts index f158d3ad2..a21b29885 100644 --- a/packages/test/src/mock-native-worker.ts +++ b/packages/test/src/mock-native-worker.ts @@ -176,7 +176,7 @@ export const defaultOptions: WorkerOptions = { taskQueue: 'test', }; -export async function isolateFreeWorker(options: WorkerOptions = defaultOptions): Promise { +export function isolateFreeWorker(options: WorkerOptions = defaultOptions): Worker { return new Worker( { async createWorkflow() { diff --git a/packages/test/src/test-custom-payload-converter.ts b/packages/test/src/test-custom-payload-converter.ts index 25d7f12cc..c3070c20a 100644 --- a/packages/test/src/test-custom-payload-converter.ts +++ b/packages/test/src/test-custom-payload-converter.ts @@ -59,31 +59,34 @@ if (RUN_INTEGRATION_TESTS) { } test('Worker throws on invalid payloadConverterPath', async (t) => { - await t.throwsAsync( - isolateFreeWorker({ - ...defaultOptions, - dataConverter: { payloadConverterPath: './wrong-path' }, - }), + await t.throws( + () => + isolateFreeWorker({ + ...defaultOptions, + dataConverter: { payloadConverterPath: './wrong-path' }, + }), { message: /Could not find a file at the specified payloadConverterPath/, } ); - await t.throwsAsync( - isolateFreeWorker({ - ...defaultOptions, - dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter-no-export') }, - }), + await t.throws( + () => + isolateFreeWorker({ + ...defaultOptions, + dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter-no-export') }, + }), { message: /Module .* does not have a `payloadConverter` named export/, } ); - await t.throwsAsync( - isolateFreeWorker({ - ...defaultOptions, - dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter-bad-export') }, - }), + await t.throws( + () => + isolateFreeWorker({ + ...defaultOptions, + dataConverter: { payloadConverterPath: require.resolve('./payload-converters/payload-converter-bad-export') }, + }), { message: /payloadConverter export at .* must be an object with toPayload and fromPayload methods/, } diff --git a/packages/test/src/test-payload-converter.ts b/packages/test/src/test-payload-converter.ts index 467a7ab33..855c39a13 100644 --- a/packages/test/src/test-payload-converter.ts +++ b/packages/test/src/test-payload-converter.ts @@ -223,7 +223,7 @@ test('DefaultPayloadConverter converts protobufs', (t) => { ); }); -test('DefaultPayloadConverter converts to payload by trying each converter in order', (t) => { +test('DefaultPayloadConverter converts to payload by trying each converter in order', async (t) => { const defaultPayloadConverterWithProtos = new DefaultPayloadConverterWithProtobufs({ protobufRoot: root }); const instance = root.ProtoActivityInput.create({ name: 'Proto', age: 1 }); t.deepEqual( @@ -240,7 +240,9 @@ test('DefaultPayloadConverter converts to payload by trying each converter in or defaultPayloadConverterWithProtos.toPayload(u8('abc')), new BinaryPayloadConverter().toPayload(u8('abc')) ); - t.is(defaultPayloadConverterWithProtos.toPayload(0n), undefined); + await t.throwsAsync(async () => defaultPayloadConverterWithProtos.toPayload(0n), { + instanceOf: UnsupportedJsonTypeError, + }); }); test('defaultPayloadConverter converts from payload by payload type', async (t) => {