From 25c0fbf830726aafbf76b0114e6c964f6cb26cf3 Mon Sep 17 00:00:00 2001 From: saadi Date: Thu, 10 Apr 2025 08:53:32 +0200 Subject: [PATCH 1/3] Add a reference project that showcases the new waitpoint primitive This reference project shows a possible approach to implement workflows using Trigger.dev and ReactFlow. It makes use of the Trigger.dev Realtime API and the new waitpoint primitive to implement a human-in-the-loop approach for approving the result of an AI workflow. --- pnpm-lock.yaml | 1482 ++++++++++++++++- references/waitpoint-tokens/.env.example | 11 + references/waitpoint-tokens/.gitignore | 44 + references/waitpoint-tokens/README.md | 64 + references/waitpoint-tokens/next.config.ts | 7 + references/waitpoint-tokens/package.json | 41 + .../waitpoint-tokens/postcss.config.mjs | 5 + .../waitpoint-tokens/src/app/actions.ts | 72 + .../waitpoint-tokens/src/app/favicon.ico | Bin 0 -> 25931 bytes .../waitpoint-tokens/src/app/globals.css | 29 + .../waitpoint-tokens/src/app/layout.tsx | 30 + references/waitpoint-tokens/src/app/logo.svg | 192 +++ references/waitpoint-tokens/src/app/page.tsx | 40 + .../src/components/ActionNode.tsx | 124 ++ .../waitpoint-tokens/src/components/Flow.tsx | 272 +++ .../src/components/InputNode.tsx | 55 + .../src/components/ReviewNode.tsx | 145 ++ references/waitpoint-tokens/src/lib/cn.ts | 6 + .../src/trigger/articleWorkflow.ts | 90 + .../src/trigger/convertTextToSpeech.ts | 72 + .../src/trigger/publishSummary.ts | 20 + .../src/trigger/reviewSummary.ts | 42 + .../src/trigger/scrapeSite.ts | 18 + .../src/trigger/sendSlackNotification.ts | 16 + .../src/trigger/summarizeArticle.ts | 23 + references/waitpoint-tokens/trigger.config.ts | 15 + references/waitpoint-tokens/tsconfig.json | 33 + 27 files changed, 2873 insertions(+), 75 deletions(-) create mode 100644 references/waitpoint-tokens/.env.example create mode 100644 references/waitpoint-tokens/.gitignore create mode 100644 references/waitpoint-tokens/README.md create mode 100644 references/waitpoint-tokens/next.config.ts create mode 100644 references/waitpoint-tokens/package.json create mode 100644 references/waitpoint-tokens/postcss.config.mjs create mode 100644 references/waitpoint-tokens/src/app/actions.ts create mode 100644 references/waitpoint-tokens/src/app/favicon.ico create mode 100644 references/waitpoint-tokens/src/app/globals.css create mode 100644 references/waitpoint-tokens/src/app/layout.tsx create mode 100644 references/waitpoint-tokens/src/app/logo.svg create mode 100644 references/waitpoint-tokens/src/app/page.tsx create mode 100644 references/waitpoint-tokens/src/components/ActionNode.tsx create mode 100644 references/waitpoint-tokens/src/components/Flow.tsx create mode 100644 references/waitpoint-tokens/src/components/InputNode.tsx create mode 100644 references/waitpoint-tokens/src/components/ReviewNode.tsx create mode 100644 references/waitpoint-tokens/src/lib/cn.ts create mode 100644 references/waitpoint-tokens/src/trigger/articleWorkflow.ts create mode 100644 references/waitpoint-tokens/src/trigger/convertTextToSpeech.ts create mode 100644 references/waitpoint-tokens/src/trigger/publishSummary.ts create mode 100644 references/waitpoint-tokens/src/trigger/reviewSummary.ts create mode 100644 references/waitpoint-tokens/src/trigger/scrapeSite.ts create mode 100644 references/waitpoint-tokens/src/trigger/sendSlackNotification.ts create mode 100644 references/waitpoint-tokens/src/trigger/summarizeArticle.ts create mode 100644 references/waitpoint-tokens/trigger.config.ts create mode 100644 references/waitpoint-tokens/tsconfig.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04abb649d59..de2c8666260 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2287,6 +2287,85 @@ importers: specifier: ^4.2.0 version: 4.2.0 + references/waitpoint-tokens: + dependencies: + '@ai-sdk/openai': + specifier: ^1.3.4 + version: 1.3.4(zod@3.24.2) + '@aws-sdk/client-s3': + specifier: ^3.777.0 + version: 3.777.0 + '@aws-sdk/s3-request-presigner': + specifier: ^3.777.0 + version: 3.777.0 + '@trigger.dev/react-hooks': + specifier: workspace:* + version: link:../../packages/react-hooks + '@trigger.dev/sdk': + specifier: workspace:* + version: link:../../packages/trigger-sdk + '@xyflow/react': + specifier: ^12.4.4 + version: 12.5.1(@types/react@19.0.12)(react-dom@19.0.0)(react@19.0.0) + ai: + specifier: ^4.2.8 + version: 4.2.8(react@19.0.0)(zod@3.24.2) + clsx: + specifier: ^2.1.1 + version: 2.1.1 + elevenlabs: + specifier: ^1.55.0 + version: 1.55.0 + html-to-text: + specifier: ^9.0.5 + version: 9.0.5 + lucide-react: + specifier: ^0.484.0 + version: 0.484.0(react@19.0.0) + next: + specifier: 15.2.4 + version: 15.2.4(@playwright/test@1.37.0)(react-dom@19.0.0)(react@19.0.0) + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + react-tippy: + specifier: ^1.4.0 + version: 1.4.0 + tailwind-merge: + specifier: ^3.2.0 + version: 3.2.0 + devDependencies: + '@tailwindcss/postcss': + specifier: ^4 + version: 4.0.17 + '@trigger.dev/build': + specifier: workspace:* + version: link:../../packages/build + '@types/html-to-text': + specifier: ^9.0.4 + version: 9.0.4 + '@types/node': + specifier: ^20 + version: 20.14.14 + '@types/react': + specifier: ^19 + version: 19.0.12 + '@types/react-dom': + specifier: ^19 + version: 19.0.4(@types/react@19.0.12) + tailwindcss: + specifier: ^4 + version: 4.0.17 + trigger.dev: + specifier: workspace:* + version: link:../../packages/cli-v3 + typescript: + specifier: ^5 + version: 5.5.4 + packages: /@aashutoshrathi/word-wrap@1.2.6: @@ -2330,6 +2409,17 @@ packages: zod: 3.23.8 dev: false + /@ai-sdk/openai@1.3.4(zod@3.24.2): + resolution: {integrity: sha512-BOw7dQpiTlpaqi1u/NU4Or2+jA6buzl6GOUuYyu/uFI7dxJs1zPkY8IjAp4DQhi+kQGH6GGbEPw0LkIbeK4BVA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + dependencies: + '@ai-sdk/provider': 1.1.0 + '@ai-sdk/provider-utils': 2.2.1(zod@3.24.2) + zod: 3.24.2 + dev: false + /@ai-sdk/provider-utils@1.0.17(zod@3.23.8): resolution: {integrity: sha512-2VyeTH5DQ6AxqvwdyytKIeiZyYTyJffpufWjE67zM2sXMIHgYl7fivo8m5wVl6Cbf1dFPSGKq//C9s+lz+NHrQ==} engines: {node: '>=18'} @@ -2389,6 +2479,18 @@ packages: secure-json-parse: 2.7.0 zod: 3.23.8 + /@ai-sdk/provider-utils@2.2.1(zod@3.24.2): + resolution: {integrity: sha512-BuExLp+NcpwsAVj1F4bgJuQkSqO/+roV9wM7RdIO+NVrcT8RBUTdXzf5arHt5T58VpK7bZyB2V9qigjaPHE+Dg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + dependencies: + '@ai-sdk/provider': 1.1.0 + nanoid: 3.3.8 + secure-json-parse: 2.7.0 + zod: 3.24.2 + dev: false + /@ai-sdk/provider-utils@2.2.3(zod@3.23.8): resolution: {integrity: sha512-o3fWTzkxzI5Af7U7y794MZkYNEsxbjLam2nxyoUZSScqkacb7vZ3EYHLh21+xCcSSzEC161C7pZAGHtC0hTUMw==} engines: {node: '>=18'} @@ -2523,6 +2625,24 @@ packages: zod: 3.23.8 dev: false + /@ai-sdk/react@1.2.3(react@19.0.0)(zod@3.24.2): + resolution: {integrity: sha512-EQ6nmmQBBAal1yg72GB/Q7QnmDXMfgYvCo9Gym2mESXUHTqwpXU0JFHtk5Kq3EEkk7CVMf1oBWlNFNvU5ckQBg==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + dependencies: + '@ai-sdk/provider-utils': 2.2.1(zod@3.24.2) + '@ai-sdk/ui-utils': 1.2.2(zod@3.24.2) + react: 19.0.0 + swr: 2.2.5(react@19.0.0) + throttleit: 2.1.0 + zod: 3.24.2 + dev: false + /@ai-sdk/solid@0.0.43(zod@3.23.8): resolution: {integrity: sha512-7PlPLaeMAu97oOY2gjywvKZMYHF+GDfUxYNcuJ4AZ3/MRBatzs/U2r4ClT1iH8uMOcMg02RX6UKzP5SgnUBjVw==} engines: {node: '>=18'} @@ -2647,6 +2767,18 @@ packages: zod: 3.23.8 zod-to-json-schema: 3.24.5(zod@3.23.8) + /@ai-sdk/ui-utils@1.2.2(zod@3.24.2): + resolution: {integrity: sha512-6rCx2jSEPuiF6fytfMNscSOinHQZp52aFCHyPVpPPkcWnOur1jPWhol+0TFCUruDl7dCfcSIfTexQUq2ioLwaA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + dependencies: + '@ai-sdk/provider': 1.1.0 + '@ai-sdk/provider-utils': 2.2.1(zod@3.24.2) + zod: 3.24.2 + zod-to-json-schema: 3.24.5(zod@3.24.2) + dev: false + /@ai-sdk/vue@0.0.45(vue@3.4.38)(zod@3.23.8): resolution: {integrity: sha512-bqeoWZqk88TQmfoPgnFUKkrvhOIcOcSH5LMPgzZ8XwDqz5tHHrMHzpPfHCj7XyYn4ROTFK/2kKdC/ta6Ko0fMw==} engines: {node: '>=18'} @@ -2781,16 +2913,44 @@ packages: resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==} dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.451.0 + '@aws-sdk/types': 3.775.0 tslib: 1.14.1 dev: false + /@aws-crypto/crc32@5.2.0: + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.775.0 + tslib: 2.8.1 + dev: false + + /@aws-crypto/crc32c@5.2.0: + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.775.0 + tslib: 2.8.1 + dev: false + /@aws-crypto/ie11-detection@3.0.0: resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==} dependencies: tslib: 1.14.1 dev: false + /@aws-crypto/sha1-browser@5.2.0: + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-locate-window': 3.310.0 + '@smithy/util-utf8': 2.0.2 + tslib: 2.8.1 + dev: false + /@aws-crypto/sha256-browser@3.0.0: resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==} dependencies: @@ -2810,10 +2970,10 @@ packages: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.714.0 + '@aws-sdk/types': 3.775.0 '@aws-sdk/util-locate-window': 3.310.0 '@smithy/util-utf8': 2.0.2 - tslib: 2.6.2 + tslib: 2.8.1 dev: false /@aws-crypto/sha256-js@3.0.0: @@ -2829,8 +2989,8 @@ packages: engines: {node: '>=16.0.0'} dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.714.0 - tslib: 2.6.2 + '@aws-sdk/types': 3.775.0 + tslib: 2.8.1 dev: false /@aws-crypto/supports-web-crypto@3.0.0: @@ -2848,7 +3008,7 @@ packages: /@aws-crypto/util@3.0.0: resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==} dependencies: - '@aws-sdk/types': 3.451.0 + '@aws-sdk/types': 3.775.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 dev: false @@ -2856,11 +3016,75 @@ packages: /@aws-crypto/util@5.2.0: resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} dependencies: - '@aws-sdk/types': 3.714.0 + '@aws-sdk/types': 3.775.0 '@smithy/util-utf8': 2.0.2 tslib: 2.8.1 dev: false + /@aws-sdk/client-s3@3.777.0: + resolution: {integrity: sha512-KVX2QD6lLczZxtzIRCpmztgNnGq+spiMIDYqkum/rCBjCX1YJoDHwMYXaMf2EtAH8tFkJmBiA/CiT/J36iN7Xg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/credential-provider-node': 3.777.0 + '@aws-sdk/middleware-bucket-endpoint': 3.775.0 + '@aws-sdk/middleware-expect-continue': 3.775.0 + '@aws-sdk/middleware-flexible-checksums': 3.775.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-location-constraint': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-sdk-s3': 3.775.0 + '@aws-sdk/middleware-ssec': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.775.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/signature-v4-multi-region': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.775.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.775.0 + '@aws-sdk/xml-builder': 3.775.0 + '@smithy/config-resolver': 4.1.0 + '@smithy/core': 3.2.0 + '@smithy/eventstream-serde-browser': 4.0.2 + '@smithy/eventstream-serde-config-resolver': 4.1.0 + '@smithy/eventstream-serde-node': 4.0.2 + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/hash-blob-browser': 4.0.2 + '@smithy/hash-node': 4.0.2 + '@smithy/hash-stream-node': 4.0.2 + '@smithy/invalid-dependency': 4.0.2 + '@smithy/md5-js': 4.0.2 + '@smithy/middleware-content-length': 4.0.2 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/middleware-retry': 4.1.0 + '@smithy/middleware-serde': 4.0.3 + '@smithy/middleware-stack': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.8 + '@smithy/util-defaults-mode-node': 4.0.8 + '@smithy/util-endpoints': 3.0.2 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-retry': 4.0.2 + '@smithy/util-stream': 4.2.0 + '@smithy/util-utf8': 4.0.0 + '@smithy/util-waiter': 4.0.3 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + /@aws-sdk/client-ses@3.716.0: resolution: {integrity: sha512-lYsg2x3Z6R5ngBX1EqFKR6jf77ewbGg+aZV6V4ucVCghaGGcGnGisRP4FAep3IgkrZuByEYeJaA6cTli98qaOQ==} engines: {node: '>=16.0.0'} @@ -3005,7 +3229,7 @@ packages: '@smithy/util-middleware': 3.0.11 '@smithy/util-retry': 3.0.11 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.2 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt dev: false @@ -3100,6 +3324,52 @@ packages: - aws-crt dev: false + /@aws-sdk/client-sso@3.777.0: + resolution: {integrity: sha512-0+z6CiAYIQa7s6FJ+dpBYPi9zr9yY5jBg/4/FGcwYbmqWPXwL9Thdtr0FearYRZgKl7bhL3m3dILCCfWqr3teQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.775.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.775.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.775.0 + '@smithy/config-resolver': 4.1.0 + '@smithy/core': 3.2.0 + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/hash-node': 4.0.2 + '@smithy/invalid-dependency': 4.0.2 + '@smithy/middleware-content-length': 4.0.2 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/middleware-retry': 4.1.0 + '@smithy/middleware-serde': 4.0.3 + '@smithy/middleware-stack': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.8 + '@smithy/util-defaults-mode-node': 4.0.8 + '@smithy/util-endpoints': 3.0.2 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-retry': 4.0.2 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + /@aws-sdk/client-sts@3.454.0: resolution: {integrity: sha512-0fDvr8WeB6IYO8BUCzcivWmahgGl/zDbaYfakzGnt4mrl5ztYaXE875WI6b7+oFcKMRvN+KLvwu5TtyFuNY+GQ==} engines: {node: '>=14.0.0'} @@ -3191,7 +3461,7 @@ packages: '@smithy/util-middleware': 3.0.11 '@smithy/util-retry': 3.0.11 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.2 + tslib: 2.8.1 transitivePeerDependencies: - aws-crt dev: false @@ -3218,7 +3488,24 @@ packages: '@smithy/types': 3.7.2 '@smithy/util-middleware': 3.0.11 fast-xml-parser: 4.4.1 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/core@3.775.0: + resolution: {integrity: sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/core': 3.2.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/signature-v4': 5.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-middleware': 4.0.2 + fast-xml-parser: 4.4.1 + tslib: 2.8.1 dev: false /@aws-sdk/credential-provider-env@3.451.0: @@ -3242,6 +3529,17 @@ packages: tslib: 2.8.1 dev: false + /@aws-sdk/credential-provider-env@3.775.0: + resolution: {integrity: sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + /@aws-sdk/credential-provider-http@3.716.0: resolution: {integrity: sha512-CZ04pl2z7igQPysQyH2xKZHM3fLwkemxQbKOlje3TmiS1NwXvcKvERhp9PE/H23kOL7beTM19NMRog/Fka/rlw==} engines: {node: '>=16.0.0'} @@ -3258,6 +3556,22 @@ packages: tslib: 2.8.1 dev: false + /@aws-sdk/credential-provider-http@3.775.0: + resolution: {integrity: sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/property-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-stream': 4.2.0 + tslib: 2.8.1 + dev: false + /@aws-sdk/credential-provider-ini@3.451.0: resolution: {integrity: sha512-TySt64Ci5/ZbqFw1F9Z0FIGvYx5JSC9e6gqDnizIYd8eMnn8wFRUscRrD7pIHKfrhvVKN5h0GdYovmMO/FMCBw==} engines: {node: '>=14.0.0'} @@ -3300,6 +3614,27 @@ packages: - aws-crt dev: false + /@aws-sdk/credential-provider-ini@3.777.0: + resolution: {integrity: sha512-1X9mCuM9JSQPmQ+D2TODt4THy6aJWCNiURkmKmTIPRdno7EIKgAqrr/LLN++K5mBf54DZVKpqcJutXU2jwo01A==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/credential-provider-env': 3.775.0 + '@aws-sdk/credential-provider-http': 3.775.0 + '@aws-sdk/credential-provider-process': 3.775.0 + '@aws-sdk/credential-provider-sso': 3.777.0 + '@aws-sdk/credential-provider-web-identity': 3.777.0 + '@aws-sdk/nested-clients': 3.777.0 + '@aws-sdk/types': 3.775.0 + '@smithy/credential-provider-imds': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + /@aws-sdk/credential-provider-node@3.451.0: resolution: {integrity: sha512-AEwM1WPyxUdKrKyUsKyFqqRFGU70e4qlDyrtBxJnSU9NRLZI8tfEZ67bN7fHSxBUBODgDXpMSlSvJiBLh5/3pw==} engines: {node: '>=14.0.0'} @@ -3334,13 +3669,33 @@ packages: '@smithy/property-provider': 3.1.11 '@smithy/shared-ini-file-loader': 3.1.12 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt dev: false + /@aws-sdk/credential-provider-node@3.777.0: + resolution: {integrity: sha512-ZD66ywx1Q0KyUSuBXZIQzBe3Q7MzX8lNwsrCU43H3Fww+Y+HB3Ncws9grhSdNhKQNeGmZ+MgKybuZYaaeLwJEQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/credential-provider-env': 3.775.0 + '@aws-sdk/credential-provider-http': 3.775.0 + '@aws-sdk/credential-provider-ini': 3.777.0 + '@aws-sdk/credential-provider-process': 3.775.0 + '@aws-sdk/credential-provider-sso': 3.777.0 + '@aws-sdk/credential-provider-web-identity': 3.777.0 + '@aws-sdk/types': 3.775.0 + '@smithy/credential-provider-imds': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + /@aws-sdk/credential-provider-process@3.451.0: resolution: {integrity: sha512-HQywSdKeD5PErcLLnZfSyCJO+6T+ZyzF+Lm/QgscSC+CbSUSIPi//s15qhBRVely/3KBV6AywxwNH+5eYgt4lQ==} engines: {node: '>=14.0.0'} @@ -3364,6 +3719,18 @@ packages: tslib: 2.8.1 dev: false + /@aws-sdk/credential-provider-process@3.775.0: + resolution: {integrity: sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + /@aws-sdk/credential-provider-sso@3.451.0: resolution: {integrity: sha512-Usm/N51+unOt8ID4HnQzxIjUJDrkAQ1vyTOC0gSEEJ7h64NSSPGD5yhN7il5WcErtRd3EEtT1a8/GTC5TdBctg==} engines: {node: '>=14.0.0'} @@ -3396,6 +3763,22 @@ packages: - aws-crt dev: false + /@aws-sdk/credential-provider-sso@3.777.0: + resolution: {integrity: sha512-9mPz7vk9uE4PBVprfINv4tlTkyq1OonNevx2DiXC1LY4mCUCNN3RdBwAY0BTLzj0uyc3k5KxFFNbn3/8ZDQP7w==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/client-sso': 3.777.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/token-providers': 3.777.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + /@aws-sdk/credential-provider-web-identity@3.451.0: resolution: {integrity: sha512-Xtg3Qw65EfDjWNG7o2xD6sEmumPfsy3WDGjk2phEzVg8s7hcZGxf5wYwe6UY7RJvlEKrU0rFA+AMn6Hfj5oOzg==} engines: {node: '>=14.0.0'} @@ -3420,6 +3803,62 @@ packages: tslib: 2.8.1 dev: false + /@aws-sdk/credential-provider-web-identity@3.777.0: + resolution: {integrity: sha512-uGCqr47fnthkqwq5luNl2dksgcpHHjSXz2jUra7TXtFOpqvnhOW8qXjoa1ivlkq8qhqlaZwCzPdbcN0lXpmLzQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/nested-clients': 3.777.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/middleware-bucket-endpoint@3.775.0: + resolution: {integrity: sha512-qogMIpVChDYr4xiUNC19/RDSw/sKoHkAhouS6Skxiy6s27HBhow1L3Z1qVYXuBmOZGSWPU0xiyZCvOyWrv9s+Q==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-config-provider': 4.0.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-expect-continue@3.775.0: + resolution: {integrity: sha512-Apd3owkIeUW5dnk3au9np2IdW2N0zc9NjTjHiH+Mx3zqwSrc+m+ANgJVgk9mnQjMzU/vb7VuxJ0eqdEbp5gYsg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-flexible-checksums@3.775.0: + resolution: {integrity: sha512-OmHLfRIb7IIXsf9/X/pMOlcSV3gzW/MmtPSZTkrz5jCTKzWXd7eRoyOJqewjsaC6KMAxIpNU77FoAd16jOZ21A==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/is-array-buffer': 4.0.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-stream': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + /@aws-sdk/middleware-host-header@3.451.0: resolution: {integrity: sha512-j8a5jAfhWmsK99i2k8oR8zzQgXrsJtgrLxc3js6U+525mcZytoiDndkWTmD5fjJ1byU1U2E5TaPq+QJeDip05Q==} engines: {node: '>=14.0.0'} @@ -3437,7 +3876,26 @@ packages: '@aws-sdk/types': 3.714.0 '@smithy/protocol-http': 4.1.8 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-host-header@3.775.0: + resolution: {integrity: sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-location-constraint@3.775.0: + resolution: {integrity: sha512-8TMXEHZXZTFTckQLyBT5aEI8fX11HZcwZseRifvBKKpj0RZDk4F0EEYGxeNSPpUQ7n+PRWyfAEnnZNRdAj/1NQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@aws-sdk/middleware-logger@3.451.0: @@ -3455,7 +3913,16 @@ packages: dependencies: '@aws-sdk/types': 3.714.0 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-logger@3.775.0: + resolution: {integrity: sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@aws-sdk/middleware-recursion-detection@3.451.0: @@ -3475,7 +3942,37 @@ packages: '@aws-sdk/types': 3.714.0 '@smithy/protocol-http': 4.1.8 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-recursion-detection@3.775.0: + resolution: {integrity: sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-sdk-s3@3.775.0: + resolution: {integrity: sha512-zsvcu7cWB28JJ60gVvjxPCI7ZU7jWGcpNACPiZGyVtjYXwcxyhXbYEVDSWKsSA6ERpz9XrpLYod8INQWfW3ECg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/core': 3.2.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/signature-v4': 5.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-stream': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 dev: false /@aws-sdk/middleware-sdk-sqs@3.451.0: @@ -3512,6 +4009,15 @@ packages: tslib: 2.6.2 dev: false + /@aws-sdk/middleware-ssec@3.775.0: + resolution: {integrity: sha512-Iw1RHD8vfAWWPzBBIKaojO4GAvQkHOYIpKdAfis/EUSUmSa79QsnXnRqsdcE0mCB0Ylj23yi+ah4/0wh9FsekA==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + /@aws-sdk/middleware-user-agent@3.451.0: resolution: {integrity: sha512-8NM/0JiKLNvT9wtAQVl1DFW0cEO7OvZyLSUBLNLTHqyvOZxKaZ8YFk7d8PL6l76LeUKRxq4NMxfZQlUIRe0eSA==} engines: {node: '>=14.0.0'} @@ -3533,7 +4039,66 @@ packages: '@smithy/core': 2.5.5 '@smithy/protocol-http': 4.1.8 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-user-agent@3.775.0: + resolution: {integrity: sha512-7Lffpr1ptOEDE1ZYH1T78pheEY1YmeXWBfFt/amZ6AGsKSLG+JPXvof3ltporTGR2bhH/eJPo7UHCglIuXfzYg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.775.0 + '@smithy/core': 3.2.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/nested-clients@3.777.0: + resolution: {integrity: sha512-bmmVRsCjuYlStYPt06hr+f8iEyWg7+AklKCA8ZLDEJujXhXIowgUIqXmqpTkXwkVvDQ9tzU7hxaONjyaQCGybA==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.775.0 + '@aws-sdk/middleware-host-header': 3.775.0 + '@aws-sdk/middleware-logger': 3.775.0 + '@aws-sdk/middleware-recursion-detection': 3.775.0 + '@aws-sdk/middleware-user-agent': 3.775.0 + '@aws-sdk/region-config-resolver': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-endpoints': 3.775.0 + '@aws-sdk/util-user-agent-browser': 3.775.0 + '@aws-sdk/util-user-agent-node': 3.775.0 + '@smithy/config-resolver': 4.1.0 + '@smithy/core': 3.2.0 + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/hash-node': 4.0.2 + '@smithy/invalid-dependency': 4.0.2 + '@smithy/middleware-content-length': 4.0.2 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/middleware-retry': 4.1.0 + '@smithy/middleware-serde': 4.0.3 + '@smithy/middleware-stack': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.8 + '@smithy/util-defaults-mode-node': 4.0.8 + '@smithy/util-endpoints': 3.0.2 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-retry': 4.0.2 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt dev: false /@aws-sdk/region-config-resolver@3.451.0: @@ -3556,7 +4121,45 @@ packages: '@smithy/types': 3.7.2 '@smithy/util-config-provider': 3.0.0 '@smithy/util-middleware': 3.0.11 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/region-config-resolver@3.775.0: + resolution: {integrity: sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/s3-request-presigner@3.777.0: + resolution: {integrity: sha512-pmGXG51DFQ8cBpFJbZOkoXKScm+rGvBgfExxkUR35VCo7b7hbhpUcN1t8XSzpgkdK/nxpbnQ6qGAmbIMUCwqqQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/signature-v4-multi-region': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@aws-sdk/util-format-url': 3.775.0 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/signature-v4-multi-region@3.775.0: + resolution: {integrity: sha512-cnGk8GDfTMJ8p7+qSk92QlIk2bmTmFJqhYxcXZ9PysjZtx0xmfCMxnG3Hjy1oU2mt5boPCVSOptqtWixayM17g==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/signature-v4': 5.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@aws-sdk/token-providers@3.451.0: @@ -3618,6 +4221,20 @@ packages: tslib: 2.8.1 dev: false + /@aws-sdk/token-providers@3.777.0: + resolution: {integrity: sha512-Yc2cDONsHOa4dTSGOev6Ng2QgTKQUEjaUnsyKd13pc/nLLz/WLqHiQ/o7PcnKERJxXGs1g1C6l3sNXiX+kbnFQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/nested-clients': 3.777.0 + '@aws-sdk/types': 3.775.0 + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + /@aws-sdk/types@3.451.0: resolution: {integrity: sha512-rhK+qeYwCIs+laJfWCcrYEjay2FR/9VABZJ2NRM89jV/fKqGVQR52E5DQqrI+oEIL5JHMhhnr4N4fyECMS35lw==} engines: {node: '>=14.0.0'} @@ -3631,7 +4248,22 @@ packages: engines: {node: '>=16.0.0'} dependencies: '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/types@3.775.0: + resolution: {integrity: sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-arn-parser@3.723.0: + resolution: {integrity: sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 dev: false /@aws-sdk/util-endpoints@3.451.0: @@ -3650,7 +4282,27 @@ packages: '@aws-sdk/types': 3.714.0 '@smithy/types': 3.7.2 '@smithy/util-endpoints': 2.1.7 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-endpoints@3.775.0: + resolution: {integrity: sha512-yjWmUgZC9tUxAo8Uaplqmq0eUh0zrbZJdwxGRKdYxfm4RG6fMw1tj52+KkatH7o+mNZvg1GDcVp/INktxonJLw==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + '@smithy/util-endpoints': 3.0.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-format-url@3.775.0: + resolution: {integrity: sha512-Nw4nBeyCbWixoGh8NcVpa/i8McMA6RXJIjQFyloJLaPr7CPquz7ZbSl0MUWMFVwP/VHaJ7B+lNN3Qz1iFCEP/Q==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/querystring-builder': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@aws-sdk/util-locate-window@3.310.0: @@ -3675,7 +4327,16 @@ packages: '@aws-sdk/types': 3.714.0 '@smithy/types': 3.7.2 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-user-agent-browser@3.775.0: + resolution: {integrity: sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A==} + dependencies: + '@aws-sdk/types': 3.775.0 + '@smithy/types': 4.2.0 + bowser: 2.11.0 + tslib: 2.8.1 dev: false /@aws-sdk/util-user-agent-node@3.451.0: @@ -3706,7 +4367,23 @@ packages: '@aws-sdk/types': 3.714.0 '@smithy/node-config-provider': 3.1.12 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-user-agent-node@3.775.0: + resolution: {integrity: sha512-N9yhTevbizTOMo3drH7Eoy6OkJ3iVPxhV7dwb6CMAObbLneS36CSfA6xQXupmHWcRvZPTz8rd1JGG3HzFOau+g==} + engines: {node: '>=18.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + dependencies: + '@aws-sdk/middleware-user-agent': 3.775.0 + '@aws-sdk/types': 3.775.0 + '@smithy/node-config-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@aws-sdk/util-utf8-browser@3.259.0: @@ -3715,6 +4392,14 @@ packages: tslib: 2.8.1 dev: false + /@aws-sdk/xml-builder@3.775.0: + resolution: {integrity: sha512-b9NGO6FKJeLGYnV7Z1yvcP1TNU4dkD5jNsLWOF1/sygZoASaQhNOlaiJ/1OH331YQ1R1oWk38nBb0frsYkDsOQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + /@babel/code-frame@7.22.13: resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} engines: {node: '>=6.9.0'} @@ -16526,6 +17211,29 @@ packages: tslib: 2.8.1 dev: false + /@smithy/abort-controller@4.0.2: + resolution: {integrity: sha512-Sl/78VDtgqKxN2+1qduaVE140XF+Xg+TafkncspwM4jFP/LHr76ZHmIY/y3V1M0mMLNk+Je6IGbzxy23RSToMw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@smithy/chunked-blob-reader-native@4.0.0: + resolution: {integrity: sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/util-base64': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/chunked-blob-reader@5.0.0: + resolution: {integrity: sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + /@smithy/config-resolver@2.0.19: resolution: {integrity: sha512-JsghnQ5zjWmjEVY8TFOulLdEOCj09SjRLugrHlkPZTIBBm7PQitCFVLThbsKPZQOP7N3ME1DU1nKUc1UaVnBog==} engines: {node: '>=14.0.0'} @@ -16545,7 +17253,18 @@ packages: '@smithy/types': 3.7.2 '@smithy/util-config-provider': 3.0.0 '@smithy/util-middleware': 3.0.11 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/config-resolver@4.1.0: + resolution: {integrity: sha512-8smPlwhga22pwl23fM5ew4T9vfLUCeFXlcqNOCD5M5h8VmNPNUE9j6bQSuRXpDSV11L/E/SwEBQuW8hr6+nS1A==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/node-config-provider': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.2 + tslib: 2.8.1 dev: false /@smithy/core@2.5.5: @@ -16559,7 +17278,21 @@ packages: '@smithy/util-middleware': 3.0.11 '@smithy/util-stream': 3.3.2 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/core@3.2.0: + resolution: {integrity: sha512-k17bgQhVZ7YmUvA8at4af1TDpl0NDMBuBKJl8Yg0nrefwmValU+CnA5l/AriVdQNthU/33H3nK71HrLgqOPr1Q==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/middleware-serde': 4.0.3 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-stream': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 dev: false /@smithy/credential-provider-imds@2.1.2: @@ -16584,6 +17317,17 @@ packages: tslib: 2.8.1 dev: false + /@smithy/credential-provider-imds@4.0.2: + resolution: {integrity: sha512-32lVig6jCaWBHnY+OEQ6e6Vnt5vDHaLiydGrwYMW9tPqO688hPGTYRamYJ1EptxEC2rAwJrHWmPoKRBl4iTa8w==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/node-config-provider': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + tslib: 2.8.1 + dev: false + /@smithy/eventstream-codec@2.0.14: resolution: {integrity: sha512-g/OU/MeWGfHDygoXgMWfG/Xb0QqDnAGcM9t2FRrVAhleXYRddGOEnfanR5cmHgB9ue52MJsyorqFjckzXsylaA==} dependencies: @@ -16593,6 +17337,51 @@ packages: tslib: 2.8.1 dev: false + /@smithy/eventstream-codec@4.0.2: + resolution: {integrity: sha512-p+f2kLSK7ZrXVfskU/f5dzksKTewZk8pJLPvER3aFHPt76C2MxD9vNatSfLzzQSQB4FNO96RK4PSXfhD1TTeMQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-hex-encoding': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/eventstream-serde-browser@4.0.2: + resolution: {integrity: sha512-CepZCDs2xgVUtH7ZZ7oDdZFH8e6Y2zOv8iiX6RhndH69nlojCALSKK+OXwZUgOtUZEUaZ5e1hULVCHYbCn7pug==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/eventstream-serde-universal': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@smithy/eventstream-serde-config-resolver@4.1.0: + resolution: {integrity: sha512-1PI+WPZ5TWXrfj3CIoKyUycYynYJgZjuQo8U+sphneOtjsgrttYybdqESFReQrdWJ+LKt6NEdbYzmmfDBmjX2A==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@smithy/eventstream-serde-node@4.0.2: + resolution: {integrity: sha512-C5bJ/C6x9ENPMx2cFOirspnF9ZsBVnBMtP6BdPl/qYSuUawdGQ34Lq0dMcf42QTjUZgWGbUIZnz6+zLxJlb9aw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/eventstream-serde-universal': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + + /@smithy/eventstream-serde-universal@4.0.2: + resolution: {integrity: sha512-St8h9JqzvnbB52FtckiHPN4U/cnXcarMniXRXTKn0r4b4XesZOGiAyUdj1aXbqqn1icSqBlzzUsCl6nPB018ng==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/eventstream-codec': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + /@smithy/fetch-http-handler@2.2.7: resolution: {integrity: sha512-iSDBjxuH9TgrtMYAr7j5evjvkvgwLY3y+9D547uep+JNkZ1ZT+BaeU20j6I/bO/i26ilCWFImrlXTPsfQtZdIQ==} dependencies: @@ -16610,7 +17399,28 @@ packages: '@smithy/querystring-builder': 3.0.11 '@smithy/types': 3.7.2 '@smithy/util-base64': 3.0.0 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/fetch-http-handler@5.0.2: + resolution: {integrity: sha512-+9Dz8sakS9pe7f2cBocpJXdeVjMopUDLgZs1yWeu7h++WqSbjUYv/JAJwKwXw1HV6gq1jyWjxuyn24E2GhoEcQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/protocol-http': 5.1.0 + '@smithy/querystring-builder': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/util-base64': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/hash-blob-browser@4.0.2: + resolution: {integrity: sha512-3g188Z3DyhtzfBRxpZjU8R9PpOQuYsbNnyStc/ZVS+9nVX1f6XeNOa9IrAh35HwwIZg+XWk8bFVtNINVscBP+g==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/chunked-blob-reader': 5.0.0 + '@smithy/chunked-blob-reader-native': 4.0.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/hash-node@2.0.16: @@ -16630,7 +17440,26 @@ packages: '@smithy/types': 3.7.2 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/hash-node@4.0.2: + resolution: {integrity: sha512-VnTpYPnRUE7yVhWozFdlxcYknv9UN7CeOqSrMH+V877v4oqtVYuoqhIhtSjmGPvYrYnAkaM61sLMKHvxL138yg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/hash-stream-node@4.0.2: + resolution: {integrity: sha512-POWDuTznzbIwlEXEvvXoPMS10y0WKXK790soe57tFRfvf4zBHyzE529HpZMqmDdwG9MfFflnyzndUQ8j78ZdSg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 dev: false /@smithy/invalid-dependency@2.0.14: @@ -16644,7 +17473,15 @@ packages: resolution: {integrity: sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==} dependencies: '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/invalid-dependency@4.0.2: + resolution: {integrity: sha512-GatB4+2DTpgWPday+mnUkoumP54u/MDM/5u44KF9hIu8jF0uafZtQLcdfIKkIcUNuF/fBojpLEHZS/56JqPeXQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/is-array-buffer@2.0.0: @@ -16661,6 +17498,13 @@ packages: tslib: 2.8.1 dev: false + /@smithy/is-array-buffer@4.0.0: + resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + /@smithy/md5-js@2.0.16: resolution: {integrity: sha512-YhWt9aKl+EMSNXyUTUo7I01WHf3HcCkPu/Hl2QmTNwrHT49eWaY7hptAMaERZuHFH0V5xHgPKgKZo2I93DFtgQ==} dependencies: @@ -16669,6 +17513,15 @@ packages: tslib: 2.6.2 dev: false + /@smithy/md5-js@4.0.2: + resolution: {integrity: sha512-Hc0R8EiuVunUewCse2syVgA2AfSRco3LyAv07B/zCOMa+jpXI9ll+Q21Nc6FAlYPcpNcAXqBzMhNs1CD/pP2bA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + /@smithy/middleware-content-length@2.0.16: resolution: {integrity: sha512-9ddDia3pp1d3XzLXKcm7QebGxLq9iwKf+J1LapvlSOhpF8EM9SjMeSrMOOFgG+2TfW5K3+qz4IAJYYm7INYCng==} engines: {node: '>=14.0.0'} @@ -16684,7 +17537,16 @@ packages: dependencies: '@smithy/protocol-http': 4.1.8 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/middleware-content-length@4.0.2: + resolution: {integrity: sha512-hAfEXm1zU+ELvucxqQ7I8SszwQ4znWMbNv6PLMndN83JJN41EPuS93AIyh2N+gJ6x8QFhzSO6b7q2e6oClDI8A==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/middleware-endpoint@2.2.1: @@ -16711,7 +17573,21 @@ packages: '@smithy/types': 3.7.2 '@smithy/url-parser': 3.0.11 '@smithy/util-middleware': 3.0.11 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/middleware-endpoint@4.1.0: + resolution: {integrity: sha512-xhLimgNCbCzsUppRTGXWkZywksuTThxaIB0HwbpsVLY5sceac4e1TZ/WKYqufQLaUy+gUSJGNdwD2jo3cXL0iA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/core': 3.2.0 + '@smithy/middleware-serde': 4.0.3 + '@smithy/node-config-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + '@smithy/url-parser': 4.0.2 + '@smithy/util-middleware': 4.0.2 + tslib: 2.8.1 dev: false /@smithy/middleware-retry@2.0.21: @@ -16739,7 +17615,22 @@ packages: '@smithy/types': 3.7.2 '@smithy/util-middleware': 3.0.11 '@smithy/util-retry': 3.0.11 - tslib: 2.6.2 + tslib: 2.8.1 + uuid: 9.0.1 + dev: false + + /@smithy/middleware-retry@4.1.0: + resolution: {integrity: sha512-2zAagd1s6hAaI/ap6SXi5T3dDwBOczOMCSkkYzktqN1+tzbk1GAsHNAdo/1uzxz3Ky02jvZQwbi/vmDA6z4Oyg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/node-config-provider': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/service-error-classification': 4.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-retry': 4.0.2 + tslib: 2.8.1 uuid: 9.0.1 dev: false @@ -16756,7 +17647,15 @@ packages: engines: {node: '>=16.0.0'} dependencies: '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/middleware-serde@4.0.3: + resolution: {integrity: sha512-rfgDVrgLEVMmMn0BI8O+8OVr6vXzjV7HZj57l0QxslhzbvVfikZbVfBVthjLHqib4BW44QhcIgJpvebHlRaC9A==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/middleware-stack@2.0.8: @@ -16772,7 +17671,15 @@ packages: engines: {node: '>=16.0.0'} dependencies: '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/middleware-stack@4.0.2: + resolution: {integrity: sha512-eSPVcuJJGVYrFYu2hEq8g8WWdJav3sdrI4o2c6z/rjnYDd3xH9j9E7deZQCzFn4QvGPouLngH3dQ+QVTxv5bOQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/node-config-provider@2.1.6: @@ -16792,7 +17699,17 @@ packages: '@smithy/property-provider': 3.1.11 '@smithy/shared-ini-file-loader': 3.1.12 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/node-config-provider@4.0.2: + resolution: {integrity: sha512-WgCkILRZfJwJ4Da92a6t3ozN/zcvYyJGUTmfGbgS/FkCcoCjl7G4FJaCDN1ySdvLvemnQeo25FdkyMSTSwulsw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/property-provider': 4.0.2 + '@smithy/shared-ini-file-loader': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/node-http-handler@2.1.10: @@ -16814,7 +17731,18 @@ packages: '@smithy/protocol-http': 4.1.8 '@smithy/querystring-builder': 3.0.11 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/node-http-handler@4.0.4: + resolution: {integrity: sha512-/mdqabuAT3o/ihBGjL94PUbTSPSRJ0eeVTdgADzow0wRJ0rN4A27EOrtlK56MYiO1fDvlO3jVTCxQtQmK9dZ1g==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/abort-controller': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/querystring-builder': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/property-provider@2.0.15: @@ -16833,6 +17761,14 @@ packages: tslib: 2.8.1 dev: false + /@smithy/property-provider@4.0.2: + resolution: {integrity: sha512-wNRoQC1uISOuNc2s4hkOYwYllmiyrvVXWMtq+TysNRVQaHm4yoafYQyjN/goYZS+QbYlPIbb/QRjaUZMuzwQ7A==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + /@smithy/protocol-http@3.0.10: resolution: {integrity: sha512-6+tjNk7rXW7YTeGo9qwxXj/2BFpJTe37kTj3EnZCoX/nH+NP/WLA7O83fz8XhkGqsaAhLUPo/bB12vvd47nsmg==} engines: {node: '>=14.0.0'} @@ -16846,7 +17782,15 @@ packages: engines: {node: '>=16.0.0'} dependencies: '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/protocol-http@5.1.0: + resolution: {integrity: sha512-KxAOL1nUNw2JTYrtviRRjEnykIDhxc84qMBzxvu1MUfQfHTuBlCG7PA6EdVwqpJjH7glw7FqQoFxUJSyBQgu7g==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/querystring-builder@2.0.14: @@ -16867,6 +17811,15 @@ packages: tslib: 2.8.1 dev: false + /@smithy/querystring-builder@4.0.2: + resolution: {integrity: sha512-NTOs0FwHw1vimmQM4ebh+wFQvOwkEf/kQL6bSM1Lock+Bv4I89B3hGYoUEPkmvYPkDKyp5UdXJYu+PoTQ3T31Q==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + '@smithy/util-uri-escape': 4.0.0 + tslib: 2.8.1 + dev: false + /@smithy/querystring-parser@2.0.14: resolution: {integrity: sha512-+cbtXWI9tNtQjlgQg3CA+pvL3zKTAxPnG3Pj6MP89CR3vi3QMmD0SOWoq84tqZDnJCxlsusbgIXk1ngMReXo+A==} engines: {node: '>=14.0.0'} @@ -16883,6 +17836,14 @@ packages: tslib: 2.8.1 dev: false + /@smithy/querystring-parser@4.0.2: + resolution: {integrity: sha512-v6w8wnmZcVXjfVLjxw8qF7OwESD9wnpjp0Dqry/Pod0/5vcEA3qxCr+BhbOHlxS8O+29eLpT3aagxXGwIoEk7Q==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + /@smithy/service-error-classification@2.0.7: resolution: {integrity: sha512-LLxgW12qGz8doYto15kZ4x1rHjtXl0BnCG6T6Wb8z2DI4PT9cJfOSvzbuLzy7+5I24PAepKgFeWHRd9GYy3Z9w==} engines: {node: '>=14.0.0'} @@ -16897,6 +17858,13 @@ packages: '@smithy/types': 3.7.2 dev: false + /@smithy/service-error-classification@4.0.2: + resolution: {integrity: sha512-LA86xeFpTKn270Hbkixqs5n73S+LVM0/VZco8dqd+JT75Dyx3Lcw/MraL7ybjmz786+160K8rPOmhsq0SocoJQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + dev: false + /@smithy/shared-ini-file-loader@2.2.5: resolution: {integrity: sha512-LHA68Iu7SmNwfAVe8egmjDCy648/7iJR/fK1UnVw+iAOUJoEYhX2DLgVd5pWllqdDiRbQQzgaHLcRokM+UFR1w==} engines: {node: '>=14.0.0'} @@ -16913,6 +17881,14 @@ packages: tslib: 2.8.1 dev: false + /@smithy/shared-ini-file-loader@4.0.2: + resolution: {integrity: sha512-J9/gTWBGVuFZ01oVA6vdb4DAjf1XbDhK6sLsu3OS9qmLrS6KB5ygpeHiM3miIbj1qgSJ96GYszXFWv6ErJ8QEw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 + dev: false + /@smithy/signature-v4@2.0.16: resolution: {integrity: sha512-ilLY85xS2kZZzTb83diQKYLIYALvart0KnBaKnIRnMBHAGEio5aHSlANQoxVn0VsonwmQ3CnWhnCT0sERD8uTg==} engines: {node: '>=14.0.0'} @@ -16941,6 +17917,20 @@ packages: tslib: 2.8.1 dev: false + /@smithy/signature-v4@5.0.2: + resolution: {integrity: sha512-Mz+mc7okA73Lyz8zQKJNyr7lIcHLiPYp0+oiqiMNc/t7/Kf2BENs5d63pEj7oPqdjaum6g0Fc8wC78dY1TgtXw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/is-array-buffer': 4.0.0 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-middleware': 4.0.2 + '@smithy/util-uri-escape': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + /@smithy/smithy-client@2.1.16: resolution: {integrity: sha512-Lw67+yQSpLl4YkDLUzI2KgS8TXclXmbzSeOJUmRFS4ueT56B4pw3RZRF/SRzvgyxM/HxgkUan8oSHXCujPDafQ==} engines: {node: '>=14.0.0'} @@ -16961,7 +17951,20 @@ packages: '@smithy/protocol-http': 4.1.8 '@smithy/types': 3.7.2 '@smithy/util-stream': 3.3.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/smithy-client@4.2.0: + resolution: {integrity: sha512-Qs65/w30pWV7LSFAez9DKy0Koaoh3iHhpcpCCJ4waj/iqwsuSzJna2+vYwq46yBaqO5ZbP9TjUsATUNxrKeBdw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/core': 3.2.0 + '@smithy/middleware-endpoint': 4.1.0 + '@smithy/middleware-stack': 4.0.2 + '@smithy/protocol-http': 5.1.0 + '@smithy/types': 4.2.0 + '@smithy/util-stream': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/types@2.6.0: @@ -16975,7 +17978,14 @@ packages: resolution: {integrity: sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==} engines: {node: '>=16.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/types@4.2.0: + resolution: {integrity: sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 dev: false /@smithy/url-parser@2.0.14: @@ -16991,7 +18001,16 @@ packages: dependencies: '@smithy/querystring-parser': 3.0.11 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/url-parser@4.0.2: + resolution: {integrity: sha512-Bm8n3j2ScqnT+kJaClSVCMeiSenK6jVAzZCNewsYWuZtnBehEz4r2qP0riZySZVfzB+03XZHJeqfmJDkeeSLiQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/querystring-parser': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/util-base64@2.0.1: @@ -17008,7 +18027,16 @@ packages: dependencies: '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-base64@4.0.0: + resolution: {integrity: sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 dev: false /@smithy/util-body-length-browser@2.0.0: @@ -17020,7 +18048,14 @@ packages: /@smithy/util-body-length-browser@3.0.0: resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} dependencies: - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-body-length-browser@4.0.0: + resolution: {integrity: sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 dev: false /@smithy/util-body-length-node@2.1.0: @@ -17034,7 +18069,14 @@ packages: resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} engines: {node: '>=16.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-body-length-node@4.0.0: + resolution: {integrity: sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 dev: false /@smithy/util-buffer-from@2.0.0: @@ -17053,6 +18095,14 @@ packages: tslib: 2.8.1 dev: false + /@smithy/util-buffer-from@4.0.0: + resolution: {integrity: sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/is-array-buffer': 4.0.0 + tslib: 2.8.1 + dev: false + /@smithy/util-config-provider@2.0.0: resolution: {integrity: sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==} engines: {node: '>=14.0.0'} @@ -17067,6 +18117,13 @@ packages: tslib: 2.8.1 dev: false + /@smithy/util-config-provider@4.0.0: + resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + /@smithy/util-defaults-mode-browser@2.0.20: resolution: {integrity: sha512-QJtnbTIl0/BbEASkx1MUFf6EaoWqWW1/IM90N++8NNscePvPf77GheYfpoPis6CBQawUWq8QepTP2QUSAdrVkw==} engines: {node: '>= 10.0.0'} @@ -17086,7 +18143,18 @@ packages: '@smithy/smithy-client': 3.5.1 '@smithy/types': 3.7.2 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-defaults-mode-browser@4.0.8: + resolution: {integrity: sha512-ZTypzBra+lI/LfTYZeop9UjoJhhGRTg3pxrNpfSTQLd3AJ37r2z4AXTKpq1rFXiiUIJsYyFgNJdjWRGP/cbBaQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/property-provider': 4.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + bowser: 2.11.0 + tslib: 2.8.1 dev: false /@smithy/util-defaults-mode-node@2.0.26: @@ -17112,7 +18180,20 @@ packages: '@smithy/property-provider': 3.1.11 '@smithy/smithy-client': 3.5.1 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-defaults-mode-node@4.0.8: + resolution: {integrity: sha512-Rgk0Jc/UDfRTzVthye/k2dDsz5Xxs9LZaKCNPgJTRyoyBoeiNCnHsYGOyu1PKN+sDyPnJzMOz22JbwxzBp9NNA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/config-resolver': 4.1.0 + '@smithy/credential-provider-imds': 4.0.2 + '@smithy/node-config-provider': 4.0.2 + '@smithy/property-provider': 4.0.2 + '@smithy/smithy-client': 4.2.0 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/util-endpoints@1.0.5: @@ -17130,7 +18211,16 @@ packages: dependencies: '@smithy/node-config-provider': 3.1.12 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-endpoints@3.0.2: + resolution: {integrity: sha512-6QSutU5ZyrpNbnd51zRTL7goojlcnuOB55+F9VBD+j8JpRY50IGamsjlycrmpn8PQkmJucFW8A0LSfXj7jjtLQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/node-config-provider': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/util-hex-encoding@2.0.0: @@ -17147,6 +18237,13 @@ packages: tslib: 2.8.1 dev: false + /@smithy/util-hex-encoding@4.0.0: + resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + /@smithy/util-middleware@2.0.7: resolution: {integrity: sha512-tRINOTlf1G9B0ECarFQAtTgMhpnrMPSa+5j4ZEwEawCLfTFTavk6757sxhE4RY5RMlD/I3x+DCS8ZUiR8ho9Pw==} engines: {node: '>=14.0.0'} @@ -17160,7 +18257,15 @@ packages: engines: {node: '>=16.0.0'} dependencies: '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-middleware@4.0.2: + resolution: {integrity: sha512-6GDamTGLuBQVAEuQ4yDQ+ti/YINf/MEmIegrEeg7DdB/sld8BX1lqt9RRuIcABOhAGTA50bRbPzErez7SlDtDQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/util-retry@2.0.7: @@ -17178,7 +18283,16 @@ packages: dependencies: '@smithy/service-error-classification': 3.0.11 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-retry@4.0.2: + resolution: {integrity: sha512-Qryc+QG+7BCpvjloFLQrmlSd0RsVRHejRXd78jNO3+oREueCjwG1CCEH1vduw/ZkM1U9TztwIKVIi3+8MJScGg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/service-error-classification': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@smithy/util-stream@2.0.21: @@ -17209,6 +18323,20 @@ packages: tslib: 2.8.1 dev: false + /@smithy/util-stream@4.2.0: + resolution: {integrity: sha512-Vj1TtwWnuWqdgQI6YTUF5hQ/0jmFiOYsc51CSMgj7QfyO+RF4EnT2HNjoviNlOOmgzgvf3f5yno+EiC4vrnaWQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/fetch-http-handler': 5.0.2 + '@smithy/node-http-handler': 4.0.4 + '@smithy/types': 4.2.0 + '@smithy/util-base64': 4.0.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + /@smithy/util-uri-escape@2.0.0: resolution: {integrity: sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==} engines: {node: '>=14.0.0'} @@ -17223,6 +18351,13 @@ packages: tslib: 2.8.1 dev: false + /@smithy/util-uri-escape@4.0.0: + resolution: {integrity: sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + /@smithy/util-utf8@2.0.2: resolution: {integrity: sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==} engines: {node: '>=14.0.0'} @@ -17236,7 +18371,15 @@ packages: engines: {node: '>=16.0.0'} dependencies: '@smithy/util-buffer-from': 3.0.0 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-utf8@4.0.0: + resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/util-buffer-from': 4.0.0 + tslib: 2.8.1 dev: false /@smithy/util-waiter@3.2.0: @@ -17245,7 +18388,16 @@ packages: dependencies: '@smithy/abort-controller': 3.1.9 '@smithy/types': 3.7.2 - tslib: 2.6.2 + tslib: 2.8.1 + dev: false + + /@smithy/util-waiter@4.0.3: + resolution: {integrity: sha512-JtaY3FxmD+te+KSI2FJuEcfNC9T/DGGVf551babM7fAaXhjJUt7oSYurH1Devxd2+BOSUACCgt3buinx4UnmEA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/abort-controller': 4.0.2 + '@smithy/types': 4.2.0 + tslib: 2.8.1 dev: false /@socket.io/component-emitter@3.1.0: @@ -17983,6 +19135,12 @@ packages: resolution: {integrity: sha512-CSAVrHAtM9wfuLJ2tpvvwCU/F22sm7rMHNN+yh9D6O6hyAms3+O0cgMpC1pm6UEUMOntuZC8bMt74PteiDUdCg==} dev: false + /@types/d3-drag@3.0.7: + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + dependencies: + '@types/d3-selection': 3.0.11 + dev: false + /@types/d3-ease@3.0.0: resolution: {integrity: sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==} dev: false @@ -18003,6 +19161,10 @@ packages: '@types/d3-time': 3.0.1 dev: false + /@types/d3-selection@3.0.11: + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + dev: false + /@types/d3-shape@3.1.3: resolution: {integrity: sha512-cHMdIq+rhF5IVwAV7t61pcEXfEHsEsrbBUPkFGBwTXuxtTAkBBrnrNA8++6OWm3jwVsXoZYQM8NEekg6CPJ3zw==} dependencies: @@ -18017,6 +19179,19 @@ packages: resolution: {integrity: sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==} dev: false + /@types/d3-transition@3.0.9: + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + dependencies: + '@types/d3-selection': 3.0.11 + dev: false + + /@types/d3-zoom@3.0.8: + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + dependencies: + '@types/d3-interpolate': 3.0.2 + '@types/d3-selection': 3.0.11 + dev: false + /@types/debug@4.1.12: resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: @@ -18128,6 +19303,10 @@ packages: '@types/unist': 3.0.3 dev: false + /@types/html-to-text@9.0.4: + resolution: {integrity: sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==} + dev: true + /@types/humanize-duration@3.27.1: resolution: {integrity: sha512-K3e+NZlpCKd6Bd/EIdqjFJRFHbrq5TzPPLwREk5Iv/YoIjQrs6ljdAUCo+Lb2xFlGNOjGSE0dqsVD19cZL137w==} dev: true @@ -19352,7 +20531,7 @@ packages: busboy: 1.6.0 fast-querystring: 1.1.2 fast-url-parser: 1.1.3 - tslib: 2.6.2 + tslib: 2.8.1 dev: false /@window-splitter/state@0.4.1: @@ -19394,6 +20573,34 @@ packages: /@xtuc/long@4.2.2: resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + /@xyflow/react@12.5.1(@types/react@19.0.12)(react-dom@19.0.0)(react@19.0.0): + resolution: {integrity: sha512-jMKQVqGwCz0x6pUyvxTIuCMbyehfua7CfEEWDj29zQSHigQpCy0/5d8aOmZrqK4cwur/pVHLQomT6Rm10gXfHg==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@xyflow/system': 0.0.53 + classcat: 5.0.5 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + zustand: 4.5.5(@types/react@19.0.12)(react@19.0.0) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + + /@xyflow/system@0.0.53: + resolution: {integrity: sha512-QTWieiTtvNYyQAz1fxpzgtUGXNpnhfh6vvZa7dFWpWS2KOz6bEHODo/DTK3s07lDu0Bq0Db5lx/5M5mNjb9VDQ==} + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + dev: false + /@zxing/text-encoding@0.9.0: resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} requiresBuild: true @@ -19694,6 +20901,26 @@ packages: zod: 3.23.8 dev: false + /ai@4.2.8(react@19.0.0)(zod@3.24.2): + resolution: {integrity: sha512-0gwfPZAuuQ+uTfk/GssrfnNTYxliCFKojbSQoEhzpbpSVaPao9NoU3iuE8vwBjWuDKqILRGzYGFE4+vTak0Oxg==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + react: + optional: true + dependencies: + '@ai-sdk/provider': 1.1.0 + '@ai-sdk/provider-utils': 2.2.1(zod@3.24.2) + '@ai-sdk/react': 1.2.3(react@19.0.0)(zod@3.24.2) + '@ai-sdk/ui-utils': 1.2.2(zod@3.24.2) + '@opentelemetry/api': 1.9.0 + jsondiffpatch: 0.6.0 + react: 19.0.0 + zod: 3.24.2 + dev: false + /ajv-formats@2.1.1(ajv@8.12.0): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -20717,7 +21944,6 @@ packages: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - dev: false /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} @@ -20742,7 +21968,6 @@ packages: dependencies: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - dev: false /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} @@ -21033,6 +22258,10 @@ packages: clsx: 2.1.1 dev: false + /classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + dev: false + /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -21240,6 +22469,10 @@ packages: /comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + /command-exists@1.2.9: + resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} + dev: false + /commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} @@ -21729,6 +22962,19 @@ packages: engines: {node: '>=12'} dev: false + /d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + dev: false + + /d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + dev: false + /d3-ease@3.0.1: resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} engines: {node: '>=12'} @@ -21762,6 +23008,11 @@ packages: d3-time-format: 4.1.0 dev: false + /d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + dev: false + /d3-shape@3.2.0: resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} engines: {node: '>=12'} @@ -21788,6 +23039,31 @@ packages: engines: {node: '>=12'} dev: false + /d3-transition@3.0.1(d3-selection@3.0.0): + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + dev: false + + /d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + dev: false + /damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: true @@ -22330,7 +23606,6 @@ packages: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 - dev: false /duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} @@ -22406,6 +23681,22 @@ packages: /electron-to-chromium@1.5.98: resolution: {integrity: sha512-bI/LbtRBxU2GzK7KK5xxFd2y9Lf9XguHooPYbcXWy6wUoT8NMnffsvRhPmSeUHLSDKAEtKuTaEtK4Ms15zkIEA==} + /elevenlabs@1.55.0: + resolution: {integrity: sha512-gcLRCBRx0f2PAgd1Lz5oBhg9m8mfwbGrkXrBmEBCgWevwgrFxwBAGmOsWB5qJfoFqiRRCgqH217QRpUFIR+r8w==} + dependencies: + command-exists: 1.2.9 + execa: 5.1.1 + form-data: 4.0.0 + form-data-encoder: 4.0.2 + formdata-node: 6.0.3 + node-fetch: 2.7.0 + qs: 6.14.0 + readable-stream: 4.5.2 + url-join: 4.0.1 + transitivePeerDependencies: + - encoding + dev: false + /email-reply-parser@1.8.0: resolution: {integrity: sha512-hiie/4vNxT5NYBux8m/jZccUQdtQqiq5ytwJwbA9QIkTWbTNPQasJXfsfdetZo23hBn4ZJleRDyOA5nV4+ssSQ==} engines: {node: '>= 10.0.0'} @@ -22631,7 +23922,6 @@ packages: /es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} - dev: false /es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} @@ -22656,7 +23946,6 @@ packages: engines: {node: '>= 0.4'} dependencies: es-errors: 1.3.0 - dev: false /es-set-tostringtag@2.0.1: resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} @@ -23866,7 +25155,6 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - dev: true /execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} @@ -24318,6 +25606,11 @@ packages: resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} dev: false + /form-data-encoder@4.0.2: + resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==} + engines: {node: '>= 18'} + dev: false + /form-data@2.3.3: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} engines: {node: '>= 0.12'} @@ -24365,6 +25658,11 @@ packages: web-streams-polyfill: 4.0.0-beta.3 dev: false + /formdata-node@6.0.3: + resolution: {integrity: sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==} + engines: {node: '>= 18'} + dev: false + /formidable@3.5.1: resolution: {integrity: sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==} dependencies: @@ -24581,7 +25879,6 @@ packages: has-symbols: 1.1.0 hasown: 2.0.2 math-intrinsics: 1.1.0 - dev: false /get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} @@ -24598,7 +25895,6 @@ packages: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - dev: false /get-source@2.0.12: resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} @@ -24623,7 +25919,6 @@ packages: /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - dev: true /get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} @@ -24845,7 +26140,6 @@ packages: /gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - dev: false /got@9.6.0: resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==} @@ -24898,7 +26192,7 @@ packages: debug: 4.4.0 interpret: 3.1.1 semver: 7.6.3 - tslib: 2.6.2 + tslib: 2.8.1 yargs: 17.7.2 transitivePeerDependencies: - supports-color @@ -25000,7 +26294,6 @@ packages: /has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - dev: false /has-tostringtag@1.0.0: resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} @@ -25240,7 +26533,6 @@ packages: /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - dev: true /human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} @@ -25406,7 +26698,7 @@ packages: dependencies: get-intrinsic: 1.2.4 has: 1.0.3 - side-channel: 1.0.4 + side-channel: 1.1.0 /internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} @@ -25414,7 +26706,7 @@ packages: dependencies: es-errors: 1.3.0 hasown: 2.0.2 - side-channel: 1.0.4 + side-channel: 1.1.0 dev: true /internmap@2.0.3: @@ -25748,7 +27040,6 @@ packages: /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - dev: true /is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} @@ -26884,7 +28175,6 @@ packages: /math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - dev: false /md-to-react-email@5.0.2(react@18.3.1): resolution: {integrity: sha512-x6kkpdzIzUhecda/yahltfEl53mH26QdWu4abUF9+S0Jgam8P//Ciro8cdhyMHnT5MQUJYrIbO6ORM2UxPiNNA==} @@ -28337,7 +29627,6 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: true /node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} @@ -28510,7 +29799,6 @@ packages: engines: {node: '>=8'} dependencies: path-key: 3.1.1 - dev: true /npm-run-path@5.1.0: resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} @@ -28567,7 +29855,6 @@ packages: /object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - dev: false /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} @@ -29477,6 +30764,11 @@ packages: trouter: 2.0.1 dev: false + /popper.js@1.16.1: + resolution: {integrity: sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==} + deprecated: You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1 + dev: false + /possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -30077,7 +31369,6 @@ packages: /process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} - dev: true /progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} @@ -30882,6 +32173,12 @@ packages: tslib: 2.8.1 dev: false + /react-tippy@1.4.0: + resolution: {integrity: sha512-r/hM5XK9Ztr2ZY7IWKuRmISTlUPS/R6ddz6PO2EuxCgW+4JBcGZRPU06XcVPRDCOIiio8ryBQFrXMhFMhsuaHA==} + dependencies: + popper.js: 1.16.1 + dev: false + /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -31048,7 +32345,6 @@ packages: events: 3.3.0 process: 0.11.10 string_decoder: 1.3.0 - dev: true /readdir-glob@1.1.3: resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} @@ -31719,7 +33015,7 @@ packages: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} requiresBuild: true dependencies: - tslib: 2.6.2 + tslib: 2.8.1 dev: true optional: true @@ -32037,7 +33333,6 @@ packages: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 - dev: false /side-channel-map@1.0.1: resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} @@ -32047,7 +33342,6 @@ packages: es-errors: 1.3.0 get-intrinsic: 1.3.0 object-inspect: 1.13.4 - dev: false /side-channel-weakmap@1.0.2: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} @@ -32058,7 +33352,6 @@ packages: get-intrinsic: 1.3.0 object-inspect: 1.13.4 side-channel-map: 1.0.1 - dev: false /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} @@ -32076,7 +33369,6 @@ packages: side-channel-list: 1.0.0 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - dev: false /siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -32685,7 +33977,6 @@ packages: /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - dev: true /strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} @@ -33014,6 +34305,10 @@ packages: resolution: {integrity: sha512-aV27Oj8B7U/tAOMhJsSGdWqelfmudnGMdXIlMnk1JfsjwSjts6o8HyfN7SFH3EztzH4YH8kk6GbLTHzITJO39Q==} dev: false + /tailwind-merge@3.2.0: + resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==} + dev: false + /tailwind-scrollbar-hide@1.1.7: resolution: {integrity: sha512-X324n9OtpTmOMqEgDUEA/RgLrNfBF/jwJdctaPZDzB3mppxJk7TLIDmOreEDm1Bq4R9LSPu4Epf8VSdovNU+iA==} dev: false @@ -33829,6 +35124,7 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false /tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -34575,6 +35871,10 @@ packages: dependencies: punycode: 2.2.0 + /url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + dev: false + /url-parse-lax@3.0.0: resolution: {integrity: sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==} engines: {node: '>=4'} @@ -36012,6 +37312,14 @@ packages: dependencies: zod: 3.23.8 + /zod-to-json-schema@3.24.5(zod@3.24.2): + resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + peerDependencies: + zod: ^3.24.1 + dependencies: + zod: 3.24.2 + dev: false + /zod-validation-error@1.5.0(zod@3.23.8): resolution: {integrity: sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw==} engines: {node: '>=16.0.0'} @@ -36024,6 +37332,10 @@ packages: /zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + /zod@3.24.2: + resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + dev: false + /zustand@4.5.5(@types/react@18.2.69)(react@18.2.0): resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} engines: {node: '>=12.7.0'} @@ -36044,5 +37356,25 @@ packages: use-sync-external-store: 1.2.2(react@18.2.0) dev: false + /zustand@4.5.5(@types/react@19.0.12)(react@19.0.0): + resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + dependencies: + '@types/react': 19.0.12 + react: 19.0.0 + use-sync-external-store: 1.2.2(react@19.0.0) + dev: false + /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} diff --git a/references/waitpoint-tokens/.env.example b/references/waitpoint-tokens/.env.example new file mode 100644 index 00000000000..dd9cde60509 --- /dev/null +++ b/references/waitpoint-tokens/.env.example @@ -0,0 +1,11 @@ +TRIGGER_SECRET_KEY= +TRIGGER_API_URL= +NEXT_PUBLIC_TRIGGER_API_URL= +OPENAI_API_KEY= +ELEVENLABS_API_KEY= +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_ENDPOINT_URL_S3= +AWS_ENDPOINT_URL_IAM= +AWS_REGION= +SLACK_WEBHOOK_URL= \ No newline at end of file diff --git a/references/waitpoint-tokens/.gitignore b/references/waitpoint-tokens/.gitignore new file mode 100644 index 00000000000..ec888e02bd3 --- /dev/null +++ b/references/waitpoint-tokens/.gitignore @@ -0,0 +1,44 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* +!.env.example + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +.trigger \ No newline at end of file diff --git a/references/waitpoint-tokens/README.md b/references/waitpoint-tokens/README.md new file mode 100644 index 00000000000..25953c7ecb8 --- /dev/null +++ b/references/waitpoint-tokens/README.md @@ -0,0 +1,64 @@ +# An AI workflow with a human-in-the-loop approval step + +This reference project shows a possible approach to implement workflows using Trigger.dev and [ReactFlow](https://reactflow.dev/). +It makes use of the Trigger.dev Realtime API and the new waitpoint token feature to implement a human-in-the-loop workflow. + +## Getting Started + +This guide assumes that you have followed the [Contributing.md](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md#setup) instructions to set up a local Trigger.dev instance. If not, please complete the setup before continuing. + +1. Run the main Trigger.dev webapp: + + ```bash + pnpm run dev --filter webapp + ``` + +2. Optionally, build the CLI and SDK if you are working on them and applying changes: + + ```bash + pnpm run dev --filter trigger.dev --filter "@trigger.dev/*" + ``` + +3. Login with the CLI: + + ```bash + cd references/trigger-flow + pnpm exec trigger login -a http://localhost:3030 + ``` + + Optionally, you can use the `profile` flag to create a new profile: + + ```bash + pnpm exec trigger login -a http://localhost:3030 --profile local + ``` + + Note that you'll need to use this profile for the subsequent commands. + +4. Run the CLI + + ```bash + pnpm exec trigger dev + ``` + + You should see now the `dev` command spitting out messages, including that it's started a background worker. + +5. Run the Trigger Flow app: + + ```bash + pnpm run dev + ``` + + Open [http://localhost:3000](http://localhost:3000) on your browser to checkout the workflow. + +## Learn More + +To learn more about the technologies used in this project, check out the following resources: + +- [Trigger.dev Docs](https://trigger.dev/docs) - learn about Trigger.dev and its features +- [Trigger.dev Waitpoint Token Docs](https://trigger.dev/docs/wait-for-token) - learn about waitpoint tokens in Trigger.dev and human-in-the-loop flows +- [Trigger.dev Realtime Docs](https://trigger.dev/docs/realtime) - learn about the Realtime feature of Trigger.dev +- [Trigger.dev Realtime Streams](https://trigger.dev/docs/realtime/streams) - learn about the different types of streams available in Trigger.dev +- [ReactFlow Docs](https://reactflow.dev/learn) - learn about building interactive diagrams using ReactFlow +- [React Hooks for Trigger.dev](https://trigger.dev/docs/frontend/react-hooks) - learn about the React hooks provided by Trigger.dev +- [ElevenLabs SDK](https://elevenlabs.io/docs/overview) - learn about ElevenLabs' AI audio capabilities +- [AI SDK Documentation](https://sdk.vercel.ai/docs/introduction) - learn about the AI SDK for working with LLMs diff --git a/references/waitpoint-tokens/next.config.ts b/references/waitpoint-tokens/next.config.ts new file mode 100644 index 00000000000..e9ffa3083ad --- /dev/null +++ b/references/waitpoint-tokens/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/references/waitpoint-tokens/package.json b/references/waitpoint-tokens/package.json new file mode 100644 index 00000000000..fad2075cf0c --- /dev/null +++ b/references/waitpoint-tokens/package.json @@ -0,0 +1,41 @@ +{ + "name": "waitpoint-tokens", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "trigger:dev": "trigger dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@ai-sdk/openai": "^1.3.4", + "@aws-sdk/client-s3": "^3.777.0", + "@aws-sdk/s3-request-presigner": "^3.777.0", + "@trigger.dev/react-hooks": "workspace:*", + "@trigger.dev/sdk": "workspace:*", + "@xyflow/react": "^12.4.4", + "ai": "^4.2.8", + "clsx": "^2.1.1", + "elevenlabs": "^1.55.0", + "html-to-text": "^9.0.5", + "lucide-react": "^0.484.0", + "next": "15.2.4", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-tippy": "^1.4.0", + "tailwind-merge": "^3.2.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@trigger.dev/build": "workspace:*", + "@types/html-to-text": "^9.0.4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "tailwindcss": "^4", + "trigger.dev": "workspace:*", + "typescript": "^5" + } +} diff --git a/references/waitpoint-tokens/postcss.config.mjs b/references/waitpoint-tokens/postcss.config.mjs new file mode 100644 index 00000000000..c7bcb4b1ee1 --- /dev/null +++ b/references/waitpoint-tokens/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/references/waitpoint-tokens/src/app/actions.ts b/references/waitpoint-tokens/src/app/actions.ts new file mode 100644 index 00000000000..4ff22f31c7d --- /dev/null +++ b/references/waitpoint-tokens/src/app/actions.ts @@ -0,0 +1,72 @@ +"use server"; + +import type { articleWorkflow } from "@/trigger/articleWorkflow"; +import type { ReviewPayload } from "@/trigger/reviewSummary"; +import { tasks, wait } from "@trigger.dev/sdk/v3"; + +// A user identifier that could be fetched from your auth mechanism. +// This is out of scope for this example, so we just hardcode it. +const user = "reactflowtest"; +const userTag = `user_${user}`; + +const randomStr = (length: number) => + [...Array(length)] + .map( + () => + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"[ + Math.floor(Math.random() * 62) + ] + ) + .join(""); + +export async function triggerArticleWorkflow(prevState: any, formData: FormData) { + const articleUrl = formData.get("articleUrl") as string; + const uniqueTag = `reactflow_${randomStr(20)}`; + + const reviewWaitpointToken = await wait.createToken({ + tags: [uniqueTag, userTag], + timeout: "1h", + idempotencyKey: `review-summary-${uniqueTag}`, + }); + + const handle = await tasks.trigger( + "article-workflow", + { + articleUrl, + approvalWaitpointTokenId: reviewWaitpointToken.id, + }, + { + tags: [uniqueTag, userTag], + } + ); + + return { + articleUrl, + runId: handle.id, + runTag: uniqueTag, + reviewWaitpointTokenId: reviewWaitpointToken.id, + }; +} + +export async function approveArticleSummary(tokenId: string) { + await wait.completeToken( + { id: tokenId }, + { + approved: true, + approvedAt: new Date(), + approvedBy: user, + } + ); +} + +export async function rejectArticleSummary(tokenId: string) { + await wait.completeToken( + { id: tokenId }, + { + approved: false, + rejectedAt: new Date(), + rejectedBy: user, + reason: "It's no good", + } + ); +} diff --git a/references/waitpoint-tokens/src/app/favicon.ico b/references/waitpoint-tokens/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/references/waitpoint-tokens/src/app/globals.css b/references/waitpoint-tokens/src/app/globals.css new file mode 100644 index 00000000000..bcdc7dc0c02 --- /dev/null +++ b/references/waitpoint-tokens/src/app/globals.css @@ -0,0 +1,29 @@ +@import "tailwindcss"; +@import "react-tippy/dist/tippy.css"; + + + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/references/waitpoint-tokens/src/app/layout.tsx b/references/waitpoint-tokens/src/app/layout.tsx new file mode 100644 index 00000000000..2fcbd7f6ebf --- /dev/null +++ b/references/waitpoint-tokens/src/app/layout.tsx @@ -0,0 +1,30 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Trigger Flow", + description: "Trigger.dev reference project with ReactFlow", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/references/waitpoint-tokens/src/app/logo.svg b/references/waitpoint-tokens/src/app/logo.svg new file mode 100644 index 00000000000..503d34b3e3f --- /dev/null +++ b/references/waitpoint-tokens/src/app/logo.svg @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/references/waitpoint-tokens/src/app/page.tsx b/references/waitpoint-tokens/src/app/page.tsx new file mode 100644 index 00000000000..e9a872b8e12 --- /dev/null +++ b/references/waitpoint-tokens/src/app/page.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import Flow from "@/components/Flow"; +import Image from "next/image"; +import logo from "./logo.svg"; +import { auth } from "@trigger.dev/sdk"; + +export default async function Home() { + // A user identifier that could be fetched from your auth mechanism. + // This is out of scope for this example, so we just hardcode it. + const user = "reactflowtest"; + const userTag = `user_${user}`; + + // We generate a public access token to use the Trigger.dev realtime API and listen to changes in task runs. + // Depending on your setup, you might want to be more granular in the scopes you grant. + // Check the frontend usage docs for a comprehensive list of the approaches to authenticate: + // https://trigger.dev/docs/frontend/overview#authentication + const publicAccessToken = await auth.createPublicToken({ + scopes: { + read: { + tags: [userTag], + }, + }, + }); + + return ( +
+
+ Logo +

+ This reference project that shows a possible approach to implement workflows using{" "} + Trigger.dev and{" "} + ReactFlow +

+
+
+ +
+
+ ); +} diff --git a/references/waitpoint-tokens/src/components/ActionNode.tsx b/references/waitpoint-tokens/src/components/ActionNode.tsx new file mode 100644 index 00000000000..1e3be647fc7 --- /dev/null +++ b/references/waitpoint-tokens/src/components/ActionNode.tsx @@ -0,0 +1,124 @@ +"use client"; + +import React, { useEffect } from "react"; +import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react"; +import type { LucideIcon } from "lucide-react"; +import { Loader2, Check, X, Layers, Asterisk, RefreshCcw } from "lucide-react"; +import { Tooltip } from "react-tippy"; +import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks"; +import { cn } from "@/lib/cn"; + +export type ActionNodeData = Node< + { + label: string; + icon?: LucideIcon; + isTerminalAction?: boolean; + trigger: { + taskIdentifier: string; + userTag: string; + currentRunTag?: string; + currentRunStatus?: + | "WAITING_FOR_DEPLOY" + | "PENDING_VERSION" + | "QUEUED" + | "EXECUTING" + | "REATTEMPTING" + | "FROZEN" + | "COMPLETED" + | "CANCELED" + | "FAILED" + | "CRASHED" + | "INTERRUPTED" + | "SYSTEM_FAILURE" + | "DELAYED" + | "EXPIRED" + | "TIMED_OUT"; + }; + }, + "action" +>; + +export const isActionNode = (node: Node): node is ActionNodeData => { + return node.type === "action"; +}; + +const triggerStatusToIcon: Record = { + QUEUED: Layers, + EXECUTING: Loader2, + REATTEMPTING: RefreshCcw, + COMPLETED: Check, + FAILED: X, +}; + +function ActionNode({ id, data }: NodeProps) { + const { runs } = useRealtimeRunsWithTag(data.trigger.userTag); + const { updateNodeData } = useReactFlow(); + + useEffect(() => { + if (!data.trigger.currentRunTag && data.trigger.currentRunStatus !== undefined) { + updateNodeData(id, { + trigger: { ...data.trigger, currentRunStatus: undefined }, + }); + return; + } + + const run = runs.find( + (run) => + run.tags.includes(data.trigger.currentRunTag as string) && + run.taskIdentifier === data.trigger.taskIdentifier + ); + if (!run) { + if (data.trigger.currentRunStatus !== undefined) { + updateNodeData(id, { + trigger: { ...data.trigger, currentRunStatus: undefined }, + }); + } + return; + } + updateNodeData(id, { + trigger: { ...data.trigger, currentRunStatus: run.status }, + }); + }, [runs, id, updateNodeData]); + + return ( +
+ {data.trigger.currentRunStatus && ( +
+ {/* @ts-ignore - there is some weird type issue with react-tippy */} + + {React.createElement(triggerStatusToIcon[data.trigger.currentRunStatus] ?? Asterisk, { + className: cn("size-3 text-white", { + "animate-spin": data.trigger.currentRunStatus === "EXECUTING", + }), + })} + +
+ )} +
+ {data.icon && } + {data.label} +
+ + {!data.isTerminalAction && ( + + )} +
+ ); +} + +export default ActionNode; diff --git a/references/waitpoint-tokens/src/components/Flow.tsx b/references/waitpoint-tokens/src/components/Flow.tsx new file mode 100644 index 00000000000..37481809a58 --- /dev/null +++ b/references/waitpoint-tokens/src/components/Flow.tsx @@ -0,0 +1,272 @@ +"use client"; + +import React, { useEffect, useMemo, useState } from "react"; +import { + Edge, + MarkerType, + NodeTypes, + ReactFlow, + useEdgesState, + useNodesState, +} from "@xyflow/react"; +import { Globe, Speech, NotepadText, BellDot, Send } from "lucide-react"; +import { TriggerAuthContext } from "@trigger.dev/react-hooks"; + +import "@xyflow/react/dist/style.css"; +import InputNode, { isInputNode, InputNodeData } from "./InputNode"; +import ActionNode, { ActionNodeData } from "./ActionNode"; +import ReviewNode, { isReviewNode, ReviewNodeData } from "./ReviewNode"; +import type { scrape } from "@/trigger/scrapeSite"; +import type { summarizeArticle } from "@/trigger/summarizeArticle"; +import type { convertTextToSpeech } from "@/trigger/convertTextToSpeech"; +import type { reviewSummary } from "@/trigger/reviewSummary"; +import type { publishSummary } from "@/trigger/publishSummary"; +import type { sendSlackNotification } from "@/trigger/sendSlackNotification"; + +type WorkflowNode = InputNodeData | ActionNodeData | ReviewNodeData; + +export default function Flow({ + triggerPublicAccessToken, + triggerUserTag, +}: { + triggerPublicAccessToken: string; + triggerUserTag: string; +}) { + const initialNodes: WorkflowNode[] = useMemo( + () => + [ + { + id: "1", + type: "input_url", + position: { x: 0, y: 150 }, + data: { + trigger: { + currentRunTag: undefined, + }, + }, + }, + { + id: "2", + type: "action", + position: { x: 370, y: 0 }, + data: { + label: "Scrape site", + icon: Globe, + trigger: { + taskIdentifier: "scrape-site" satisfies (typeof scrape)["id"], + userTag: triggerUserTag, + }, + }, + }, + { + id: "3", + type: "action", + position: { x: 370, y: 150 }, + data: { + label: "Generate summary", + icon: NotepadText, + trigger: { + taskIdentifier: "summarize-article" satisfies (typeof summarizeArticle)["id"], + userTag: triggerUserTag, + }, + }, + }, + { + id: "4", + type: "action", + position: { x: 370, y: 310 }, + data: { + label: "Convert to speech", + icon: Speech, + trigger: { + taskIdentifier: "convert-text-to-speech" satisfies (typeof convertTextToSpeech)["id"], + userTag: triggerUserTag, + }, + }, + }, + { + id: "5", + type: "review", + position: { x: 620, y: 115 }, + data: { + trigger: { + taskIdentifier: "review-summary" satisfies (typeof reviewSummary)["id"], + userTag: triggerUserTag, + }, + }, + }, + { + id: "6", + type: "action", + position: { x: 880, y: 110 }, + data: { + label: "Publish result", + icon: Send, + isTerminalAction: true, + trigger: { + taskIdentifier: "publish-summary" satisfies (typeof publishSummary)["id"], + userTag: triggerUserTag, + }, + }, + }, + { + id: "7", + type: "action", + position: { x: 880, y: 190 }, + data: { + label: "Slack Notification", + icon: BellDot, + isTerminalAction: true, + trigger: { + taskIdentifier: + "send-slack-notification" satisfies (typeof sendSlackNotification)["id"], + userTag: triggerUserTag, + }, + }, + }, + ] satisfies WorkflowNode[], + [triggerUserTag] + ); + const initialEdges: Edge[] = useMemo( + () => [ + { + id: "1->2", + source: "1", + target: "2", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "2->3", + source: "2", + target: "3", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "3->4", + source: "3", + target: "4", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "4->5", + source: "4", + target: "5", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "5->6", + source: "5", + target: "6", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "5->7", + source: "5", + target: "7", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + ], + [] + ); + + const nodeTypes: NodeTypes = useMemo( + () => ({ input_url: InputNode, action: ActionNode, review: ReviewNode }), + [] + ); + + const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); + + const [currentRunTag, setCurrentRunTag] = useState(undefined); + + // Once the task in Trigger.dev is triggered, the unique tag for that run is set on the input node. + // We propagate this run tag to the other nodes here. + // An alternative approach to avoid doing the propagation at the root level + // would be to do this in the nodes themselves by using the `useNodeConnections` hook from ReactFlow. + useEffect(() => { + const inputNode = nodes.find((node) => isInputNode(node)); + if (!inputNode || inputNode.data.trigger.currentRunTag === currentRunTag) { + return; + } + + setCurrentRunTag(inputNode.data.trigger.currentRunTag); + }, [nodes, currentRunTag, setCurrentRunTag]); + + useEffect(() => { + setNodes((nds) => + nds.map((node) => { + if (isInputNode(node)) { + return node; + } + + if (isReviewNode(node)) { + return { + ...node, + data: { + ...node.data, + trigger: { + ...node.data.trigger, + currentRunTag, + }, + }, + }; + } + + return { + ...node, + data: { + ...node.data, + trigger: { + ...node.data.trigger, + currentRunTag, + }, + }, + }; + }) + ); + }, [currentRunTag, setNodes]); + + // Animate the edges for an active workflow run based on the node status + useEffect(() => { + if (!currentRunTag) { + return; + } + + setEdges((eds) => { + return eds.map((edge) => { + const sourceNode = nodes.find((node) => node.id === edge.source); + + if (!sourceNode || isInputNode(sourceNode)) { + return edge; + } + + return { + ...edge, + // A bit of a simplified approach. + // You might want to differentiate further and style the edges differently if the status of a node is `FAILED`, for example. + animated: sourceNode?.data?.trigger.currentRunStatus !== "COMPLETED", + }; + }); + }); + }, [nodes, currentRunTag, setEdges]); + + return ( +
+ + + +
+ ); +} diff --git a/references/waitpoint-tokens/src/components/InputNode.tsx b/references/waitpoint-tokens/src/components/InputNode.tsx new file mode 100644 index 00000000000..14f7433e7a1 --- /dev/null +++ b/references/waitpoint-tokens/src/components/InputNode.tsx @@ -0,0 +1,55 @@ +"use client"; + +import React, { useActionState, useEffect } from "react"; +import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react"; +import { triggerArticleWorkflow } from "@/app/actions"; + +export type InputNodeData = Node<{ trigger: { currentRunTag?: string } }, "input_url">; + +export const isInputNode = (node: Node): node is InputNodeData => { + return node.type === "input_url"; +}; + +function InputNode({ id }: NodeProps) { + const [state, formAction, pending] = useActionState(triggerArticleWorkflow, undefined); + const { updateNodeData } = useReactFlow(); + + useEffect(() => { + if (state) { + updateNodeData(id, { trigger: { currentRunTag: state.runTag } }); + } + }, [state, id, updateNodeData]); + + return ( +
+ {state?.articleUrl && ( + + {state?.articleUrl} + + )} + +
+
+ + +
+
+ + +
+ ); +} + +export default InputNode; diff --git a/references/waitpoint-tokens/src/components/ReviewNode.tsx b/references/waitpoint-tokens/src/components/ReviewNode.tsx new file mode 100644 index 00000000000..353c3e9ea42 --- /dev/null +++ b/references/waitpoint-tokens/src/components/ReviewNode.tsx @@ -0,0 +1,145 @@ +"use client"; + +import React, { useEffect, useState, useTransition } from "react"; +import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react"; +import { Split, Check, X, Clock } from "lucide-react"; +import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks"; +import { approveArticleSummary, rejectArticleSummary } from "@/app/actions"; +import type { ReviewStatus } from "@/trigger/reviewSummary"; + +export type ReviewNodeData = Node< + { + trigger: { + taskIdentifier: string; + userTag: string; + currentRunTag?: string; + currentRunStatus?: + | "WAITING_FOR_DEPLOY" + | "PENDING_VERSION" + | "QUEUED" + | "EXECUTING" + | "REATTEMPTING" + | "FROZEN" + | "COMPLETED" + | "CANCELED" + | "FAILED" + | "CRASHED" + | "INTERRUPTED" + | "SYSTEM_FAILURE" + | "DELAYED" + | "EXPIRED" + | "TIMED_OUT"; + }; + }, + "review" +>; + +export const isReviewNode = (node: Node): node is ReviewNodeData => { + return node.type === "review"; +}; + +function ReviewNode({ id, data }: NodeProps) { + const { runs } = useRealtimeRunsWithTag(data.trigger.userTag); + const { updateNodeData } = useReactFlow(); + + const [waitpointTokenId, setWaitpointTokenId] = useState(undefined); + const [audioSummaryUrl, setAudioSummaryUrl] = useState(undefined); + const [reviewStatus, setReviewStatus] = useState(undefined); + const [isReviewActionPending, startReviewActionTransition] = useTransition(); + + useEffect(() => { + if (!data.trigger.currentRunTag && data.trigger.currentRunStatus !== undefined) { + updateNodeData(id, { trigger: { ...data.trigger, currentRunStatus: undefined } }); + setWaitpointTokenId(undefined); + setAudioSummaryUrl(undefined); + setReviewStatus(undefined); + return; + } + + const run = runs.find( + (run) => + run.tags.includes(data.trigger.currentRunTag as string) && + run.taskIdentifier === data.trigger.taskIdentifier + ); + if (!run) { + if (data.trigger.currentRunStatus !== undefined) { + updateNodeData(id, { trigger: { ...data.trigger, currentRunStatus: undefined } }); + setWaitpointTokenId(undefined); + setAudioSummaryUrl(undefined); + setReviewStatus(undefined); + } + return; + } + setWaitpointTokenId(run.metadata?.waitpointTokenId as string); + setAudioSummaryUrl(run.metadata?.audioSummaryUrl as string); + setReviewStatus(run.metadata?.reviewStatus as ReviewStatus); + updateNodeData(id, { trigger: { ...data.trigger, currentRunStatus: run.status } }); + }, [runs, id, updateNodeData]); + + return ( +
+ {reviewStatus === "pending" && ( +
+ + + + + + +
+ )} +
+ Review summary + + {reviewStatus === "approved" && ( +
+ Approved +
+ )} + {reviewStatus === "rejected" && ( +
+ Rejected +
+ )} + {reviewStatus === "timeout" && ( +
+ Timed out +
+ )} + {(reviewStatus === undefined || reviewStatus === "pending") && ( +
+ + +
+ )} +
+ + + +
+ ); +} + +export default ReviewNode; diff --git a/references/waitpoint-tokens/src/lib/cn.ts b/references/waitpoint-tokens/src/lib/cn.ts new file mode 100644 index 00000000000..bd0c391ddd1 --- /dev/null +++ b/references/waitpoint-tokens/src/lib/cn.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/references/waitpoint-tokens/src/trigger/articleWorkflow.ts b/references/waitpoint-tokens/src/trigger/articleWorkflow.ts new file mode 100644 index 00000000000..0cbe2ac5a19 --- /dev/null +++ b/references/waitpoint-tokens/src/trigger/articleWorkflow.ts @@ -0,0 +1,90 @@ +import { batch, Context, task } from "@trigger.dev/sdk/v3"; +import { scrape } from "./scrapeSite"; +import { summarizeArticle } from "./summarizeArticle"; +import { convertTextToSpeech } from "./convertTextToSpeech"; +import { reviewSummary } from "./reviewSummary"; +import { publishSummary } from "./publishSummary"; +import { sendSlackNotification } from "./sendSlackNotification"; + +export const articleWorkflow = task({ + id: "article-workflow", + run: async ( + payload: { articleUrl: string; approvalWaitpointTokenId: string }, + { ctx }: { ctx: Context } + ) => { + const scrapeResult = await scrape.triggerAndWait( + { + url: payload.articleUrl, + }, + { tags: ctx.run.tags } + ); + + if (!scrapeResult.ok) { + throw new Error("Failed to scrape site"); + } + + const summarizeResult = await summarizeArticle.triggerAndWait( + { + content: scrapeResult.output.content, + }, + { tags: ctx.run.tags } + ); + + if (!summarizeResult.ok) { + throw new Error("Failed to summarize article"); + } + + const convertTextToSpeechResult = await convertTextToSpeech.triggerAndWait( + { + text: summarizeResult.output.summary, + }, + { tags: ctx.run.tags } + ); + + if (!convertTextToSpeechResult.ok) { + throw new Error("Failed to convert text to speech"); + } + + const reviewSummaryResult = await reviewSummary.triggerAndWait( + { + audioSummaryUrl: convertTextToSpeechResult.output.audioUrl, + waitpointTokenId: payload.approvalWaitpointTokenId, + }, + { tags: ctx.run.tags } + ); + + if (!reviewSummaryResult.ok) { + throw new Error("Failed to review summary"); + } + + if (reviewSummaryResult.output.approved) { + const { + runs: [sendSlackNotificationRun, publishSummaryRun], + } = await batch.triggerByTaskAndWait([ + { + task: sendSlackNotification, + payload: { + message: `Article summary was approved by ${reviewSummaryResult.output.approvedBy} at ${reviewSummaryResult.output.approvedAt}`, + }, + options: { tags: ctx.run.tags }, + }, + { + task: publishSummary, + payload: { + audioSummaryUrl: convertTextToSpeechResult.output.audioUrl, + articleUrl: payload.articleUrl, + }, + options: { tags: ctx.run.tags }, + }, + ]); + + if (!sendSlackNotificationRun.ok) { + throw new Error("Failed to send Slack notification"); + } + + if (!publishSummaryRun.ok) { + throw new Error("Failed to publish summary"); + } + } + }, +}); diff --git a/references/waitpoint-tokens/src/trigger/convertTextToSpeech.ts b/references/waitpoint-tokens/src/trigger/convertTextToSpeech.ts new file mode 100644 index 00000000000..039e0251cc1 --- /dev/null +++ b/references/waitpoint-tokens/src/trigger/convertTextToSpeech.ts @@ -0,0 +1,72 @@ +import { logger, task } from "@trigger.dev/sdk/v3"; +import { randomUUID } from "crypto"; +import { ElevenLabsClient } from "elevenlabs"; +import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; + +const eleventLabs = new ElevenLabsClient(); +const s3 = new S3Client({ + forcePathStyle: false, +}); + +export const createAudioStreamFromText = async (text: string): Promise => { + const audioStream = await eleventLabs.textToSpeech.convertAsStream("M6N6IdXhi5YNZyZSDe7k", { + model_id: "eleven_multilingual_v2", + text, + output_format: "mp3_44100_128", + voice_settings: { + stability: 0, + similarity_boost: 1.0, + use_speaker_boost: true, + speed: 1.0, + }, + }); + const chunks: Buffer[] = []; + for await (const chunk of audioStream) { + chunks.push(chunk); + } + const content = Buffer.concat(chunks); + return content; +}; + +export const generatePresignedUrl = async (objectKey: string) => { + const getObjectParams = { + Bucket: "articles", + Key: objectKey, + Expires: 3600, + }; + const command = new GetObjectCommand(getObjectParams); + const url = await getSignedUrl(s3, command, { expiresIn: 3600 }); + return url; +}; +export const uploadAudioStreamToS3 = async (audioStream: Buffer) => { + const remotePath = `${randomUUID()}.mp3`; + await s3.send( + new PutObjectCommand({ + Bucket: "articles", + Key: remotePath, + Body: audioStream, + ContentType: "audio/mpeg", + }) + ); + return remotePath; +}; + +export const convertTextToSpeech = task({ + id: "convert-text-to-speech", + maxDuration: 300, + run: async (payload: { text: string }) => { + const audioStream = await createAudioStreamFromText(payload.text); + logger.info("Audio stream created"); + + const s3path = await uploadAudioStreamToS3(audioStream); + logger.info("Audio stream uploaded to S3"); + + const audioUrl = await generatePresignedUrl(s3path); + logger.info("Audio URL generated"); + + return { + audioUrl, + }; + }, +}); diff --git a/references/waitpoint-tokens/src/trigger/publishSummary.ts b/references/waitpoint-tokens/src/trigger/publishSummary.ts new file mode 100644 index 00000000000..843dde36c91 --- /dev/null +++ b/references/waitpoint-tokens/src/trigger/publishSummary.ts @@ -0,0 +1,20 @@ +import { logger, task, wait, Context } from "@trigger.dev/sdk/v3"; + +export const publishSummary = task({ + id: "publish-summary", + retry: { + maxAttempts: 4, + }, + run: async (payload: { audioSummaryUrl: string; articleUrl: string }, { ctx }) => { + // This task does not actually do anything, it's just a placeholder step in the workflow. + // The actual logic would depend on your use case. + + if (ctx.attempt.number <= 2) { + // just a dummy error to test the retry + throw new Error("Unlucky attempt!"); + } + + const { audioSummaryUrl, articleUrl } = payload; + logger.info("Summary published", { audioSummaryUrl, articleUrl }); + }, +}); diff --git a/references/waitpoint-tokens/src/trigger/reviewSummary.ts b/references/waitpoint-tokens/src/trigger/reviewSummary.ts new file mode 100644 index 00000000000..14f4afa6592 --- /dev/null +++ b/references/waitpoint-tokens/src/trigger/reviewSummary.ts @@ -0,0 +1,42 @@ +import { logger, metadata, task, wait, WaitpointTimeoutError } from "@trigger.dev/sdk/v3"; + +export type ReviewPayload = + | { + approved: true; + approvedAt: Date; + approvedBy: string; + } + | { + approved: false; + rejectedAt: Date; + rejectedBy: string; + reason: string; + }; +export type ReviewStatus = "pending" | "approved" | "rejected" | "timeout"; + +export const reviewSummary = task({ + id: "review-summary", + run: async (payload: { audioSummaryUrl: string; waitpointTokenId: string }) => { + metadata.set("waitpointTokenId", payload.waitpointTokenId); + metadata.set("audioSummaryUrl", payload.audioSummaryUrl); + metadata.set("reviewStatus", "pending" satisfies ReviewStatus); + + const result = await wait.forToken({ id: payload.waitpointTokenId }); + + if (!result.ok) { + if (result.error instanceof WaitpointTimeoutError) { + metadata.set("reviewStatus", "timeout" satisfies ReviewStatus); + logger.warn("Review summary waitpoint timed out"); + } + + throw result.error; + } + + metadata.set( + "reviewStatus", + (result.output.approved ? "approved" : "rejected") satisfies ReviewStatus + ); + + return { ...result.output, audioSummaryUrl: payload.audioSummaryUrl }; + }, +}); diff --git a/references/waitpoint-tokens/src/trigger/scrapeSite.ts b/references/waitpoint-tokens/src/trigger/scrapeSite.ts new file mode 100644 index 00000000000..88b6305cb48 --- /dev/null +++ b/references/waitpoint-tokens/src/trigger/scrapeSite.ts @@ -0,0 +1,18 @@ +import { logger, task } from "@trigger.dev/sdk/v3"; +import { htmlToText } from "html-to-text"; + +export const scrape = task({ + id: "scrape-site", + maxDuration: 300, + run: async (payload: { url: string }) => { + const response = await fetch(payload.url); + const html = await response.text(); + const content = htmlToText(html); + + logger.info("Site scraped successfully", { url: payload.url }); + + return { + content, + }; + }, +}); diff --git a/references/waitpoint-tokens/src/trigger/sendSlackNotification.ts b/references/waitpoint-tokens/src/trigger/sendSlackNotification.ts new file mode 100644 index 00000000000..8b432a319da --- /dev/null +++ b/references/waitpoint-tokens/src/trigger/sendSlackNotification.ts @@ -0,0 +1,16 @@ +import { logger, task } from "@trigger.dev/sdk/v3"; + +export const sendSlackNotification = task({ + id: "send-slack-notification", + run: async (payload: { message: string }) => { + await fetch(process.env.SLACK_WEBHOOK_URL, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ text: payload.message }), + }); + + logger.info("Slack notification sent"); + }, +}); diff --git a/references/waitpoint-tokens/src/trigger/summarizeArticle.ts b/references/waitpoint-tokens/src/trigger/summarizeArticle.ts new file mode 100644 index 00000000000..cf799a2b60c --- /dev/null +++ b/references/waitpoint-tokens/src/trigger/summarizeArticle.ts @@ -0,0 +1,23 @@ +import { logger, task } from "@trigger.dev/sdk/v3"; +import { generateText } from "ai"; +import { openai } from "@ai-sdk/openai"; + +export const summarizeArticle = task({ + id: "summarize-article", + maxDuration: 300, + run: async (payload: { content: string }) => { + const { text } = await generateText({ + model: openai("gpt-4o-mini"), + prompt: ` + Summarize the following article in a concise manner, focus on the main body of the article and generate a summary that sounds good in speech too. + The result will be converted to speech for a news report. Make it no longer than 500 words. + Content: ${payload.content}`, + }); + + logger.info("Article summary generated successfully"); + + return { + summary: text, + }; + }, +}); diff --git a/references/waitpoint-tokens/trigger.config.ts b/references/waitpoint-tokens/trigger.config.ts new file mode 100644 index 00000000000..7c1762db79c --- /dev/null +++ b/references/waitpoint-tokens/trigger.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from "@trigger.dev/sdk/v3"; + +export default defineConfig({ + project: "proj_gwfcnhvehupfxamxvzrg", + runtime: "node", + logLevel: "log", + // The max compute seconds a task is allowed to run. If the task run exceeds this duration, it will be stopped. + // You can override this on an individual task. + // See https://trigger.dev/docs/runs/max-duration + maxDuration: 3600, + dirs: ["./src/trigger"], + retries: { + enabledInDev: true, + }, +}); diff --git a/references/waitpoint-tokens/tsconfig.json b/references/waitpoint-tokens/tsconfig.json new file mode 100644 index 00000000000..d0a3585421f --- /dev/null +++ b/references/waitpoint-tokens/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "trigger.config.ts" + ], + "exclude": ["node_modules"] +} From 427a7415d1e4b2b0a080036c57c9fa5968f4daf3 Mon Sep 17 00:00:00 2001 From: saadi Date: Sat, 12 Apr 2025 20:42:31 +0200 Subject: [PATCH 2/3] Add descriptions for the variables in the .env.example file --- references/waitpoint-tokens/.env.example | 16 ++++++++++++++++ references/waitpoint-tokens/README.md | 6 ++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/references/waitpoint-tokens/.env.example b/references/waitpoint-tokens/.env.example index dd9cde60509..4784ecab407 100644 --- a/references/waitpoint-tokens/.env.example +++ b/references/waitpoint-tokens/.env.example @@ -1,11 +1,27 @@ +# Trigger.dev secret key +# https://trigger.dev/docs/apikeys TRIGGER_SECRET_KEY= TRIGGER_API_URL= NEXT_PUBLIC_TRIGGER_API_URL= + +# OpenAI API key for generating article summaries. +# https://platform.openai.com OPENAI_API_KEY= + +# ElevenLabs API key for converting text to speech. +# https://elevenlabs.io/docs/quickstart#create-an-api-key ELEVENLABS_API_KEY= + +# Credentials for writing audio streams to an S3-compatible bucket. +# Does not necessarily need to be AWS S3 bucket, as long as it is compatible to the S3 SDK, e.g., https://www.tigrisdata.com/ +# https://docs.aws.amazon.com/sdkref/latest/guide/environment-variables.html AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_ENDPOINT_URL_S3= AWS_ENDPOINT_URL_IAM= +AWS_S3_BUCKET= AWS_REGION= + +# Slack webhook URL for sending messages to a Slack channel. +# Follow the guide in https://api.slack.com/messaging/webhooks to generate a webhook URL. SLACK_WEBHOOK_URL= \ No newline at end of file diff --git a/references/waitpoint-tokens/README.md b/references/waitpoint-tokens/README.md index 25953c7ecb8..56cb3501852 100644 --- a/references/waitpoint-tokens/README.md +++ b/references/waitpoint-tokens/README.md @@ -34,7 +34,9 @@ This guide assumes that you have followed the [Contributing.md](https://github.c Note that you'll need to use this profile for the subsequent commands. -4. Run the CLI +4. Create an `.env` file by copying [.env.example](.env.example) and fill in the required environment variables. The example file includes a description for each variable. + +5. Run the CLI ```bash pnpm exec trigger dev @@ -42,7 +44,7 @@ This guide assumes that you have followed the [Contributing.md](https://github.c You should see now the `dev` command spitting out messages, including that it's started a background worker. -5. Run the Trigger Flow app: +6. Run the ReactFlow app: ```bash pnpm run dev From bee650a66f67c9d825d36e1b7f566b193f4b99bc Mon Sep 17 00:00:00 2001 From: saadi Date: Sat, 12 Apr 2025 20:56:31 +0200 Subject: [PATCH 3/3] Improve the realtime hooks approach in the waitpoint reference project --- .../waitpoint-tokens/src/app/actions.ts | 54 +-- references/waitpoint-tokens/src/app/page.tsx | 20 +- .../src/components/ActionNode.tsx | 72 +--- .../waitpoint-tokens/src/components/Flow.tsx | 326 +++++++++--------- .../src/components/InputNode.tsx | 12 +- .../src/components/ReviewNode.tsx | 65 +--- .../src/trigger/convertTextToSpeech.ts | 18 +- 7 files changed, 236 insertions(+), 331 deletions(-) diff --git a/references/waitpoint-tokens/src/app/actions.ts b/references/waitpoint-tokens/src/app/actions.ts index 4ff22f31c7d..44af008fc4b 100644 --- a/references/waitpoint-tokens/src/app/actions.ts +++ b/references/waitpoint-tokens/src/app/actions.ts @@ -2,12 +2,7 @@ import type { articleWorkflow } from "@/trigger/articleWorkflow"; import type { ReviewPayload } from "@/trigger/reviewSummary"; -import { tasks, wait } from "@trigger.dev/sdk/v3"; - -// A user identifier that could be fetched from your auth mechanism. -// This is out of scope for this example, so we just hardcode it. -const user = "reactflowtest"; -const userTag = `user_${user}`; +import { auth, tasks, wait } from "@trigger.dev/sdk/v3"; const randomStr = (length: number) => [...Array(length)] @@ -21,30 +16,41 @@ const randomStr = (length: number) => export async function triggerArticleWorkflow(prevState: any, formData: FormData) { const articleUrl = formData.get("articleUrl") as string; - const uniqueTag = `reactflow_${randomStr(20)}`; + const workflowTag = `reactflow_${randomStr(20)}`; const reviewWaitpointToken = await wait.createToken({ - tags: [uniqueTag, userTag], + tags: [workflowTag], timeout: "1h", - idempotencyKey: `review-summary-${uniqueTag}`, + idempotencyKey: `review-summary-${workflowTag}`, }); - const handle = await tasks.trigger( - "article-workflow", - { - articleUrl, - approvalWaitpointTokenId: reviewWaitpointToken.id, - }, - { - tags: [uniqueTag, userTag], - } - ); + const [workflowPublicAccessToken] = await Promise.all([ + // We generate a public access token to use the Trigger.dev realtime API and listen to changes in task runs using react hooks. + // This token has access to all runs tagged with the unique workflow tag. + auth.createPublicToken({ + scopes: { + read: { + tags: [workflowTag], + }, + }, + }), + , + tasks.trigger( + "article-workflow", + { + articleUrl, + approvalWaitpointTokenId: reviewWaitpointToken.id, + }, + { + tags: [workflowTag], + } + ), + ]); return { articleUrl, - runId: handle.id, - runTag: uniqueTag, - reviewWaitpointTokenId: reviewWaitpointToken.id, + workflowTag, + workflowPublicAccessToken, }; } @@ -54,7 +60,7 @@ export async function approveArticleSummary(tokenId: string) { { approved: true, approvedAt: new Date(), - approvedBy: user, + approvedBy: "Alice", } ); } @@ -65,7 +71,7 @@ export async function rejectArticleSummary(tokenId: string) { { approved: false, rejectedAt: new Date(), - rejectedBy: user, + rejectedBy: "Alice", reason: "It's no good", } ); diff --git a/references/waitpoint-tokens/src/app/page.tsx b/references/waitpoint-tokens/src/app/page.tsx index e9a872b8e12..94f76babb82 100644 --- a/references/waitpoint-tokens/src/app/page.tsx +++ b/references/waitpoint-tokens/src/app/page.tsx @@ -2,26 +2,8 @@ import React from "react"; import Flow from "@/components/Flow"; import Image from "next/image"; import logo from "./logo.svg"; -import { auth } from "@trigger.dev/sdk"; export default async function Home() { - // A user identifier that could be fetched from your auth mechanism. - // This is out of scope for this example, so we just hardcode it. - const user = "reactflowtest"; - const userTag = `user_${user}`; - - // We generate a public access token to use the Trigger.dev realtime API and listen to changes in task runs. - // Depending on your setup, you might want to be more granular in the scopes you grant. - // Check the frontend usage docs for a comprehensive list of the approaches to authenticate: - // https://trigger.dev/docs/frontend/overview#authentication - const publicAccessToken = await auth.createPublicToken({ - scopes: { - read: { - tags: [userTag], - }, - }, - }); - return (
@@ -33,7 +15,7 @@ export default async function Home() {

- +
); diff --git a/references/waitpoint-tokens/src/components/ActionNode.tsx b/references/waitpoint-tokens/src/components/ActionNode.tsx index 1e3be647fc7..d9ebf9d7f5b 100644 --- a/references/waitpoint-tokens/src/components/ActionNode.tsx +++ b/references/waitpoint-tokens/src/components/ActionNode.tsx @@ -1,11 +1,11 @@ "use client"; -import React, { useEffect } from "react"; -import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react"; +import React from "react"; +import { Handle, Position, NodeProps, Node } from "@xyflow/react"; import type { LucideIcon } from "lucide-react"; import { Loader2, Check, X, Layers, Asterisk, RefreshCcw } from "lucide-react"; import { Tooltip } from "react-tippy"; -import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks"; +import type { RealtimeRun, AnyTask } from "@trigger.dev/sdk"; import { cn } from "@/lib/cn"; export type ActionNodeData = Node< @@ -15,24 +15,7 @@ export type ActionNodeData = Node< isTerminalAction?: boolean; trigger: { taskIdentifier: string; - userTag: string; - currentRunTag?: string; - currentRunStatus?: - | "WAITING_FOR_DEPLOY" - | "PENDING_VERSION" - | "QUEUED" - | "EXECUTING" - | "REATTEMPTING" - | "FROZEN" - | "COMPLETED" - | "CANCELED" - | "FAILED" - | "CRASHED" - | "INTERRUPTED" - | "SYSTEM_FAILURE" - | "DELAYED" - | "EXPIRED" - | "TIMED_OUT"; + currentRun?: RealtimeRun; }; }, "action" @@ -50,60 +33,33 @@ const triggerStatusToIcon: Record = { FAILED: X, }; -function ActionNode({ id, data }: NodeProps) { - const { runs } = useRealtimeRunsWithTag(data.trigger.userTag); - const { updateNodeData } = useReactFlow(); - - useEffect(() => { - if (!data.trigger.currentRunTag && data.trigger.currentRunStatus !== undefined) { - updateNodeData(id, { - trigger: { ...data.trigger, currentRunStatus: undefined }, - }); - return; - } - - const run = runs.find( - (run) => - run.tags.includes(data.trigger.currentRunTag as string) && - run.taskIdentifier === data.trigger.taskIdentifier - ); - if (!run) { - if (data.trigger.currentRunStatus !== undefined) { - updateNodeData(id, { - trigger: { ...data.trigger, currentRunStatus: undefined }, - }); - } - return; - } - updateNodeData(id, { - trigger: { ...data.trigger, currentRunStatus: run.status }, - }); - }, [runs, id, updateNodeData]); +function ActionNode({ data }: NodeProps) { + const { currentRun } = data.trigger; return (
- {data.trigger.currentRunStatus && ( + {currentRun && (
{/* @ts-ignore - there is some weird type issue with react-tippy */} - {React.createElement(triggerStatusToIcon[data.trigger.currentRunStatus] ?? Asterisk, { + {React.createElement(triggerStatusToIcon[currentRun.status] ?? Asterisk, { className: cn("size-3 text-white", { - "animate-spin": data.trigger.currentRunStatus === "EXECUTING", + "animate-spin": currentRun.status === "EXECUTING", }), })} diff --git a/references/waitpoint-tokens/src/components/Flow.tsx b/references/waitpoint-tokens/src/components/Flow.tsx index 37481809a58..51b259eef49 100644 --- a/references/waitpoint-tokens/src/components/Flow.tsx +++ b/references/waitpoint-tokens/src/components/Flow.tsx @@ -10,7 +10,6 @@ import { useNodesState, } from "@xyflow/react"; import { Globe, Speech, NotepadText, BellDot, Send } from "lucide-react"; -import { TriggerAuthContext } from "@trigger.dev/react-hooks"; import "@xyflow/react/dist/style.css"; import InputNode, { isInputNode, InputNodeData } from "./InputNode"; @@ -22,153 +21,131 @@ import type { convertTextToSpeech } from "@/trigger/convertTextToSpeech"; import type { reviewSummary } from "@/trigger/reviewSummary"; import type { publishSummary } from "@/trigger/publishSummary"; import type { sendSlackNotification } from "@/trigger/sendSlackNotification"; +import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks"; type WorkflowNode = InputNodeData | ActionNodeData | ReviewNodeData; -export default function Flow({ - triggerPublicAccessToken, - triggerUserTag, -}: { - triggerPublicAccessToken: string; - triggerUserTag: string; -}) { - const initialNodes: WorkflowNode[] = useMemo( - () => - [ - { - id: "1", - type: "input_url", - position: { x: 0, y: 150 }, - data: { - trigger: { - currentRunTag: undefined, - }, - }, - }, - { - id: "2", - type: "action", - position: { x: 370, y: 0 }, - data: { - label: "Scrape site", - icon: Globe, - trigger: { - taskIdentifier: "scrape-site" satisfies (typeof scrape)["id"], - userTag: triggerUserTag, - }, - }, - }, - { - id: "3", - type: "action", - position: { x: 370, y: 150 }, - data: { - label: "Generate summary", - icon: NotepadText, - trigger: { - taskIdentifier: "summarize-article" satisfies (typeof summarizeArticle)["id"], - userTag: triggerUserTag, - }, - }, - }, - { - id: "4", - type: "action", - position: { x: 370, y: 310 }, - data: { - label: "Convert to speech", - icon: Speech, - trigger: { - taskIdentifier: "convert-text-to-speech" satisfies (typeof convertTextToSpeech)["id"], - userTag: triggerUserTag, - }, - }, - }, - { - id: "5", - type: "review", - position: { x: 620, y: 115 }, - data: { - trigger: { - taskIdentifier: "review-summary" satisfies (typeof reviewSummary)["id"], - userTag: triggerUserTag, - }, - }, - }, - { - id: "6", - type: "action", - position: { x: 880, y: 110 }, - data: { - label: "Publish result", - icon: Send, - isTerminalAction: true, - trigger: { - taskIdentifier: "publish-summary" satisfies (typeof publishSummary)["id"], - userTag: triggerUserTag, - }, - }, - }, - { - id: "7", - type: "action", - position: { x: 880, y: 190 }, - data: { - label: "Slack Notification", - icon: BellDot, - isTerminalAction: true, - trigger: { - taskIdentifier: - "send-slack-notification" satisfies (typeof sendSlackNotification)["id"], - userTag: triggerUserTag, - }, - }, - }, - ] satisfies WorkflowNode[], - [triggerUserTag] - ); - const initialEdges: Edge[] = useMemo( - () => [ - { - id: "1->2", - source: "1", - target: "2", - markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, +const initialNodes: WorkflowNode[] = [ + { + id: "1", + type: "input_url", + position: { x: 0, y: 150 }, + data: {}, + }, + { + id: "2", + type: "action", + position: { x: 370, y: 0 }, + data: { + label: "Scrape site", + icon: Globe, + trigger: { + taskIdentifier: "scrape-site" satisfies (typeof scrape)["id"], }, - { - id: "2->3", - source: "2", - target: "3", - markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + }, + { + id: "3", + type: "action", + position: { x: 370, y: 150 }, + data: { + label: "Generate summary", + icon: NotepadText, + trigger: { + taskIdentifier: "summarize-article" satisfies (typeof summarizeArticle)["id"], }, - { - id: "3->4", - source: "3", - target: "4", - markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + }, + { + id: "4", + type: "action", + position: { x: 370, y: 310 }, + data: { + label: "Convert to speech", + icon: Speech, + trigger: { + taskIdentifier: "convert-text-to-speech" satisfies (typeof convertTextToSpeech)["id"], }, - { - id: "4->5", - source: "4", - target: "5", - markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + }, + { + id: "5", + type: "review", + position: { x: 620, y: 115 }, + data: { + trigger: { + taskIdentifier: "review-summary" satisfies (typeof reviewSummary)["id"], }, - { - id: "5->6", - source: "5", - target: "6", - markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + }, + { + id: "6", + type: "action", + position: { x: 880, y: 110 }, + data: { + label: "Publish result", + icon: Send, + isTerminalAction: true, + trigger: { + taskIdentifier: "publish-summary" satisfies (typeof publishSummary)["id"], }, - { - id: "5->7", - source: "5", - target: "7", - markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + }, + { + id: "7", + type: "action", + position: { x: 880, y: 190 }, + data: { + label: "Slack Notification", + icon: BellDot, + isTerminalAction: true, + trigger: { + taskIdentifier: "send-slack-notification" satisfies (typeof sendSlackNotification)["id"], }, - ], - [] - ); + }, + }, +]; +const initialEdges: Edge[] = [ + { + id: "1->2", + source: "1", + target: "2", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "2->3", + source: "2", + target: "3", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "3->4", + source: "3", + target: "4", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "4->5", + source: "4", + target: "5", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "5->6", + source: "5", + target: "6", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, + { + id: "5->7", + source: "5", + target: "7", + markerEnd: { type: MarkerType.Arrow, width: 20, height: 20 }, + }, +]; + +export default function Flow() { const nodeTypes: NodeTypes = useMemo( () => ({ input_url: InputNode, action: ActionNode, review: ReviewNode }), [] @@ -177,28 +154,52 @@ export default function Flow({ const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); - const [currentRunTag, setCurrentRunTag] = useState(undefined); + const [currentWorkflowRun, setCurrentWorkflowRun] = useState< + { tag: string; accessToken: string } | undefined + >(undefined); + + // We listen to changes in the runs for the current workflow run using the Trigger.dev realtime hooks. + // We then pass the run data to its corresponding node, by filtering with the task identifier. + // Alternatively, you could choose to listen to the run changes in the node components themselves. A couple of caveats with that approach: + // - The implementation of the custom node components becomes a bit more involved. In the current implementation, they are mostly presentation-only components. + // - Currently it is not possible to filter the runs in `useRealtimeRunsWithTag` to only listen to runs for a specific task. Only filtering by tag is supported. + // which the current implementation does not make use of. More filtering functionality might be added in the future though. + const { runs } = useRealtimeRunsWithTag(currentWorkflowRun?.tag ?? [], { + enabled: currentWorkflowRun !== undefined, + accessToken: currentWorkflowRun?.accessToken, + baseURL: process.env.NEXT_PUBLIC_TRIGGER_API_URL, + }); // Once the task in Trigger.dev is triggered, the unique tag for that run is set on the input node. - // We propagate this run tag to the other nodes here. - // An alternative approach to avoid doing the propagation at the root level - // would be to do this in the nodes themselves by using the `useNodeConnections` hook from ReactFlow. + // We need the unique tag and public access token to listen to the run status changes via the Trigger.dev realtime hooks. useEffect(() => { const inputNode = nodes.find((node) => isInputNode(node)); - if (!inputNode || inputNode.data.trigger.currentRunTag === currentRunTag) { + if (!inputNode || inputNode.data.workflowRun?.tag === currentWorkflowRun?.tag) { return; } - setCurrentRunTag(inputNode.data.trigger.currentRunTag); - }, [nodes, currentRunTag, setCurrentRunTag]); + setCurrentWorkflowRun(inputNode.data.workflowRun); + }, [nodes, currentWorkflowRun, setCurrentWorkflowRun]); + // Update the node data on changes in the runs useEffect(() => { + if (!runs || runs.length === 0) { + return; + } + setNodes((nds) => nds.map((node) => { if (isInputNode(node)) { return node; } + const currentRun = + runs.find( + (run) => + run.taskIdentifier === node.data.trigger.taskIdentifier && + run.tags.includes(currentWorkflowRun?.tag as string) + ) ?? undefined; + if (isReviewNode(node)) { return { ...node, @@ -206,7 +207,7 @@ export default function Flow({ ...node.data, trigger: { ...node.data.trigger, - currentRunTag, + currentRun, }, }, }; @@ -218,17 +219,17 @@ export default function Flow({ ...node.data, trigger: { ...node.data.trigger, - currentRunTag, + currentRun, }, }, }; }) ); - }, [currentRunTag, setNodes]); + }, [runs, setNodes]); // Animate the edges for an active workflow run based on the node status useEffect(() => { - if (!currentRunTag) { + if (!currentWorkflowRun) { return; } @@ -244,29 +245,22 @@ export default function Flow({ ...edge, // A bit of a simplified approach. // You might want to differentiate further and style the edges differently if the status of a node is `FAILED`, for example. - animated: sourceNode?.data?.trigger.currentRunStatus !== "COMPLETED", + animated: sourceNode?.data?.trigger.currentRun?.status !== "COMPLETED", }; }); }); - }, [nodes, currentRunTag, setEdges]); + }, [nodes, currentWorkflowRun, setEdges]); return (
- - - +
); } diff --git a/references/waitpoint-tokens/src/components/InputNode.tsx b/references/waitpoint-tokens/src/components/InputNode.tsx index 14f7433e7a1..2d31360e0d3 100644 --- a/references/waitpoint-tokens/src/components/InputNode.tsx +++ b/references/waitpoint-tokens/src/components/InputNode.tsx @@ -4,7 +4,10 @@ import React, { useActionState, useEffect } from "react"; import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react"; import { triggerArticleWorkflow } from "@/app/actions"; -export type InputNodeData = Node<{ trigger: { currentRunTag?: string } }, "input_url">; +export type InputNodeData = Node< + { workflowRun?: { tag: string; accessToken: string } }, + "input_url" +>; export const isInputNode = (node: Node): node is InputNodeData => { return node.type === "input_url"; @@ -16,7 +19,12 @@ function InputNode({ id }: NodeProps) { useEffect(() => { if (state) { - updateNodeData(id, { trigger: { currentRunTag: state.runTag } }); + updateNodeData(id, { + workflowRun: { + tag: state.workflowTag, + accessToken: state.workflowPublicAccessToken, + }, + }); } }, [state, id, updateNodeData]); diff --git a/references/waitpoint-tokens/src/components/ReviewNode.tsx b/references/waitpoint-tokens/src/components/ReviewNode.tsx index 353c3e9ea42..6465c0d37a8 100644 --- a/references/waitpoint-tokens/src/components/ReviewNode.tsx +++ b/references/waitpoint-tokens/src/components/ReviewNode.tsx @@ -1,34 +1,17 @@ "use client"; -import React, { useEffect, useState, useTransition } from "react"; -import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react"; +import React, { useTransition } from "react"; +import { Handle, Position, NodeProps, Node } from "@xyflow/react"; import { Split, Check, X, Clock } from "lucide-react"; -import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks"; import { approveArticleSummary, rejectArticleSummary } from "@/app/actions"; import type { ReviewStatus } from "@/trigger/reviewSummary"; +import type { RealtimeRun, AnyTask } from "@trigger.dev/sdk"; export type ReviewNodeData = Node< { trigger: { taskIdentifier: string; - userTag: string; - currentRunTag?: string; - currentRunStatus?: - | "WAITING_FOR_DEPLOY" - | "PENDING_VERSION" - | "QUEUED" - | "EXECUTING" - | "REATTEMPTING" - | "FROZEN" - | "COMPLETED" - | "CANCELED" - | "FAILED" - | "CRASHED" - | "INTERRUPTED" - | "SYSTEM_FAILURE" - | "DELAYED" - | "EXPIRED" - | "TIMED_OUT"; + currentRun?: RealtimeRun; }; }, "review" @@ -38,44 +21,14 @@ export const isReviewNode = (node: Node): node is ReviewNodeData => { return node.type === "review"; }; -function ReviewNode({ id, data }: NodeProps) { - const { runs } = useRealtimeRunsWithTag(data.trigger.userTag); - const { updateNodeData } = useReactFlow(); +function ReviewNode({ data }: NodeProps) { + const { currentRun } = data.trigger; + const waitpointTokenId = currentRun?.metadata?.waitpointTokenId as string | undefined; + const audioSummaryUrl = currentRun?.metadata?.audioSummaryUrl as string | undefined; + const reviewStatus = currentRun?.metadata?.reviewStatus as ReviewStatus | undefined; - const [waitpointTokenId, setWaitpointTokenId] = useState(undefined); - const [audioSummaryUrl, setAudioSummaryUrl] = useState(undefined); - const [reviewStatus, setReviewStatus] = useState(undefined); const [isReviewActionPending, startReviewActionTransition] = useTransition(); - useEffect(() => { - if (!data.trigger.currentRunTag && data.trigger.currentRunStatus !== undefined) { - updateNodeData(id, { trigger: { ...data.trigger, currentRunStatus: undefined } }); - setWaitpointTokenId(undefined); - setAudioSummaryUrl(undefined); - setReviewStatus(undefined); - return; - } - - const run = runs.find( - (run) => - run.tags.includes(data.trigger.currentRunTag as string) && - run.taskIdentifier === data.trigger.taskIdentifier - ); - if (!run) { - if (data.trigger.currentRunStatus !== undefined) { - updateNodeData(id, { trigger: { ...data.trigger, currentRunStatus: undefined } }); - setWaitpointTokenId(undefined); - setAudioSummaryUrl(undefined); - setReviewStatus(undefined); - } - return; - } - setWaitpointTokenId(run.metadata?.waitpointTokenId as string); - setAudioSummaryUrl(run.metadata?.audioSummaryUrl as string); - setReviewStatus(run.metadata?.reviewStatus as ReviewStatus); - updateNodeData(id, { trigger: { ...data.trigger, currentRunStatus: run.status } }); - }, [runs, id, updateNodeData]); - return (
{reviewStatus === "pending" && ( diff --git a/references/waitpoint-tokens/src/trigger/convertTextToSpeech.ts b/references/waitpoint-tokens/src/trigger/convertTextToSpeech.ts index 039e0251cc1..d951de2b988 100644 --- a/references/waitpoint-tokens/src/trigger/convertTextToSpeech.ts +++ b/references/waitpoint-tokens/src/trigger/convertTextToSpeech.ts @@ -29,9 +29,9 @@ export const createAudioStreamFromText = async (text: string): Promise = return content; }; -export const generatePresignedUrl = async (objectKey: string) => { +export const generatePresignedUrl = async (objectKey: string, bucket: string) => { const getObjectParams = { - Bucket: "articles", + Bucket: bucket, Key: objectKey, Expires: 3600, }; @@ -39,11 +39,12 @@ export const generatePresignedUrl = async (objectKey: string) => { const url = await getSignedUrl(s3, command, { expiresIn: 3600 }); return url; }; -export const uploadAudioStreamToS3 = async (audioStream: Buffer) => { + +export const uploadAudioStreamToS3 = async (audioStream: Buffer, bucket: string) => { const remotePath = `${randomUUID()}.mp3`; await s3.send( new PutObjectCommand({ - Bucket: "articles", + Bucket: bucket, Key: remotePath, Body: audioStream, ContentType: "audio/mpeg", @@ -56,13 +57,18 @@ export const convertTextToSpeech = task({ id: "convert-text-to-speech", maxDuration: 300, run: async (payload: { text: string }) => { + const bucket = process.env.AWS_S3_BUCKET; + if (!bucket) { + throw new Error("AWS_S3_BUCKET is not set"); + } + const audioStream = await createAudioStreamFromText(payload.text); logger.info("Audio stream created"); - const s3path = await uploadAudioStreamToS3(audioStream); + const s3path = await uploadAudioStreamToS3(audioStream, bucket); logger.info("Audio stream uploaded to S3"); - const audioUrl = await generatePresignedUrl(s3path); + const audioUrl = await generatePresignedUrl(s3path, bucket); logger.info("Audio URL generated"); return {