diff --git a/packages/twenty-server/nest-cli.json b/packages/twenty-server/nest-cli.json index 08b16289dd..a2fe919aae 100644 --- a/packages/twenty-server/nest-cli.json +++ b/packages/twenty-server/nest-cli.json @@ -32,6 +32,18 @@ "include": "engine/core-modules/logic-function/logic-function-drivers/constants/executor/index.mjs", "outDir": "dist/assets" }, + { + "include": "engine/core-modules/logic-function/logic-function-drivers/constants/yarn-install/index.mjs", + "outDir": "dist/assets" + }, + { + "include": "engine/core-modules/logic-function/logic-function-drivers/constants/builder/index.mjs", + "outDir": "dist/assets" + }, + { + "include": "engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies/**", + "outDir": "dist/assets" + }, { "include": "database/clickHouse/migrations/*.sql", "outDir": "dist" diff --git a/packages/twenty-server/package.json b/packages/twenty-server/package.json index 199bc0d183..3696a9a324 100644 --- a/packages/twenty-server/package.json +++ b/packages/twenty-server/package.json @@ -28,6 +28,7 @@ "@aws-sdk/client-sesv2": "3.1001.0", "@aws-sdk/client-sts": "3.1001.0", "@aws-sdk/credential-providers": "3.1001.0", + "@aws-sdk/s3-request-presigner": "3.1001.0", "@azure/msal-node": "^3.8.4", "@babel/preset-env": "7.26.9", "@blocknote/server-util": "^0.47.1", diff --git a/packages/twenty-server/src/engine/core-modules/application/application-package/constants/yarn-engine/.yarnrc.yml b/packages/twenty-server/src/engine/core-modules/application/application-package/constants/yarn-engine/.yarnrc.yml index 928b2f0323..f85ca9c632 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-package/constants/yarn-engine/.yarnrc.yml +++ b/packages/twenty-server/src/engine/core-modules/application/application-package/constants/yarn-engine/.yarnrc.yml @@ -5,3 +5,13 @@ enableScripts: false nodeLinker: node-modules yarnPath: .yarn/releases/yarn-4.9.2.cjs + +supportedArchitectures: + os: + - current + - linux + - darwin + cpu: + - current + - x64 + - arm64 diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/builder/index.mjs b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/builder/index.mjs new file mode 100644 index 0000000000..86937ff9e3 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/builder/index.mjs @@ -0,0 +1,65 @@ +import { randomBytes } from 'crypto'; +import { promises as fs } from 'fs'; +import { dirname, resolve } from 'path'; + +import { build } from 'esbuild'; + +const BANNER = { + js: "import { createRequire as __createRequire } from 'module';\nconst require = __createRequire(import.meta.url);", +}; + +const assertPathInsideDir = (filePath, dir) => { + const resolved = resolve(dir, filePath); + + if (!resolved.startsWith(dir + '/')) { + throw new Error(`Path traversal detected: ${filePath}`); + } + + return resolved; +}; + +export const handler = async (event) => { + const { action, sourceCode, sourceFileName, builtFileName } = event; + + if (action !== 'transpile') { + throw new Error(`Unknown action: ${action}`); + } + + if (!sourceCode || !sourceFileName || !builtFileName) { + throw new Error( + 'Missing required fields: sourceCode, sourceFileName, builtFileName', + ); + } + + const randomId = randomBytes(16).toString('hex'); + const workDir = `/tmp/${randomId}`; + + await fs.mkdir(workDir, { recursive: true }); + + try { + const entryFilePath = assertPathInsideDir(sourceFileName, workDir); + const outFilePath = assertPathInsideDir(builtFileName, workDir); + + await fs.mkdir(dirname(entryFilePath), { recursive: true }); + await fs.writeFile(entryFilePath, sourceCode, 'utf-8'); + await fs.mkdir(dirname(outFilePath), { recursive: true }); + + await build({ + entryPoints: [entryFilePath], + outfile: outFilePath, + platform: 'node', + format: 'esm', + target: 'es2017', + bundle: true, + sourcemap: true, + packages: 'external', + banner: BANNER, + }); + + const builtCode = await fs.readFile(outFilePath, 'utf-8'); + + return { builtCode }; + } finally { + await fs.rm(workDir, { recursive: true, force: true }); + } +}; diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies-dirname.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies-dirname.ts new file mode 100644 index 0000000000..8a9982bf88 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies-dirname.ts @@ -0,0 +1,11 @@ +import path from 'path'; + +import { ASSET_PATH } from 'src/constants/assets-path'; + +export const COMMON_LAYER_DEPENDENCIES_DIRNAME = path.resolve( + __dirname, + path.join( + ASSET_PATH, + 'engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies', + ), +); diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies/package.json b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies/package.json new file mode 100644 index 0000000000..d25082318a --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "archiver": "^7.0.1", + "esbuild": "^0.25.0" + } +} diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies/yarn.lock b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies/yarn.lock new file mode 100644 index 0000000000..2d44fd200c --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies/yarn.lock @@ -0,0 +1,1030 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@esbuild/aix-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/aix-ppc64@npm:0.25.12" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm64@npm:0.25.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm@npm:0.25.12" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-x64@npm:0.25.12" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-arm64@npm:0.25.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-x64@npm:0.25.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-arm64@npm:0.25.12" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-x64@npm:0.25.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm64@npm:0.25.12" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm@npm:0.25.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ia32@npm:0.25.12" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-loong64@npm:0.25.12" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-mips64el@npm:0.25.12" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ppc64@npm:0.25.12" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-riscv64@npm:0.25.12" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-s390x@npm:0.25.12" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-x64@npm:0.25.12" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-arm64@npm:0.25.12" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-x64@npm:0.25.12" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-arm64@npm:0.25.12" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-x64@npm:0.25.12" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openharmony-arm64@npm:0.25.12" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/sunos-x64@npm:0.25.12" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-arm64@npm:0.25.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-ia32@npm:0.25.12" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-x64@npm:0.25.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + +"abort-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "abort-controller@npm:3.0.0" + dependencies: + event-target-shim: "npm:^5.0.0" + checksum: 10c0/90ccc50f010250152509a344eb2e71977fbf8db0ab8f1061197e3275ddf6c61a41a6edfd7b9409c664513131dd96e962065415325ef23efa5db931b382d24ca5 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + +"ansi-regex@npm:^6.2.2": + version: 6.2.2 + resolution: "ansi-regex@npm:6.2.2" + checksum: 10c0/05d4acb1d2f59ab2cf4b794339c7b168890d44dda4bf0ce01152a8da0213aca207802f930442ce8cd22d7a92f44907664aac6508904e75e038fa944d2601b30f + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 + languageName: node + linkType: hard + +"ansi-styles@npm:^6.1.0": + version: 6.2.3 + resolution: "ansi-styles@npm:6.2.3" + checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 + languageName: node + linkType: hard + +"archiver-utils@npm:^5.0.0, archiver-utils@npm:^5.0.2": + version: 5.0.2 + resolution: "archiver-utils@npm:5.0.2" + dependencies: + glob: "npm:^10.0.0" + graceful-fs: "npm:^4.2.0" + is-stream: "npm:^2.0.1" + lazystream: "npm:^1.0.0" + lodash: "npm:^4.17.15" + normalize-path: "npm:^3.0.0" + readable-stream: "npm:^4.0.0" + checksum: 10c0/3782c5fa9922186aa1a8e41ed0c2867569faa5f15c8e5e6418ea4c1b730b476e21bd68270b3ea457daf459ae23aaea070b2b9f90cf90a59def8dc79b9e4ef538 + languageName: node + linkType: hard + +"archiver@npm:^7.0.1": + version: 7.0.1 + resolution: "archiver@npm:7.0.1" + dependencies: + archiver-utils: "npm:^5.0.2" + async: "npm:^3.2.4" + buffer-crc32: "npm:^1.0.0" + readable-stream: "npm:^4.0.0" + readdir-glob: "npm:^1.1.2" + tar-stream: "npm:^3.0.0" + zip-stream: "npm:^6.0.1" + checksum: 10c0/02afd87ca16f6184f752db8e26884e6eff911c476812a0e7f7b26c4beb09f06119807f388a8e26ed2558aa8ba9db28646ebd147a4f99e46813b8b43158e1438e + languageName: node + linkType: hard + +"async@npm:^3.2.4": + version: 3.2.6 + resolution: "async@npm:3.2.6" + checksum: 10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70 + languageName: node + linkType: hard + +"b4a@npm:^1.6.4": + version: 1.8.0 + resolution: "b4a@npm:1.8.0" + peerDependencies: + react-native-b4a: "*" + peerDependenciesMeta: + react-native-b4a: + optional: true + checksum: 10c0/27eab5c50ea1f1314f36256f160d2e6d6950f55f02ee4942732ecafd8bcc4b3a2ed209fab532b288770d41df2befa97a2745175c06471875b716eb87abf31519 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"bare-events@npm:^2.5.4, bare-events@npm:^2.7.0": + version: 2.8.2 + resolution: "bare-events@npm:2.8.2" + peerDependencies: + bare-abort-controller: "*" + peerDependenciesMeta: + bare-abort-controller: + optional: true + checksum: 10c0/53fef240cf2cdcca62f78b6eead90ddb5a59b0929f414b13a63764c2b4f9de98ea8a578d033b04d64bb7b86dfbc402e937984e69950855cc3754c7b63da7db21 + languageName: node + linkType: hard + +"bare-fs@npm:^4.5.5": + version: 4.5.5 + resolution: "bare-fs@npm:4.5.5" + dependencies: + bare-events: "npm:^2.5.4" + bare-path: "npm:^3.0.0" + bare-stream: "npm:^2.6.4" + bare-url: "npm:^2.2.2" + fast-fifo: "npm:^1.3.2" + peerDependencies: + bare-buffer: "*" + peerDependenciesMeta: + bare-buffer: + optional: true + checksum: 10c0/1f8b31b73848639fff4ab46fb9d8c0477dc571813fd6790ec75edc192abc467310f1082ecb81170aeffca91b4d08f0e9a002d6f9fa6968a07d11ea22be1597ff + languageName: node + linkType: hard + +"bare-os@npm:^3.0.1": + version: 3.7.1 + resolution: "bare-os@npm:3.7.1" + checksum: 10c0/66f4eb063314b4187a5f6d89bc93b2aa1fa39598fa6898b85780199a799a26b1ee3cd66aa4b9f2ed41f4095ac75de048d22fbde57c9c6dd0bd9e4f0fa317a254 + languageName: node + linkType: hard + +"bare-path@npm:^3.0.0": + version: 3.0.0 + resolution: "bare-path@npm:3.0.0" + dependencies: + bare-os: "npm:^3.0.1" + checksum: 10c0/56a3ca82a9f808f4976cb1188640ac206546ce0ddff582afafc7bd2a6a5b31c3bd16422653aec656eeada2830cfbaa433c6cbf6d6b4d9eba033d5e06d60d9a68 + languageName: node + linkType: hard + +"bare-stream@npm:^2.6.4": + version: 2.8.0 + resolution: "bare-stream@npm:2.8.0" + dependencies: + streamx: "npm:^2.21.0" + teex: "npm:^1.0.1" + peerDependencies: + bare-buffer: "*" + bare-events: "*" + peerDependenciesMeta: + bare-buffer: + optional: true + bare-events: + optional: true + checksum: 10c0/91b722b26758c3a6940b681803811cb8fd0c50867cb393ef807a615ddb89024a6cd765b7dfe1425564ec5b5cec0f96bcf3536963c1ca59bf6f39dc8363ce92c7 + languageName: node + linkType: hard + +"bare-url@npm:^2.2.2": + version: 2.3.2 + resolution: "bare-url@npm:2.3.2" + dependencies: + bare-path: "npm:^3.0.0" + checksum: 10c0/4fd0046314390a54404519d9db20e130ab3a341ef638d040f9603ae3fa0a1d84f6970357d21c8fc64e6163d1f61fd212cb1cfa4cb537dfead99fb06e3c030b15 + languageName: node + linkType: hard + +"base64-js@npm:^1.3.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1, brace-expansion@npm:^2.0.2": + version: 2.0.2 + resolution: "brace-expansion@npm:2.0.2" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf + languageName: node + linkType: hard + +"buffer-crc32@npm:^1.0.0": + version: 1.0.0 + resolution: "buffer-crc32@npm:1.0.0" + checksum: 10c0/8b86e161cee4bb48d5fa622cbae4c18f25e4857e5203b89e23de59e627ab26beb82d9d7999f2b8de02580165f61f83f997beaf02980cdf06affd175b651921ab + languageName: node + linkType: hard + +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.2.1" + checksum: 10c0/2a905fbbcde73cc5d8bd18d1caa23715d5f83a5935867c2329f0ac06104204ba7947be098fe1317fbd8830e26090ff8e764f08cd14fefc977bb248c3487bcbd0 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 + languageName: node + linkType: hard + +"compress-commons@npm:^6.0.2": + version: 6.0.2 + resolution: "compress-commons@npm:6.0.2" + dependencies: + crc-32: "npm:^1.2.0" + crc32-stream: "npm:^6.0.0" + is-stream: "npm:^2.0.1" + normalize-path: "npm:^3.0.0" + readable-stream: "npm:^4.0.0" + checksum: 10c0/2347031b7c92c8ed5011b07b93ec53b298fa2cd1800897532ac4d4d1aeae06567883f481b6e35f13b65fc31b190c751df6635434d525562f0203fde76f1f0814 + languageName: node + linkType: hard + +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 10c0/90a0e40abbddfd7618f8ccd63a74d88deea94e77d0e8dbbea059fa7ebebb8fbb4e2909667fe26f3a467073de1a542ebe6ae4c73a73745ac5833786759cd906c9 + languageName: node + linkType: hard + +"crc-32@npm:^1.2.0": + version: 1.2.2 + resolution: "crc-32@npm:1.2.2" + bin: + crc32: bin/crc32.njs + checksum: 10c0/11dcf4a2e77ee793835d49f2c028838eae58b44f50d1ff08394a610bfd817523f105d6ae4d9b5bef0aad45510f633eb23c903e9902e4409bed1ce70cb82b9bf0 + languageName: node + linkType: hard + +"crc32-stream@npm:^6.0.0": + version: 6.0.0 + resolution: "crc32-stream@npm:6.0.0" + dependencies: + crc-32: "npm:^1.2.0" + readable-stream: "npm:^4.0.0" + checksum: 10c0/bf9c84571ede2d119c2b4f3a9ef5eeb9ff94b588493c0d3862259af86d3679dcce1c8569dd2b0a6eff2f35f5e2081cc1263b846d2538d4054da78cf34f262a3d + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + +"esbuild@npm:^0.25.0": + version: 0.25.12 + resolution: "esbuild@npm:0.25.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.12" + "@esbuild/android-arm": "npm:0.25.12" + "@esbuild/android-arm64": "npm:0.25.12" + "@esbuild/android-x64": "npm:0.25.12" + "@esbuild/darwin-arm64": "npm:0.25.12" + "@esbuild/darwin-x64": "npm:0.25.12" + "@esbuild/freebsd-arm64": "npm:0.25.12" + "@esbuild/freebsd-x64": "npm:0.25.12" + "@esbuild/linux-arm": "npm:0.25.12" + "@esbuild/linux-arm64": "npm:0.25.12" + "@esbuild/linux-ia32": "npm:0.25.12" + "@esbuild/linux-loong64": "npm:0.25.12" + "@esbuild/linux-mips64el": "npm:0.25.12" + "@esbuild/linux-ppc64": "npm:0.25.12" + "@esbuild/linux-riscv64": "npm:0.25.12" + "@esbuild/linux-s390x": "npm:0.25.12" + "@esbuild/linux-x64": "npm:0.25.12" + "@esbuild/netbsd-arm64": "npm:0.25.12" + "@esbuild/netbsd-x64": "npm:0.25.12" + "@esbuild/openbsd-arm64": "npm:0.25.12" + "@esbuild/openbsd-x64": "npm:0.25.12" + "@esbuild/openharmony-arm64": "npm:0.25.12" + "@esbuild/sunos-x64": "npm:0.25.12" + "@esbuild/win32-arm64": "npm:0.25.12" + "@esbuild/win32-ia32": "npm:0.25.12" + "@esbuild/win32-x64": "npm:0.25.12" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b + languageName: node + linkType: hard + +"event-target-shim@npm:^5.0.0": + version: 5.0.1 + resolution: "event-target-shim@npm:5.0.1" + checksum: 10c0/0255d9f936215fd206156fd4caa9e8d35e62075d720dc7d847e89b417e5e62cf1ce6c9b4e0a1633a9256de0efefaf9f8d26924b1f3c8620cffb9db78e7d3076b + languageName: node + linkType: hard + +"events-universal@npm:^1.0.0": + version: 1.0.1 + resolution: "events-universal@npm:1.0.1" + dependencies: + bare-events: "npm:^2.7.0" + checksum: 10c0/a1d9a5e9f95843650f8ec240dd1221454c110189a9813f32cdf7185759b43f1f964367ac7dca4ebc69150b59043f2d77c7e122b0d03abf7c25477ea5494785a5 + languageName: node + linkType: hard + +"events@npm:^3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: 10c0/d6b6f2adbccbcda74ddbab52ed07db727ef52e31a61ed26db9feb7dc62af7fc8e060defa65e5f8af9449b86b52cc1a1f6a79f2eafcf4e62add2b7a1fa4a432f6 + languageName: node + linkType: hard + +"fast-fifo@npm:^1.2.0, fast-fifo@npm:^1.3.2": + version: 1.3.2 + resolution: "fast-fifo@npm:1.3.2" + checksum: 10c0/d53f6f786875e8b0529f784b59b4b05d4b5c31c651710496440006a398389a579c8dbcd2081311478b5bf77f4b0b21de69109c5a4eabea9d8e8783d1eb864e4c + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.3.1 + resolution: "foreground-child@npm:3.3.1" + dependencies: + cross-spawn: "npm:^7.0.6" + signal-exit: "npm:^4.0.1" + checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3 + languageName: node + linkType: hard + +"glob@npm:^10.0.0": + version: 10.5.0 + resolution: "glob@npm:10.5.0" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/100705eddbde6323e7b35e1d1ac28bcb58322095bd8e63a7d0bef1a2cdafe0d0f7922a981b2b48369a4f8c1b077be5c171804534c3509dfe950dde15fbe6d828 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.2.0": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"ieee754@npm:^1.2.1": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb + languageName: node + linkType: hard + +"inherits@npm:~2.0.3": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + +"is-stream@npm:^2.0.1": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: 10c0/7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 + languageName: node + linkType: hard + +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: 10c0/18b5be6669be53425f0b84098732670ed4e727e3af33bc7f948aac01782110eb9a18b3b329c5323bcdd3acdaae547ee077d3951317e7f133bff7105264b3003d + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 + languageName: node + linkType: hard + +"lazystream@npm:^1.0.0": + version: 1.0.1 + resolution: "lazystream@npm:1.0.1" + dependencies: + readable-stream: "npm:^2.0.5" + checksum: 10c0/ea4e509a5226ecfcc303ba6782cc269be8867d372b9bcbd625c88955df1987ea1a20da4643bf9270336415a398d33531ebf0d5f0d393b9283dc7c98bfcbd7b69 + languageName: node + linkType: hard + +"lodash@npm:^4.17.15": + version: 4.17.23 + resolution: "lodash@npm:4.17.23" + checksum: 10c0/1264a90469f5bb95d4739c43eb6277d15b6d9e186df4ac68c3620443160fc669e2f14c11e7d8b2ccf078b81d06147c01a8ccced9aab9f9f63d50dcf8cace6bf6 + languageName: node + linkType: hard + +"lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb + languageName: node + linkType: hard + +"minimatch@npm:^5.1.0": + version: 5.1.9 + resolution: "minimatch@npm:5.1.9" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/4202718683815a7288b13e470160a4f9560cf392adef4f453927505817e01ef6b3476ecde13cfcaed17e7326dd3b69ad44eb2daeb19a217c5500f9277893f1d6 + languageName: node + linkType: hard + +"minimatch@npm:^9.0.4": + version: 9.0.9 + resolution: "minimatch@npm:9.0.9" + dependencies: + brace-expansion: "npm:^2.0.2" + checksum: 10c0/0b6a58530dbb00361745aa6c8cffaba4c90f551afe7c734830bd95fd88ebf469dd7355a027824ea1d09e37181cfeb0a797fb17df60c15ac174303ac110eb7e86 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.1.2": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb + languageName: node + linkType: hard + +"normalize-path@npm:^3.0.0": + version: 3.0.0 + resolution: "normalize-path@npm:3.0.0" + checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b + languageName: node + linkType: hard + +"path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c + languageName: node + linkType: hard + +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + +"process-nextick-args@npm:~2.0.0": + version: 2.0.1 + resolution: "process-nextick-args@npm:2.0.1" + checksum: 10c0/bec089239487833d46b59d80327a1605e1c5287eaad770a291add7f45fda1bb5e28b38e0e061add0a1d0ee0984788ce74fa394d345eed1c420cacf392c554367 + languageName: node + linkType: hard + +"process@npm:^0.11.10": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: 10c0/40c3ce4b7e6d4b8c3355479df77aeed46f81b279818ccdc500124e6a5ab882c0cc81ff7ea16384873a95a74c4570b01b120f287abbdd4c877931460eca6084b3 + languageName: node + linkType: hard + +"readable-stream@npm:^2.0.5": + version: 2.3.8 + resolution: "readable-stream@npm:2.3.8" + dependencies: + core-util-is: "npm:~1.0.0" + inherits: "npm:~2.0.3" + isarray: "npm:~1.0.0" + process-nextick-args: "npm:~2.0.0" + safe-buffer: "npm:~5.1.1" + string_decoder: "npm:~1.1.1" + util-deprecate: "npm:~1.0.1" + checksum: 10c0/7efdb01f3853bc35ac62ea25493567bf588773213f5f4a79f9c365e1ad13bab845ac0dae7bc946270dc40c3929483228415e92a3fc600cc7e4548992f41ee3fa + languageName: node + linkType: hard + +"readable-stream@npm:^4.0.0": + version: 4.7.0 + resolution: "readable-stream@npm:4.7.0" + dependencies: + abort-controller: "npm:^3.0.0" + buffer: "npm:^6.0.3" + events: "npm:^3.3.0" + process: "npm:^0.11.10" + string_decoder: "npm:^1.3.0" + checksum: 10c0/fd86d068da21cfdb10f7a4479f2e47d9c0a9b0c862fc0c840a7e5360201580a55ac399c764b12a4f6fa291f8cee74d9c4b7562e0d53b3c4b2769f2c98155d957 + languageName: node + linkType: hard + +"readdir-glob@npm:^1.1.2": + version: 1.1.3 + resolution: "readdir-glob@npm:1.1.3" + dependencies: + minimatch: "npm:^5.1.0" + checksum: 10c0/a37e0716726650845d761f1041387acd93aa91b28dd5381950733f994b6c349ddc1e21e266ec7cc1f9b92e205a7a972232f9b89d5424d07361c2c3753d5dbace + languageName: node + linkType: hard + +"root-workspace-0b6124@workspace:.": + version: 0.0.0-use.local + resolution: "root-workspace-0b6124@workspace:." + dependencies: + archiver: "npm:^7.0.1" + esbuild: "npm:^0.25.0" + languageName: unknown + linkType: soft + +"safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": + version: 5.1.2 + resolution: "safe-buffer@npm:5.1.2" + checksum: 10c0/780ba6b5d99cc9a40f7b951d47152297d0e260f0df01472a1b99d4889679a4b94a13d644f7dbc4f022572f09ae9005fa2fbb93bbbd83643316f365a3e9a45b21 + languageName: node + linkType: hard + +"safe-buffer@npm:~5.2.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + +"streamx@npm:^2.12.5, streamx@npm:^2.15.0, streamx@npm:^2.21.0": + version: 2.23.0 + resolution: "streamx@npm:2.23.0" + dependencies: + events-universal: "npm:^1.0.0" + fast-fifo: "npm:^1.3.2" + text-decoder: "npm:^1.1.0" + checksum: 10c0/15708ce37818d588632fe1104e8febde573e33e8c0868bf583fce0703f3faf8d2a063c278e30df2270206811b69997f64eb78792099933a1fe757e786fbcbd44 + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + +"string_decoder@npm:^1.3.0": + version: 1.3.0 + resolution: "string_decoder@npm:1.3.0" + dependencies: + safe-buffer: "npm:~5.2.0" + checksum: 10c0/810614ddb030e271cd591935dcd5956b2410dd079d64ff92a1844d6b7588bf992b3e1b69b0f4d34a3e06e0bd73046ac646b5264c1987b20d0601f81ef35d731d + languageName: node + linkType: hard + +"string_decoder@npm:~1.1.1": + version: 1.1.1 + resolution: "string_decoder@npm:1.1.1" + dependencies: + safe-buffer: "npm:~5.1.0" + checksum: 10c0/b4f89f3a92fd101b5653ca3c99550e07bdf9e13b35037e9e2a1c7b47cec4e55e06ff3fc468e314a0b5e80bfbaf65c1ca5a84978764884ae9413bec1fc6ca924e + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1": + version: 7.2.0 + resolution: "strip-ansi@npm:7.2.0" + dependencies: + ansi-regex: "npm:^6.2.2" + checksum: 10c0/544d13b7582f8254811ea97db202f519e189e59d35740c46095897e254e4f1aa9fe1524a83ad6bc5ad67d4dd6c0281d2e0219ed62b880a6238a16a17d375f221 + languageName: node + linkType: hard + +"tar-stream@npm:^3.0.0": + version: 3.1.8 + resolution: "tar-stream@npm:3.1.8" + dependencies: + b4a: "npm:^1.6.4" + bare-fs: "npm:^4.5.5" + fast-fifo: "npm:^1.2.0" + streamx: "npm:^2.15.0" + checksum: 10c0/c4bf369de2302fcf30218d091167a5372ee79b69a1b5bb493ddb7714193ca805719558966334bab1f2775c8142826865f24e25459ff1c5f0a096bc3a3d5c5ce2 + languageName: node + linkType: hard + +"teex@npm:^1.0.1": + version: 1.0.1 + resolution: "teex@npm:1.0.1" + dependencies: + streamx: "npm:^2.12.5" + checksum: 10c0/8df9166c037ba694b49d32a49858e314c60e513d55ac5e084dbf1ddbb827c5fa43cc389a81e87684419c21283308e9d68bb068798189c767ec4c252f890b8a77 + languageName: node + linkType: hard + +"text-decoder@npm:^1.1.0": + version: 1.2.7 + resolution: "text-decoder@npm:1.2.7" + dependencies: + b4a: "npm:^1.6.4" + checksum: 10c0/929938ed154fbadb660a7f3d1aca30b7e53649a731af7583168fcfba0c158046325d35d945926e2a512bb62d1a49a7818151c987ea38b48853f01e1615722fc5 + languageName: node + linkType: hard + +"util-deprecate@npm:~1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + +"zip-stream@npm:^6.0.1": + version: 6.0.1 + resolution: "zip-stream@npm:6.0.1" + dependencies: + archiver-utils: "npm:^5.0.0" + compress-commons: "npm:^6.0.2" + readable-stream: "npm:^4.0.0" + checksum: 10c0/50f2fb30327fb9d09879abf7ae2493705313adf403e794b030151aaae00009162419d60d0519e807673ec04d442e140c8879ca14314df0a0192de3b233e8f28b + languageName: node + linkType: hard diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/yarn-install/index.mjs b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/yarn-install/index.mjs new file mode 100644 index 0000000000..50d71b2d65 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/constants/yarn-install/index.mjs @@ -0,0 +1,133 @@ +import { randomBytes } from 'crypto'; +import { createWriteStream, promises as fs } from 'fs'; +import { join, resolve } from 'path'; + +import archiver from 'archiver'; +import { pipeline } from 'stream/promises'; + +const YARN_INSTALL_TIMEOUT_MS = 240_000; +const YARN_ENGINE_DIR = resolve('yarn-engine'); +const YARN_ENGINE_PATH = join(YARN_ENGINE_DIR, '.yarn/releases/yarn-4.9.2.cjs'); + +const writePackageFiles = async (nodejsDir, packageJson, yarnLock) => { + await fs.mkdir(nodejsDir, { recursive: true }); + await Promise.all([ + fs.writeFile(join(nodejsDir, 'package.json'), packageJson, 'utf-8'), + fs.writeFile(join(nodejsDir, 'yarn.lock'), yarnLock, 'utf-8'), + ]); +}; + +const copyYarnEngine = async (nodejsDir) => { + await fs.cp('yarn-engine', nodejsDir, { recursive: true }); +}; + +const runYarnInstall = async (nodejsDir) => { + const { execFile } = await import('child_process'); + const { promisify } = await import('util'); + const execFilePromise = promisify(execFile); + + const { NODE_OPTIONS: _nodeOptions, ...cleanEnv } = process.env; + + // Lambda runs as a sandboxed user whose $HOME doesn't exist. + // Yarn needs a writable HOME for its global cache/config. + cleanEnv.HOME = '/tmp'; + + try { + await execFilePromise( + process.execPath, + [YARN_ENGINE_PATH, 'workspaces', 'focus', '--all', '--production'], + { + cwd: nodejsDir, + env: cleanEnv, + timeout: YARN_INSTALL_TIMEOUT_MS, + }, + ); + } catch (error) { + const details = [error?.stdout, error?.stderr].filter(Boolean).join('\n'); + + throw new Error(`yarn install failed: ${details || error?.message}`); + } + + // Remove everything except node_modules + const entries = await fs.readdir(nodejsDir); + + await Promise.all( + entries + .filter((entry) => entry !== 'node_modules') + .map(async (entry) => { + const fullPath = join(nodejsDir, entry); + const stat = await fs.stat(fullPath); + + return stat.isDirectory() + ? fs.rm(fullPath, { recursive: true, force: true }) + : fs.rm(fullPath); + }), + ); +}; + +const createZip = async (buildDir, zipPath) => { + const output = createWriteStream(zipPath); + const archive = archiver('zip', { zlib: { level: 9 } }); + + const p = pipeline(archive, output); + + archive.directory(buildDir, false); + archive.finalize(); + + return p; +}; + +const uploadToPresignedUrl = async (zipPath, presignedUploadUrl) => { + const zipBuffer = await fs.readFile(zipPath); + + const response = await fetch(presignedUploadUrl, { + method: 'PUT', + body: zipBuffer, + headers: { 'Content-Type': 'application/zip' }, + }); + + if (!response.ok) { + throw new Error( + `Failed to upload zip to S3: ${response.status} ${response.statusText}`, + ); + } +}; + +export const handler = async (event) => { + const { action, packageJson, yarnLock, presignedUploadUrl } = event; + + if (action !== 'createLayer') { + throw new Error(`Unknown action: ${action}`); + } + + if (!packageJson || !yarnLock) { + throw new Error('Missing required fields: packageJson, yarnLock'); + } + + if (!presignedUploadUrl) { + throw new Error('Missing required field: presignedUploadUrl'); + } + + const randomId = randomBytes(16).toString('hex'); + const buildDir = `/tmp/${randomId}`; + const nodejsDir = join(buildDir, 'nodejs'); + const zipPath = `/tmp/${randomId}.zip`; + + try { + await writePackageFiles(nodejsDir, packageJson, yarnLock); + await copyYarnEngine(nodejsDir); + await runYarnInstall(nodejsDir); + await createZip(buildDir, zipPath); + + await uploadToPresignedUrl(zipPath, presignedUploadUrl); + + return { success: true }; + } finally { + await fs.rm(buildDir, { recursive: true, force: true }); + await fs.rm(zipPath, { force: true }); + await fs.rm(join(YARN_ENGINE_DIR, '.yarn/cache'), { + recursive: true, + force: true, + }); + } +}; diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/disabled.driver.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/disabled.driver.ts index 586a7b46ea..c83f2beca7 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/disabled.driver.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/disabled.driver.ts @@ -1,6 +1,7 @@ import { type LogicFunctionDriver, type LogicFunctionExecuteResult, + type LogicFunctionTranspileResult, } from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface'; import { @@ -19,4 +20,11 @@ export class DisabledDriver implements LogicFunctionDriver { LogicFunctionExceptionCode.LOGIC_FUNCTION_DISABLED, ); } + + async transpile(): Promise { + throw new LogicFunctionException( + 'Logic function transpilation is disabled. Set LOGIC_FUNCTION_TYPE to LOCAL or LAMBDA to enable.', + LogicFunctionExceptionCode.LOGIC_FUNCTION_DISABLED, + ); + } } diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/lambda.driver.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/lambda.driver.ts index 47dd3c97eb..af370522e3 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/lambda.driver.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/lambda.driver.ts @@ -1,5 +1,6 @@ +import { createHash } from 'crypto'; import * as fs from 'fs/promises'; -import { join } from 'path'; +import { resolve, join } from 'path'; import { CreateFunctionCommand, @@ -14,10 +15,11 @@ import { type ListLayerVersionsCommandInput, LogType, PublishLayerVersionCommand, - type PublishLayerVersionCommandInput, ResourceNotFoundException, - waitUntilFunctionUpdatedV2, + waitUntilFunctionActiveV2, } from '@aws-sdk/client-lambda'; +import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; +import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; import { AssumeRoleCommand, STSClient } from '@aws-sdk/client-sts'; import { isDefined } from 'twenty-shared/utils'; @@ -25,10 +27,17 @@ import { type LogicFunctionExecuteParams, type LogicFunctionExecuteResult, type LogicFunctionDriver, + type LogicFunctionTranspileParams, + type LogicFunctionTranspileResult, } from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface'; +import { ASSET_PATH } from 'src/constants/assets-path'; import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; +import { COMMON_LAYER_DEPENDENCIES_DIRNAME } from 'src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies-dirname'; +import { copyBuilder } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-builder'; +import { copyCommonLayerDependencies } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-common-layer-dependencies'; import { copyExecutor } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-executor'; +import { copyYarnInstall } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-yarn-install'; import { createZipFile } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/create-zip-file'; import { TemporaryDirManager } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/temporary-dir-manager'; import { LogicFunctionExecutionStatus } from 'src/engine/metadata-modules/logic-function/dtos/logic-function-execution-result.dto'; @@ -38,12 +47,32 @@ import { LogicFunctionExceptionCode, } from 'src/engine/metadata-modules/logic-function/logic-function.exception'; import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type'; -import { copyYarnEngineAndBuildDependencies } from 'src/engine/core-modules/application/application-package/utils/copy-yarn-engine-and-build-dependencies'; import { type LogicFunctionResourceService } from 'src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service'; import { callWithTimeout } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/call-with-timeout'; const UPDATE_FUNCTION_DURATION_TIMEOUT_IN_SECONDS = 60; const CREDENTIALS_DURATION_IN_SECONDS = 60 * 60; // 1h +const YARN_INSTALL_LAMBDA_TIMEOUT_SECONDS = 300; +const YARN_INSTALL_LAMBDA_MEMORY_MB = 1024; +const COMMON_LAYER_NAME_PREFIX = 'twenty-common-layer'; +const BUILDER_LAMBDA_TIMEOUT_SECONDS = 60; +const BUILDER_LAMBDA_MEMORY_MB = 512; + +const YARN_INSTALL_HANDLER_PATH = resolve( + __dirname, + join( + ASSET_PATH, + 'engine/core-modules/logic-function/logic-function-drivers/constants/yarn-install/index.mjs', + ), +); + +const BUILDER_HANDLER_PATH = resolve( + __dirname, + join( + ASSET_PATH, + 'engine/core-modules/logic-function/logic-function-drivers/constants/builder/index.mjs', + ), +); type LambdaDriverExecutorPayload = { code: string; @@ -52,11 +81,35 @@ type LambdaDriverExecutorPayload = { handlerName: string; }; +export type YarnInstallLambdaPayload = { + action: 'createLayer'; + packageJson: string; + yarnLock: string; + presignedUploadUrl: string; +}; + +export type YarnInstallLambdaResult = { + success: boolean; +}; + +export type BuilderLambdaPayload = { + action: 'transpile'; + sourceCode: string; + sourceFileName: string; + builtFileName: string; +}; + +export type BuilderLambdaResult = { + builtCode: string; +}; + export interface LambdaDriverOptions extends LambdaClientConfig { logicFunctionResourceService: LogicFunctionResourceService; region: string; lambdaRole: string; subhostingRole?: string; + layerBucket: string; + layerBucketRegion: string; } export class LambdaDriver implements LogicFunctionDriver { @@ -120,17 +173,33 @@ export class LambdaDriver implements LogicFunctionDriver { }; } - private async waitFunctionUpdates( - flatLogicFunction: FlatLogicFunction, + private async generatePresignedUploadUrl( + s3Key: string, + expiresIn: number = 300, + ): Promise { + const s3Client = new S3Client({ + region: this.options.layerBucketRegion, + credentials: isDefined(this.options.subhostingRole) + ? await this.getAssumeRoleCredentials() + : this.options.credentials, + }); + + const putCommand = new PutObjectCommand({ + Bucket: this.options.layerBucket, + Key: s3Key, + ContentType: 'application/zip', + }); + + return getSignedUrl(s3Client, putCommand, { expiresIn }); + } + + private async waitFunctionActive( + functionName: string, maxWaitTime: number = UPDATE_FUNCTION_DURATION_TIMEOUT_IN_SECONDS, ) { - const waitParams = { - FunctionName: flatLogicFunction.id, - }; - - await waitUntilFunctionUpdatedV2( + await waitUntilFunctionActiveV2( { client: await this.getLambdaClient(), maxWaitTime }, - waitParams, + { FunctionName: functionName }, ); } @@ -138,7 +207,398 @@ export class LambdaDriver implements LogicFunctionDriver { return flatApplication.yarnLockChecksum ?? 'default'; } - private async createLayerIfNotExists({ + private yarnInstallFunctionName: string | undefined; + private builderFunctionName: string | undefined; + private commonLayerName: string | undefined; + + private async getCommonLayerName(): Promise { + if (isDefined(this.commonLayerName)) { + return this.commonLayerName; + } + + const [packageJson, yarnLock] = await Promise.all([ + fs.readFile( + join(COMMON_LAYER_DEPENDENCIES_DIRNAME, 'package.json'), + 'utf-8', + ), + fs.readFile( + join(COMMON_LAYER_DEPENDENCIES_DIRNAME, 'yarn.lock'), + 'utf-8', + ), + ]); + + const checksum = createHash('sha256') + .update(packageJson) + .update(yarnLock) + .digest('hex') + .slice(0, 12); + + this.commonLayerName = `${COMMON_LAYER_NAME_PREFIX}-${checksum}`; + + return this.commonLayerName; + } + + private async getYarnInstallFunctionName(): Promise { + if (isDefined(this.yarnInstallFunctionName)) { + return this.yarnInstallFunctionName; + } + + const handlerContent = await fs.readFile( + YARN_INSTALL_HANDLER_PATH, + 'utf-8', + ); + const checksum = createHash('sha256') + .update(handlerContent) + .digest('hex') + .slice(0, 12); + + this.yarnInstallFunctionName = `twenty-yarn-install-${checksum}`; + + return this.yarnInstallFunctionName; + } + + private async getBuilderFunctionName(): Promise { + if (isDefined(this.builderFunctionName)) { + return this.builderFunctionName; + } + + const handlerContent = await fs.readFile(BUILDER_HANDLER_PATH, 'utf-8'); + const checksum = createHash('sha256') + .update(handlerContent) + .digest('hex') + .slice(0, 12); + + this.builderFunctionName = `twenty-builder-${checksum}`; + + return this.builderFunctionName; + } + + private async ensureCommonLayerExists(): Promise { + const commonLayerName = await this.getCommonLayerName(); + const existingArn = await this.getExistingLayerArn(commonLayerName); + + if (isDefined(existingArn)) { + return existingArn; + } + + const temporaryDirManager = new TemporaryDirManager(); + const { sourceTemporaryDir, lambdaZipPath } = + await temporaryDirManager.init(); + + try { + await copyCommonLayerDependencies(sourceTemporaryDir); + + await createZipFile(sourceTemporaryDir, lambdaZipPath); + + const lambdaClient = await this.getLambdaClient(); + + const result = await lambdaClient.send( + new PublishLayerVersionCommand({ + LayerName: commonLayerName, + Content: { ZipFile: await fs.readFile(lambdaZipPath) }, + CompatibleRuntimes: [ + LogicFunctionRuntime.NODE18, + LogicFunctionRuntime.NODE22, + ], + }), + ); + + if (!result.LayerVersionArn) { + throw new Error( + 'PublishLayerVersion did not return a LayerVersionArn for common layer', + ); + } + + return result.LayerVersionArn; + } finally { + await temporaryDirManager.clean(); + } + } + + private async ensureYarnInstallLambdaExists(): Promise { + const yarnInstallFunctionName = await this.getYarnInstallFunctionName(); + const lambdaClient = await this.getLambdaClient(); + + try { + await lambdaClient.send( + new GetFunctionCommand({ FunctionName: yarnInstallFunctionName }), + ); + + return; + } catch (error) { + if (!(error instanceof ResourceNotFoundException)) { + throw error; + } + } + + const commonLayerArn = await this.ensureCommonLayerExists(); + + const temporaryDirManager = new TemporaryDirManager(); + const { sourceTemporaryDir, lambdaZipPath } = + await temporaryDirManager.init(); + + try { + await copyYarnInstall(sourceTemporaryDir); + + await createZipFile(sourceTemporaryDir, lambdaZipPath); + + const params: CreateFunctionCommandInput = { + Code: { + ZipFile: await fs.readFile(lambdaZipPath), + }, + FunctionName: yarnInstallFunctionName, + Layers: [commonLayerArn], + Handler: 'index.handler', + Role: this.options.lambdaRole, + Runtime: LogicFunctionRuntime.NODE22, + Timeout: YARN_INSTALL_LAMBDA_TIMEOUT_SECONDS, + MemorySize: YARN_INSTALL_LAMBDA_MEMORY_MB, + }; + + await lambdaClient.send(new CreateFunctionCommand(params)); + } finally { + await temporaryDirManager.clean(); + } + + await this.waitFunctionActive(yarnInstallFunctionName); + } + + private async invokeYarnInstallLambda( + payload: YarnInstallLambdaPayload, + ): Promise { + const lambdaClient = await this.getLambdaClient(); + + const yarnInstallFunctionName = await this.getYarnInstallFunctionName(); + + const result = await callWithTimeout({ + callback: () => + lambdaClient.send( + new InvokeCommand({ + FunctionName: yarnInstallFunctionName, + Payload: JSON.stringify(payload), + LogType: LogType.Tail, + }), + ), + timeoutMs: YARN_INSTALL_LAMBDA_TIMEOUT_SECONDS * 1000, + }); + + if (result.FunctionError) { + const parsedResult = result.Payload + ? JSON.parse(result.Payload.transformToString()) + : {}; + + throw new LogicFunctionException( + `Yarn install Lambda failed: ${JSON.stringify(parsedResult)}`, + LogicFunctionExceptionCode.LOGIC_FUNCTION_CREATE_FAILED, + ); + } + + const parsedResult: YarnInstallLambdaResult = result.Payload + ? JSON.parse(result.Payload.transformToString()) + : {}; + + if (!parsedResult.success) { + throw new Error('Yarn install Lambda did not report success'); + } + + return parsedResult; + } + + private async ensureBuilderLambdaExists(): Promise { + const builderFunctionName = await this.getBuilderFunctionName(); + const lambdaClient = await this.getLambdaClient(); + + try { + await lambdaClient.send( + new GetFunctionCommand({ FunctionName: builderFunctionName }), + ); + + return; + } catch (error) { + if (!(error instanceof ResourceNotFoundException)) { + throw error; + } + } + + const commonLayerArn = await this.ensureCommonLayerExists(); + + const temporaryDirManager = new TemporaryDirManager(); + const { sourceTemporaryDir, lambdaZipPath } = + await temporaryDirManager.init(); + + try { + await copyBuilder(sourceTemporaryDir); + + await createZipFile(sourceTemporaryDir, lambdaZipPath); + + const params: CreateFunctionCommandInput = { + Code: { + ZipFile: await fs.readFile(lambdaZipPath), + }, + FunctionName: builderFunctionName, + Layers: [commonLayerArn], + Handler: 'index.handler', + Role: this.options.lambdaRole, + Runtime: LogicFunctionRuntime.NODE22, + Timeout: BUILDER_LAMBDA_TIMEOUT_SECONDS, + MemorySize: BUILDER_LAMBDA_MEMORY_MB, + }; + + await lambdaClient.send(new CreateFunctionCommand(params)); + } finally { + await temporaryDirManager.clean(); + } + + await this.waitFunctionActive(builderFunctionName); + } + + async transpile({ + sourceCode, + sourceFileName, + builtFileName, + }: LogicFunctionTranspileParams): Promise { + await this.ensureBuilderLambdaExists(); + + const lambdaClient = await this.getLambdaClient(); + const builderFunctionName = await this.getBuilderFunctionName(); + + const payload: BuilderLambdaPayload = { + action: 'transpile', + sourceCode, + sourceFileName, + builtFileName, + }; + + const result = await callWithTimeout({ + callback: () => + lambdaClient.send( + new InvokeCommand({ + FunctionName: builderFunctionName, + Payload: JSON.stringify(payload), + LogType: LogType.Tail, + }), + ), + timeoutMs: BUILDER_LAMBDA_TIMEOUT_SECONDS * 1000, + }); + + if (result.FunctionError) { + const parsedResult = result.Payload + ? JSON.parse(result.Payload.transformToString()) + : {}; + + throw new LogicFunctionException( + `Builder Lambda failed: ${JSON.stringify(parsedResult)}`, + LogicFunctionExceptionCode.LOGIC_FUNCTION_CREATE_FAILED, + ); + } + + const parsedResult: BuilderLambdaResult = result.Payload + ? JSON.parse(result.Payload.transformToString()) + : {}; + + if (!parsedResult.builtCode) { + throw new Error('Builder Lambda did not return builtCode'); + } + + return { builtCode: parsedResult.builtCode }; + } + + private async getExistingLayerArn( + layerName: string, + ): Promise { + const listLayerParams: ListLayerVersionsCommandInput = { + LayerName: layerName, + MaxItems: 1, + }; + + const listLayerResult = await ( + await this.getLambdaClient() + ).send(new ListLayerVersionsCommand(listLayerParams)); + + return listLayerResult.LayerVersions?.[0]?.LayerVersionArn; + } + + private async getDependencyContents( + flatApplication: FlatApplication, + applicationUniversalIdentifier: string, + ): Promise<{ packageJson: string; yarnLock: string }> { + const temporaryDirManager = new TemporaryDirManager(); + const { sourceTemporaryDir } = await temporaryDirManager.init(); + + try { + await this.logicFunctionResourceService.copyDependenciesInMemory({ + applicationUniversalIdentifier, + workspaceId: flatApplication.workspaceId, + inMemoryFolderPath: sourceTemporaryDir, + }); + + const [packageJson, yarnLock] = await Promise.all([ + fs.readFile(`${sourceTemporaryDir}/package.json`, 'utf-8'), + fs.readFile(`${sourceTemporaryDir}/yarn.lock`, 'utf-8'), + ]); + + return { packageJson, yarnLock }; + } finally { + await temporaryDirManager.clean(); + } + } + + private async createLayerIfNotExist({ + flatApplication, + applicationUniversalIdentifier, + }: { + flatApplication: FlatApplication; + applicationUniversalIdentifier: string; + }): Promise { + const layerName = this.getLayerName(flatApplication); + + const existingArn = await this.getExistingLayerArn(layerName); + + if (isDefined(existingArn)) { + return; + } + + const { packageJson, yarnLock } = await this.getDependencyContents( + flatApplication, + applicationUniversalIdentifier, + ); + + await this.ensureYarnInstallLambdaExists(); + + const s3Key = `lambda-layers/${layerName}.zip`; + + const presignedUploadUrl = await this.generatePresignedUploadUrl(s3Key); + + await this.invokeYarnInstallLambda({ + action: 'createLayer', + packageJson, + yarnLock, + presignedUploadUrl, + }); + + const bucket = this.options.layerBucket; + + const lambdaClient = await this.getLambdaClient(); + + const publishResult = await lambdaClient.send( + new PublishLayerVersionCommand({ + LayerName: layerName, + Content: { S3Bucket: bucket, S3Key: s3Key }, + CompatibleRuntimes: [ + LogicFunctionRuntime.NODE18, + LogicFunctionRuntime.NODE22, + ], + }), + ); + + if (!publishResult.LayerVersionArn) { + throw new Error( + `PublishLayerVersion did not return a LayerVersionArn for layer '${layerName}'`, + ); + } + } + + private async getLayerArn({ flatApplication, applicationUniversalIdentifier, }: { @@ -147,58 +607,26 @@ export class LambdaDriver implements LogicFunctionDriver { }): Promise { const layerName = this.getLayerName(flatApplication); - const listLayerParams: ListLayerVersionsCommandInput = { - LayerName: layerName, - MaxItems: 1, - }; + const existingArn = await this.getExistingLayerArn(layerName); - const listLayerCommand = new ListLayerVersionsCommand(listLayerParams); - - const listLayerResult = await ( - await this.getLambdaClient() - ).send(listLayerCommand); - - if (isDefined(listLayerResult.LayerVersions?.[0]?.LayerVersionArn)) { - return listLayerResult.LayerVersions[0].LayerVersionArn; + if (isDefined(existingArn)) { + return existingArn; } - const temporaryDirManager = new TemporaryDirManager(); - const { sourceTemporaryDir, lambdaZipPath } = - await temporaryDirManager.init(); - - const nodeDependenciesFolder = join(sourceTemporaryDir, 'nodejs'); - - await this.logicFunctionResourceService.copyDependenciesInMemory({ + await this.createLayerIfNotExist({ + flatApplication, applicationUniversalIdentifier, - workspaceId: flatApplication.workspaceId, - inMemoryFolderPath: nodeDependenciesFolder, }); - await copyYarnEngineAndBuildDependencies(nodeDependenciesFolder); - await createZipFile(sourceTemporaryDir, lambdaZipPath); + const newArn = await this.getExistingLayerArn(layerName); - const params: PublishLayerVersionCommandInput = { - LayerName: layerName, - Content: { - ZipFile: await fs.readFile(lambdaZipPath), - }, - CompatibleRuntimes: [ - LogicFunctionRuntime.NODE18, - LogicFunctionRuntime.NODE22, - ], - }; - - const command = new PublishLayerVersionCommand(params); - - const result = await (await this.getLambdaClient()).send(command); - - await temporaryDirManager.clean(); - - if (!isDefined(result.LayerVersionArn)) { - throw new Error('new layer version arn if undefined'); + if (!isDefined(newArn)) { + throw new Error( + `Layer '${layerName}' was not created by the yarn install Lambda`, + ); } - return result.LayerVersionArn; + return newArn; } private async getLambdaExecutor(flatLogicFunction: FlatLogicFunction) { @@ -269,7 +697,7 @@ export class LambdaDriver implements LogicFunctionDriver { return; } - const layerArn = await this.createLayerIfNotExists({ + const layerArn = await this.getLayerArn({ flatApplication, applicationUniversalIdentifier, }); @@ -331,7 +759,7 @@ export class LambdaDriver implements LogicFunctionDriver { applicationUniversalIdentifier, }); - await this.waitFunctionUpdates(flatLogicFunction); + await this.waitFunctionActive(flatLogicFunction.id); const startTime = Date.now(); diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/local.driver.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/local.driver.ts index 1a526d74d8..6521564dac 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/local.driver.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/drivers/local.driver.ts @@ -1,11 +1,16 @@ import { promises as fs } from 'fs'; import { spawn } from 'node:child_process'; -import { join } from 'path'; +import { dirname, join } from 'path'; + +import { build } from 'esbuild'; +import { NODE_ESM_CJS_BANNER } from 'twenty-shared/application'; import { type LogicFunctionExecuteParams, type LogicFunctionExecuteResult, type LogicFunctionDriver, + type LogicFunctionTranspileParams, + type LogicFunctionTranspileResult, } from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface'; import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; @@ -34,7 +39,7 @@ export class LocalDriver implements LogicFunctionDriver { return join(LOGIC_FUNCTION_EXECUTOR_TMPDIR_FOLDER, checksum); }; - private async createLayerIfNotExists({ + private async createLayerIfNotExist({ flatApplication, applicationUniversalIdentifier, }: { @@ -56,16 +61,52 @@ export class LocalDriver implements LogicFunctionDriver { } } + async transpile({ + sourceCode, + sourceFileName, + builtFileName, + }: LogicFunctionTranspileParams): Promise { + const temporaryDirManager = new TemporaryDirManager(); + const { sourceTemporaryDir } = await temporaryDirManager.init(); + + try { + const entryFilePath = join(sourceTemporaryDir, sourceFileName); + const builtBundleFilePath = join(sourceTemporaryDir, builtFileName); + + await fs.mkdir(dirname(entryFilePath), { recursive: true }); + await fs.writeFile(entryFilePath, sourceCode, 'utf-8'); + await fs.mkdir(dirname(builtBundleFilePath), { recursive: true }); + + await build({ + entryPoints: [entryFilePath], + outfile: builtBundleFilePath, + platform: 'node', + format: 'esm', + target: 'es2017', + bundle: true, + sourcemap: true, + packages: 'external', + banner: NODE_ESM_CJS_BANNER, + }); + + const builtCode = await fs.readFile(builtBundleFilePath, 'utf-8'); + + return { builtCode }; + } finally { + await temporaryDirManager.clean(); + } + } + async delete() {} - private async build({ + async build({ flatApplication, applicationUniversalIdentifier, }: { flatApplication: FlatApplication; applicationUniversalIdentifier: string; }) { - await this.createLayerIfNotExists({ + await this.createLayerIfNotExist({ flatApplication, applicationUniversalIdentifier, }); @@ -79,7 +120,7 @@ export class LocalDriver implements LogicFunctionDriver { env, timeoutMs = 900_000, }: LogicFunctionExecuteParams): Promise { - await this.build({ + await this.createLayerIfNotExist({ flatApplication, applicationUniversalIdentifier, }); diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/factories/logic-function-module.factory.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/factories/logic-function-module.factory.ts index ceb098eaf1..f7f72819d2 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/factories/logic-function-module.factory.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/factories/logic-function-module.factory.ts @@ -41,6 +41,17 @@ export const logicFunctionModuleFactory = async ( 'LOGIC_FUNCTION_LAMBDA_SUBHOSTING_ROLE', ); + const s3BucketName = twentyConfigService.get('STORAGE_S3_NAME'); + + const layerBucket = + twentyConfigService.get('LOGIC_FUNCTION_LAMBDA_LAYER_BUCKET') ?? + s3BucketName ?? + 'twenty-lambda-layer'; + + const layerBucketRegion = + twentyConfigService.get('LOGIC_FUNCTION_LAMBDA_LAYER_BUCKET_REGION') ?? + region; + return { type: LogicFunctionDriverType.LAMBDA, options: { @@ -56,6 +67,8 @@ export const logicFunctionModuleFactory = async ( region, lambdaRole, subhostingRole, + layerBucket, + layerBucketRegion, }, }; } diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface.ts index 72666b6d7c..d6d89147fa 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface.ts @@ -29,11 +29,24 @@ export type LogicFunctionExecuteParams = { timeoutMs?: number; }; +export type LogicFunctionTranspileParams = { + sourceCode: string; + sourceFileName: string; + builtFileName: string; +}; + +export type LogicFunctionTranspileResult = { + builtCode: string; +}; + export interface LogicFunctionDriver { delete(flatLogicFunction: FlatLogicFunction): Promise; execute( params: LogicFunctionExecuteParams, ): Promise; + transpile( + params: LogicFunctionTranspileParams, + ): Promise; } export enum LogicFunctionDriverType { diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-builder.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-builder.ts new file mode 100644 index 0000000000..131a60d852 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-builder.ts @@ -0,0 +1,17 @@ +import { promises as fs } from 'fs'; +import { resolve, join } from 'path'; + +import { ASSET_PATH } from 'src/constants/assets-path'; + +const BUILDER_FILE_PATH = resolve( + __dirname, + join( + ASSET_PATH, + 'engine/core-modules/logic-function/logic-function-drivers/constants/builder', + ), +); + +export const copyBuilder = async (buildDirectory: string) => { + await fs.mkdir(buildDirectory, { recursive: true }); + await fs.cp(BUILDER_FILE_PATH, buildDirectory, { recursive: true }); +}; diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-common-layer-dependencies.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-common-layer-dependencies.ts new file mode 100644 index 0000000000..869d05a0d0 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-common-layer-dependencies.ts @@ -0,0 +1,22 @@ +import { promises as fs } from 'fs'; +import { join } from 'path'; + +import { COMMON_LAYER_DEPENDENCIES_DIRNAME } from 'src/engine/core-modules/logic-function/logic-function-drivers/constants/common-layer-dependencies-dirname'; +import { copyYarnEngineAndBuildDependencies } from 'src/engine/core-modules/application/application-package/utils/copy-yarn-engine-and-build-dependencies'; + +// Builds a Lambda layer with the common dependencies (e.g. archiver) +// by copying the package.json + yarn.lock, then running yarn install locally. +// The result follows the Lambda layer structure: nodejs/node_modules/... +export const copyCommonLayerDependencies = async ( + buildDirectory: string, +): Promise => { + const nodejsDir = join(buildDirectory, 'nodejs'); + + await fs.mkdir(nodejsDir, { recursive: true }); + + await fs.cp(COMMON_LAYER_DEPENDENCIES_DIRNAME, nodejsDir, { + recursive: true, + }); + + await copyYarnEngineAndBuildDependencies(nodejsDir); +}; diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-yarn-install.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-yarn-install.ts new file mode 100644 index 0000000000..b5fefb35b5 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-yarn-install.ts @@ -0,0 +1,23 @@ +import { promises as fs } from 'fs'; +import { resolve, join } from 'path'; + +import { ASSET_PATH } from 'src/constants/assets-path'; +import { YARN_ENGINE_DIRNAME } from 'src/engine/core-modules/application/application-package/constants/yarn-engine-dirname'; + +const YARN_INSTALL_FILE_PATH = resolve( + __dirname, + join( + ASSET_PATH, + 'engine/core-modules/logic-function/logic-function-drivers/constants/yarn-install', + ), +); + +export const copyYarnInstall = async (buildDirectory: string) => { + await fs.mkdir(buildDirectory, { recursive: true }); + + await fs.cp(YARN_INSTALL_FILE_PATH, buildDirectory, { recursive: true }); + + const yarnEngineDestination = join(buildDirectory, 'yarn-engine'); + + await fs.cp(YARN_ENGINE_DIRNAME, yarnEngineDestination, { recursive: true }); +}; diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts index bf66b510fc..b367629110 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts @@ -10,6 +10,8 @@ import { isDefined } from 'twenty-shared/utils'; import { LogicFunctionDriver, type LogicFunctionExecuteResult, + type LogicFunctionTranspileParams, + type LogicFunctionTranspileResult, } from 'src/engine/core-modules/logic-function/logic-function-drivers/interfaces/logic-function-driver.interface'; import { FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; @@ -101,6 +103,12 @@ export class LogicFunctionExecutorService { return resultLogicFunction; } + async transpile( + params: LogicFunctionTranspileParams, + ): Promise { + return this.driver.transpile(params); + } + private async throttleExecution(workspaceId: string) { try { await this.throttlerService.tokenBucketThrottleOrThrow( diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service.ts index e8e45356e2..1b32e70e98 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-resource/logic-function-resource.service.ts @@ -1,18 +1,14 @@ import { Injectable } from '@nestjs/common'; import crypto from 'crypto'; -import fs from 'fs/promises'; -import { dirname, join } from 'path'; +import { join } from 'path'; -import { build } from 'esbuild'; import { FileFolder } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { NODE_ESM_CJS_BANNER } from 'twenty-shared/application'; import { FileStorageExceptionCode } from 'src/engine/core-modules/file-storage/interfaces/file-storage-exception'; import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service'; -import { TemporaryDirManager } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/temporary-dir-manager'; import { getLogicFunctionSeedProjectFiles, LogicFunctionSeedProjectFile, @@ -45,11 +41,6 @@ type UpdateSourceFilesParams = Omit< sourceHandlerCode: string; }; -type BuildFromSourceParams = Identifier & { - sourceHandlerPath: string; - builtHandlerPath: string; -}; - type GetSourceCodeParams = Identifier & { sourceHandlerPath: string; }; @@ -148,52 +139,27 @@ export class LogicFunctionResourceService { }); } - async buildFromSourceFile({ - sourceHandlerPath, - builtHandlerPath, + async uploadBuiltFile({ workspaceId, applicationUniversalIdentifier, - }: BuildFromSourceParams): Promise<{ checksum: string }> { - const temporaryDirManager = new TemporaryDirManager(); - - try { - const { sourceTemporaryDir } = await temporaryDirManager.init(); - - await this.fileStorageService.downloadFile({ - workspaceId, - applicationUniversalIdentifier, - fileFolder: FileFolder.Source, - resourcePath: sourceHandlerPath, - localPath: join(sourceTemporaryDir, sourceHandlerPath), - }); - - const builtBundleFilePath = await this.buildInMemory({ - sourceTemporaryDir, - sourceHandlerPath, - builtHandlerPath, - }); - - const builtFile = await fs.readFile(builtBundleFilePath, 'utf-8'); - - await this.fileStorageService.writeFile({ - workspaceId, - applicationUniversalIdentifier, - fileFolder: FileFolder.BuiltLogicFunction, - resourcePath: builtHandlerPath, - sourceFile: builtFile, - mimeType: 'application/javascript', - settings: { - isTemporaryFile: false, - toDelete: false, - }, - }); - - return { - checksum: crypto.createHash('md5').update(builtFile).digest('hex'), - }; - } finally { - await temporaryDirManager.clean(); - } + builtHandlerPath, + builtCode, + }: Identifier & { + builtHandlerPath: string; + builtCode: string; + }): Promise { + await this.fileStorageService.writeFile({ + workspaceId, + applicationUniversalIdentifier, + fileFolder: FileFolder.BuiltLogicFunction, + resourcePath: builtHandlerPath, + sourceFile: builtCode, + mimeType: 'application/javascript', + settings: { + isTemporaryFile: false, + toDelete: false, + }, + }); } async getSourceFile({ @@ -328,33 +294,4 @@ export class LogicFunctionResourceService { return localPath; } - - private async buildInMemory({ - sourceTemporaryDir, - sourceHandlerPath, - builtHandlerPath, - }: { - sourceTemporaryDir: string; - sourceHandlerPath: string; - builtHandlerPath: string; - }): Promise { - const entryFilePath = join(sourceTemporaryDir, sourceHandlerPath); - const builtBundleFilePath = join(sourceTemporaryDir, builtHandlerPath); - - await fs.mkdir(dirname(builtBundleFilePath), { recursive: true }); - - await build({ - entryPoints: [entryFilePath], - outfile: builtBundleFilePath, - platform: 'node', - format: 'esm', - target: 'es2017', - bundle: true, - sourcemap: true, - packages: 'external', - banner: NODE_ESM_CJS_BANNER, - }); - - return builtBundleFilePath; - } } diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts index 205348ff76..9f64e1f806 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts @@ -563,6 +563,30 @@ export class ConfigVariables { @IsOptional() LOGIC_FUNCTION_LAMBDA_SECRET_ACCESS_KEY: string; + @ConfigVariablesMetadata({ + group: ConfigVariablesGroup.LOGIC_FUNCTION_CONFIG, + description: 'S3 bucket for uploading Lambda layer zip files', + type: ConfigVariableType.STRING, + }) + @ValidateIf( + (env) => env.LOGIC_FUNCTION_TYPE === LogicFunctionDriverType.LAMBDA, + ) + @IsOptional() + LOGIC_FUNCTION_LAMBDA_LAYER_BUCKET?: string; + + @ConfigVariablesMetadata({ + group: ConfigVariablesGroup.LOGIC_FUNCTION_CONFIG, + description: + 'AWS region of the S3 bucket for Lambda layer uploads (defaults to LOGIC_FUNCTION_LAMBDA_REGION)', + type: ConfigVariableType.STRING, + }) + @ValidateIf( + (env) => env.LOGIC_FUNCTION_TYPE === LogicFunctionDriverType.LAMBDA, + ) + @IsOptional() + @IsAWSRegion() + LOGIC_FUNCTION_LAMBDA_LAYER_BUCKET_REGION?: AwsRegion; + @ConfigVariablesMetadata({ group: ConfigVariablesGroup.CODE_INTERPRETER_CONFIG, description: diff --git a/packages/twenty-server/src/engine/metadata-modules/logic-function/services/logic-function-from-source.service.ts b/packages/twenty-server/src/engine/metadata-modules/logic-function/services/logic-function-from-source.service.ts index 3fd3bbcaec..7181696b2b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/logic-function/services/logic-function-from-source.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/logic-function/services/logic-function-from-source.service.ts @@ -1,5 +1,7 @@ import { Injectable } from '@nestjs/common'; +import crypto from 'crypto'; + import { v4 } from 'uuid'; import { isDefined } from 'twenty-shared/utils'; import { SEED_LOGIC_FUNCTION_INPUT_SCHEMA } from 'twenty-shared/logic-function'; @@ -302,14 +304,33 @@ export class LogicFunctionFromSourceService { workspaceId, }); - const { checksum } = - await this.logicFunctionResourceService.buildFromSourceFile({ - workspaceId, - applicationUniversalIdentifier: - ownerFlatApplication.universalIdentifier, - sourceHandlerPath: flatLogicFunction.sourceHandlerPath, - builtHandlerPath: flatLogicFunction.builtHandlerPath, - }); + const sourceCode = await this.logicFunctionResourceService.getSourceFile({ + workspaceId, + applicationUniversalIdentifier: ownerFlatApplication.universalIdentifier, + sourceHandlerPath: flatLogicFunction.sourceHandlerPath, + }); + + if (!sourceCode) { + throw new LogicFunctionException( + 'Source file not found', + LogicFunctionExceptionCode.LOGIC_FUNCTION_NOT_FOUND, + ); + } + + const { builtCode } = await this.logicFunctionExecutorService.transpile({ + sourceCode, + sourceFileName: flatLogicFunction.sourceHandlerPath, + builtFileName: flatLogicFunction.builtHandlerPath, + }); + + await this.logicFunctionResourceService.uploadBuiltFile({ + workspaceId, + applicationUniversalIdentifier: ownerFlatApplication.universalIdentifier, + builtHandlerPath: flatLogicFunction.builtHandlerPath, + builtCode, + }); + + const checksum = crypto.createHash('md5').update(builtCode).digest('hex'); await this.helperService.updateOneFromMetadata({ flatLogicFunctionToUpdate: { diff --git a/yarn.lock b/yarn.lock index 0d0dcb61c0..5aa78085f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1662,6 +1662,22 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/s3-request-presigner@npm:3.1001.0": + version: 3.1001.0 + resolution: "@aws-sdk/s3-request-presigner@npm:3.1001.0" + dependencies: + "@aws-sdk/signature-v4-multi-region": "npm:^3.996.4" + "@aws-sdk/types": "npm:^3.973.4" + "@aws-sdk/util-format-url": "npm:^3.972.6" + "@smithy/middleware-endpoint": "npm:^4.4.21" + "@smithy/protocol-http": "npm:^5.3.10" + "@smithy/smithy-client": "npm:^4.12.1" + "@smithy/types": "npm:^4.13.0" + tslib: "npm:^2.6.2" + checksum: 10c0/51cbe9bcef780a391bf5e0a084490ff40bba317d6741ed14b3b5d5f6c761fc161e71de7241527e02f8b56e0b6ac4bce9045a43aeed8a79cb0418d4f78f2134dd + languageName: node + linkType: hard + "@aws-sdk/signature-v4-multi-region@npm:^3.996.4": version: 3.996.4 resolution: "@aws-sdk/signature-v4-multi-region@npm:3.996.4" @@ -1711,6 +1727,16 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/types@npm:^3.973.5": + version: 3.973.5 + resolution: "@aws-sdk/types@npm:3.973.5" + dependencies: + "@smithy/types": "npm:^4.13.0" + tslib: "npm:^2.6.2" + checksum: 10c0/9d21676eb696afc71e915b96f87ae8c67f53a52b3cf3b20f6c35e05654c5a36b67f917310ecd1dd6b696be2019e184dc5bd278318ee1f0051e51f80b292ed578 + languageName: node + linkType: hard + "@aws-sdk/util-arn-parser@npm:^3.972.2": version: 3.972.2 resolution: "@aws-sdk/util-arn-parser@npm:3.972.2" @@ -1733,6 +1759,18 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/util-format-url@npm:^3.972.6": + version: 3.972.7 + resolution: "@aws-sdk/util-format-url@npm:3.972.7" + dependencies: + "@aws-sdk/types": "npm:^3.973.5" + "@smithy/querystring-builder": "npm:^4.2.11" + "@smithy/types": "npm:^4.13.0" + tslib: "npm:^2.6.2" + checksum: 10c0/eb3511476c49c1044184f78f0ee9a3d057fb4f91711700d9e2b53ad715d59e97a2782314fb25eec0d8310c53349a4471edf6fbf3be42c6db5a7a30bb682f3e63 + languageName: node + linkType: hard + "@aws-sdk/util-locate-window@npm:^3.0.0": version: 3.568.0 resolution: "@aws-sdk/util-locate-window@npm:3.568.0" @@ -21097,6 +21135,17 @@ __metadata: languageName: node linkType: hard +"@smithy/querystring-builder@npm:^4.2.11": + version: 4.2.11 + resolution: "@smithy/querystring-builder@npm:4.2.11" + dependencies: + "@smithy/types": "npm:^4.13.0" + "@smithy/util-uri-escape": "npm:^4.2.2" + tslib: "npm:^2.6.2" + checksum: 10c0/88790ee8c7a01730beba913fdf38d1790113c82c956d20cccd43a5b527f19e23df7eff86ddf41e3e615d0af562e31f61a7fed014c9fce8db924623258a4d14f8 + languageName: node + linkType: hard + "@smithy/querystring-parser@npm:^4.2.10": version: 4.2.10 resolution: "@smithy/querystring-parser@npm:4.2.10" @@ -21365,6 +21414,15 @@ __metadata: languageName: node linkType: hard +"@smithy/util-uri-escape@npm:^4.2.2": + version: 4.2.2 + resolution: "@smithy/util-uri-escape@npm:4.2.2" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/33b6546086c975278d16b5029e6555df551b4bd1e3a84042544d1ef956a287fe033b317954b1737b2773e82b6f27ebde542956ff79ef0e8a813dc0dbf9d34a58 + languageName: node + linkType: hard + "@smithy/util-utf8@npm:^2.0.0": version: 2.3.0 resolution: "@smithy/util-utf8@npm:2.3.0" @@ -55935,6 +55993,7 @@ __metadata: "@aws-sdk/client-sesv2": "npm:3.1001.0" "@aws-sdk/client-sts": "npm:3.1001.0" "@aws-sdk/credential-providers": "npm:3.1001.0" + "@aws-sdk/s3-request-presigner": "npm:3.1001.0" "@azure/msal-node": "npm:^3.8.4" "@babel/preset-env": "npm:7.26.9" "@blocknote/server-util": "npm:^0.47.1"