From 77b8cb222e2a81d52a7f4d2a1564c7e7847019b6 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Tue, 7 Apr 2026 22:15:21 +0200 Subject: [PATCH 1/8] Add class registering way --- ts/examples/simple-example-ts/index.ts | 48 ++- .../simple-example-ts/package-lock.json | 19 +- ts/package-lock.json | 217 ++++++++------ ts/package.json | 5 +- ts/src/action_sdk.ts | 277 +++++++++++------- ts/src/types.ts | 111 +++++-- 6 files changed, 421 insertions(+), 256 deletions(-) diff --git a/ts/examples/simple-example-ts/index.ts b/ts/examples/simple-example-ts/index.ts index f1a9ede..dfbaaa3 100644 --- a/ts/examples/simple-example-ts/index.ts +++ b/ts/examples/simple-example-ts/index.ts @@ -1,8 +1,8 @@ import { createSdk, HerculesActionProjectConfiguration, - HerculesFunctionContext, - RuntimeErrorException + HerculesFunctionContext, Identifier, LinkedDataTypeIdentifiers, + RuntimeErrorException, RuntimeParameter, Signature } from "@code0-tech/hercules"; const sdk = createSdk({ @@ -22,33 +22,29 @@ sdk.registerDataTypes({ type: "any", }) -sdk.registerRuntimeFunctionDefinitionsAndFunctionDefinitions({ - definition: { - signature: "(number: number) => number", - parameters: [ - { - runtimeName: "number", - defaultValue: 20, - } - ], - runtimeName: "fib", - }, - //This param is optional and can be omitted - handler: (context: HerculesFunctionContext, number: bigint): bigint => { - console.log(context) - console.log("Project id:", context.projectId); - console.log("Execution id:", context.executionId); - console.log("Matched configs:", context.matchedConfig); // matched configs for the current execution - - function fibonacci(num: bigint): bigint { - if (num <= 1) return num; - return fibonacci(num - 1n) + fibonacci(num - 2n); - } +@Identifier("fib") +@Signature("(number: number) => number") +@RuntimeParameter({ + runtimeName: "number", + defaultValue: 20 +}) +class FibonacciFunction { + run(context: HerculesFunctionContext, number: number): number { + console.log(context) + console.log("Project id:", context.projectId); + console.log("Execution id:", context.executionId); + console.log("Matched configs:", context.matchedConfig); // matched configs for the current execution - return fibonacci(number) + function fibonacci(num: number): number { + if (num <= 1) return num; + return fibonacci(num - 1) + fibonacci(num - 2); } + + return fibonacci(number) } -) +} + +sdk.registerRuntimeFunctionDefinitionClass(FibonacciFunction) sdk.registerFlowTypes( { diff --git a/ts/examples/simple-example-ts/package-lock.json b/ts/examples/simple-example-ts/package-lock.json index b9a1c24..ef97b74 100644 --- a/ts/examples/simple-example-ts/package-lock.json +++ b/ts/examples/simple-example-ts/package-lock.json @@ -15,23 +15,24 @@ "node_modules/@code0-tech/hercules": { "version": "0.0.0", "resolved": "file:../../code0-tech-hercules-0.0.0.tgz", - "integrity": "sha512-0J+8A+AN0rmy8YTXBAGPBgHYUARwf60k63kiWVHA2dob9xJfX7PyyncnRfPdRG0DtENLVN26qmtTmCRo4mM0kQ==", + "integrity": "sha512-KgSdzgsOzIyHCMebtf0B/HUgSJNgYe7yuj8tpuqdJIP1UzhVU/2Tptwv6z9RZK/iv+EG4yq0gntZWXnHwZAS0w==", "license": "ISC", "dependencies": { - "@code0-tech/tucana": "0.0.67", + "@code0-tech/tucana": "0.0.68", "@grpc/grpc-js": "^1.14.3", "@protobuf-ts/grpc-backend": "^2.11.1", "@protobuf-ts/grpc-transport": "^2.11.1", "@protobuf-ts/runtime": "^2.11.1", "@protobuf-ts/runtime-rpc": "^2.11.1", "pino": "^10.3.1", - "pino-pretty": "^13.1.3" + "pino-pretty": "^13.1.3", + "reflect-metadata": "^0.2.2" } }, "node_modules/@code0-tech/tucana": { - "version": "0.0.67", - "resolved": "https://registry.npmjs.org/@code0-tech/tucana/-/tucana-0.0.67.tgz", - "integrity": "sha512-3V1h3LB+iWeXnt80nsIQo+TeMgVTgbDxvDsAArbffHyU37ii1tsAAV6Ty7wAdkrCSsObyFRpdiIZgzgYVVKfzw==", + "version": "0.0.68", + "resolved": "https://registry.npmjs.org/@code0-tech/tucana/-/tucana-0.0.68.tgz", + "integrity": "sha512-kbjLiKjJyZLnPmNi5JabWRNZcWZyXnfnld8jCFrUkvKbGj77CAwggR0uH8SqaxwWNWqQZlavYzCZWO/ob3+uKA==", "license": "Apache-2.0" }, "node_modules/@grpc/grpc-js": { @@ -509,6 +510,12 @@ "node": ">= 12.13.0" } }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", diff --git a/ts/package-lock.json b/ts/package-lock.json index 5f6a676..c69bfdb 100644 --- a/ts/package-lock.json +++ b/ts/package-lock.json @@ -9,14 +9,15 @@ "version": "0.0.0", "license": "ISC", "dependencies": { - "@code0-tech/tucana": "0.0.67", + "@code0-tech/tucana": "0.0.68", "@grpc/grpc-js": "^1.14.3", "@protobuf-ts/grpc-backend": "^2.11.1", "@protobuf-ts/grpc-transport": "^2.11.1", "@protobuf-ts/runtime": "^2.11.1", "@protobuf-ts/runtime-rpc": "^2.11.1", "pino": "^10.3.1", - "pino-pretty": "^13.1.3" + "pino-pretty": "^13.1.3", + "reflect-metadata": "^0.2.2" }, "devDependencies": { "@types/node": "^25.0.10", @@ -78,9 +79,9 @@ } }, "node_modules/@code0-tech/tucana": { - "version": "0.0.67", - "resolved": "https://registry.npmjs.org/@code0-tech/tucana/-/tucana-0.0.67.tgz", - "integrity": "sha512-3V1h3LB+iWeXnt80nsIQo+TeMgVTgbDxvDsAArbffHyU37ii1tsAAV6Ty7wAdkrCSsObyFRpdiIZgzgYVVKfzw==", + "version": "0.0.68", + "resolved": "https://registry.npmjs.org/@code0-tech/tucana/-/tucana-0.0.68.tgz", + "integrity": "sha512-kbjLiKjJyZLnPmNi5JabWRNZcWZyXnfnld8jCFrUkvKbGj77CAwggR0uH8SqaxwWNWqQZlavYzCZWO/ob3+uKA==", "license": "Apache-2.0" }, "node_modules/@emnapi/core": { @@ -640,9 +641,9 @@ } }, "node_modules/@microsoft/api-extractor": { - "version": "7.58.0", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.58.0.tgz", - "integrity": "sha512-XcHlDylX5GblbGbs1eBoexjVyvdJMioTPuBCgCorE2rqijzTYxi6eudXyez3xACRxtH9aDtahoL9fYM4XTvQmg==", + "version": "7.58.1", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.58.1.tgz", + "integrity": "sha512-kF3GFME4lN22O5zbnXk2RP4y/4PDQdps0xKiYTipMYprkwCmmpsWLZt/N2Fkbil540cSLfJX0BW7LkHzgMVUYg==", "dev": true, "license": "MIT", "dependencies": { @@ -654,7 +655,7 @@ "@rushstack/terminal": "0.22.4", "@rushstack/ts-command-line": "5.3.4", "diff": "~8.0.2", - "lodash": "~4.17.23", + "lodash": "~4.18.1", "minimatch": "10.2.3", "resolve": "~1.22.1", "semver": "~7.5.4", @@ -708,26 +709,28 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", - "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "version": "0.123.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.123.0.tgz", + "integrity": "sha512-YtECP/y8Mj1lSHiUWGSRzy/C6teUKlS87dEfuVKT09LgQbUsBW1rNg+MiJ4buGu3yuADV60gbIvo9/HplA56Ew==", "dev": true, "license": "MIT", "funding": { @@ -846,9 +849,9 @@ "license": "BSD-3-Clause" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-5ZiiecKH2DXAVJTNN13gNMUcCDg4Jy8ZjbXEsPnqa248wgOVeYRX0iqXXD5Jz4bI9BFHgKsI2qmyJynstbmr+g==", "cpu": [ "arm64" ], @@ -863,9 +866,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-tz/v/8G77seu8zAB3A5sK3UFoOl06zcshEzhUO62sAEtrEuW/H1CcyoupOrD+NbQJytYgA4CppXPzlrmp4JZKA==", "cpu": [ "arm64" ], @@ -880,9 +883,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.13.tgz", + "integrity": "sha512-8DakphqOz8JrMYWTJmWA+vDJxut6LijZ8Xcdc4flOlAhU7PNVwo2MaWBF9iXjJAPo5rC/IxEFZDhJ3GC7NHvug==", "cpu": [ "x64" ], @@ -897,9 +900,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.13.tgz", + "integrity": "sha512-4wBQFfjDuXYN/SVI8inBF3Aa+isq40rc6VMFbk5jcpolUBTe5cYnMsHZ51nFWsx3PVyyNN3vgoESki0Hmr/4BA==", "cpu": [ "x64" ], @@ -914,9 +917,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.13.tgz", + "integrity": "sha512-JW/e4yPIXLms+jmnbwwy5LA/LxVwZUWLN8xug+V200wzaVi5TEGIWQlh8o91gWYFxW609euI98OCCemmWGuPrw==", "cpu": [ "arm" ], @@ -931,13 +934,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-ZfKWpXiUymDnavepCaM6KG/uGydJ4l2nBmMxg60Ci4CbeefpqjPWpfaZM7PThOhk2dssqBAcwLc6rAyr0uTdXg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -948,13 +954,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.13.tgz", + "integrity": "sha512-bmRg3O6Z0gq9yodKKWCIpnlH051sEfdVwt+6m5UDffAQMUUqU0xjnQqqAUm+Gu7ofAAly9DqiQDtKu2nPDEABA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -965,13 +974,16 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-8Wtnbw4k7pMYN9B/mOEAsQ8HOiq7AZ31Ig4M9BKn2So4xRaFEhtCSa4ZJaOutOWq50zpgR4N5+L/opnlaCx8wQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -982,13 +994,16 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-D/0Nlo8mQuxSMohNJUF2lDXWRsFDsHldfRRgD9bRgktj+EndGPj4DOV37LqDKPYS+osdyhZEH7fTakTAEcW7qg==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -999,13 +1014,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-eRrPvat2YaVQcwwKi/JzOP6MKf1WRnOCr+VaI3cTWz3ZoLcP/654z90lVCJ4dAuMEpPdke0n+qyAqXDZdIC4rA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1016,13 +1034,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.13.tgz", + "integrity": "sha512-PsdONiFRp8hR8KgVjTWjZ9s7uA3uueWL0t74/cKHfM4dR5zXYv4AjB8BvA+QDToqxAFg4ZkcVEqeu5F7inoz5w==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1033,9 +1054,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-hCNXgC5dI3TVOLrPT++PKFNZ+1EtS0mLQwfXXXSUD/+rGlB65gZDwN/IDuxLpQP4x8RYYHqGomlUXzpO8aVI2w==", "cpu": [ "arm64" ], @@ -1050,9 +1071,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.13.tgz", + "integrity": "sha512-viLS5C5et8NFtLWw9Sw3M/w4vvnVkbWkO7wSNh3C+7G1+uCkGpr6PcjNDSFcNtmXY/4trjPBqUfcOL+P3sWy/g==", "cpu": [ "wasm32" ], @@ -1060,16 +1081,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" + "@emnapi/core": "1.9.1", + "@emnapi/runtime": "1.9.1", + "@napi-rs/wasm-runtime": "^1.1.2" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.13.tgz", + "integrity": "sha512-Fqa3Tlt1xL4wzmAYxGNFV36Hb+VfPc9PYU+E25DAnswXv3ODDu/yyWjQDbXMo5AGWkQVjLgQExuVu8I/UaZhPQ==", "cpu": [ "arm64" ], @@ -1084,9 +1107,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.13.tgz", + "integrity": "sha512-/pLI5kPkGEi44TDlnbio3St/5gUFeN51YWNAk/Gnv6mEQBOahRBh52qVFVBpmrnU01n2yysvBML9Ynu7K4kGAQ==", "cpu": [ "x64" ], @@ -1101,9 +1124,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.13.tgz", + "integrity": "sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==", "dev": true, "license": "MIT" }, @@ -2899,9 +2922,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, @@ -3366,6 +3389,12 @@ "node": ">= 12.13.0" } }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3417,14 +3446,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.13.tgz", + "integrity": "sha512-bvVj8YJmf0rq4pSFmH7laLa6pYrhghv3PRzrCdRAr23g66zOKVJ4wkvFtgohtPLWmthgg8/rkaqRHrpUEh0Zbw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" + "@oxc-project/types": "=0.123.0", + "@rolldown/pluginutils": "1.0.0-rc.13" }, "bin": { "rolldown": "bin/cli.mjs" @@ -3433,21 +3462,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + "@rolldown/binding-android-arm64": "1.0.0-rc.13", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.13", + "@rolldown/binding-darwin-x64": "1.0.0-rc.13", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.13", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.13", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.13", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.13", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.13", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.13", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.13" } }, "node_modules/rollup": { @@ -3895,16 +3924,16 @@ } }, "node_modules/vite": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", - "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.7.tgz", + "integrity": "sha512-P1PbweD+2/udplnThz3btF4cf6AgPky7kk23RtHUkJIU5BIxwPprhRGmOAHs6FTI7UiGbTNrgNP6jSYD6JaRnw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", + "rolldown": "1.0.0-rc.13", "tinyglobby": "^0.2.15" }, "bin": { @@ -3922,7 +3951,7 @@ "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", diff --git a/ts/package.json b/ts/package.json index 251652a..699e1f3 100644 --- a/ts/package.json +++ b/ts/package.json @@ -30,14 +30,15 @@ "license": "ISC", "type": "module", "dependencies": { - "@code0-tech/tucana": "0.0.67", + "@code0-tech/tucana": "0.0.68", "@grpc/grpc-js": "^1.14.3", "@protobuf-ts/grpc-backend": "^2.11.1", "@protobuf-ts/grpc-transport": "^2.11.1", "@protobuf-ts/runtime": "^2.11.1", "@protobuf-ts/runtime-rpc": "^2.11.1", "pino": "^10.3.1", - "pino-pretty": "^13.1.3" + "pino-pretty": "^13.1.3", + "reflect-metadata": "^0.2.2" }, "devDependencies": { "@types/node": "^25.0.10", diff --git a/ts/src/action_sdk.ts b/ts/src/action_sdk.ts index f8c1137..fdf2559 100644 --- a/ts/src/action_sdk.ts +++ b/ts/src/action_sdk.ts @@ -4,7 +4,9 @@ import {RpcOptions} from "@protobuf-ts/runtime-rpc"; import { ActionSdk, HerculesActionConfigurationDefinition, HerculesActionProjectConfiguration, HerculesFunctionContext, SdkState, RuntimeErrorException, - HerculesRegisterRuntimeFunctionParameter, HerculesRegisterFunctionDefinition + HerculesFunctionDefinition, RuntimeFunctionDefinitionClass, + FunctionDefinitionConstructor, RegisteredRuntimeFunction, HerculesFunctionDefinitionParameter, + HerculesRuntimeFunctionDefinitionParameter, HerculesRuntimeFunctionDefinition, RegisteredFunction } from "./types.js"; import { ActionTransferServiceClient, @@ -18,10 +20,11 @@ import { } from "@code0-tech/tucana/aquila"; import { ActionConfigurations, - FlowTypeSetting, + FlowTypeSetting, RuntimeParameterDefinition, } from "@code0-tech/tucana/shared"; import {constructValue, toAllowedValue} from "@code0-tech/tucana/helpers"; import {logger} from "./logger"; +import 'reflect-metadata'; const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActionConfigurationDefinition[]): ActionSdk => { const transport = new GrpcTransport( @@ -54,74 +57,148 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi fullyConnected: false } - const registerFunctionDefinitions = async (...functionDefinitions: Array) => { - for (const functionDefinition of functionDefinitions) { - state.functions.push({ - identifier: functionDefinition.runtimeName, - definition: { - displayMessage: functionDefinition.displayMessage || [], - name: functionDefinition.name || [], - documentation: functionDefinition.documentation || [], - description: functionDefinition.description || [], - deprecationMessage: functionDefinition.deprecationMessage || [], - displayIcon: functionDefinition.displayIcon || "", - alias: functionDefinition.alias || [], - linkedDataTypeIdentifiers: functionDefinition.linkedDataTypes || [], - definitionSource: "action", - version: functionDefinition.version || config.version, - runtimeName: functionDefinition.runtimeName, - parameterDefinitions: (functionDefinition.parameters || []).map(param => ({ - runtimeName: param.runtimeName, + const buildRuntimeFunctionDefinition = (klass: RuntimeFunctionDefinitionClass): RegisteredRuntimeFunction => { + const identifier: string = Reflect.getMetadata('hercules:identifier', klass) + const runtimeParameters: HerculesRuntimeFunctionDefinitionParameter[] = Reflect.getMetadata('hercules:runtime_parameters', klass) + const names: HerculesRuntimeFunctionDefinition["name"] = Reflect.getMetadata('hercules:name', klass) || [] + const displayMessage: HerculesRuntimeFunctionDefinition["displayMessage"] = Reflect.getMetadata('hercules:display_message', klass) || [] + const description: HerculesRuntimeFunctionDefinition["description"] = Reflect.getMetadata('hercules:description', klass) || [] + const deprecationMessage: HerculesRuntimeFunctionDefinition["deprecationMessage"] = Reflect.getMetadata('hercules:deprecation_message', klass) || [] + const alias: HerculesRuntimeFunctionDefinition["alias"] = Reflect.getMetadata('hercules:alias', klass) || [] + const documentation: HerculesRuntimeFunctionDefinition["documentation"] = Reflect.getMetadata('hercules:documentation', klass) || [] + const signature: HerculesRuntimeFunctionDefinition["signature"] = Reflect.getMetadata('hercules:signature', klass) + const linkedDataTypeIdentifiers: HerculesRuntimeFunctionDefinition["linkedDataTypes"] = Reflect.getMetadata('hercules:linked_data_type_identifiers', klass) || [] + const version: HerculesRuntimeFunctionDefinition["version"] = Reflect.getMetadata('hercules:version', klass) || config.version + const displayIcon: HerculesRuntimeFunctionDefinition["displayIcon"] = Reflect.getMetadata('hercules:display_icon', klass) || "" + const throwsError: HerculesRuntimeFunctionDefinition["throwsError"] = Reflect.getMetadata('hercules:throws_error', klass) || false + const runFunction = new klass().run + + if (!identifier) { + throw new Error(`Runtime function class ${klass.name} is missing an identifier. Please add @Identifier("your_identifier") decorator to the class.`) + } + if (!signature) { + throw new Error(`Runtime function class ${klass.name} is missing a signature. Please add @Signature("(param1: TYPE_1): RETURN_TYPE") decorator to the class.`) + } + + return { + identifier: identifier as string, + definition: { + alias: alias || [], + name: names || [], + description: description || [], + version: version || config.version, + runtimeName: identifier, + deprecationMessage: deprecationMessage || [], + displayIcon: displayIcon || "", + displayMessage: displayMessage || [], + documentation: documentation || [], + linkedDataTypeIdentifiers: linkedDataTypeIdentifiers || [], + runtimeParameterDefinitions: runtimeParameters.map(param => { + return { + ...param, name: param.name || [], description: param.description || [], documentation: param.documentation || [], - defaultValue: constructValue(param.defaultValue || null), hidden: param.hidden || false, - optional: param.hidden || false, - runtimeDefinitionName: param.runtimeDefinitionName || param.runtimeName - })), - signature: functionDefinition.signature, - throwsError: functionDefinition.throwsError || false, - runtimeDefinitionName: functionDefinition.runtimeDefinitionName + optional: param.optional || false, + defaultValue: param.defaultValue ? constructValue(param.defaultValue) : undefined, + } as RuntimeParameterDefinition + }), + signature: signature, + throwsError: throwsError || false, + definitionSource: "action" + }, + handler: runFunction + } as RegisteredRuntimeFunction + } + + return { + registerFunctionDefinitionClass(klass: FunctionDefinitionConstructor): Promise { + const parentClass = Object.getPrototypeOf(klass) + const runtimeFunction = buildRuntimeFunctionDefinition(parentClass); + const runtimeDefinition = runtimeFunction.definition + + const functionParameters: HerculesFunctionDefinitionParameter[] = Reflect.getMetadata('hercules:function_parameters', klass) + const names: HerculesFunctionDefinition["name"] = Reflect.getMetadata('hercules:name', klass) + const displayMessage: HerculesFunctionDefinition["displayMessage"] = Reflect.getMetadata('hercules:display_message', klass) + const description: HerculesFunctionDefinition["description"] = Reflect.getMetadata('hercules:description', klass) + const deprecationMessage: HerculesFunctionDefinition["deprecationMessage"] = Reflect.getMetadata('hercules:deprecation_message', klass) + const alias: HerculesFunctionDefinition["alias"] = Reflect.getMetadata('hercules:alias', klass) + const documentation: HerculesFunctionDefinition["documentation"] = Reflect.getMetadata('hercules:documentation', klass) + const signature: HerculesFunctionDefinition["signature"] = Reflect.getMetadata('hercules:signature', klass) + const linkedDataTypeIdentifiers: HerculesFunctionDefinition["linkedDataTypes"] = Reflect.getMetadata('hercules:linked_data_type_identifiers', klass) + const version: HerculesFunctionDefinition["version"] = Reflect.getMetadata('hercules:version', klass) + const displayIcon: HerculesFunctionDefinition["displayIcon"] = Reflect.getMetadata('hercules:display_icon', klass) + const throwsError: HerculesFunctionDefinition["throwsError"] = Reflect.getMetadata('hercules:throws_error', klass) + + runtimeDefinition.runtimeParameterDefinitions.forEach(runtimeDefinition => { + if (functionParameters.find((param: HerculesFunctionDefinitionParameter) => param.runtimeName === runtimeDefinition.runtimeName)) { + return; } - }); - } - return Promise.resolve() - }; - const registerRuntimeFunctionDefinitions = async (...runtimeFunctionDefinitions: HerculesRegisterRuntimeFunctionParameter[]) => { - for (const registeredFunction of runtimeFunctionDefinitions) { - const handler = registeredFunction.handler; - const functionDefinition = registeredFunction.definition; - state.runtimeFunctions.push({ - identifier: functionDefinition.runtimeName, + functionParameters.push({ + ...runtimeDefinition, + runtimeDefinitionName: runtimeDefinition.runtimeName + }) + }) + + state.functions.push({ + identifier: runtimeFunction.identifier, definition: { - displayMessage: functionDefinition.displayMessage || [], - name: functionDefinition.name || [], - documentation: functionDefinition.documentation || [], - description: functionDefinition.description || [], - deprecationMessage: functionDefinition.deprecationMessage || [], - displayIcon: functionDefinition.displayIcon || "", - alias: functionDefinition.alias || [], - linkedDataTypeIdentifiers: functionDefinition.linkedDataTypes || [], + runtimeDefinitionName: runtimeDefinition.runtimeName, + runtimeName: runtimeDefinition.runtimeName || runtimeDefinition.runtimeName, + signature: signature || runtimeDefinition.signature, + throwsError: throwsError || runtimeDefinition.throwsError, + alias: alias || runtimeDefinition.alias, + version: version || runtimeDefinition.version, + description: description || runtimeDefinition.description, + name: names || runtimeDefinition.name, + documentation: documentation || runtimeDefinition.documentation, + deprecationMessage: deprecationMessage || runtimeDefinition.deprecationMessage, + displayMessage: displayMessage || runtimeDefinition.displayMessage, + displayIcon: displayIcon || runtimeDefinition.displayIcon, definitionSource: "action", - version: functionDefinition.version || config.version, - runtimeName: functionDefinition.runtimeName, - runtimeParameterDefinitions: (functionDefinition.parameters || []).map(param => ({ - runtimeName: param.runtimeName, - name: param.name || [], - description: param.description || [], - documentation: param.documentation || [], - defaultValue: constructValue(param.defaultValue || null), - })), - signature: functionDefinition.signature, - throwsError: functionDefinition.throwsError || false, + linkedDataTypeIdentifiers: linkedDataTypeIdentifiers || runtimeDefinition.linkedDataTypeIdentifiers, + parameterDefinitions: functionParameters.map(value => { + return { + ...value, + runtimeDefinitionName: value.runtimeDefinitionName || value.runtimeName, + name: value.name || [], + description: value.description || [], + documentation: value.documentation || [], + hidden: value.hidden || false, + optional: value.optional || false, + defaultValue: value.defaultValue ? constructValue(value.defaultValue || null) : undefined, + } + }) }, - handler: handler, - }); - } - return Promise.resolve() - }; - return { + } as RegisteredFunction) + + return Promise.resolve(); + }, registerRuntimeFunctionDefinitionClass(klass: RuntimeFunctionDefinitionClass): Promise { + const omitFunctionDefinition = Reflect.getMetadata('hercules:omit_function_definition', klass) || false + + const runtimeFunction = buildRuntimeFunctionDefinition(klass); + const definition = runtimeFunction.definition + + state.runtimeFunctions.push(runtimeFunction as RegisteredRuntimeFunction) + if (!omitFunctionDefinition) { + state.functions.push({ + identifier: definition.runtimeName, + definition: { + ...definition, + runtimeDefinitionName: definition.runtimeName, + parameterDefinitions: definition.runtimeParameterDefinitions.map(param => { + return { + ...param, + runtimeDefinitionName: param.runtimeName + } + }) + } + } as RegisteredFunction) + } + + return Promise.resolve(); + }, fullyConnected(): boolean { return state.fullyConnected; }, @@ -209,29 +286,6 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi }) return Promise.resolve() }, - registerRuntimeFunctionDefinitionsAndFunctionDefinitions: async (...runtimeFunctionDefinitions) => { - await Promise.all(runtimeFunctionDefinitions.map(async (register) => { - await Promise.all([ - registerRuntimeFunctionDefinitions( - register - ), - registerFunctionDefinitions( - { - ...register.definition, - runtimeDefinitionName: register.definition.runtimeName, - parameters: register.definition.parameters?.map(param => { - return { - ...param, - runtimeDefinitionName: param.runtimeName, - } - }) - } - ) - ]) - })); - }, - registerRuntimeFunctionDefinitions: registerRuntimeFunctionDefinitions, - registerFunctionDefinitions: registerFunctionDefinitions, dispatchEvent: async (eventType, projectId, payload) => { if (!state.stream) { return Promise.reject("SDK is not connected. Call connect() before dispatching events."); @@ -309,27 +363,40 @@ async function connect(state: SdkState, config: ActionSdk["config"], options?: R logger.debug("Successfully sent logon request") - const runtimeFunctionDefinitionClient = new RuntimeFunctionDefinitionServiceClient(state.transport) - await runtimeFunctionDefinitionClient.update( - RuntimeFunctionDefinitionUpdateRequest.create( - { - runtimeFunctions: [ - ...state.runtimeFunctions.map(func => ({ - ...func.definition, - })) - ] - } - ), builtOptions - ).then(value => { - if (!value.response.success) { - logger.error({ - err: value.response, - request: value.request, - config, - }) - return Promise.reject(value.response); + const request = RuntimeFunctionDefinitionUpdateRequest.create( + { + runtimeFunctions: [ + ...state.runtimeFunctions.map(func => ({ + ...func.definition, + })) + ] } - }) + ); + try { + const runtimeFunctionDefinitionClient = new RuntimeFunctionDefinitionServiceClient(state.transport) + await runtimeFunctionDefinitionClient.update( + request, builtOptions + ).then(value => { + if (!value.response.success) { + logger.error({ + err: value.response, + request: value.request, + config, + }) + return Promise.reject(value.response); + } + }) + } catch (error) { + logger.debug({ + ...request.runtimeFunctions[0].runtimeParameterDefinitions[0].defaultValue + }) + logger.error({ + err: error, + request: request, + config, + }, "Error while updating runtime function definitions") + return Promise.reject(error); + } logger.debug("Successfully updated runtime function definitions") const FunctionDefinitionClient = new FunctionDefinitionServiceClient(state.transport) diff --git a/ts/src/types.ts b/ts/src/types.ts index 7215e86..9ed34a9 100644 --- a/ts/src/types.ts +++ b/ts/src/types.ts @@ -10,6 +10,7 @@ import { } from "@code0-tech/tucana/shared"; import {PlainValue} from "@code0-tech/tucana/helpers"; import {ActionTransferServiceClient, TransferRequest, TransferResponse} from "@code0-tech/tucana/aquila"; +import 'reflect-metadata'; export interface HerculesFunctionContext { @@ -55,18 +56,19 @@ export interface HerculesFlowType { displayIcon?: string, } +export interface HerculesRuntimeFunctionDefinitionParameter { + runtimeName: string, + defaultValue?: PlainValue, + name?: Translation[], + description?: Translation[], + documentation?: Translation[], + hidden?: boolean, + optional?: boolean +} export interface HerculesRuntimeFunctionDefinition { runtimeName: string, - parameters?: { - runtimeName: string, - defaultValue?: PlainValue, - name?: Translation[], - description?: Translation[], - documentation?: Translation[], - hidden?: boolean, - optional?: boolean - }[], + parameters?: HerculesRuntimeFunctionDefinitionParameter[], signature: string, throwsError?: boolean, name?: Translation[], @@ -80,19 +82,21 @@ export interface HerculesRuntimeFunctionDefinition { displayIcon?: string, } -export interface HerculesRegisterFunctionDefinition { +export interface HerculesFunctionDefinitionParameter { + runtimeName: string, + defaultValue?: PlainValue, + name?: Translation[], + description?: Translation[], + documentation?: Translation[], + hidden?: boolean, + optional?: boolean, + runtimeDefinitionName?: string +} + +export interface HerculesFunctionDefinition { runtimeDefinitionName: string, runtimeName: string, - parameters?: { - runtimeName: string, - defaultValue?: PlainValue, - name?: Translation[], - description?: Translation[], - documentation?: Translation[], - hidden?: boolean, - optional?: boolean, - runtimeDefinitionName?: string - }[], + parameters?: HerculesFunctionDefinitionParameter[], signature: string, throwsError?: boolean, name?: Translation[], @@ -145,9 +149,8 @@ export interface ActionSdk { registerConfigDefinitions: (...actionConfigurations: Array) => Promise, registerDataTypes: (...dataType: Array) => Promise, registerFlowTypes: (...flowTypes: Array) => Promise, - registerRuntimeFunctionDefinitionsAndFunctionDefinitions: (...runtimeFunctionDefinitions: Array) => Promise, - registerFunctionDefinitions: (...functionDefinitions: Array) => Promise, - registerRuntimeFunctionDefinitions: (...runtimeFunctionDefinitions: Array) => Promise, + registerRuntimeFunctionDefinitionClass: (klass: RuntimeFunctionDefinitionClass) => Promise, + registerFunctionDefinitionClass: (klass: FunctionDefinitionConstructor) => Promise, dispatchEvent: (eventType: string, projectId: number | bigint, payload: PlainValue) => Promise, } @@ -163,6 +166,68 @@ export class RuntimeErrorException extends Error { } } + +export type FunctionDefinitionConstructor = new () => T; + +export type RuntimeFunctionDefinitionRunnable = { + run: (...args: any[]) => Promise | PlainValue; +}; + +export type RuntimeFunctionDefinitionClass = + new () => T; + + +export const Identifier = (id: string): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:identifier', id, target); + +export const OmitFunctionDefinition = (): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:omit_function_definition', true, target) + +export const RuntimeParameter = (parameter: HerculesRuntimeFunctionDefinitionParameter): ClassDecorator => + (target) => { + const parameters = Reflect.getMetadata('hercules:runtime_parameters', target) || []; + parameters.push(parameter); + + Reflect.defineMetadata('hercules:runtime_parameters', parameters, target) + } + +export const FunctionParameter = (parameter: HerculesFunctionDefinitionParameter): ClassDecorator => + (target) => { + const parameters = Reflect.getMetadata('hercules:function_parameters', target) || []; + parameters.push(parameter); + + Reflect.defineMetadata('hercules:function_parameters', parameters, target) + } + +export const Name = (...translation: Translation[]): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:name', translation, target) + +export const DisplayMessage = (...translation: Translation[]): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:display_message', translation, target) +export const Description = (...translation: Translation[]): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:description', translation, target) +export const DeprecationMessage = (...translation: Translation[]): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:deprecation_message', translation, target) +export const Alias = (...translation: Translation[]): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:alias', translation, target) +export const Documentation = (...translation: Translation[]): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:documentation', translation, target) + +export const Signature = (signature: string): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:signature', signature, target) + +export const LinkedDataTypeIdentifiers = (...linkedDataTypeIdentifiers: string[]): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:linked_data_type_identifiers', linkedDataTypeIdentifiers, target) + +export const Version = (version: string): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:version', version, target) + +export const ThrowsError = (throwsError: boolean = true): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:throws_error', throwsError, target) + +export const DisplayIcon = (displayIcon: string): ClassDecorator => + (target) => Reflect.defineMetadata('hercules:display_icon', displayIcon, target) + export interface RegisteredFunction { identifier: string, definition: FunctionDefinition From 508fd709441d291c56859a2c4b751fbf6c3dc620 Mon Sep 17 00:00:00 2001 From: Dario Pranjic <96529060+Knerio@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:30:33 +0200 Subject: [PATCH 2/8] Fix signature Co-authored-by: Nico Sammito Signed-off-by: Dario Pranjic <96529060+Knerio@users.noreply.github.com> --- ts/examples/simple-example-ts/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/examples/simple-example-ts/index.ts b/ts/examples/simple-example-ts/index.ts index dfbaaa3..824d3c3 100644 --- a/ts/examples/simple-example-ts/index.ts +++ b/ts/examples/simple-example-ts/index.ts @@ -23,7 +23,7 @@ sdk.registerDataTypes({ }) @Identifier("fib") -@Signature("(number: number) => number") +@Signature("(number: number): number") @RuntimeParameter({ runtimeName: "number", defaultValue: 20 From 52d7868f2b6e9e3e841206cec3788cf89e0a39a9 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Thu, 9 Apr 2026 20:57:42 +0200 Subject: [PATCH 3/8] Change tooling to vite build --- .../simple-example-ts/package-lock.json | 6 +++--- ts/examples/simple-example-ts/package.json | 4 ++-- ts/examples/simple-example-ts/tsconfig.json | 5 ++++- ts/examples/simple-example-ts/vite.config.ts | 18 ++++++++++++++++++ ts/tsconfig.json | 3 ++- 5 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 ts/examples/simple-example-ts/vite.config.ts diff --git a/ts/examples/simple-example-ts/package-lock.json b/ts/examples/simple-example-ts/package-lock.json index ef97b74..52fa24c 100644 --- a/ts/examples/simple-example-ts/package-lock.json +++ b/ts/examples/simple-example-ts/package-lock.json @@ -188,9 +188,9 @@ "license": "BSD-3-Clause" }, "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", "license": "MIT", "dependencies": { "undici-types": "~7.18.0" diff --git a/ts/examples/simple-example-ts/package.json b/ts/examples/simple-example-ts/package.json index 80046ca..dd44023 100644 --- a/ts/examples/simple-example-ts/package.json +++ b/ts/examples/simple-example-ts/package.json @@ -7,8 +7,8 @@ "type": "module", "main": "index.js", "scripts": { - "build": "tsc -p tsconfig.json", - "dev": "cd ../.. && npm install && npm run build && npm pack && cd examples/simple-example-ts && npm install ../../code0-tech-hercules-0.0.0.tgz && npx tsx index.ts" + "build": "vite build .", + "start": "cd ../.. && npm install && npm run build && npm pack && cd examples/simple-example-ts && npm install && npm run build && node dist/index.js" }, "dependencies": { "@code0-tech/hercules": "file:../../code0-tech-hercules-0.0.0.tgz" diff --git a/ts/examples/simple-example-ts/tsconfig.json b/ts/examples/simple-example-ts/tsconfig.json index 7193a07..64bfbd7 100644 --- a/ts/examples/simple-example-ts/tsconfig.json +++ b/ts/examples/simple-example-ts/tsconfig.json @@ -11,7 +11,10 @@ "baseUrl": ".", "paths": { "@code0-tech/hercules": ["../../ts"] - } + }, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "types": ["vite/client"] }, "include": [ "index.ts" diff --git a/ts/examples/simple-example-ts/vite.config.ts b/ts/examples/simple-example-ts/vite.config.ts new file mode 100644 index 0000000..001ed7e --- /dev/null +++ b/ts/examples/simple-example-ts/vite.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import { resolve } from 'path'; + +export default defineConfig({ + build: { + target: "node18", + ssr: resolve(__dirname, 'index.ts'), // ✅ correct SSR entry + rollupOptions: { + external: (id) => + [ + 'fs', + 'path', + 'typescript', + '@code0-tech/hercules' + ].includes(id) || id.startsWith('node:') + } + } +}); \ No newline at end of file diff --git a/ts/tsconfig.json b/ts/tsconfig.json index afd60b6..a48423f 100644 --- a/ts/tsconfig.json +++ b/ts/tsconfig.json @@ -6,7 +6,8 @@ "target": "es2020", "lib": ["es2020", "dom"], "strict": true, - "skipLibCheck": true + "skipLibCheck": true, + "types": ["vite/client"] }, "include": ["src/**/*"] } \ No newline at end of file From 1e7396228f37b304820477463fc2278f83489c00 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Thu, 9 Apr 2026 21:38:50 +0200 Subject: [PATCH 4/8] Refactor Hercules interfaces to use HerculesTranslation for localization --- ts/src/types.ts | 79 +++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/ts/src/types.ts b/ts/src/types.ts index 9ed34a9..7e11f86 100644 --- a/ts/src/types.ts +++ b/ts/src/types.ts @@ -5,13 +5,16 @@ import { DefinitionDataType, DefinitionDataTypeRule, FlowType, FlowTypeSetting_UniquenessScope, FunctionDefinition, - RuntimeFunctionDefinition, - Translation + RuntimeFunctionDefinition } from "@code0-tech/tucana/shared"; import {PlainValue} from "@code0-tech/tucana/helpers"; import {ActionTransferServiceClient, TransferRequest, TransferResponse} from "@code0-tech/tucana/aquila"; import 'reflect-metadata'; +export interface HerculesTranslation { + code: "en-US" | "de-DE" | string, + content: string +} export interface HerculesFunctionContext { projectId: number | bigint, @@ -21,9 +24,9 @@ export interface HerculesFunctionContext { export interface HerculesDataType { identifier: string, - name?: Translation[], - displayMessage?: Translation[], - alias?: Translation[], + name?: HerculesTranslation[], + displayMessage?: HerculesTranslation[], + alias?: HerculesTranslation[], rules?: DefinitionDataTypeRule[], genericKeys?: string[], type: string, @@ -37,8 +40,8 @@ export interface HerculesFlowTypeSetting { unique?: FlowTypeSetting_UniquenessScope, linkedDataTypeIdentifiers?: string[], defaultValue?: PlainValue, - name?: Translation[], - description?: Translation[], + name?: HerculesTranslation[], + description?: HerculesTranslation[], } export interface HerculesFlowType { @@ -47,11 +50,11 @@ export interface HerculesFlowType { signature: string, linkedDataTypes?: string[], editable: boolean, - name?: Translation[], - description?: Translation[], - documentation?: Translation[], - displayMessage?: Translation[], - alias?: Translation[], + name?: HerculesTranslation[], + description?: HerculesTranslation[], + documentation?: HerculesTranslation[], + displayMessage?: HerculesTranslation[], + alias?: HerculesTranslation[], version?: string, displayIcon?: string, } @@ -59,9 +62,9 @@ export interface HerculesFlowType { export interface HerculesRuntimeFunctionDefinitionParameter { runtimeName: string, defaultValue?: PlainValue, - name?: Translation[], - description?: Translation[], - documentation?: Translation[], + name?: HerculesTranslation[], + description?: HerculesTranslation[], + documentation?: HerculesTranslation[], hidden?: boolean, optional?: boolean } @@ -71,12 +74,12 @@ export interface HerculesRuntimeFunctionDefinition { parameters?: HerculesRuntimeFunctionDefinitionParameter[], signature: string, throwsError?: boolean, - name?: Translation[], - description?: Translation[], - documentation?: Translation[], - deprecationMessage?: Translation[], - displayMessage?: Translation[], - alias?: Translation[], + name?: HerculesTranslation[], + description?: HerculesTranslation[], + documentation?: HerculesTranslation[], + deprecationMessage?: HerculesTranslation[], + displayMessage?: HerculesTranslation[], + alias?: HerculesTranslation[], linkedDataTypes?: string[], version?: string, displayIcon?: string, @@ -85,9 +88,9 @@ export interface HerculesRuntimeFunctionDefinition { export interface HerculesFunctionDefinitionParameter { runtimeName: string, defaultValue?: PlainValue, - name?: Translation[], - description?: Translation[], - documentation?: Translation[], + name?: HerculesTranslation[], + description?: HerculesTranslation[], + documentation?: HerculesTranslation[], hidden?: boolean, optional?: boolean, runtimeDefinitionName?: string @@ -99,12 +102,12 @@ export interface HerculesFunctionDefinition { parameters?: HerculesFunctionDefinitionParameter[], signature: string, throwsError?: boolean, - name?: Translation[], - description?: Translation[], - documentation?: Translation[], - deprecationMessage?: Translation[], - displayMessage?: Translation[], - alias?: Translation[], + name?: HerculesTranslation[], + description?: HerculesTranslation[], + documentation?: HerculesTranslation[], + deprecationMessage?: HerculesTranslation[], + displayMessage?: HerculesTranslation[], + alias?: HerculesTranslation[], linkedDataTypes?: string[], version?: string, displayIcon?: string, @@ -120,8 +123,8 @@ export interface HerculesActionProjectConfiguration { } export interface HerculesActionConfigurationDefinition { - name?: Translation[], - description?: Translation[], + name?: HerculesTranslation[], + description?: HerculesTranslation[], type: string, linkedDataTypes?: string[], defaultValue?: PlainValue, @@ -199,18 +202,18 @@ export const FunctionParameter = (parameter: HerculesFunctionDefinitionParameter Reflect.defineMetadata('hercules:function_parameters', parameters, target) } -export const Name = (...translation: Translation[]): ClassDecorator => +export const Name = (...translation: HerculesTranslation[]): ClassDecorator => (target) => Reflect.defineMetadata('hercules:name', translation, target) -export const DisplayMessage = (...translation: Translation[]): ClassDecorator => +export const DisplayMessage = (...translation: HerculesTranslation[]): ClassDecorator => (target) => Reflect.defineMetadata('hercules:display_message', translation, target) -export const Description = (...translation: Translation[]): ClassDecorator => +export const Description = (...translation: HerculesTranslation[]): ClassDecorator => (target) => Reflect.defineMetadata('hercules:description', translation, target) -export const DeprecationMessage = (...translation: Translation[]): ClassDecorator => +export const DeprecationMessage = (...translation: HerculesTranslation[]): ClassDecorator => (target) => Reflect.defineMetadata('hercules:deprecation_message', translation, target) -export const Alias = (...translation: Translation[]): ClassDecorator => +export const Alias = (...translation: HerculesTranslation[]): ClassDecorator => (target) => Reflect.defineMetadata('hercules:alias', translation, target) -export const Documentation = (...translation: Translation[]): ClassDecorator => +export const Documentation = (...translation: HerculesTranslation[]): ClassDecorator => (target) => Reflect.defineMetadata('hercules:documentation', translation, target) export const Signature = (signature: string): ClassDecorator => From 667265f01b11d2e399a4f5c272be06269a9d98b6 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Thu, 9 Apr 2026 22:23:22 +0200 Subject: [PATCH 5/8] Refactor some code to make the structure clearer --- ts/.gitignore | 3 +- ts/src/action_sdk.ts | 539 +----------------- .../registerRuntimeFunctionDefinitionClass.ts | 35 ++ ts/src/sdk/builder/builder.ts | 66 +++ .../registerFunctionDefinitionClass.ts | 78 +++ ts/src/sdk/connection/connection.ts | 92 +++ ts/src/sdk/connection/dataType.ts | 29 + ts/src/sdk/connection/flowTypes.ts | 34 ++ ts/src/sdk/connection/functionDefinition.ts | 37 ++ ts/src/sdk/connection/logon.ts | 32 ++ .../connection/runtimeFunctionDefinition.ts | 45 ++ ts/src/sdk/execution.ts | 195 +++++++ 12 files changed, 657 insertions(+), 528 deletions(-) create mode 100644 ts/src/builder/registerRuntimeFunctionDefinitionClass.ts create mode 100644 ts/src/sdk/builder/builder.ts create mode 100644 ts/src/sdk/builder/registerFunctionDefinitionClass.ts create mode 100644 ts/src/sdk/connection/connection.ts create mode 100644 ts/src/sdk/connection/dataType.ts create mode 100644 ts/src/sdk/connection/flowTypes.ts create mode 100644 ts/src/sdk/connection/functionDefinition.ts create mode 100644 ts/src/sdk/connection/logon.ts create mode 100644 ts/src/sdk/connection/runtimeFunctionDefinition.ts create mode 100644 ts/src/sdk/execution.ts diff --git a/ts/.gitignore b/ts/.gitignore index 3c10377..48269aa 100644 --- a/ts/.gitignore +++ b/ts/.gitignore @@ -1,3 +1,4 @@ node_modules dist -code0-tech-hercules-*.tgz \ No newline at end of file +code0-tech-hercules-*.tgz +.env \ No newline at end of file diff --git a/ts/src/action_sdk.ts b/ts/src/action_sdk.ts index fdf2559..6b2ec4f 100644 --- a/ts/src/action_sdk.ts +++ b/ts/src/action_sdk.ts @@ -1,30 +1,14 @@ import {GrpcTransport} from "@protobuf-ts/grpc-transport"; import {ChannelCredentials} from "@grpc/grpc-js"; -import {RpcOptions} from "@protobuf-ts/runtime-rpc"; -import { - ActionSdk, HerculesActionConfigurationDefinition, - HerculesActionProjectConfiguration, HerculesFunctionContext, SdkState, RuntimeErrorException, - HerculesFunctionDefinition, RuntimeFunctionDefinitionClass, - FunctionDefinitionConstructor, RegisteredRuntimeFunction, HerculesFunctionDefinitionParameter, - HerculesRuntimeFunctionDefinitionParameter, HerculesRuntimeFunctionDefinition, RegisteredFunction -} from "./types.js"; -import { - ActionTransferServiceClient, - DataTypeServiceClient, - DataTypeUpdateRequest, ExecutionRequest, - FlowTypeServiceClient, - FlowTypeUpdateRequest, - FunctionDefinitionServiceClient, - FunctionDefinitionUpdateRequest, RuntimeFunctionDefinitionServiceClient, RuntimeFunctionDefinitionUpdateRequest, - TransferRequest, TransferResponse -} from "@code0-tech/tucana/aquila"; -import { - ActionConfigurations, - FlowTypeSetting, RuntimeParameterDefinition, -} from "@code0-tech/tucana/shared"; +import type {ActionSdk, HerculesActionConfigurationDefinition, SdkState} from "./types.js"; +import {ActionTransferServiceClient, TransferRequest} from "@code0-tech/tucana/aquila"; +import {FlowTypeSetting,} from "@code0-tech/tucana/shared"; import {constructValue, toAllowedValue} from "@code0-tech/tucana/helpers"; -import {logger} from "./logger"; import 'reflect-metadata'; +import {connect as connectHelper} from "./sdk/connection/connection"; +import {registerFunctionDefinitionClass} from "./sdk/builder/registerFunctionDefinitionClass"; +import {registerRuntimeFunctionDefinitionClass} from "./builder/registerRuntimeFunctionDefinitionClass"; + const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActionConfigurationDefinition[]): ActionSdk => { const transport = new GrpcTransport( @@ -57,148 +41,9 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi fullyConnected: false } - const buildRuntimeFunctionDefinition = (klass: RuntimeFunctionDefinitionClass): RegisteredRuntimeFunction => { - const identifier: string = Reflect.getMetadata('hercules:identifier', klass) - const runtimeParameters: HerculesRuntimeFunctionDefinitionParameter[] = Reflect.getMetadata('hercules:runtime_parameters', klass) - const names: HerculesRuntimeFunctionDefinition["name"] = Reflect.getMetadata('hercules:name', klass) || [] - const displayMessage: HerculesRuntimeFunctionDefinition["displayMessage"] = Reflect.getMetadata('hercules:display_message', klass) || [] - const description: HerculesRuntimeFunctionDefinition["description"] = Reflect.getMetadata('hercules:description', klass) || [] - const deprecationMessage: HerculesRuntimeFunctionDefinition["deprecationMessage"] = Reflect.getMetadata('hercules:deprecation_message', klass) || [] - const alias: HerculesRuntimeFunctionDefinition["alias"] = Reflect.getMetadata('hercules:alias', klass) || [] - const documentation: HerculesRuntimeFunctionDefinition["documentation"] = Reflect.getMetadata('hercules:documentation', klass) || [] - const signature: HerculesRuntimeFunctionDefinition["signature"] = Reflect.getMetadata('hercules:signature', klass) - const linkedDataTypeIdentifiers: HerculesRuntimeFunctionDefinition["linkedDataTypes"] = Reflect.getMetadata('hercules:linked_data_type_identifiers', klass) || [] - const version: HerculesRuntimeFunctionDefinition["version"] = Reflect.getMetadata('hercules:version', klass) || config.version - const displayIcon: HerculesRuntimeFunctionDefinition["displayIcon"] = Reflect.getMetadata('hercules:display_icon', klass) || "" - const throwsError: HerculesRuntimeFunctionDefinition["throwsError"] = Reflect.getMetadata('hercules:throws_error', klass) || false - const runFunction = new klass().run - - if (!identifier) { - throw new Error(`Runtime function class ${klass.name} is missing an identifier. Please add @Identifier("your_identifier") decorator to the class.`) - } - if (!signature) { - throw new Error(`Runtime function class ${klass.name} is missing a signature. Please add @Signature("(param1: TYPE_1): RETURN_TYPE") decorator to the class.`) - } - - return { - identifier: identifier as string, - definition: { - alias: alias || [], - name: names || [], - description: description || [], - version: version || config.version, - runtimeName: identifier, - deprecationMessage: deprecationMessage || [], - displayIcon: displayIcon || "", - displayMessage: displayMessage || [], - documentation: documentation || [], - linkedDataTypeIdentifiers: linkedDataTypeIdentifiers || [], - runtimeParameterDefinitions: runtimeParameters.map(param => { - return { - ...param, - name: param.name || [], - description: param.description || [], - documentation: param.documentation || [], - hidden: param.hidden || false, - optional: param.optional || false, - defaultValue: param.defaultValue ? constructValue(param.defaultValue) : undefined, - } as RuntimeParameterDefinition - }), - signature: signature, - throwsError: throwsError || false, - definitionSource: "action" - }, - handler: runFunction - } as RegisteredRuntimeFunction - } - return { - registerFunctionDefinitionClass(klass: FunctionDefinitionConstructor): Promise { - const parentClass = Object.getPrototypeOf(klass) - const runtimeFunction = buildRuntimeFunctionDefinition(parentClass); - const runtimeDefinition = runtimeFunction.definition - - const functionParameters: HerculesFunctionDefinitionParameter[] = Reflect.getMetadata('hercules:function_parameters', klass) - const names: HerculesFunctionDefinition["name"] = Reflect.getMetadata('hercules:name', klass) - const displayMessage: HerculesFunctionDefinition["displayMessage"] = Reflect.getMetadata('hercules:display_message', klass) - const description: HerculesFunctionDefinition["description"] = Reflect.getMetadata('hercules:description', klass) - const deprecationMessage: HerculesFunctionDefinition["deprecationMessage"] = Reflect.getMetadata('hercules:deprecation_message', klass) - const alias: HerculesFunctionDefinition["alias"] = Reflect.getMetadata('hercules:alias', klass) - const documentation: HerculesFunctionDefinition["documentation"] = Reflect.getMetadata('hercules:documentation', klass) - const signature: HerculesFunctionDefinition["signature"] = Reflect.getMetadata('hercules:signature', klass) - const linkedDataTypeIdentifiers: HerculesFunctionDefinition["linkedDataTypes"] = Reflect.getMetadata('hercules:linked_data_type_identifiers', klass) - const version: HerculesFunctionDefinition["version"] = Reflect.getMetadata('hercules:version', klass) - const displayIcon: HerculesFunctionDefinition["displayIcon"] = Reflect.getMetadata('hercules:display_icon', klass) - const throwsError: HerculesFunctionDefinition["throwsError"] = Reflect.getMetadata('hercules:throws_error', klass) - - runtimeDefinition.runtimeParameterDefinitions.forEach(runtimeDefinition => { - if (functionParameters.find((param: HerculesFunctionDefinitionParameter) => param.runtimeName === runtimeDefinition.runtimeName)) { - return; - } - functionParameters.push({ - ...runtimeDefinition, - runtimeDefinitionName: runtimeDefinition.runtimeName - }) - }) - - state.functions.push({ - identifier: runtimeFunction.identifier, - definition: { - runtimeDefinitionName: runtimeDefinition.runtimeName, - runtimeName: runtimeDefinition.runtimeName || runtimeDefinition.runtimeName, - signature: signature || runtimeDefinition.signature, - throwsError: throwsError || runtimeDefinition.throwsError, - alias: alias || runtimeDefinition.alias, - version: version || runtimeDefinition.version, - description: description || runtimeDefinition.description, - name: names || runtimeDefinition.name, - documentation: documentation || runtimeDefinition.documentation, - deprecationMessage: deprecationMessage || runtimeDefinition.deprecationMessage, - displayMessage: displayMessage || runtimeDefinition.displayMessage, - displayIcon: displayIcon || runtimeDefinition.displayIcon, - definitionSource: "action", - linkedDataTypeIdentifiers: linkedDataTypeIdentifiers || runtimeDefinition.linkedDataTypeIdentifiers, - parameterDefinitions: functionParameters.map(value => { - return { - ...value, - runtimeDefinitionName: value.runtimeDefinitionName || value.runtimeName, - name: value.name || [], - description: value.description || [], - documentation: value.documentation || [], - hidden: value.hidden || false, - optional: value.optional || false, - defaultValue: value.defaultValue ? constructValue(value.defaultValue || null) : undefined, - } - }) - }, - } as RegisteredFunction) - - return Promise.resolve(); - }, registerRuntimeFunctionDefinitionClass(klass: RuntimeFunctionDefinitionClass): Promise { - const omitFunctionDefinition = Reflect.getMetadata('hercules:omit_function_definition', klass) || false - - const runtimeFunction = buildRuntimeFunctionDefinition(klass); - const definition = runtimeFunction.definition - - state.runtimeFunctions.push(runtimeFunction as RegisteredRuntimeFunction) - if (!omitFunctionDefinition) { - state.functions.push({ - identifier: definition.runtimeName, - definition: { - ...definition, - runtimeDefinitionName: definition.runtimeName, - parameterDefinitions: definition.runtimeParameterDefinitions.map(param => { - return { - ...param, - runtimeDefinitionName: param.runtimeName - } - }) - } - } as RegisteredFunction) - } - - return Promise.resolve(); - }, + registerFunctionDefinitionClass: registerFunctionDefinitionClass(config, state), + registerRuntimeFunctionDefinitionClass: registerRuntimeFunctionDefinitionClass(config, state), fullyConnected(): boolean { return state.fullyConnected; }, @@ -207,7 +52,7 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi state.stream?.responses.onError(handler) }, connect: options => { - return connect(state, config, options); + return connectHelper(state, config, options); }, getProjectActionConfigurations: () => { return state.projectConfigurations.map(value => { @@ -267,7 +112,7 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi name: flowType.name || [], alias: flowType.alias || [], description: flowType.description || [], - displayIcon: flowType.displayIcon || "", + displayIcon: flowType.displayIcon || "tabler:note", displayMessage: flowType.displayMessage || [], documentation: flowType.documentation || [], definitionSource: "action", @@ -314,367 +159,7 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi } } -async function connect(state: SdkState, config: ActionSdk["config"], options?: RpcOptions): Promise { - const builtOptions: RpcOptions = { - meta: { - "Authorization": config.authToken, - }, - ...options - } - state.stream = state.client.transfer(builtOptions); - - const dataTypeClient = new DataTypeServiceClient(state.transport) - await dataTypeClient.update(DataTypeUpdateRequest.create({ - dataTypes: [ - ...state.dataTypes - ] - }), builtOptions).then(value => { - if (!value.response.success) { - return Promise.reject(value.response); - } - }).catch(reason => { - logger.error({ - err: reason, - config, - }, "Error while updating data types") - return Promise.reject(reason); - }) - logger.debug("Sent data types request") - - await state.stream.requests.send( - TransferRequest.create({ - data: { - oneofKind: "logon", - logon: { - actionIdentifier: config.actionId, - version: config.version, - actionConfigurations: state.configurationDefinitions - } - } - } - ), - ).catch(reason => { - logger.error({ - err: reason, - config, - }, "Failed to send logon request") - return Promise.reject(reason); - }) - - logger.debug("Successfully sent logon request") - - const request = RuntimeFunctionDefinitionUpdateRequest.create( - { - runtimeFunctions: [ - ...state.runtimeFunctions.map(func => ({ - ...func.definition, - })) - ] - } - ); - try { - const runtimeFunctionDefinitionClient = new RuntimeFunctionDefinitionServiceClient(state.transport) - await runtimeFunctionDefinitionClient.update( - request, builtOptions - ).then(value => { - if (!value.response.success) { - logger.error({ - err: value.response, - request: value.request, - config, - }) - return Promise.reject(value.response); - } - }) - } catch (error) { - logger.debug({ - ...request.runtimeFunctions[0].runtimeParameterDefinitions[0].defaultValue - }) - logger.error({ - err: error, - request: request, - config, - }, "Error while updating runtime function definitions") - return Promise.reject(error); - } - logger.debug("Successfully updated runtime function definitions") - - const FunctionDefinitionClient = new FunctionDefinitionServiceClient(state.transport) - try { - const finishedCall = await FunctionDefinitionClient.update( - FunctionDefinitionUpdateRequest.create( - { - functions: [ - ...state.functions.map(func => ({ - ...func.definition, - })) - ] - } - ), builtOptions - ); - - if (!finishedCall.response.success) { - logger.error({ - err: finishedCall.response, - request: finishedCall.request, - config, - }, "Error while updating function definitions") - return Promise.reject(finishedCall.response); - } - } catch (error) { - logger.error({ - err: error, - config, - }, "Error while updating function definitions") - return Promise.reject(error); - } - logger.debug("Updated function definitions") - - const flowTypeClient = new FlowTypeServiceClient(state.transport) - await flowTypeClient.update(FlowTypeUpdateRequest.create({ - flowTypes: [ - ...state.flowTypes - ] - }), builtOptions).then(value => { - if (!value.response.success) { - logger.error({ - err: value.response, - request: value.request, - config, - }) - return Promise.reject(value.response); - } - }) - logger.info("Connected successfully to aquila") - - - return new Promise(async (resolve, reject) => { - try { - for await (let message of state?.stream?.responses || []) { - logger.debug({ - message: message, - config, - }, "Received message from stream") - switch (message?.data.oneofKind) { - case "actionConfigurations": { - logger.info("Received action configurations") - - const configs = message.data.actionConfigurations as ActionConfigurations; - state.projectConfigurations = configs.actionConfigurations - resolve(state.projectConfigurations.map(value => { - return { - projectId: value.projectId, - configValues: value.actionConfigurations.map(value => { - return { - identifier: value.identifier, - value: toAllowedValue(value.value || constructValue(null)), - } - }), - findConfig: identifier => { - const config = value.actionConfigurations.find(config => config.identifier === identifier); - return config ? toAllowedValue(config.value || constructValue(null)) : undefined; - } - } - })); - state.fullyConnected = true - break; - } - case "execution": { - logger.info({ - executionD: message.data.execution.executionIdentifier, - config, - }, "Handling execution request") - - logger.debug({ - message: message, - config, - }, "Handling execution request") - handleExecutionRequest(state, message) - break; - } - } - } - } catch (reason) { - logger.error({ - err: reason, - config - }, "Error occurred in stream") - reject(reason); - } - }) -} - -function handleExecutionRequest(state: SdkState, message: TransferResponse) { - if (!message.data || message.data.oneofKind !== "execution") { - return - } - const execution = message.data.execution as ExecutionRequest; - const func = state.runtimeFunctions.find(value => value.identifier == execution.functionIdentifier); - - if (!func) { - logger.warn({ - message - }, "Received execution request but no matching function found") - return; - } - - const params = Object.entries(execution.parameters!.fields!).map(([key, value]) => { - const param = func.definition.runtimeParameterDefinitions - .find(p => p.runtimeName === key); - - const parameterValue = param ? toAllowedValue(value) : undefined; - if (!parameterValue) return parameterValue - return parameterValue; - }); - - logger.debug({ - message, - BuiltParameter: params - }) - - let conf = state.projectConfigurations.find(config => { - //TODO - return true - }) - - if (!conf) { - logger.error({ - message, - execution - }, "No configuration found") - conf = { - projectId: 0n, - actionConfigurations: [] - } - } - logger.debug({ - message, - conf - }) - - const context: HerculesFunctionContext = { - projectId: execution.projectId, - executionId: execution.executionIdentifier, - matchedConfig: { - projectId: conf.projectId, - configValues: conf.actionConfigurations.map(value => { - return { - identifier: value.identifier, - value: toAllowedValue(value.value || constructValue(null)), - } - }), - findConfig: identifier => { - const config = conf.actionConfigurations.find(config => config.identifier === identifier); - return config ? toAllowedValue(config.value || constructValue(null)) : undefined; - } - } - } - - if (func.handler.length == params.length + 1) { - // handler has context parameter - params.unshift(context) - } else if (func.handler.length > params.length + 1) { - logger.error({ - params, - func, - }, "Handler has more parameters than provided arguments") - return; - } - - const result = new Promise((resolve, reject) => { - try { - resolve(func.handler(...params)) - } catch (e) { - reject(e) - } - }) - result.then((value: any) => { - const request = TransferRequest.create({ - data: { - oneofKind: "result", - result: { - executionIdentifier: execution.executionIdentifier, - result: { - oneofKind: "success", - success: constructValue(value) - }, - } - } - }); - logger.debug({ - request: request - }, "Responding with execution result") - - state.stream!.requests.send(request).catch(reason => { - logger.error({ - err: reason, - request: request, - message, - execution - }, "Responding with execution result lead to error") - }); - }).catch(reason => { - logger.warn({ - err: reason - }, "Executed function lead to error") - let request - if (reason instanceof RuntimeErrorException) { - request = TransferRequest.create({ - data: { - oneofKind: "result", - result: { - executionIdentifier: execution.executionIdentifier, - result: { - oneofKind: "error", - error: { - code: reason.code, - description: reason.description - } - }, - } - } - }); - } else { - request = TransferRequest.create({ - data: { - oneofKind: "result", - result: { - executionIdentifier: execution.executionIdentifier, - result: { - oneofKind: "error", - error: { - code: "UNKNOWN_ERROR", - description: reason.toString() - } - }, - } - } - }); - logger.warn({ - err: reason, - func, - execution - }, "Error occured while executing function, but not an RuntimeErrorException") - } - - logger.debug({ - request: request - }, "Responding with execution error") - - state.stream!.requests.send( - request - ).catch(reason => { - logger.error({ - err: reason, - request: request, - execution, - message - }, "Failed to send execution result error") - }); - }) -} - export { createSdk, - connect + connectHelper as connect } \ No newline at end of file diff --git a/ts/src/builder/registerRuntimeFunctionDefinitionClass.ts b/ts/src/builder/registerRuntimeFunctionDefinitionClass.ts new file mode 100644 index 0000000..50951a1 --- /dev/null +++ b/ts/src/builder/registerRuntimeFunctionDefinitionClass.ts @@ -0,0 +1,35 @@ +import type {RegisteredFunction, RegisteredRuntimeFunction, RuntimeFunctionDefinitionClass, SdkState} from "../types"; +import {buildRuntimeFunctionDefinition} from "../sdk/builder/builder"; + +export function registerRuntimeFunctionDefinitionClass(config: { + authToken: string; + aquilaUrl: string; + actionId: string; + version: string +}, state: SdkState) { + return (klass: RuntimeFunctionDefinitionClass): Promise => { + const omitFunctionDefinition = Reflect.getMetadata('hercules:omit_function_definition', klass) || false + + const runtimeFunction = buildRuntimeFunctionDefinition(klass, config); + const definition = runtimeFunction.definition + + state.runtimeFunctions.push(runtimeFunction as RegisteredRuntimeFunction) + if (!omitFunctionDefinition) { + state.functions.push({ + identifier: definition.runtimeName, + definition: { + ...definition, + runtimeDefinitionName: definition.runtimeName, + parameterDefinitions: definition.runtimeParameterDefinitions.map(param => { + return { + ...param, + runtimeDefinitionName: param.runtimeName + } + }) + } + } as RegisteredFunction) + } + + return Promise.resolve(); + }; +} \ No newline at end of file diff --git a/ts/src/sdk/builder/builder.ts b/ts/src/sdk/builder/builder.ts new file mode 100644 index 0000000..5b68e79 --- /dev/null +++ b/ts/src/sdk/builder/builder.ts @@ -0,0 +1,66 @@ +import 'reflect-metadata'; +import {constructValue} from "@code0-tech/tucana/helpers"; +import { + RuntimeFunctionDefinitionClass, + RegisteredRuntimeFunction, + HerculesRuntimeFunctionDefinitionParameter, + HerculesRuntimeFunctionDefinition, + ActionSdk, +} from "../../types"; +import {RuntimeParameterDefinition} from "@code0-tech/tucana/shared"; + +export function buildRuntimeFunctionDefinition(klass: RuntimeFunctionDefinitionClass, config: ActionSdk["config"]): RegisteredRuntimeFunction { + const identifier: string = Reflect.getMetadata('hercules:identifier', klass) + const runtimeParameters: HerculesRuntimeFunctionDefinitionParameter[] = Reflect.getMetadata('hercules:runtime_parameters', klass) + const names: HerculesRuntimeFunctionDefinition["name"] = Reflect.getMetadata('hercules:name', klass) || [] + const displayMessage: HerculesRuntimeFunctionDefinition["displayMessage"] = Reflect.getMetadata('hercules:display_message', klass) || [] + const description: HerculesRuntimeFunctionDefinition["description"] = Reflect.getMetadata('hercules:description', klass) || [] + const deprecationMessage: HerculesRuntimeFunctionDefinition["deprecationMessage"] = Reflect.getMetadata('hercules:deprecation_message', klass) || [] + const alias: HerculesRuntimeFunctionDefinition["alias"] = Reflect.getMetadata('hercules:alias', klass) || [] + const documentation: HerculesRuntimeFunctionDefinition["documentation"] = Reflect.getMetadata('hercules:documentation', klass) || [] + const signature: HerculesRuntimeFunctionDefinition["signature"] = Reflect.getMetadata('hercules:signature', klass) + const linkedDataTypeIdentifiers: HerculesRuntimeFunctionDefinition["linkedDataTypes"] = Reflect.getMetadata('hercules:linked_data_type_identifiers', klass) || [] + const version: HerculesRuntimeFunctionDefinition["version"] = Reflect.getMetadata('hercules:version', klass) || config.version + const displayIcon: HerculesRuntimeFunctionDefinition["displayIcon"] = Reflect.getMetadata('hercules:display_icon', klass) || "" + const throwsError: HerculesRuntimeFunctionDefinition["throwsError"] = Reflect.getMetadata('hercules:throws_error', klass) || false + const runFunction = new klass().run + + if (!identifier) { + throw new Error(`Runtime function class ${klass.name} is missing an identifier. Please add @Identifier("your_identifier") decorator to the class.`) + } + if (!signature) { + throw new Error(`Runtime function class ${klass.name} is missing a signature. Please add @Signature("(param1: TYPE_1): RETURN_TYPE") decorator to the class.`) + } + + return { + identifier: identifier as string, + definition: { + alias: alias || [], + name: names || [], + description: description || [], + version: version || config.version, + runtimeName: identifier, + deprecationMessage: deprecationMessage || [], + displayIcon: displayIcon || "tabler:note", + displayMessage: displayMessage || [], + documentation: documentation || [], + linkedDataTypeIdentifiers: linkedDataTypeIdentifiers || [], + runtimeParameterDefinitions: runtimeParameters.map(param => { + return { + ...param, + name: param.name || [], + description: param.description || [], + documentation: param.documentation || [], + hidden: param.hidden || false, + optional: param.optional || false, + defaultValue: param.defaultValue ? constructValue(param.defaultValue) : undefined, + } as RuntimeParameterDefinition + }), + signature: signature, + throwsError: throwsError || false, + definitionSource: "action" + }, + handler: runFunction + } as RegisteredRuntimeFunction +} + diff --git a/ts/src/sdk/builder/registerFunctionDefinitionClass.ts b/ts/src/sdk/builder/registerFunctionDefinitionClass.ts new file mode 100644 index 0000000..1383c15 --- /dev/null +++ b/ts/src/sdk/builder/registerFunctionDefinitionClass.ts @@ -0,0 +1,78 @@ +import type { + FunctionDefinitionConstructor, + HerculesFunctionDefinition, + HerculesFunctionDefinitionParameter, RegisteredFunction, + SdkState +} from "../../types"; +import {buildRuntimeFunctionDefinition} from "./builder"; +import {constructValue} from "@code0-tech/tucana/helpers"; + +export function registerFunctionDefinitionClass(config: { + authToken: string; + aquilaUrl: string; + actionId: string; + version: string +}, state: SdkState) { + return (klass: FunctionDefinitionConstructor): Promise => { + const parentClass = Object.getPrototypeOf(klass) + const runtimeFunction = buildRuntimeFunctionDefinition(parentClass, config); + const runtimeDefinition = runtimeFunction.definition + + const functionParameters: HerculesFunctionDefinitionParameter[] = Reflect.getMetadata('hercules:function_parameters', klass) + const names: HerculesFunctionDefinition["name"] = Reflect.getMetadata('hercules:name', klass) + const displayMessage: HerculesFunctionDefinition["displayMessage"] = Reflect.getMetadata('hercules:display_message', klass) + const description: HerculesFunctionDefinition["description"] = Reflect.getMetadata('hercules:description', klass) + const deprecationMessage: HerculesFunctionDefinition["deprecationMessage"] = Reflect.getMetadata('hercules:deprecation_message', klass) + const alias: HerculesFunctionDefinition["alias"] = Reflect.getMetadata('hercules:alias', klass) + const documentation: HerculesFunctionDefinition["documentation"] = Reflect.getMetadata('hercules:documentation', klass) + const signature: HerculesFunctionDefinition["signature"] = Reflect.getMetadata('hercules:signature', klass) + const linkedDataTypeIdentifiers: HerculesFunctionDefinition["linkedDataTypes"] = Reflect.getMetadata('hercules:linked_data_type_identifiers', klass) + const version: HerculesFunctionDefinition["version"] = Reflect.getMetadata('hercules:version', klass) + const displayIcon: HerculesFunctionDefinition["displayIcon"] = Reflect.getMetadata('hercules:display_icon', klass) + const throwsError: HerculesFunctionDefinition["throwsError"] = Reflect.getMetadata('hercules:throws_error', klass) + + runtimeDefinition.runtimeParameterDefinitions.forEach(runtimeDefinition => { + if (functionParameters.find((param: HerculesFunctionDefinitionParameter) => param.runtimeName === runtimeDefinition.runtimeName)) { + return; + } + functionParameters.push({ + ...runtimeDefinition, + runtimeDefinitionName: runtimeDefinition.runtimeName + }) + }) + + state.functions.push({ + identifier: runtimeFunction.identifier, + definition: { + runtimeDefinitionName: runtimeDefinition.runtimeName, + runtimeName: runtimeDefinition.runtimeName || runtimeDefinition.runtimeName, + signature: signature || runtimeDefinition.signature, + throwsError: throwsError || runtimeDefinition.throwsError, + alias: alias || runtimeDefinition.alias, + version: version || runtimeDefinition.version, + description: description || runtimeDefinition.description, + name: names || runtimeDefinition.name, + documentation: documentation || runtimeDefinition.documentation, + deprecationMessage: deprecationMessage || runtimeDefinition.deprecationMessage, + displayMessage: displayMessage || runtimeDefinition.displayMessage, + displayIcon: displayIcon || runtimeDefinition.displayIcon, + definitionSource: "action", + linkedDataTypeIdentifiers: linkedDataTypeIdentifiers || runtimeDefinition.linkedDataTypeIdentifiers, + parameterDefinitions: functionParameters.map(value => { + return { + ...value, + runtimeDefinitionName: value.runtimeDefinitionName || value.runtimeName, + name: value.name || [], + description: value.description || [], + documentation: value.documentation || [], + hidden: value.hidden || false, + optional: value.optional || false, + defaultValue: value.defaultValue ? constructValue(value.defaultValue || null) : undefined, + } + }) + }, + } as RegisteredFunction) + + return Promise.resolve(); + }; +} diff --git a/ts/src/sdk/connection/connection.ts b/ts/src/sdk/connection/connection.ts new file mode 100644 index 0000000..05963bc --- /dev/null +++ b/ts/src/sdk/connection/connection.ts @@ -0,0 +1,92 @@ +import type {RpcOptions} from "@protobuf-ts/runtime-rpc"; +import {logger} from "../../logger"; +import {constructValue, toAllowedValue} from "@code0-tech/tucana/helpers"; +import type { + ActionSdk, + SdkState, + HerculesActionProjectConfiguration +} from "../../types"; +import {handleExecutionRequest} from "../execution"; +import {handleLogon} from "./logon"; +import {handleRuntimeFunctionDefinitions} from "./runtimeFunctionDefinition"; +import {handleDataTypes} from "./dataType"; +import {handleFunctionDefinitions} from "./functionDefinition"; +import {handleFlowTypes} from "./flowTypes"; + + +export async function connect(state: SdkState, config: ActionSdk["config"], options?: RpcOptions): Promise { + logger.debug("Trying to connect to aquila") + const builtOptions: RpcOptions = { + meta: { + "Authorization": config.authToken, + }, + ...options + } + state.stream = state.client.transfer(builtOptions); + + await handleDataTypes(state, builtOptions, config); + await handleLogon(state, config); + await handleRuntimeFunctionDefinitions(state, builtOptions, config) + await handleFunctionDefinitions(state, builtOptions, config) + await handleFlowTypes(state, builtOptions, config) + + logger.info("Connected successfully to aquila") + + + return new Promise(async (resolve, reject) => { + try { + for await (let message of state?.stream?.responses || []) { + logger.debug({ + message: message, + config, + }, "Received message from stream") + switch (message?.data.oneofKind) { + case "actionConfigurations": { + logger.info("Received action configurations") + + const configs = message.data.actionConfigurations as any; + state.projectConfigurations = configs.actionConfigurations + resolve(state.projectConfigurations.map(value => { + return { + projectId: value.projectId, + configValues: value.actionConfigurations.map(value => { + return { + identifier: value.identifier, + value: toAllowedValue(value.value || constructValue(null)), + } + }), + findConfig: identifier => { + const config = value.actionConfigurations.find(config => config.identifier === identifier); + return config ? toAllowedValue(config.value || constructValue(null)) : undefined; + } + } + })); + state.fullyConnected = true + break; + } + + case "execution": { + logger.info({ + executionD: message.data.execution.executionIdentifier, + config, + }, "Handling execution request") + + logger.debug({ + message: message, + config, + }, "Handling execution request") + handleExecutionRequest(state, message) + break; + } + } + } + } catch (reason) { + logger.error({ + err: reason, + config + }, "Error occurred in stream") + reject(reason); + } + }) +} + diff --git a/ts/src/sdk/connection/dataType.ts b/ts/src/sdk/connection/dataType.ts new file mode 100644 index 0000000..bcb241a --- /dev/null +++ b/ts/src/sdk/connection/dataType.ts @@ -0,0 +1,29 @@ +import {DataTypeServiceClient, DataTypeUpdateRequest} from "@code0-tech/tucana/aquila"; +import {logger} from "../../logger"; +import {SdkState} from "../../types"; +import {RpcOptions} from "@protobuf-ts/runtime-rpc"; + +export async function handleDataTypes(state: SdkState, builtOptions: RpcOptions, config: { + authToken: string; + aquilaUrl: string; + actionId: string; + version: string +}) { + const dataTypeClient = new DataTypeServiceClient(state.transport) + await dataTypeClient.update(DataTypeUpdateRequest.create({ + dataTypes: [ + ...state.dataTypes + ] + }), builtOptions).then(value => { + if (!value.response.success) { + return Promise.reject(value.response); + } + }).catch(reason => { + logger.error({ + err: reason, + config, + }, "Error while updating data types") + return Promise.reject(reason); + }) + logger.debug("Sent data types request") +} diff --git a/ts/src/sdk/connection/flowTypes.ts b/ts/src/sdk/connection/flowTypes.ts new file mode 100644 index 0000000..3a2cdba --- /dev/null +++ b/ts/src/sdk/connection/flowTypes.ts @@ -0,0 +1,34 @@ +import {ActionSdk, SdkState} from "../../types"; +import {FlowTypeServiceClient, FlowTypeUpdateRequest} from "@code0-tech/tucana/aquila"; +import {RpcOptions} from "@protobuf-ts/runtime-rpc"; +import {logger} from "../../logger"; + +export async function handleFlowTypes(state: SdkState, builtOptions: RpcOptions | undefined, config: ActionSdk["config"]) { + const flowTypeClient = new FlowTypeServiceClient(state.transport) + const request = { + flowTypes: [ + ...state.flowTypes + ] + }; + try { + + await flowTypeClient.update(FlowTypeUpdateRequest.create(request), builtOptions).then(value => { + if (!value.response.success) { + logger.error({ + err: value.response, + request: value.request, + config, + }) + return Promise.reject(value.response); + } + }) + + } catch (error) { + logger.error({ + err: error, + request, + config, + }, "Error while updating flow types") + return Promise.reject(error); + } +} diff --git a/ts/src/sdk/connection/functionDefinition.ts b/ts/src/sdk/connection/functionDefinition.ts new file mode 100644 index 0000000..01065fe --- /dev/null +++ b/ts/src/sdk/connection/functionDefinition.ts @@ -0,0 +1,37 @@ +import type {ActionSdk, SdkState} from "../../types"; +import type {RpcOptions} from "@protobuf-ts/runtime-rpc"; +import {FunctionDefinitionServiceClient, FunctionDefinitionUpdateRequest} from "@code0-tech/tucana/aquila"; +import {logger} from "../../logger"; + +export async function handleFunctionDefinitions(state: SdkState, builtOptions: RpcOptions | undefined, config: ActionSdk["config"]) { + const FunctionDefinitionClient = new FunctionDefinitionServiceClient(state.transport) + try { + const finishedCall = await FunctionDefinitionClient.update( + FunctionDefinitionUpdateRequest.create( + { + functions: [ + ...state.functions.map(func => ({ + ...func.definition, + })) + ] + } + ), builtOptions + ); + + if (!finishedCall.response.success) { + logger.error({ + err: finishedCall.response, + request: finishedCall.request, + config, + }, "Error while updating function definitions") + return Promise.reject(finishedCall.response); + } + } catch (error) { + logger.error({ + err: error, + config, + }, "Error while updating function definitions") + return Promise.reject(error); + } + logger.debug("Updated function definitions") +} diff --git a/ts/src/sdk/connection/logon.ts b/ts/src/sdk/connection/logon.ts new file mode 100644 index 0000000..a5c576a --- /dev/null +++ b/ts/src/sdk/connection/logon.ts @@ -0,0 +1,32 @@ +import type {SdkState} from "../../types"; +import {TransferRequest} from "@code0-tech/tucana/aquila"; +import {logger} from "../../logger"; + +export async function handleLogon(state: SdkState, config: { + authToken: string; + aquilaUrl: string; + actionId: string; + version: string +}) { + await state.stream!.requests.send( + TransferRequest.create({ + data: { + oneofKind: "logon", + logon: { + actionIdentifier: config.actionId, + version: config.version, + actionConfigurations: state.configurationDefinitions + } + } + } + ), + ).catch(reason => { + logger.error({ + err: reason, + config, + }, "Failed to send logon request") + return Promise.reject(reason); + }) + + logger.debug("Successfully sent logon request") +} diff --git a/ts/src/sdk/connection/runtimeFunctionDefinition.ts b/ts/src/sdk/connection/runtimeFunctionDefinition.ts new file mode 100644 index 0000000..5f5354b --- /dev/null +++ b/ts/src/sdk/connection/runtimeFunctionDefinition.ts @@ -0,0 +1,45 @@ +import type {ActionSdk, SdkState} from "../../types"; +import type {RpcOptions} from "@protobuf-ts/runtime-rpc"; +import { + RuntimeFunctionDefinitionServiceClient, + RuntimeFunctionDefinitionUpdateRequest +} from "@code0-tech/tucana/aquila"; +import {logger} from "../../logger"; + +export async function handleRuntimeFunctionDefinitions(state: SdkState, builtOptions: RpcOptions | undefined, config: ActionSdk["config"]) { + const request = RuntimeFunctionDefinitionUpdateRequest.create( + { + runtimeFunctions: [ + ...state.runtimeFunctions.map(func => ({ + ...func.definition, + })) + ] + } + ); + try { + const runtimeFunctionDefinitionClient = new RuntimeFunctionDefinitionServiceClient(state.transport) + await runtimeFunctionDefinitionClient.update( + request, builtOptions + ).then(value => { + if (!value.response.success) { + logger.error({ + err: value.response, + request: value.request, + config, + }) + return Promise.reject(value.response); + } + }) + } catch (error) { + logger.debug({ + ...request.runtimeFunctions[0].runtimeParameterDefinitions[0].defaultValue + }) + logger.error({ + err: error, + request: request, + config, + }, "Error while updating runtime function definitions") + return Promise.reject(error); + } + logger.debug("Successfully updated runtime function definitions") +} diff --git a/ts/src/sdk/execution.ts b/ts/src/sdk/execution.ts new file mode 100644 index 0000000..c371829 --- /dev/null +++ b/ts/src/sdk/execution.ts @@ -0,0 +1,195 @@ +import {logger} from "../logger"; +import {constructValue, PlainValue, toAllowedValue} from "@code0-tech/tucana/helpers"; +import { + SdkState, + HerculesFunctionContext, + RuntimeErrorException, RegisteredRuntimeFunction +} from "../types.js"; +import {TransferRequest, ExecutionRequest, TransferResponse} from "@code0-tech/tucana/aquila"; +import {ActionProjectConfiguration} from "@code0-tech/tucana/shared"; + +function buildParams(execution: ExecutionRequest, func: RegisteredRuntimeFunction, message: TransferResponse) { + const params = Object.entries(execution.parameters!.fields!).map(([key, value]) => { + const param = func.definition.runtimeParameterDefinitions + .find(p => p.runtimeName === key); + + const parameterValue = param ? toAllowedValue(value) : undefined; + if (!parameterValue) return parameterValue + return parameterValue; + }); + + logger.debug({ + message, + BuiltParameter: params + }) + return params +} + +function buildContext(message: TransferResponse, execution: ExecutionRequest, state: SdkState): { + func: RegisteredRuntimeFunction, + context: HerculesFunctionContext, + conf: ActionProjectConfiguration, + params: (PlainValue | undefined)[] +} { + const func = state.runtimeFunctions.find(value => value.identifier == execution.functionIdentifier); + + if (!func) { + logger.error({ + message, + state + }, "Received execution request but no matching function found") + throw new Error("Received execution request for function " + execution.functionIdentifier + " but no matching function found") + } + + const params = buildParams(execution, func, message); + + let conf = state.projectConfigurations.find(config => { + //TODO + return true + }) + + if (!conf) { + logger.error({ + message, + execution + }, "No configuration found") + conf = { + projectId: 0n, + actionConfigurations: [] + } + } + const context: HerculesFunctionContext = { + projectId: execution.projectId, + executionId: execution.executionIdentifier, + matchedConfig: { + projectId: conf.projectId, + configValues: conf.actionConfigurations.map(value => { + return { + identifier: value.identifier, + value: toAllowedValue(value.value || constructValue(null)), + } + }), + findConfig: identifier => { + const config = conf.actionConfigurations.find(config => config.identifier === identifier); + return config ? toAllowedValue(config.value || constructValue(null)) : undefined; + } + } + } + return { + func, + context, + conf, + params + }; +} + +export function handleExecutionRequest(state: SdkState, message: TransferResponse) { + if (!message.data || message.data.oneofKind !== "execution") { + return + } + const execution = message.data.execution as ExecutionRequest; + + + const {func, context, conf, params} = buildContext(message, execution, state); + logger.debug({ + message, + conf, + context + }) + + const funcHandler = (func as any).handler + + if (funcHandler.length == params.length + 1) { + // handler has context parameter + params.unshift(context) + } else if (funcHandler.length > params.length + 1) { + logger.error({ + params, + func, + }, "Handler has more parameters than provided arguments") + return; + } + + const result = new Promise((resolve, reject) => { + try { + resolve(funcHandler(...params)) + } catch (e) { + reject(e) + } + }) + result.then((value: any) => { + const request = TransferRequest.create({ + data: { + oneofKind: "result", + result: { + executionIdentifier: execution.executionIdentifier, + result: { + oneofKind: "success", + success: constructValue(value) + }, + } + } + }); + logger.debug({ + request: request + }, "Responding with execution result") + + state.stream!.requests.send(request).catch(reason => { + logger.error({ + err: reason, + request: request, + message, + execution + }, "Responding with execution result lead to error") + }); + + }).catch(reason => { + logger.warn({ + err: reason + }, "Executed function lead to error") + let errorData + if (reason instanceof RuntimeErrorException) { + errorData = { + code: reason.code, + description: reason.description + } + } else { + errorData = { + code: "UNKNOWN_ERROR", + description: reason.toString() + } + + logger.warn({ + err: reason, + func, + execution + }, "Error occured while executing function, but not an RuntimeErrorException") + } + + + logger.debug({errorData}, "Responding with execution error") + + const request = TransferRequest.create({ + data: { + oneofKind: "result", + result: { + executionIdentifier: execution.executionIdentifier, + result: { + oneofKind: "error", + error: errorData + }, + } + } + }); + + state.stream!.requests.send(request).catch(reason => { + logger.error({ + err: reason, + request: request, + execution, + message + }, "Failed to send execution result error") + }); + }) +} + From 8719635db1578dfb1c3ac29bb2092b4bc73fa6e3 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Sat, 11 Apr 2026 17:33:33 +0200 Subject: [PATCH 6/8] Apply some ideas of code review --- ts/examples/simple-example-ts/example.env | 4 + ts/examples/simple-example-ts/index.ts | 79 ------------------- .../simple-example-ts/package-lock.json | 2 +- .../simple-example-ts/src/exampleDataType.ts | 6 ++ .../simple-example-ts/src/exampleEventType.ts | 7 ++ .../src/fibonacciFunction.ts | 23 ++++++ ts/examples/simple-example-ts/src/index.ts | 44 +++++++++++ ts/examples/simple-example-ts/tsconfig.json | 2 +- ts/examples/simple-example-ts/vite.config.ts | 2 +- ts/src/action_sdk.ts | 14 ++-- ts/src/sdk/builder/builder.ts | 3 +- .../registerFunctionDefinitionClass.ts | 3 +- ts/src/sdk/connection/connection.ts | 2 +- ts/src/sdk/execution.ts | 14 +--- ts/src/types.ts | 18 ++--- 15 files changed, 105 insertions(+), 118 deletions(-) create mode 100644 ts/examples/simple-example-ts/example.env delete mode 100644 ts/examples/simple-example-ts/index.ts create mode 100644 ts/examples/simple-example-ts/src/exampleDataType.ts create mode 100644 ts/examples/simple-example-ts/src/exampleEventType.ts create mode 100644 ts/examples/simple-example-ts/src/fibonacciFunction.ts create mode 100644 ts/examples/simple-example-ts/src/index.ts diff --git a/ts/examples/simple-example-ts/example.env b/ts/examples/simple-example-ts/example.env new file mode 100644 index 0000000..8f27264 --- /dev/null +++ b/ts/examples/simple-example-ts/example.env @@ -0,0 +1,4 @@ +AUTH_TOKEN=your_auth_token_here +AQUILA_URL=https://127.0.0.1:8000 +ACTION_ID=your_action_id_here +VERSION=0.0.0 \ No newline at end of file diff --git a/ts/examples/simple-example-ts/index.ts b/ts/examples/simple-example-ts/index.ts deleted file mode 100644 index 824d3c3..0000000 --- a/ts/examples/simple-example-ts/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { - createSdk, - HerculesActionProjectConfiguration, - HerculesFunctionContext, Identifier, LinkedDataTypeIdentifiers, - RuntimeErrorException, RuntimeParameter, Signature -} from "@code0-tech/hercules"; - -const sdk = createSdk({ - authToken: "token", - aquilaUrl: "127.0.0.1:8081", - actionId: "example", - version: "0.0.0", -}, [ - { - type: "string[]", - identifier: "config_discord_bot_token", - } -]) - -sdk.registerDataTypes({ - identifier: "SOME_DATATYPE", - type: "any", -}) - -@Identifier("fib") -@Signature("(number: number): number") -@RuntimeParameter({ - runtimeName: "number", - defaultValue: 20 -}) -class FibonacciFunction { - run(context: HerculesFunctionContext, number: number): number { - console.log(context) - console.log("Project id:", context.projectId); - console.log("Execution id:", context.executionId); - console.log("Matched configs:", context.matchedConfig); // matched configs for the current execution - - function fibonacci(num: number): number { - if (num <= 1) return num; - return fibonacci(num - 1) + fibonacci(num - 2); - } - - return fibonacci(number) - } -} - -sdk.registerRuntimeFunctionDefinitionClass(FibonacciFunction) - -sdk.registerFlowTypes( - { - signature: "(): string", - editable: false, - identifier: "test_flow", - } -) - - -connectToSdk(); - -function connectToSdk() { - sdk.connect().then((configs: HerculesActionProjectConfiguration[]) => { - console.log("SDK connected successfully"); - - sdk.dispatchEvent("test_flow", configs[0].projectId, "Hello, World! Configs loaded: " + configs.length).then(() => { - console.log("Event dispatched successfully"); - }) - }).catch(() => { - // will be handled by logger internally - process.exit(1) - }) - - sdk.onError((error) => { - console.error("SDK Error occurred:", error.message); - console.log("Attempting to reconnect in 5s..."); - setTimeout(() => { - connectToSdk(); - }, 5000) - }) -} diff --git a/ts/examples/simple-example-ts/package-lock.json b/ts/examples/simple-example-ts/package-lock.json index 52fa24c..ca17b67 100644 --- a/ts/examples/simple-example-ts/package-lock.json +++ b/ts/examples/simple-example-ts/package-lock.json @@ -15,7 +15,7 @@ "node_modules/@code0-tech/hercules": { "version": "0.0.0", "resolved": "file:../../code0-tech-hercules-0.0.0.tgz", - "integrity": "sha512-KgSdzgsOzIyHCMebtf0B/HUgSJNgYe7yuj8tpuqdJIP1UzhVU/2Tptwv6z9RZK/iv+EG4yq0gntZWXnHwZAS0w==", + "integrity": "sha512-WNWRc5+auZ15MBHcilAA+HPpASKW0CaTTe1QAYCUlcC1a+t9a8/yBEWQr4Ol8FA72yrR3+1/5r4C0YPm5cDVcQ==", "license": "ISC", "dependencies": { "@code0-tech/tucana": "0.0.68", diff --git a/ts/examples/simple-example-ts/src/exampleDataType.ts b/ts/examples/simple-example-ts/src/exampleDataType.ts new file mode 100644 index 0000000..c9ad177 --- /dev/null +++ b/ts/examples/simple-example-ts/src/exampleDataType.ts @@ -0,0 +1,6 @@ +import {HerculesDataType} from "@code0-tech/hercules"; + +export const someDataType: HerculesDataType = { + identifier: "SOME_DATATYPE", + type: "any", +} \ No newline at end of file diff --git a/ts/examples/simple-example-ts/src/exampleEventType.ts b/ts/examples/simple-example-ts/src/exampleEventType.ts new file mode 100644 index 0000000..9069207 --- /dev/null +++ b/ts/examples/simple-example-ts/src/exampleEventType.ts @@ -0,0 +1,7 @@ +import {HerculesEventType} from "@code0-tech/hercules" + +export const someEventType: HerculesEventType = { + signature: "(): string", + editable: false, + identifier: "test_flow", +} diff --git a/ts/examples/simple-example-ts/src/fibonacciFunction.ts b/ts/examples/simple-example-ts/src/fibonacciFunction.ts new file mode 100644 index 0000000..34a7a69 --- /dev/null +++ b/ts/examples/simple-example-ts/src/fibonacciFunction.ts @@ -0,0 +1,23 @@ +import {HerculesFunctionContext, Identifier, RuntimeParameter, Signature} from "@code0-tech/hercules"; + +@Identifier("fib") +@Signature("(number: number): number") +@RuntimeParameter({ + runtimeName: "number", + defaultValue: 20 +}) +export class FibonacciFunction { + run(context: HerculesFunctionContext, number: number): number { + console.log(context) + console.log("Project id:", context.projectId); + console.log("Execution id:", context.executionId); + console.log("Matched configs:", context.matchedConfig); // matched configs for the current execution + + function fibonacci(num: number): number { + if (num <= 1) return num; + return fibonacci(num - 1) + fibonacci(num - 2); + } + + return fibonacci(number) + } +} \ No newline at end of file diff --git a/ts/examples/simple-example-ts/src/index.ts b/ts/examples/simple-example-ts/src/index.ts new file mode 100644 index 0000000..5d5ce03 --- /dev/null +++ b/ts/examples/simple-example-ts/src/index.ts @@ -0,0 +1,44 @@ +import {createSdk, HerculesActionProjectConfiguration} from "@code0-tech/hercules"; +import {FibonacciFunction} from "./fibonacciFunction"; +import {someEventType} from "./exampleEventType"; +import {someDataType} from "./exampleDataType"; + +const sdk = createSdk({ + authToken: process.env.AUTH_TOKEN || "token", + aquilaUrl: process.env.AQUILA_URL || "127.0.0.1:8081", + actionId: process.env.ACTION_ID || "example", + version: process.env.VERSION || "0.0.0", +}, [ + { + type: "string[]", + identifier: "EXAMPLE_CONFIG_IDENTIFIER", + } +]) + +sdk.registerDataTypes(someDataType) +sdk.registerRuntimeFunctionDefinitionClass(FibonacciFunction) +sdk.registerEventTypes(someEventType) + + +connectToSdk(); + +function connectToSdk() { + sdk.connect().then((configs: HerculesActionProjectConfiguration[]) => { + console.log("SDK connected successfully"); + + sdk.dispatchEvent("test_flow", configs[0].projectId, "Hello, World! Configs loaded: " + configs.length).then(() => { + console.log("Event dispatched successfully"); + }) + }).catch(() => { + // will be handled by logger internally + process.exit(1) + }) + + sdk.onError((error) => { + console.error("SDK Error occurred:", error.message); + console.log("Attempting to reconnect in 5s..."); + setTimeout(() => { + connectToSdk(); + }, 5000) + }) +} diff --git a/ts/examples/simple-example-ts/tsconfig.json b/ts/examples/simple-example-ts/tsconfig.json index 64bfbd7..d2af2bc 100644 --- a/ts/examples/simple-example-ts/tsconfig.json +++ b/ts/examples/simple-example-ts/tsconfig.json @@ -17,6 +17,6 @@ "types": ["vite/client"] }, "include": [ - "index.ts" + "src/index.ts" ] } \ No newline at end of file diff --git a/ts/examples/simple-example-ts/vite.config.ts b/ts/examples/simple-example-ts/vite.config.ts index 001ed7e..303b4a4 100644 --- a/ts/examples/simple-example-ts/vite.config.ts +++ b/ts/examples/simple-example-ts/vite.config.ts @@ -4,7 +4,7 @@ import { resolve } from 'path'; export default defineConfig({ build: { target: "node18", - ssr: resolve(__dirname, 'index.ts'), // ✅ correct SSR entry + ssr: resolve(__dirname, 'src/index.ts'), rollupOptions: { external: (id) => [ diff --git a/ts/src/action_sdk.ts b/ts/src/action_sdk.ts index 6b2ec4f..1bffdd2 100644 --- a/ts/src/action_sdk.ts +++ b/ts/src/action_sdk.ts @@ -5,7 +5,7 @@ import {ActionTransferServiceClient, TransferRequest} from "@code0-tech/tucana/a import {FlowTypeSetting,} from "@code0-tech/tucana/shared"; import {constructValue, toAllowedValue} from "@code0-tech/tucana/helpers"; import 'reflect-metadata'; -import {connect as connectHelper} from "./sdk/connection/connection"; +import {connect} from "./sdk/connection/connection"; import {registerFunctionDefinitionClass} from "./sdk/builder/registerFunctionDefinitionClass"; import {registerRuntimeFunctionDefinitionClass} from "./builder/registerRuntimeFunctionDefinitionClass"; @@ -52,7 +52,7 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi state.stream?.responses.onError(handler) }, connect: options => { - return connectHelper(state, config, options); + return connect(state, config, options); }, getProjectActionConfigurations: () => { return state.projectConfigurations.map(value => { @@ -97,14 +97,14 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi linkedDataTypeIdentifiers: dataType.linkedDataTypes || [], displayMessage: dataType.displayMessage || [], definitionSource: "action", - version: dataType.version || config.version, + version: config.version, }); }) return Promise.resolve() }, - registerFlowTypes: async (...flowTypes) => { + registerEventTypes: async (...flowTypes) => { flowTypes.forEach(flowType => { state.flowTypes.push({ signature: flowType.signature, @@ -116,7 +116,7 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi displayMessage: flowType.displayMessage || [], documentation: flowType.documentation || [], definitionSource: "action", - version: flowType.version || config.version, + version: config.version, linkedDataTypeIdentifiers: flowType.linkedDataTypes || [], settings: (flowType.settings || []).map(setting => ({ name: setting.name || [], @@ -146,7 +146,7 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi event: { projectId: projectIdBigInt, eventType: eventType, - payload: constructValue(payload) || constructValue(null), + payload: constructValue(payload || null), } } }) @@ -161,5 +161,5 @@ const createSdk = (config: ActionSdk["config"], configDefinitions?: HerculesActi export { createSdk, - connectHelper as connect + connect } \ No newline at end of file diff --git a/ts/src/sdk/builder/builder.ts b/ts/src/sdk/builder/builder.ts index 5b68e79..5f38180 100644 --- a/ts/src/sdk/builder/builder.ts +++ b/ts/src/sdk/builder/builder.ts @@ -20,7 +20,6 @@ export function buildRuntimeFunctionDefinition(klass: RuntimeFunctionDefinitionC const documentation: HerculesRuntimeFunctionDefinition["documentation"] = Reflect.getMetadata('hercules:documentation', klass) || [] const signature: HerculesRuntimeFunctionDefinition["signature"] = Reflect.getMetadata('hercules:signature', klass) const linkedDataTypeIdentifiers: HerculesRuntimeFunctionDefinition["linkedDataTypes"] = Reflect.getMetadata('hercules:linked_data_type_identifiers', klass) || [] - const version: HerculesRuntimeFunctionDefinition["version"] = Reflect.getMetadata('hercules:version', klass) || config.version const displayIcon: HerculesRuntimeFunctionDefinition["displayIcon"] = Reflect.getMetadata('hercules:display_icon', klass) || "" const throwsError: HerculesRuntimeFunctionDefinition["throwsError"] = Reflect.getMetadata('hercules:throws_error', klass) || false const runFunction = new klass().run @@ -38,7 +37,7 @@ export function buildRuntimeFunctionDefinition(klass: RuntimeFunctionDefinitionC alias: alias || [], name: names || [], description: description || [], - version: version || config.version, + version: config.version, runtimeName: identifier, deprecationMessage: deprecationMessage || [], displayIcon: displayIcon || "tabler:note", diff --git a/ts/src/sdk/builder/registerFunctionDefinitionClass.ts b/ts/src/sdk/builder/registerFunctionDefinitionClass.ts index 1383c15..765ec9f 100644 --- a/ts/src/sdk/builder/registerFunctionDefinitionClass.ts +++ b/ts/src/sdk/builder/registerFunctionDefinitionClass.ts @@ -27,7 +27,6 @@ export function registerFunctionDefinitionClass(config: { const documentation: HerculesFunctionDefinition["documentation"] = Reflect.getMetadata('hercules:documentation', klass) const signature: HerculesFunctionDefinition["signature"] = Reflect.getMetadata('hercules:signature', klass) const linkedDataTypeIdentifiers: HerculesFunctionDefinition["linkedDataTypes"] = Reflect.getMetadata('hercules:linked_data_type_identifiers', klass) - const version: HerculesFunctionDefinition["version"] = Reflect.getMetadata('hercules:version', klass) const displayIcon: HerculesFunctionDefinition["displayIcon"] = Reflect.getMetadata('hercules:display_icon', klass) const throwsError: HerculesFunctionDefinition["throwsError"] = Reflect.getMetadata('hercules:throws_error', klass) @@ -49,7 +48,7 @@ export function registerFunctionDefinitionClass(config: { signature: signature || runtimeDefinition.signature, throwsError: throwsError || runtimeDefinition.throwsError, alias: alias || runtimeDefinition.alias, - version: version || runtimeDefinition.version, + version: config.version, description: description || runtimeDefinition.description, name: names || runtimeDefinition.name, documentation: documentation || runtimeDefinition.documentation, diff --git a/ts/src/sdk/connection/connection.ts b/ts/src/sdk/connection/connection.ts index 05963bc..ec92230 100644 --- a/ts/src/sdk/connection/connection.ts +++ b/ts/src/sdk/connection/connection.ts @@ -35,7 +35,7 @@ export async function connect(state: SdkState, config: ActionSdk["config"], opti return new Promise(async (resolve, reject) => { try { - for await (let message of state?.stream?.responses || []) { + for await (const message of state?.stream?.responses || []) { logger.debug({ message: message, config, diff --git a/ts/src/sdk/execution.ts b/ts/src/sdk/execution.ts index c371829..183f0d2 100644 --- a/ts/src/sdk/execution.ts +++ b/ts/src/sdk/execution.ts @@ -44,8 +44,7 @@ function buildContext(message: TransferResponse, execution: ExecutionRequest, st const params = buildParams(execution, func, message); let conf = state.projectConfigurations.find(config => { - //TODO - return true + return config.projectId === execution.projectId }) if (!conf) { @@ -97,7 +96,7 @@ export function handleExecutionRequest(state: SdkState, message: TransferRespons context }) - const funcHandler = (func as any).handler + const funcHandler = func.handler if (funcHandler.length == params.length + 1) { // handler has context parameter @@ -110,14 +109,7 @@ export function handleExecutionRequest(state: SdkState, message: TransferRespons return; } - const result = new Promise((resolve, reject) => { - try { - resolve(funcHandler(...params)) - } catch (e) { - reject(e) - } - }) - result.then((value: any) => { + Promise.resolve(funcHandler(...params)) .then((value: any) => { const request = TransferRequest.create({ data: { oneofKind: "result", diff --git a/ts/src/types.ts b/ts/src/types.ts index 7e11f86..76bd448 100644 --- a/ts/src/types.ts +++ b/ts/src/types.ts @@ -30,12 +30,10 @@ export interface HerculesDataType { rules?: DefinitionDataTypeRule[], genericKeys?: string[], type: string, - linkedDataTypes?: string[], - // Will default to sdk version - version?: string + linkedDataTypes?: string[] } -export interface HerculesFlowTypeSetting { +export interface HerculesEventTypeSetting { identifier: string, unique?: FlowTypeSetting_UniquenessScope, linkedDataTypeIdentifiers?: string[], @@ -44,9 +42,9 @@ export interface HerculesFlowTypeSetting { description?: HerculesTranslation[], } -export interface HerculesFlowType { +export interface HerculesEventType { identifier: string, - settings?: HerculesFlowTypeSetting[], + settings?: HerculesEventTypeSetting[], signature: string, linkedDataTypes?: string[], editable: boolean, @@ -55,7 +53,6 @@ export interface HerculesFlowType { documentation?: HerculesTranslation[], displayMessage?: HerculesTranslation[], alias?: HerculesTranslation[], - version?: string, displayIcon?: string, } @@ -81,7 +78,6 @@ export interface HerculesRuntimeFunctionDefinition { displayMessage?: HerculesTranslation[], alias?: HerculesTranslation[], linkedDataTypes?: string[], - version?: string, displayIcon?: string, } @@ -109,7 +105,6 @@ export interface HerculesFunctionDefinition { displayMessage?: HerculesTranslation[], alias?: HerculesTranslation[], linkedDataTypes?: string[], - version?: string, displayIcon?: string, } @@ -151,7 +146,7 @@ export interface ActionSdk { registerConfigDefinitions: (...actionConfigurations: Array) => Promise, registerDataTypes: (...dataType: Array) => Promise, - registerFlowTypes: (...flowTypes: Array) => Promise, + registerEventTypes: (...flowTypes: Array) => Promise, registerRuntimeFunctionDefinitionClass: (klass: RuntimeFunctionDefinitionClass) => Promise, registerFunctionDefinitionClass: (klass: FunctionDefinitionConstructor) => Promise, dispatchEvent: (eventType: string, projectId: number | bigint, payload: PlainValue) => Promise, @@ -222,9 +217,6 @@ export const Signature = (signature: string): ClassDecorator => export const LinkedDataTypeIdentifiers = (...linkedDataTypeIdentifiers: string[]): ClassDecorator => (target) => Reflect.defineMetadata('hercules:linked_data_type_identifiers', linkedDataTypeIdentifiers, target) -export const Version = (version: string): ClassDecorator => - (target) => Reflect.defineMetadata('hercules:version', version, target) - export const ThrowsError = (throwsError: boolean = true): ClassDecorator => (target) => Reflect.defineMetadata('hercules:throws_error', throwsError, target) From c161e781a215751d7ed914905046aa419e943fb8 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Sun, 12 Apr 2026 13:05:17 +0200 Subject: [PATCH 7/8] Reorder function calls in connection.ts and update README for clarity --- README.md | 9 ++++----- ts/src/sdk/connection/connection.ts | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c39e6fa..1590bb5 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,16 @@ sequenceDiagram Hercules->>Aquila: Register datatypes
because maybe they are needed in the config definitions Aquila-->>Hercules: Validation result - - Hercules->>Stream: Open bi-directional stream - Hercules->>Stream: ActionLogon request - - Hercules->>Aquila: Register function definitions Aquila-->>Hercules: Validation result Hercules->>Aquila: Register flow types Aquila-->>Hercules: Validation result + Hercules->>Stream: Open bi-directional stream + Hercules->>Stream: ActionLogon request + Stream-->>Hercules: Receive action configurations ``` @@ -62,3 +60,4 @@ To use a simple test server use the following command: ./bin/test_server.rb ``` This will start a test server on `localhost:50051` that you can connect to with the action sdk. +Watch out this test server isnt really working its just an way to test the connection. diff --git a/ts/src/sdk/connection/connection.ts b/ts/src/sdk/connection/connection.ts index ec92230..0bdc740 100644 --- a/ts/src/sdk/connection/connection.ts +++ b/ts/src/sdk/connection/connection.ts @@ -25,10 +25,10 @@ export async function connect(state: SdkState, config: ActionSdk["config"], opti state.stream = state.client.transfer(builtOptions); await handleDataTypes(state, builtOptions, config); - await handleLogon(state, config); await handleRuntimeFunctionDefinitions(state, builtOptions, config) await handleFunctionDefinitions(state, builtOptions, config) await handleFlowTypes(state, builtOptions, config) + await handleLogon(state, config); logger.info("Connected successfully to aquila") From 1396dff1996c2415af0ca0a8b57f632574c2c0e0 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Sun, 12 Apr 2026 14:10:42 +0200 Subject: [PATCH 8/8] Change auth header to lowercase --- ts/src/sdk/connection/connection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/src/sdk/connection/connection.ts b/ts/src/sdk/connection/connection.ts index 0bdc740..c220e67 100644 --- a/ts/src/sdk/connection/connection.ts +++ b/ts/src/sdk/connection/connection.ts @@ -18,7 +18,7 @@ export async function connect(state: SdkState, config: ActionSdk["config"], opti logger.debug("Trying to connect to aquila") const builtOptions: RpcOptions = { meta: { - "Authorization": config.authToken, + "authorization": config.authToken, }, ...options }