diff --git a/servers/cu/package-lock.json b/servers/cu/package-lock.json index 8f90460ab..15ff6add4 100644 --- a/servers/cu/package-lock.json +++ b/servers/cu/package-lock.json @@ -8,6 +8,7 @@ "name": "@permaweb/ao-cu", "version": "1.0.0", "dependencies": { + "@aws-sdk/client-s3": "^3.740.0", "@fastify/middie": "^9.0.3", "@permaweb/ao-loader": "^0.0.44", "@permaweb/ao-scheduler-utils": "^0.0.25", @@ -46,217 +47,1699 @@ "node": ">=18" } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dependencies": { + "@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.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.740.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.740.0.tgz", + "integrity": "sha512-X9aQOFJC3TsYwQP3AGcNhfYcFehVEHRKCHtHYOIKv5t1ydSJxpN/v34OrMMKvG1jFWMNkSYiSCVB9ZVo9KUwVA==", + "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.734.0", + "@aws-sdk/credential-provider-node": "3.738.0", + "@aws-sdk/middleware-bucket-endpoint": "3.734.0", + "@aws-sdk/middleware-expect-continue": "3.734.0", + "@aws-sdk/middleware-flexible-checksums": "3.735.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-location-constraint": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-sdk-s3": "3.740.0", + "@aws-sdk/middleware-ssec": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.734.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/signature-v4-multi-region": "3.740.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.734.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.734.0", + "@aws-sdk/xml-builder": "3.734.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/eventstream-serde-browser": "^4.0.1", + "@smithy/eventstream-serde-config-resolver": "^4.0.1", + "@smithy/eventstream-serde-node": "^4.0.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-blob-browser": "^4.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/hash-stream-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/md5-js": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.2", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@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.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-stream": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.734.0.tgz", + "integrity": "sha512-oerepp0mut9VlgTwnG5Ds/lb0C0b2/rQ+hL/rF6q+HGKPfGsCuPvFx1GtwGKCXd49ase88/jVgrhcA9OQbz3kg==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.734.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.734.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.734.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.734.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.2", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@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.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.734.0.tgz", + "integrity": "sha512-SxnDqf3vobdm50OLyAKfqZetv6zzwnSqwIwd3jrbopxxHKqNIM/I0xcYjD6Tn+mPig+u7iRKb9q3QnEooFTlmg==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.734.0.tgz", + "integrity": "sha512-gtRkzYTGafnm1FPpiNO8VBmJrYMoxhDlGPYDVcijzx3DlF8dhWnowuSBCxLSi+MJMx5hvwrX2A+e/q0QAeHqmw==", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.734.0.tgz", + "integrity": "sha512-JFSL6xhONsq+hKM8xroIPhM5/FOhiQ1cov0lZxhzZWj6Ai3UAjucy3zyIFDr9MgP1KfCYNdvyaUq9/o+HWvEDg==", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.734.0.tgz", + "integrity": "sha512-HEyaM/hWI7dNmb4NhdlcDLcgJvrilk8G4DQX6qz0i4pBZGC2l4iffuqP8K6ZQjUfz5/6894PzeFuhTORAMd+cg==", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/credential-provider-env": "3.734.0", + "@aws-sdk/credential-provider-http": "3.734.0", + "@aws-sdk/credential-provider-process": "3.734.0", + "@aws-sdk/credential-provider-sso": "3.734.0", + "@aws-sdk/credential-provider-web-identity": "3.734.0", + "@aws-sdk/nested-clients": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.738.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.738.0.tgz", + "integrity": "sha512-3MuREsazwBxghKb2sQQHvie+uuK4dX4/ckFYiSoffzJQd0YHxaGxf8cr4NOSCQCUesWu8D3Y0SzlnHGboVSkpA==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.734.0", + "@aws-sdk/credential-provider-http": "3.734.0", + "@aws-sdk/credential-provider-ini": "3.734.0", + "@aws-sdk/credential-provider-process": "3.734.0", + "@aws-sdk/credential-provider-sso": "3.734.0", + "@aws-sdk/credential-provider-web-identity": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.734.0.tgz", + "integrity": "sha512-zvjsUo+bkYn2vjT+EtLWu3eD6me+uun+Hws1IyWej/fKFAqiBPwyeyCgU7qjkiPQSXqk1U9+/HG9IQ6Iiz+eBw==", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.734.0.tgz", + "integrity": "sha512-cCwwcgUBJOsV/ddyh1OGb4gKYWEaTeTsqaAK19hiNINfYV/DO9r4RMlnWAo84sSBfJuj9shUNsxzyoe6K7R92Q==", + "dependencies": { + "@aws-sdk/client-sso": "3.734.0", + "@aws-sdk/core": "3.734.0", + "@aws-sdk/token-providers": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.734.0.tgz", + "integrity": "sha512-t4OSOerc+ppK541/Iyn1AS40+2vT/qE+MFMotFkhCgCJbApeRF2ozEdnDN6tGmnl4ybcUuxnp9JWLjwDVlR/4g==", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/nested-clients": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.734.0.tgz", + "integrity": "sha512-etC7G18aF7KdZguW27GE/wpbrNmYLVT755EsFc8kXpZj8D6AFKxc7OuveinJmiy0bYXAMspJUWsF6CrGpOw6CQ==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-arn-parser": "3.723.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.734.0.tgz", + "integrity": "sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.735.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.735.0.tgz", + "integrity": "sha512-Tx7lYTPwQFRe/wQEHMR6Drh/S+X0ToAEq1Ava9QyxV1riwtepzRLojpNDELFb3YQVVYbX7FEiBMCJLMkmIIY+A==", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", + "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.734.0.tgz", + "integrity": "sha512-EJEIXwCQhto/cBfHdm3ZOeLxd2NlJD+X2F+ZTOxzokuhBtY0IONfC/91hOo5tWQweerojwshSMHRCKzRv1tlwg==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz", + "integrity": "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz", + "integrity": "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.740.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.740.0.tgz", + "integrity": "sha512-VML9TzNoQdAs5lSPQSEgZiPgMUSz2H7SltaLb9g4tHwKK5xQoTq5WcDd6V1d2aPxSN5Q2Q63aiVUBby6MdUN/Q==", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-arn-parser": "3.723.0", + "@smithy/core": "^3.1.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.734.0.tgz", + "integrity": "sha512-d4yd1RrPW/sspEXizq2NSOUivnheac6LPeLSLnaeTbBG9g1KqIqvCzP1TfXEqv2CrWfHEsWtJpX7oyjySSPvDQ==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.734.0.tgz", + "integrity": "sha512-MFVzLWRkfFz02GqGPjqSOteLe5kPfElUrXZft1eElnqulqs6RJfVSpOV7mO90gu293tNAeggMWAVSGRPKIYVMg==", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.734.0", + "@smithy/core": "^3.1.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.734.0.tgz", + "integrity": "sha512-iph2XUy8UzIfdJFWo1r0Zng9uWj3253yvW9gljhtu+y/LNmNvSnJxQk1f3D2BC5WmcoPZqTS3UsycT3mLPSzWA==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.734.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.734.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.734.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.734.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.2", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@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.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz", + "integrity": "sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.740.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.740.0.tgz", + "integrity": "sha512-w+psidN3i+kl51nQEV3V+fKjKUqcEbqUA1GtubruDBvBqrl5El/fU2NF3Lo53y8CfI9wCdf3V7KOEpHIqxHNng==", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.740.0", + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.734.0.tgz", + "integrity": "sha512-2U6yWKrjWjZO8Y5SHQxkFvMVWHQWbS0ufqfAIBROqmIZNubOL7jXCiVdEFekz6MZ9LF2tvYGnOW4jX8OKDGfIw==", + "dependencies": { + "@aws-sdk/nested-clients": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.734.0.tgz", + "integrity": "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.723.0.tgz", + "integrity": "sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.734.0.tgz", + "integrity": "sha512-w2+/E88NUbqql6uCVAsmMxDQKu7vsKV0KqhlQb0lL+RCq4zy07yXYptVNs13qrnuTfyX7uPXkXrlugvK9R1Ucg==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.723.0.tgz", + "integrity": "sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.734.0.tgz", + "integrity": "sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.734.0.tgz", + "integrity": "sha512-c6Iinh+RVQKs6jYUFQ64htOU2HUXFQ3TVx+8Tu3EDF19+9vzWi9UukhIMH9rqyyEXIAkk9XL7avt8y2Uyw2dGA==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.734.0.tgz", + "integrity": "sha512-Zrjxi5qwGEcUsJ0ru7fRtW74WcTS0rbLcehoFB+rN1GRi2hbLcFaYs4PwVA5diLeAJH0gszv3x4Hr/S87MfbKQ==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "license": "MIT", "engines": { - "node": ">=0.1.90" + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@fastify/ajv-compiler": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.1.tgz", + "integrity": "sha512-DxrBdgsjNLP0YM6W5Hd6/Fmj43S8zMKiFJYgi+Ri3htTGAowPVG/tG1wpnWLMjufEnehRivUCKZ1pLDIoZdTuw==", + "license": "MIT", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@fastify/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.0.0.tgz", + "integrity": "sha512-OO/SA8As24JtT1usTUTKgGH7uLvhfwZPwlptRi2Dp5P4KKmJI3gvsZ8MIHnNwDs4sLf/aai5LzTyl66xr7qMxA==", + "license": "MIT" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.1.tgz", + "integrity": "sha512-f2d3JExJgFE3UbdFcpPwqNUEoHWmt8pAKf8f+9YuLESdefA0WgqxeT6DrGL4Yrf/9ihXNSKOqpjEmurV405meA==", + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^6.0.0" + } + }, + "node_modules/@fastify/forwarded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz", + "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==", + "license": "MIT" + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@fastify/middie": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@fastify/middie/-/middie-9.0.3.tgz", + "integrity": "sha512-7OYovKXp9UKYeVMcjcFLMcSpoMkmcZmfnG+eAvtdiatN35W7c+r9y1dRfpA+pfFVNuHGGqI3W+vDTmjvcfLcMA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/error": "^4.0.0", + "fastify-plugin": "^5.0.0", + "path-to-regexp": "^8.1.0", + "reusify": "^1.0.4" + } + }, + "node_modules/@fastify/proxy-addr": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz", + "integrity": "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==", + "license": "MIT", + "dependencies": { + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@permaweb/ao-loader": { + "version": "0.0.44", + "resolved": "https://registry.npmjs.org/@permaweb/ao-loader/-/ao-loader-0.0.44.tgz", + "integrity": "sha512-O/5XuwqxCD9dTIN/jZ6x4rmqIA/Css0bqaXScOrXc0xTz7VjYseM+PNXFf8vAXiOgnNFmrZzDJ0or94cjmqhZA==", + "dependencies": { + "@permaweb/wasm-metering": "^0.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@permaweb/ao-scheduler-utils": { + "version": "0.0.25", + "resolved": "https://registry.npmjs.org/@permaweb/ao-scheduler-utils/-/ao-scheduler-utils-0.0.25.tgz", + "integrity": "sha512-b0UYSTgnLMIYLScrfNBgcqK7ZMmd78L3J0Jz4RIsIq2P5PtkdRqQ7fYqLlltg7bD1f3dvl4TkO1925ED4ei7LA==", + "dependencies": { + "lru-cache": "^10.2.2", + "ramda": "^0.30.0", + "zod": "^3.23.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@permaweb/ao-scheduler-utils/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/@permaweb/wasm-json-toolkit": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@permaweb/wasm-json-toolkit/-/wasm-json-toolkit-0.2.9.tgz", + "integrity": "sha512-CGCeUwS+UeqUdvORiyG0LykkQXLTwS5TWc590CUkDfOYyBUSPv8pse0sJStvTC9LKAzuNx3ELBvmqHCI4muUAA==", + "license": "MPL-2.0", + "dependencies": { + "buffer-pipe": "0.0.3", + "leb128": "0.0.4", + "safe-buffer": "^5.1.2" + }, + "bin": { + "json2wasm": "bin/json2wasm", + "wasm2json": "bin/wasm2json" + } + }, + "node_modules/@permaweb/wasm-metering": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@permaweb/wasm-metering/-/wasm-metering-0.2.2.tgz", + "integrity": "sha512-xM2MbPkHc4rzhTR6VH5eXtfC+liaYSuNCa0kPRaqSZO2gr1SirJWnzUBDa5VOfTBTgIlIVv5RW+Mkbt/VuK+oA==", + "license": "MPL-2.0", + "dependencies": { + "@permaweb/wasm-json-toolkit": "^0.2.9", + "leb128": "^0.0.4" + }, + "bin": { + "wasm-meter": "bin/wasm-meter" + } + }, + "node_modules/@permaweb/weavedrive": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@permaweb/weavedrive/-/weavedrive-0.0.18.tgz", + "integrity": "sha512-wPux02vYjuxPOqKJZdhLfqZQ18JFExzUCxMStZK9603YOWGKIiXu8f+U5CEz7QtWlYRhZApgul+2MTEh7X/ANA==", + "license": "MIT", + "dependencies": { + "arweave": "^1.15.5" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", + "integrity": "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", + "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", + "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", + "dependencies": { + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz", + "integrity": "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.2.tgz", + "integrity": "sha512-htwQXkbdF13uwwDevz9BEzL5ABK+1sJpVQXywwGSH973AVOvisHNfpcB8A8761G6XgHoS2kHPqc9DqHJ2gp+/Q==", + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz", + "integrity": "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.1.tgz", + "integrity": "sha512-Q2bCAAR6zXNVtJgifsU16ZjKGqdw/DyecKNgIgi7dlqw04fqDu0mnq+JmGphqheypVc64CYq3azSuCpAdFk2+A==", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.1.tgz", + "integrity": "sha512-HbIybmz5rhNg+zxKiyVAnvdM3vkzjE6ccrJ620iPL8IXcJEntd3hnBl+ktMwIy12Te/kyrSbUb8UCdnUT4QEdA==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.0.1.tgz", + "integrity": "sha512-lSipaiq3rmHguHa3QFF4YcCM3VJOrY9oq2sow3qlhFY+nBSTF/nrO82MUQRPrxHQXA58J5G1UnU2WuJfi465BA==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.1.tgz", + "integrity": "sha512-o4CoOI6oYGYJ4zXo34U8X9szDe3oGjmHgsMGiZM0j4vtNoT+h80TLnkUcrLZR3+E6HIxqW+G+9WHAVfl0GXK0Q==", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.1.tgz", + "integrity": "sha512-Z94uZp0tGJuxds3iEAZBqGU2QiaBHP4YytLUjwZWx+oUeohCsLyUm33yp4MMBmhkuPqSbQCXq5hDet6JGUgHWA==", + "dependencies": { + "@smithy/eventstream-codec": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz", + "integrity": "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.1.tgz", + "integrity": "sha512-rkFIrQOKZGS6i1D3gKJ8skJ0RlXqDvb1IyAphksaFOMzkn3v3I1eJ8m7OkLj0jf1McP63rcCEoLlkAn/HjcTRw==", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.0.0", + "@smithy/chunked-blob-reader-native": "^4.0.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.1.tgz", + "integrity": "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.1.tgz", + "integrity": "sha512-U1rAE1fxmReCIr6D2o/4ROqAQX+GffZpyMt3d7njtGDr2pUNmAKRWa49gsNVhCh2vVAuf3wXzWwNr2YN8PAXIw==", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz", + "integrity": "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.1.tgz", + "integrity": "sha512-HLZ647L27APi6zXkZlzSFZIjpo8po45YiyjMGJZM3gyDY8n7dPGdmxIIljLm4gPt/7rRvutLTTkYJpZVfG5r+A==", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz", + "integrity": "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.3.tgz", + "integrity": "sha512-YdbmWhQF5kIxZjWqPIgboVfi8i5XgiYMM7GGKFMTvBei4XjNQfNv8sukT50ITvgnWKKKpOtp0C0h7qixLgb77Q==", + "dependencies": { + "@smithy/core": "^3.1.2", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.4.tgz", + "integrity": "sha512-wmxyUBGHaYUqul0wZiset4M39SMtDBOtUr2KpDuftKNN74Do9Y36Go6Eqzj9tL0mIPpr31ulB5UUtxcsCeGXsQ==", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.2.tgz", + "integrity": "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz", + "integrity": "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz", + "integrity": "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.2.tgz", + "integrity": "sha512-X66H9aah9hisLLSnGuzRYba6vckuFtGE+a5DcHLliI/YlqKrGoxhisD5XbX44KyoeRzoNlGr94eTsMVHFAzPOw==", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz", + "integrity": "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz", + "integrity": "sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "license": "MIT", + "node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz", + "integrity": "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==", "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@fastify/ajv-compiler": { + "node_modules/@smithy/querystring-parser": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.1.tgz", - "integrity": "sha512-DxrBdgsjNLP0YM6W5Hd6/Fmj43S8zMKiFJYgi+Ri3htTGAowPVG/tG1wpnWLMjufEnehRivUCKZ1pLDIoZdTuw==", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz", + "integrity": "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==", "dependencies": { - "ajv": "^8.12.0", - "ajv-formats": "^3.0.1", - "fast-uri": "^3.0.0" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "license": "MIT", + "node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz", + "integrity": "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==", + "dependencies": { + "@smithy/types": "^4.1.0" + }, "engines": { - "node": ">=14" + "node": ">=18.0.0" } }, - "node_modules/@fastify/error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.0.0.tgz", - "integrity": "sha512-OO/SA8As24JtT1usTUTKgGH7uLvhfwZPwlptRi2Dp5P4KKmJI3gvsZ8MIHnNwDs4sLf/aai5LzTyl66xr7qMxA==", - "license": "MIT" + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz", + "integrity": "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@fastify/fast-json-stringify-compiler": { + "node_modules/@smithy/signature-v4": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.1.tgz", - "integrity": "sha512-f2d3JExJgFE3UbdFcpPwqNUEoHWmt8pAKf8f+9YuLESdefA0WgqxeT6DrGL4Yrf/9ihXNSKOqpjEmurV405meA==", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.1.tgz", + "integrity": "sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==", "dependencies": { - "fast-json-stringify": "^6.0.0" + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@fastify/forwarded": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.0.tgz", - "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==", - "license": "MIT" + "node_modules/@smithy/smithy-client": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.3.tgz", + "integrity": "sha512-A2Hz85pu8BJJaYFdX8yb1yocqigyqBzn+OVaVgm+Kwi/DkN8vhN2kbDVEfADo6jXf5hPKquMLGA3UINA64UZ7A==", + "dependencies": { + "@smithy/core": "^3.1.2", + "@smithy/middleware-endpoint": "^4.0.3", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@fastify/merge-json-schemas": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", - "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", - "license": "MIT", + "node_modules/@smithy/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz", + "integrity": "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==", "dependencies": { - "fast-deep-equal": "^3.1.3" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@fastify/middie": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@fastify/middie/-/middie-9.0.3.tgz", - "integrity": "sha512-7OYovKXp9UKYeVMcjcFLMcSpoMkmcZmfnG+eAvtdiatN35W7c+r9y1dRfpA+pfFVNuHGGqI3W+vDTmjvcfLcMA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", + "node_modules/@smithy/url-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz", + "integrity": "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==", "dependencies": { - "@fastify/error": "^4.0.0", - "fastify-plugin": "^5.0.0", - "path-to-regexp": "^8.1.0", - "reusify": "^1.0.4" + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@fastify/proxy-addr": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.0.0.tgz", - "integrity": "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==", - "license": "MIT", + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", "dependencies": { - "@fastify/forwarded": "^3.0.0", - "ipaddr.js": "^2.1.0" + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=8.0.0" + "node": ">=18.0.0" } }, - "node_modules/@permaweb/ao-loader": { - "version": "0.0.44", - "resolved": "https://registry.npmjs.org/@permaweb/ao-loader/-/ao-loader-0.0.44.tgz", - "integrity": "sha512-O/5XuwqxCD9dTIN/jZ6x4rmqIA/Css0bqaXScOrXc0xTz7VjYseM+PNXFf8vAXiOgnNFmrZzDJ0or94cjmqhZA==", + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.4.tgz", + "integrity": "sha512-Ej1bV5sbrIfH++KnWxjjzFNq9nyP3RIUq2c9Iqq7SmMO/idUR24sqvKH2LUQFTSPy/K7G4sB2m8n7YYlEAfZaw==", "dependencies": { - "@permaweb/wasm-metering": "^0.2.2" + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@permaweb/ao-scheduler-utils": { - "version": "0.0.25", - "resolved": "https://registry.npmjs.org/@permaweb/ao-scheduler-utils/-/ao-scheduler-utils-0.0.25.tgz", - "integrity": "sha512-b0UYSTgnLMIYLScrfNBgcqK7ZMmd78L3J0Jz4RIsIq2P5PtkdRqQ7fYqLlltg7bD1f3dvl4TkO1925ED4ei7LA==", + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.4.tgz", + "integrity": "sha512-HE1I7gxa6yP7ZgXPCFfZSDmVmMtY7SHqzFF55gM/GPegzZKaQWZZ+nYn9C2Cc3JltCMyWe63VPR3tSFDEvuGjw==", "dependencies": { - "lru-cache": "^10.2.2", - "ramda": "^0.30.0", - "zod": "^3.23.5" + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.3", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@permaweb/ao-scheduler-utils/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" + "node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz", + "integrity": "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@permaweb/wasm-json-toolkit": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@permaweb/wasm-json-toolkit/-/wasm-json-toolkit-0.2.9.tgz", - "integrity": "sha512-CGCeUwS+UeqUdvORiyG0LykkQXLTwS5TWc590CUkDfOYyBUSPv8pse0sJStvTC9LKAzuNx3ELBvmqHCI4muUAA==", - "license": "MPL-2.0", + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", "dependencies": { - "buffer-pipe": "0.0.3", - "leb128": "0.0.4", - "safe-buffer": "^5.1.2" + "tslib": "^2.6.2" }, - "bin": { - "json2wasm": "bin/json2wasm", - "wasm2json": "bin/wasm2json" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@permaweb/wasm-metering": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@permaweb/wasm-metering/-/wasm-metering-0.2.2.tgz", - "integrity": "sha512-xM2MbPkHc4rzhTR6VH5eXtfC+liaYSuNCa0kPRaqSZO2gr1SirJWnzUBDa5VOfTBTgIlIVv5RW+Mkbt/VuK+oA==", - "license": "MPL-2.0", + "node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz", + "integrity": "sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==", "dependencies": { - "@permaweb/wasm-json-toolkit": "^0.2.9", - "leb128": "^0.0.4" + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" }, - "bin": { - "wasm-meter": "bin/wasm-meter" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@permaweb/weavedrive": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/@permaweb/weavedrive/-/weavedrive-0.0.18.tgz", - "integrity": "sha512-wPux02vYjuxPOqKJZdhLfqZQ18JFExzUCxMStZK9603YOWGKIiXu8f+U5CEz7QtWlYRhZApgul+2MTEh7X/ANA==", - "license": "MIT", + "node_modules/@smithy/util-retry": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz", + "integrity": "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==", "dependencies": { - "arweave": "^1.15.5" + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.2.tgz", + "integrity": "sha512-0eZ4G5fRzIoewtHtwaYyl8g2C+osYOT4KClXgfdNEDAgkbe2TYPqcnw4GAWabqkZCax2ihRGPe9LZnsPdIUIHA==", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/types": "^4.1.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.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.2.tgz", + "integrity": "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@types/triple-beam": { @@ -486,6 +1969,11 @@ "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", "license": "MIT" }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -828,6 +2316,27 @@ "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", "license": "BSD-3-Clause" }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastify": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.2.1.tgz", @@ -1996,6 +3505,11 @@ "node": ">=0.10.0" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2101,6 +3615,11 @@ "node": ">= 14.0.0" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/servers/cu/package.json b/servers/cu/package.json index 3b27b5d90..e4a37e1a0 100644 --- a/servers/cu/package.json +++ b/servers/cu/package.json @@ -11,6 +11,7 @@ "test": "node --experimental-wasm-memory64 --test" }, "dependencies": { + "@aws-sdk/client-s3": "^3.740.0", "@fastify/middie": "^9.0.3", "@permaweb/ao-loader": "^0.0.44", "@permaweb/ao-scheduler-utils": "^0.0.25", diff --git a/servers/cu/src/app.js b/servers/cu/src/app.js index 6cc1faf28..6d0ba4122 100644 --- a/servers/cu/src/app.js +++ b/servers/cu/src/app.js @@ -45,6 +45,9 @@ export const server = pipeP( logger('Checkpoint Interval Reached. Attempting to Checkpoint all Processes currently in WASM heap cache...') await domain.apis.checkpointWasmMemoryCache().toPromise() logger('Interval Checkpoint Done. Done checkpointing all processes in WASM heap cache.') + logger('Evaluations dump interval reached. Attempting to dump all evaluations...') + await domain.apis.dumpEvaluations().toPromise() + logger('Evaluations dump done.') }, config.PROCESS_MEMORY_CACHE_CHECKPOINT_INTERVAL) cacheCheckpointInterval.unref() } @@ -58,14 +61,18 @@ export const server = pipeP( logger('Received SIGTERM. Attempting to Checkpoint all Processes currently in WASM heap cache...') await domain.apis.checkpointWasmMemoryCache().toPromise() - logger('Done checkpointing all processes in WASM heap cache. Exiting...') + logger('Done checkpointing all processes in WASM heap cache. Attempting to dump all evaluations...') + await domain.apis.dumpEvaluations().toPromise() + logger('Done dumping all evaluations. Exiting...') process.exit() }) process.on('SIGUSR2', async () => { logger('Received SIGUSR2. Manually Attempting to Checkpoint all Processes currently in WASM heap cache...') await domain.apis.checkpointWasmMemoryCache().toPromise() - logger('SIGUSR2 Done. Done checkpointing all processes in WASM heap cache.') + logger('Done checkpointing all processes in WASM heap cache. Attempting to dump all evaluations...') + await domain.apis.dumpEvaluations().toPromise() + logger('SIGUSR2 Done. Done checkpointing all processes in WASM heap cache and dumping all evaluations.') }) process.on('uncaughtException', (err) => { diff --git a/servers/cu/src/bootstrap.js b/servers/cu/src/bootstrap.js index bd9b69547..d696ca473 100644 --- a/servers/cu/src/bootstrap.js +++ b/servers/cu/src/bootstrap.js @@ -88,7 +88,6 @@ export const createApis = async (ctx) => { const onCreateWorker = (type) => () => { const workerId = randomBytes(8).toString('hex') ctx.logger('Spinning up "%s" pool worker with id "%s"...', type, workerId) - return { workerThreadOpts: { workerData: { @@ -104,7 +103,13 @@ export const createApis = async (ctx) => { MODE: ctx.MODE, LOG_CONFIG_PATH: ctx.LOG_CONFIG_PATH, DEFAULT_LOG_LEVEL: ctx.DEFAULT_LOG_LEVEL, - DISABLE_PROCESS_EVALUATION_CACHE: ctx.DISABLE_PROCESS_EVALUATION_CACHE + DISABLE_PROCESS_EVALUATION_CACHE: ctx.DISABLE_PROCESS_EVALUATION_CACHE, + EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, + EVALUATION_RESULT_BUCKET: ctx.EVALUATION_RESULT_BUCKET, + AWS_ACCESS_KEY_ID: ctx.AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY: ctx.AWS_SECRET_ACCESS_KEY, + AWS_REGION: ctx.AWS_REGION, + __dirname } } } @@ -163,6 +168,14 @@ export const createApis = async (ctx) => { }) const dryRunWorkQueue = new PQueue({ concurrency: maxDryRunWorkerTheads }) + const loadEvaluationWorker = join(__dirname, 'effects', 'worker', 'loadEvaluation', 'index.js') + const loadEvaluationWorkerPool = workerpool.pool(loadEvaluationWorker, { + minWorkers: (ctx.EVALUATION_RESULT_BUCKET && ctx.EVALUATION_RESULT_DIR) ? 1 : 0, + maxWorkers: (ctx.EVALUATION_RESULT_BUCKET && ctx.EVALUATION_RESULT_DIR) ? 2 : 0, + onCreateWorker: onCreateWorker('loadEvaluation') + }) + const loadEvaluationWorkQueue = new PQueue({ concurrency: 2 }) + const arweave = ArweaveClient.createWalletClient() const address = ArweaveClient.addressWith({ WALLET: ctx.WALLET, arweave }) @@ -257,6 +270,14 @@ export const createApis = async (ctx) => { cache: WasmClient.createWasmModuleCache({ MAX_SIZE: ctx.WASM_MODULE_CACHE_MAX_SIZE }) }) + const loadEvaluation = (args) => loadEvaluationWorkQueue.add(() => + Promise.resolve() + .then(() => loadEvaluationWorkerPool.exec('loadEvaluation', [args])) + .catch((e) => { + throw new Error(`Error in loadEvaluation worker: ${e}`) + }) + ) + const stats = statsWith({ gauge, loadWorkerStats: () => ({ @@ -280,6 +301,10 @@ export const createApis = async (ctx) => { */ pendingTasks: dryRunWorkQueue.size }) + // hydrator: ({ + // ...hydratorWorkerPool.stats(), + // pendingTasks: hydratorWorkQueue.size + // }) }), /** * https://nodejs.org/api/process.html#processmemoryusage @@ -344,8 +369,30 @@ export const createApis = async (ctx) => { evaluationCounter, // gasCounter, saveProcess: AoProcessClient.saveProcessWith({ db, logger }), - findEvaluation: AoEvaluationClient.findEvaluationWith({ db, logger }), - saveEvaluation: AoEvaluationClient.saveEvaluationWith({ db, logger }), + findEvaluation: AoEvaluationClient.findEvaluationWith({ + db, + logger, + EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, + EVALUATION_RESULT_BUCKET: ctx.EVALUATION_RESULT_BUCKET, + loadEvaluation: (args) => { + return loadEvaluationWorkQueue.add(() => + Promise.resolve() + .then(() => { + return loadEvaluationWorkerPool.exec('loadEvaluation', [args]) + }) + .catch((e) => { + console.error('Error in loadEvaluation worker', e) + throw e + }) + ) + } + }), + saveEvaluation: AoEvaluationClient.saveEvaluationWith({ + db, + logger, + EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, + EVALUATION_RESULT_BUCKET: ctx.EVALUATION_RESULT_BUCKET + }), findBlocks: AoBlockClient.findBlocksWith({ db, logger }), saveBlocks: AoBlockClient.saveBlocksWith({ db, logger }), loadBlocksMeta: AoBlockClient.loadBlocksMetaWith({ fetch: ctx.fetch, GRAPHQL_URLS: BLOCK_GRAPHQL_ARRAY, pageSize: 90, logger }), @@ -467,13 +514,13 @@ export const createApis = async (ctx) => { const readCronResultsLogger = ctx.logger.child('readCronResults') const readCronResults = readCronResultsWith({ ...sharedDeps(readCronResultsLogger), - findEvaluations: AoEvaluationClient.findEvaluationsWith({ db, logger: readCronResultsLogger }) + findEvaluations: AoEvaluationClient.findEvaluationsWith({ db, logger: readCronResultsLogger, loadEvaluation, EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, EVALUATION_RESULT_BUCKET: ctx.EVALUATION_RESULT_BUCKET }) }) const readResultsLogger = ctx.logger.child('readResults') const readResults = readResultsWith({ ...sharedDeps(readResultsLogger), - findEvaluations: AoEvaluationClient.findEvaluationsWith({ db, logger: readResultsLogger }) + findEvaluations: AoEvaluationClient.findEvaluationsWith({ db, logger: readResultsLogger, loadEvaluation, EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, EVALUATION_RESULT_BUCKET: ctx.EVALUATION_RESULT_BUCKET }) }) let checkpointP @@ -529,5 +576,15 @@ export const createApis = async (ctx) => { const healthcheck = healthcheckWith({ walletAddress: address }) - return { metrics, stats, pendingReadStates, readState, dryRun, readResult, readResults, readCronResults, checkpointWasmMemoryCache, healthcheck } + const dumpEvaluations = fromPromise(async (args) => loadEvaluationWorkQueue.add(() => + Promise.resolve() + .then(() => loadEvaluationWorkerPool.exec('dumpEvaluations', [args])) + .catch((e) => { + ctx.logger('Error in loadEvaluation worker while dumping evaluations', e) + throw new Error(`Error in loadEvaluation worker: ${e}`) + }) + ) + ) + + return { metrics, stats, pendingReadStates, readState, dryRun, readResult, readResults, readCronResults, checkpointWasmMemoryCache, healthcheck, dumpEvaluations } } diff --git a/servers/cu/src/config.js b/servers/cu/src/config.js index 70f668554..14d39e43a 100644 --- a/servers/cu/src/config.js +++ b/servers/cu/src/config.js @@ -172,7 +172,12 @@ const CONFIG_ENVS = { BUSY_THRESHOLD: process.env.BUSY_THRESHOLD || 0, // disabled RESTRICT_PROCESSES: process.env.RESTRICT_PROCESSES || [], ALLOW_PROCESSES: process.env.ALLOW_PROCESSES || [], - ALLOW_OWNERS: process.env.ALLOW_OWNERS || [] + ALLOW_OWNERS: process.env.ALLOW_OWNERS || [], + EVALUATION_RESULT_DIR: process.env.EVALUATION_RESULT_DIR, + EVALUATION_RESULT_BUCKET: process.env.EVALUATION_RESULT_BUCKET, + AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY, + AWS_REGION: process.env.AWS_REGION }, production: { MODE, @@ -224,7 +229,12 @@ const CONFIG_ENVS = { BUSY_THRESHOLD: process.env.BUSY_THRESHOLD || 0, // disabled RESTRICT_PROCESSES: process.env.RESTRICT_PROCESSES || [], ALLOW_PROCESSES: process.env.ALLOW_PROCESSES || [], - ALLOW_OWNERS: process.env.ALLOW_OWNERS || [] + ALLOW_OWNERS: process.env.ALLOW_OWNERS || [], + EVALUATION_RESULT_DIR: process.env.EVALUATION_RESULT_DIR, + EVALUATION_RESULT_BUCKET: process.env.EVALUATION_RESULT_BUCKET, + AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY, + AWS_REGION: process.env.AWS_REGION } } diff --git a/servers/cu/src/domain/dal.js b/servers/cu/src/domain/dal.js index c5e026867..8a6e7e377 100644 --- a/servers/cu/src/domain/dal.js +++ b/servers/cu/src/domain/dal.js @@ -102,7 +102,8 @@ export const findEvaluationSchema = z.function() processId: z.string(), to: z.coerce.number().nullish(), ordinate: z.coerce.string().nullish(), - cron: z.string().nullish() + cron: z.string().nullish(), + messageId: z.string().nullish() })) .returns(z.promise(evaluationSchema)) diff --git a/servers/cu/src/domain/lib/chainEvaluation.js b/servers/cu/src/domain/lib/chainEvaluation.js index 803b439fe..84c542c3b 100644 --- a/servers/cu/src/domain/lib/chainEvaluation.js +++ b/servers/cu/src/domain/lib/chainEvaluation.js @@ -23,7 +23,8 @@ function loadLatestEvaluationWith ({ findEvaluation, findLatestProcessMemory, lo processId: ctx.id, to: ctx.to, ordinate: ctx.ordinate, - cron: ctx.cron + cron: ctx.cron, + messageId: ctx.messageId }) .map((evaluation) => { logger( diff --git a/servers/cu/src/domain/lib/gatherResults.js b/servers/cu/src/domain/lib/gatherResults.js index 91235851c..0cea8897c 100644 --- a/servers/cu/src/domain/lib/gatherResults.js +++ b/servers/cu/src/domain/lib/gatherResults.js @@ -64,7 +64,7 @@ export function gatherResultsWith (env) { return findEvaluations({ processId: ctx.processId, from: ctx.from, - to: ctx.to, + to: ctx.to ?? {}, sort, limit: ctx.limit, onlyCron: ctx.onlyCron diff --git a/servers/cu/src/domain/lib/loadProcess.js b/servers/cu/src/domain/lib/loadProcess.js index 637c3e5ef..a3560df15 100644 --- a/servers/cu/src/domain/lib/loadProcess.js +++ b/servers/cu/src/domain/lib/loadProcess.js @@ -77,7 +77,8 @@ function loadLatestEvaluationWith ({ findEvaluation, findLatestProcessMemory, sa processId: ctx.id, to: ctx.to, ordinate: ctx.ordinate, - cron: ctx.cron + cron: ctx.cron, + messageId: ctx.messageId }) .map((evaluation) => { logger( diff --git a/servers/cu/src/domain/model.js b/servers/cu/src/domain/model.js index c8d77addf..9e2f0fe3f 100644 --- a/servers/cu/src/domain/model.js +++ b/servers/cu/src/domain/model.js @@ -204,7 +204,27 @@ export const domainConfigSchema = z.object({ * A list of wallets whose processes the CU should exclusively allow * aka. whitelist of processes created by these wallets */ - ALLOW_OWNERS: commaDelimitedArraySchema + ALLOW_OWNERS: commaDelimitedArraySchema, + /** + * The directory to store evaluation results + */ + EVALUATION_RESULT_DIR: z.string().optional(), + /** + * The bucket to store evaluation results + */ + EVALUATION_RESULT_BUCKET: z.string().optional(), + /** + * The access key id for the bucket to store evaluation results + */ + AWS_ACCESS_KEY_ID: z.string().optional(), + /** + * The secret access key for the bucket to store evaluation results + */ + AWS_SECRET_ACCESS_KEY: z.string().optional(), + /** + * The region of the bucket to store evaluation results + */ + AWS_REGION: z.string().optional() }) export const bufferSchema = z.any().refine(buffer => { @@ -387,6 +407,16 @@ export const scheduleSchema = z.object({ message: z.any() }) +export const outputSchema = z.object({ + Memory: z.any().nullish(), + Messages: z.array(z.any()).nullish(), + Assignments: z.array(z.any()).nullish(), + Spawns: z.array(z.any()).nullish(), + Output: z.any().nullish(), + GasUsed: z.number().nullish(), + Error: z.any().nullish() +}) + export const evaluationSchema = z.object({ /** * the id of the process that the message was performed upon @@ -448,13 +478,5 @@ export const evaluationSchema = z.object({ * * This is the output of process, after the action was applied */ - output: z.object({ - Memory: z.any().nullish(), - Messages: z.array(z.any()).nullish(), - Assignments: z.array(z.any()).nullish(), - Spawns: z.array(z.any()).nullish(), - Output: z.any().nullish(), - GasUsed: z.number().nullish(), - Error: z.any().nullish() - }) + output: outputSchema }) diff --git a/servers/cu/src/effects/ao-evaluation.js b/servers/cu/src/effects/ao-evaluation.js index 177ecc9f3..b75833238 100644 --- a/servers/cu/src/effects/ao-evaluation.js +++ b/servers/cu/src/effects/ao-evaluation.js @@ -17,7 +17,7 @@ const evaluationDocSchema = z.object({ blockHeight: evaluationSchema.shape.blockHeight, cron: evaluationSchema.shape.cron, evaluatedAt: evaluationSchema.shape.evaluatedAt, - output: evaluationSchema.shape.output.omit({ Memory: true }) + output: evaluationSchema.shape.output.omit({ Memory: true }).nullish() // This can now be nullish as the DB may not have the output }) function createEvaluationId ({ processId, timestamp, ordinate, cron }) { @@ -60,7 +60,7 @@ const toEvaluation = applySpec({ const fromEvaluationDoc = pipe( evolve({ - output: JSON.parse + output: (old) => typeof old === 'string' ? JSON.parse(old) : old }), /** * Ensure the input matches the expected @@ -70,7 +70,17 @@ const fromEvaluationDoc = pipe( toEvaluation ) -export function findEvaluationWith ({ db }) { +export function findEvaluationFromDirOrS3With ({ loadEvaluation }) { + return ({ processId, messageId }) => { + return Promise.resolve({ processId, messageId }) + .then(loadEvaluation) + .catch((err) => { + throw new Error(`Error finding evaluation from dir or s3: ${err}`) + }) + } +} + +export function findEvaluationFromDbWith ({ db }) { function createQuery ({ processId, timestamp, ordinate, cron }) { return { sql: ` @@ -96,7 +106,29 @@ export function findEvaluationWith ({ db }) { } } -export function saveEvaluationWith ({ DISABLE_PROCESS_EVALUATION_CACHE, db, logger: _logger }) { +export function findEvaluationWith ({ db, loadEvaluation, EVALUATION_RESULT_DIR, EVALUATION_RESULT_BUCKET }) { + const findEvaluationFromDir = findEvaluationFromDirOrS3With({ loadEvaluation }) + const findEvaluationFromDb = fromPromise(findEvaluationFromDbWith({ db })) + return ({ processId, to, ordinate, cron, messageId }) => { + return of({ processId, to, ordinate, cron, messageId }) + .chain(findEvaluationFromDb) + .chain( + fromPromise(async (result) => { + if (EVALUATION_RESULT_DIR && EVALUATION_RESULT_BUCKET && !result.output) { + const evaluationOutput = await findEvaluationFromDir({ processId, messageId }) + if (evaluationOutput === 'AWS Credentials not set') { + return Rejected({ status: 404, message: 'Could not find evaluation: AWS Credentials not set' }).toPromise() + } + return { ...result, output: evaluationOutput } + } + return result + }) + ) + .toPromise() + } +} + +export function saveEvaluationWith ({ DISABLE_PROCESS_EVALUATION_CACHE, db, logger: _logger, saveEvaluationToDir, EVALUATION_RESULT_DIR, EVALUATION_RESULT_BUCKET }) { const toEvaluationDoc = pipe( converge( unapply(mergeAll), @@ -141,30 +173,54 @@ export function saveEvaluationWith ({ DISABLE_PROCESS_EVALUATION_CACHE, db, logg const statements = [] if (!DISABLE_PROCESS_EVALUATION_CACHE) { - statements.push({ - sql: ` - INSERT OR IGNORE INTO ${EVALUATIONS_TABLE} - (id, "processId", "messageId", "deepHash", nonce, epoch, timestamp, ordinate, "blockHeight", cron, "evaluatedAt", output) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); - `, - parameters: [ - // evaluations insert - evalDoc.id, - evalDoc.processId, - evalDoc.messageId, - evalDoc.deepHash, - evalDoc.nonce, - evalDoc.epoch, - evalDoc.timestamp, - evalDoc.ordinate, - evalDoc.blockHeight, - evalDoc.cron, - evalDoc.evaluatedAt.getTime(), - JSON.stringify(evalDoc.output) - ] - }) + // If we have a directory and bucket, we need to save the evaluation to the directory, not sqlite + if (EVALUATION_RESULT_DIR && EVALUATION_RESULT_BUCKET) { + statements.push({ + sql: ` + INSERT OR IGNORE INTO ${EVALUATIONS_TABLE} + (id, "processId", "messageId", "deepHash", nonce, epoch, timestamp, ordinate, "blockHeight", cron, "evaluatedAt") + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + `, + parameters: [ + // evaluations insert + evalDoc.id, + evalDoc.processId, + evalDoc.messageId, + evalDoc.deepHash, + evalDoc.nonce, + evalDoc.epoch, + evalDoc.timestamp, + evalDoc.ordinate, + evalDoc.blockHeight, + evalDoc.cron, + evalDoc.evaluatedAt.getTime() + ] + }) + } else { + statements.push({ + sql: ` + INSERT OR IGNORE INTO ${EVALUATIONS_TABLE} + (id, "processId", "messageId", "deepHash", nonce, epoch, timestamp, ordinate, "blockHeight", cron, "evaluatedAt", output) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + `, + parameters: [ + // evaluations insert + evalDoc.id, + evalDoc.processId, + evalDoc.messageId, + evalDoc.deepHash, + evalDoc.nonce, + evalDoc.epoch, + evalDoc.timestamp, + evalDoc.ordinate, + evalDoc.blockHeight, + evalDoc.cron, + evalDoc.evaluatedAt.getTime(), + JSON.stringify(evalDoc.output) + ] + }) + } } - /** * Cron messages are not needed to be saved in the messages table */ @@ -193,14 +249,26 @@ export function saveEvaluationWith ({ DISABLE_PROCESS_EVALUATION_CACHE, db, logg .chain((data) => of(data) .map((data) => createQuery(data)) - .chain(fromPromise((statements) => db.transaction(statements))) + .chain(fromPromise((statements) => { + return db.transaction(statements) + })) .map(always(data.id)) ) + .chain((id) => { + if (EVALUATION_RESULT_DIR && EVALUATION_RESULT_BUCKET) { + return of({ messageId: evaluation.messageId, processId: evaluation.processId, output: evaluation.output }) + .chain((args) => { + return Resolved(saveEvaluationToDir(args)) + }) + } + return Resolved(id) + }) .toPromise() } } -export function findEvaluationsWith ({ db }) { +export function findEvaluationsWith ({ db, loadEvaluation, EVALUATION_RESULT_DIR, EVALUATION_RESULT_BUCKET }) { + const findEvaluationFromDir = findEvaluationFromDirOrS3With({ loadEvaluation }) function createQuery ({ processId, from, to, onlyCron, sort, limit }) { return { sql: ` @@ -228,7 +296,7 @@ export function findEvaluationsWith ({ db }) { : createEvaluationId({ processId, timestamp: from.timestamp, ordinate: from.ordinate, cron: from.cron }), isEmpty(to) ? createEvaluationId({ processId, timestamp: COLLATION_SEQUENCE_MAX_CHAR }) - : createEvaluationId({ processId, timestamp: to.timestamp, ordinate: to.ordinate || COLLATION_SEQUENCE_MAX_CHAR, cron: to.cron }), + : createEvaluationId({ processId, timestamp: to?.timestamp || null, ordinate: to?.ordinate || COLLATION_SEQUENCE_MAX_CHAR, cron: to?.cron || null }), limit ] } @@ -238,6 +306,21 @@ export function findEvaluationsWith ({ db }) { return of({ processId, from, to, onlyCron, sort: sort.toUpperCase(), limit }) .map(createQuery) .chain(fromPromise((query) => db.query(query))) + .chain(fromPromise(async (results) => { + if (EVALUATION_RESULT_DIR && EVALUATION_RESULT_BUCKET) { + return await Promise.all(results.map(async (result) => { + if (result.processId && result.messageId && !result.output) { + const evaluationOutput = await findEvaluationFromDir({ processId: result.processId, messageId: result.messageId }) + if (evaluationOutput === 'AWS Credentials not set') { + return Rejected({ status: 404, message: 'Could not find evaluation: AWS Credentials not set' }) + } + return { ...result, output: evaluationOutput } + } + return result + })) + } + return results + })) .map(map(fromEvaluationDoc)) .toPromise() } diff --git a/servers/cu/src/effects/ao-evaluation.test.js b/servers/cu/src/effects/ao-evaluation.test.js index 04a78be8a..002e7a618 100644 --- a/servers/cu/src/effects/ao-evaluation.test.js +++ b/servers/cu/src/effects/ao-evaluation.test.js @@ -4,7 +4,7 @@ import assert from 'node:assert' import { createTestLogger } from '../domain/logger.js' import { findEvaluationSchema, findEvaluationsSchema, findMessageBeforeSchema, saveEvaluationSchema } from '../domain/dal.js' -import { findMessageBeforeWith, findEvaluationWith, findEvaluationsWith, saveEvaluationWith } from './ao-evaluation.js' +import { findMessageBeforeWith, findEvaluationsWith, saveEvaluationWith, findEvaluationWith } from './ao-evaluation.js' import { COLLATION_SEQUENCE_MAX_CHAR, EVALUATIONS_TABLE, MESSAGES_TABLE } from './db.js' const logger = createTestLogger({ name: 'ao-cu:readState' }) @@ -62,7 +62,7 @@ describe('ao-evaluation', () => { }) }) - test('return 404 status if not found', async () => { + test('return 404 status if not found and dir/bucket not set', async () => { const findEvaluation = findEvaluationSchema.implement( findEvaluationWith({ db: { @@ -86,6 +86,110 @@ describe('ao-evaluation', () => { assert(res.ok) }) + + test('find evaluation from directory if dir/bucket are set', async () => { + const findEvaluation = findEvaluationSchema.implement( + findEvaluationWith({ + db: { + engine: 'sqlite', + query: async ({ parameters }) => { + assert.deepStrictEqual(parameters, ['process-124,1702677252111,1']) + return [{ + id: 'process-124,1702677252111,1', + processId: 'process-124', + messageId: 'message-123', + deepHash: 'deepHash-123', + nonce: 1, + epoch: 0, + timestamp: 1702677252111, + ordinate: '1', + blockHeight: 1234, + cron: undefined, + evaluatedAt: evaluatedAt.getTime(), + output: undefined + }] + } + }, + loadEvaluation: async ({ processId, messageId }) => { + assert.equal(processId, 'process-124') + assert.equal(messageId, 'message-123') + return { Messages: [{ foo: 'bar' }] } + }, + EVALUATION_RESULT_DIR: 'test-directory', + EVALUATION_RESULT_BUCKET: 'test-bucket' + }) + ) + + const res = await findEvaluation({ + processId: 'process-124', + to: 1702677252111, + ordinate: '1', + cron: undefined, + messageId: 'message-123' + }) + assert.deepStrictEqual(res, { + processId: 'process-124', + messageId: 'message-123', + deepHash: 'deepHash-123', + nonce: 1, + epoch: 0, + timestamp: 1702677252111, + ordinate: '1', + blockHeight: 1234, + cron: undefined, + evaluatedAt, + output: { Messages: [{ foo: 'bar' }] } + }) + }) + + test('return 404 if dir/bucket are set but AWS Credentials are not set', async () => { + const findEvaluation = findEvaluationSchema.implement( + findEvaluationWith({ + db: { + engine: 'sqlite', + query: async ({ parameters }) => { + assert.deepStrictEqual(parameters, ['process-124,1702677252111,1']) + return [{ + id: 'process-124,1702677252111,1', + processId: 'process-124', + messageId: 'message-123', + deepHash: 'deepHash-123', + nonce: 1, + epoch: 0, + timestamp: 1702677252111, + ordinate: '1', + blockHeight: 1234, + cron: undefined, + evaluatedAt: evaluatedAt.getTime(), + output: undefined + }] + } + }, + loadEvaluation: async ({ processId, messageId }) => { + assert.equal(processId, 'process-124') + assert.equal(messageId, 'message-123') + return 'AWS Credentials not set' + }, + EVALUATION_RESULT_DIR: 'test-directory', + EVALUATION_RESULT_BUCKET: 'test-bucket' + }) + ) + + const res = await findEvaluation({ + processId: 'process-124', + to: 1702677252111, + ordinate: '1', + cron: undefined, + messageId: 'message-123' + }) + .catch(err => { + assert.equal(err.status, 404) + assert.equal(err.message, 'Could not find evaluation: AWS Credentials not set') + return { ok: true } + }) + + assert(res.ok) + }) }) describe('saveEvaluation', () => { @@ -135,7 +239,10 @@ describe('ao-evaluation', () => { return Promise.resolve('process-123,1702677252111,1') } }, - logger + logger, + saveEvaluationToDir: async () => { + assert.fail('saveEvaluationToDir should not be called') + } }) ) @@ -172,7 +279,10 @@ describe('ao-evaluation', () => { return Promise.resolve('process-123,1702677252111,1') } }, - logger + logger, + saveEvaluationToDir: async () => { + assert.fail('saveEvaluationToDir should not be called') + } }) ) @@ -213,7 +323,60 @@ describe('ao-evaluation', () => { return Promise.resolve('process-123,1702677252111,1') } }, - logger + logger, + saveEvaluationToDir: async () => { + assert.fail('saveEvaluationToDir should not be called') + } + }) + ) + + // no deepHash + await saveEvaluation({ + ...args, + deepHash: undefined + }) + }) + + test('with bucket and dir set, save the evaluation and message', async () => { + const saveEvaluation = saveEvaluationSchema.implement( + saveEvaluationWith({ + db: { + engine: 'sqlite', + transaction: async ([{ parameters: evaluationDocParams }, { parameters: messageDocParams }]) => { + assert.deepStrictEqual(evaluationDocParams, [ + 'process-123,1702677252111,1', + 'process-123', + 'message-123', + undefined, + 1, + 0, + 1702677252111, + '1', + 1234, + undefined, + evaluatedAt.getTime() + // No output because we are going to save it to directory + ]) + + assert.deepStrictEqual(messageDocParams, [ + 'message-123', + 'process-123', + '0:1' + ]) + + return Promise.resolve('process-123,1702677252111,1') + } + }, + logger, + saveEvaluationToDir: async ({ messageId, processId, output }) => { + delete output.Memory + assert.deepStrictEqual(messageId, 'message-123') + assert.deepStrictEqual(processId, 'process-123') + assert.deepStrictEqual(output, { Messages: [{ foo: 'bar' }] }) + return output + }, + EVALUATION_RESULT_DIR: 'test-directory', + EVALUATION_RESULT_BUCKET: 'test-bucket' }) ) @@ -380,56 +543,73 @@ describe('ao-evaluation', () => { assert.equal(res.length, 2) }) - }) - describe('findMessageBeforeWith', () => { - test('find the prior message by deepHash', async () => { - const findMessageBefore = findMessageBeforeSchema.implement( - findMessageBeforeWith({ + test("return the evaluations between 'from' and 'to', get outputs from dir/bucket", async () => { + const evaluatedAt = new Date() + const mockEval = { + id: 'process-123,1702677252111', + timestamp: 1702677252111, + ordinate: '1', + blockHeight: 1234, + processId: 'process-123', + messageId: 'message-123', + output: undefined, + evaluatedAt: evaluatedAt.getTime() + } + const findEvaluations = findEvaluationsSchema.implement( + findEvaluationsWith({ db: { engine: 'sqlite', - query: async ({ parameters }) => { + query: async ({ sql, parameters }) => { + /** + * no onlyCron + */ + assert.ok(!sql.includes('AND cron IS NOT NULL')) + assert.deepStrictEqual(parameters, [ - 'deepHash-123', - 'process-123', - 0, - 0, - 3 + 'process-123,1702677252111,3', // gt + `process-123,1702677252111,${COLLATION_SEQUENCE_MAX_CHAR}`, // lte + 10 // limit ]) - const mockAssigment = { - id: 'deepHash-123', - processId: 'process-123', - seq: '0:3' - } - - return [mockAssigment] + return [ + mockEval, + mockEval + ] } - } - }) - ) + }, + loadEvaluation: async ({ processId, messageId }) => { + assert.equal(processId, 'process-123') + assert.equal(messageId, 'message-123') + return { Messages: [{ foo: 'bar' }] } + }, + EVALUATION_RESULT_DIR: 'test-directory', + EVALUATION_RESULT_BUCKET: 'test-bucket', + logger + })) - const res = await findMessageBefore({ + const res = await findEvaluations({ processId: 'process-123', - messageId: 'message-123', - deepHash: 'deepHash-123', - isAssignment: false, - epoch: 0, - nonce: 3 + from: { timestamp: 1702677252111, ordinate: '3' }, + to: { timestamp: 1702677252111 }, + limit: 10, + sort: 'ASC' }) - assert.deepStrictEqual(res, { id: 'deepHash-123' }) + assert.equal(res.length, 2) + assert.deepStrictEqual(res[0].output, { Messages: [{ foo: 'bar' }] }) + assert.deepStrictEqual(res[1].output, { Messages: [{ foo: 'bar' }] }) }) - describe('find the prior message by messageId', () => { - test('if no deepHash was calculated', async () => { + describe('findMessageBeforeWith', () => { + test('find the prior message by deepHash', async () => { const findMessageBefore = findMessageBeforeSchema.implement( findMessageBeforeWith({ db: { engine: 'sqlite', query: async ({ parameters }) => { assert.deepStrictEqual(parameters, [ - 'message-123', + 'deepHash-123', 'process-123', 0, 0, @@ -437,7 +617,7 @@ describe('ao-evaluation', () => { ]) const mockAssigment = { - id: 'message-123', + id: 'deepHash-123', processId: 'process-123', seq: '0:3' } @@ -451,79 +631,142 @@ describe('ao-evaluation', () => { const res = await findMessageBefore({ processId: 'process-123', messageId: 'message-123', - deepHash: undefined, + deepHash: 'deepHash-123', isAssignment: false, epoch: 0, nonce: 3 }) - assert.deepStrictEqual(res, { id: 'message-123' }) + assert.deepStrictEqual(res, { id: 'deepHash-123' }) }) - test('if it is an assignment', async () => { - const findMessageBefore = findMessageBeforeSchema.implement( - findMessageBeforeWith({ - db: { - engine: 'sqlite', - query: async ({ sql, parameters }) => { - // Only the postgres engine uses POSITION - assert.ok(!sql.includes('POSITION')) - assert.deepStrictEqual(parameters, [ - 'message-123', - 'process-123', - 0, - 0, - 3 - ]) - - const mockAssigment = { - id: 'message-123', - processId: 'process-123', - seq: '0:3' + describe('find the prior message by messageId', () => { + test('if no deepHash was calculated', async () => { + const findMessageBefore = findMessageBeforeSchema.implement( + findMessageBeforeWith({ + db: { + engine: 'sqlite', + query: async ({ parameters }) => { + assert.deepStrictEqual(parameters, [ + 'message-123', + 'process-123', + 0, + 0, + 3 + ]) + + const mockAssigment = { + id: 'message-123', + processId: 'process-123', + seq: '0:3' + } + + return [mockAssigment] } + } + }) + ) + + const res = await findMessageBefore({ + processId: 'process-123', + messageId: 'message-123', + deepHash: undefined, + isAssignment: false, + epoch: 0, + nonce: 3 + }) - return [mockAssigment] + assert.deepStrictEqual(res, { id: 'message-123' }) + }) + + test('if it is an assignment', async () => { + const findMessageBefore = findMessageBeforeSchema.implement( + findMessageBeforeWith({ + db: { + engine: 'sqlite', + query: async ({ sql, parameters }) => { + // Only the postgres engine uses POSITION + assert.ok(!sql.includes('POSITION')) + assert.deepStrictEqual(parameters, [ + 'message-123', + 'process-123', + 0, + 0, + 3 + ]) + + const mockAssigment = { + id: 'message-123', + processId: 'process-123', + seq: '0:3' + } + + return [mockAssigment] + } } - } + }) + ) + + const res = await findMessageBefore({ + processId: 'process-123', + messageId: 'message-123', + deepHash: 'deepHash-123', + isAssignment: true, + epoch: 0, + nonce: 3 }) - ) - const res = await findMessageBefore({ - processId: 'process-123', - messageId: 'message-123', - deepHash: 'deepHash-123', - isAssignment: true, - epoch: 0, - nonce: 3 + assert.deepStrictEqual(res, { id: 'message-123' }) }) + test('if it is an assignment, postgres', async () => { + const findMessageBefore = findMessageBeforeSchema.implement( + findMessageBeforeWith({ + db: { + engine: 'postgres', + query: async ({ sql, parameters }) => { + // Only the postgres engine uses POSITION + assert.ok(sql.includes('POSITION')) + assert.deepStrictEqual(parameters, [ + 'message-123', + 'process-123', + 0, + 0, + 3 + ]) + + const mockAssigment = { + id: 'message-123', + processId: 'process-123', + seq: '0:3' + } + + return [mockAssigment] + } + } + }) + ) + + const res = await findMessageBefore({ + processId: 'process-123', + messageId: 'message-123', + deepHash: 'deepHash-123', + isAssignment: true, + epoch: 0, + nonce: 3 + }) - assert.deepStrictEqual(res, { id: 'message-123' }) + assert.deepStrictEqual(res, { id: 'message-123' }) + }) }) - test('if it is an assignment, postgres', async () => { + + test('return 404 status if not found', async () => { const findMessageBefore = findMessageBeforeSchema.implement( findMessageBeforeWith({ db: { - engine: 'postgres', - query: async ({ sql, parameters }) => { - // Only the postgres engine uses POSITION - assert.ok(sql.includes('POSITION')) - assert.deepStrictEqual(parameters, [ - 'message-123', - 'process-123', - 0, - 0, - 3 - ]) - - const mockAssigment = { - id: 'message-123', - processId: 'process-123', - seq: '0:3' - } - - return [mockAssigment] - } - } + engine: 'sqlite', + query: async () => [] + }, + logger }) ) @@ -535,36 +778,13 @@ describe('ao-evaluation', () => { epoch: 0, nonce: 3 }) + .catch(err => { + assert.equal(err.status, 404) + return { ok: true } + }) - assert.deepStrictEqual(res, { id: 'message-123' }) - }) - }) - - test('return 404 status if not found', async () => { - const findMessageBefore = findMessageBeforeSchema.implement( - findMessageBeforeWith({ - db: { - engine: 'sqlite', - query: async () => [] - }, - logger - }) - ) - - const res = await findMessageBefore({ - processId: 'process-123', - messageId: 'message-123', - deepHash: 'deepHash-123', - isAssignment: true, - epoch: 0, - nonce: 3 + assert(res.ok) }) - .catch(err => { - assert.equal(err.status, 404) - return { ok: true } - }) - - assert(res.ok) }) }) }) diff --git a/servers/cu/src/effects/worker/dumpEvaluations.js b/servers/cu/src/effects/worker/dumpEvaluations.js new file mode 100644 index 000000000..403459ab7 --- /dev/null +++ b/servers/cu/src/effects/worker/dumpEvaluations.js @@ -0,0 +1,34 @@ +import path from 'path' +import { S3Client } from '@aws-sdk/client-s3' + +export function dumpEvaluationsWith ({ EVALUATION_RESULT_DIR, EVALUATION_RESULT_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, uploadFileToS3With, globSync, logger }) { + return async (processId) => { + if (!AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY || !AWS_REGION) { + return 'AWS Credentials not set' + } + const globPath = path.join(EVALUATION_RESULT_DIR, `${processId ?? ''}**.json`) + const files = globSync(globPath) + const s3Client = new S3Client({ + region: AWS_REGION, + credentials: { + accessKeyId: AWS_ACCESS_KEY_ID, + secretAccessKey: AWS_SECRET_ACCESS_KEY + } + }) + logger('Dumping evaluations to S3 Bucket: %s', EVALUATION_RESULT_BUCKET) + const uploadFileToS3 = uploadFileToS3With(s3Client) + for (const file of files) { + const bucketName = EVALUATION_RESULT_BUCKET + const key = file.split('/').pop() + logger('Uploading file %s to S3 Bucket: %s', key, bucketName) + try { + await uploadFileToS3(file, bucketName, key) + logger('Successfully uploaded file %s to S3 Bucket: %s', key, bucketName) + } catch (e) { + logger('Error uploading file %s to S3 Bucket: %s', key, bucketName) + throw new Error(`Error uploading file ${key} to S3: ${e}`) + } + } + logger('Successfully dumped evaluations to S3 Bucket: %s', EVALUATION_RESULT_BUCKET) + } +} diff --git a/servers/cu/src/effects/worker/dumpEvaluations.test.js b/servers/cu/src/effects/worker/dumpEvaluations.test.js new file mode 100644 index 000000000..7c172af0d --- /dev/null +++ b/servers/cu/src/effects/worker/dumpEvaluations.test.js @@ -0,0 +1,72 @@ +/* eslint-disable no-throw-literal */ +import { describe, test } from 'node:test' +import assert from 'node:assert' + +import { createTestLogger } from '../../domain/logger.js' +import { dumpEvaluationsWith } from './dumpEvaluations.js' + +const logger = createTestLogger({ name: 'ao-cu:worker' }) + +describe('dumpEvaluations', async () => { + describe('dumpEvaluationsWith', async () => { + test('AWS Credentials not set', async () => { + const dumpEvaluations = dumpEvaluationsWith({ + EVALUATION_RESULT_DIR: './test/evaluation-results', + EVALUATION_RESULT_BUCKET: 'test-bucket' + }) + const result = await dumpEvaluations('test-process-id') + assert.strictEqual(result, 'AWS Credentials not set') + }) + + test('Correct glob path', async () => { + let globCalled = false + const dumpEvaluations = dumpEvaluationsWith({ + EVALUATION_RESULT_DIR: 'test-dir/', + EVALUATION_RESULT_BUCKET: 'test-bucket', + AWS_ACCESS_KEY_ID: 'test-access-key-id', + AWS_SECRET_ACCESS_KEY: 'test-secret-access-key', + AWS_REGION: 'test-region', + globSync: (path) => { + globCalled = true + assert.strictEqual(path, 'test-dir/test-process-id**.json') + return [] + }, + uploadFileToS3With: async () => null, + logger + }) + await dumpEvaluations('test-process-id') + assert.ok(globCalled) + }) + + test('Upload file to S3', async () => { + let globCalled = false + let uploadFileToS3Called = false + const dumpEvaluations = dumpEvaluationsWith({ + EVALUATION_RESULT_DIR: 'test-dir/', + EVALUATION_RESULT_BUCKET: 'test-bucket', + AWS_ACCESS_KEY_ID: 'test-access-key-id', + AWS_SECRET_ACCESS_KEY: 'test-secret-access-key', + AWS_REGION: 'test-region', + globSync: (path) => { + globCalled = true + assert.strictEqual(path, 'test-dir/test-process-id**.json') + return ['test-dir/test-process-id-test-message-1.json', 'test-dir/test-process-id-test-message-2.json'] + }, + uploadFileToS3With: () => { + uploadFileToS3Called = true + let i = 1 + return async (file, bucket, key) => { + assert.strictEqual(file, `test-dir/test-process-id-test-message-${i}.json`) + assert.strictEqual(bucket, 'test-bucket') + assert.strictEqual(key, `test-process-id-test-message-${i}.json`) + i++ + } + }, + logger + }) + await dumpEvaluations('test-process-id') + assert.ok(globCalled) + assert.ok(uploadFileToS3Called) + }) + }) +}) diff --git a/servers/cu/src/effects/worker/evaluator/main.js b/servers/cu/src/effects/worker/evaluator/main.js index ed01449bc..3dc568c8e 100644 --- a/servers/cu/src/effects/worker/evaluator/main.js +++ b/servers/cu/src/effects/worker/evaluator/main.js @@ -1,8 +1,11 @@ import * as WasmClient from '../../wasm.js' import * as AoEvaluationClient from '../../ao-evaluation.js' import * as DbClient from '../../db.js' - +import workerpool from 'workerpool' +import PQueue from 'p-queue' import { evaluateWith } from '../evaluate.js' +import { join } from 'path' +import { randomBytes } from 'crypto' export const createApis = async (ctx) => { const db = await DbClient.createDbClient({ url: ctx.DB_URL, bootstrap: false, max: 5 }) @@ -10,6 +13,28 @@ export const createApis = async (ctx) => { const close = async (streamId) => wasmInstanceCache.delete(streamId) + const __dirname = ctx.__dirname + const saveEvaluationWorker = join(__dirname, 'effects', 'worker', 'saveEvaluation', 'index.js') + const onCreateSaveEvaluationWorker = () => () => { + const workerId = randomBytes(8).toString('hex') + ctx.logger('Spinning up save evaluation worker with id "%s"...', workerId) + + return { + workerThreadOpts: { + workerData: { + id: workerId, + EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, + EVALUATION_RESULT_BUCKET: ctx.EVALUATION_RESULT_BUCKET + } + } + } + } + const saveEvaluationWorkerPool = workerpool.pool(saveEvaluationWorker, { + maxWorkers: ctx.EVALUATION_RESULT_BUCKET && ctx.EVALUATION_RESULT_DIR ? 2 : 0, + onCreateWorker: onCreateSaveEvaluationWorker() + }) + const saveEvaluationWorkQueue = new PQueue({ concurrency: 2 }) + const evaluate = evaluateWith({ wasmInstanceCache, addExtension: WasmClient.addExtensionWith({ @@ -21,7 +46,18 @@ export const createApis = async (ctx) => { saveEvaluation: AoEvaluationClient.saveEvaluationWith({ DISABLE_PROCESS_EVALUATION_CACHE: ctx.DISABLE_PROCESS_EVALUATION_CACHE, db, - logger: ctx.logger + logger: ctx.logger, + saveEvaluationToDir: (args) => { + return saveEvaluationWorkQueue.add(() => + Promise.resolve() + .then(async () => await saveEvaluationWorkerPool.exec('saveEvaluationToDir', [args])) + .catch((e) => { + throw new Error(`Error in saveEvaluation worker: ${e}`) + }) + ) + }, + EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, + EVALUATION_RESULT_BUCKET: ctx.EVALUATION_RESULT_BUCKET }), ARWEAVE_URL: ctx.ARWEAVE_URL, logger: ctx.logger diff --git a/servers/cu/src/effects/worker/loadEvaluation.js b/servers/cu/src/effects/worker/loadEvaluation.js new file mode 100644 index 000000000..29123abc1 --- /dev/null +++ b/servers/cu/src/effects/worker/loadEvaluation.js @@ -0,0 +1,55 @@ +import path from 'path' +import { S3Client } from '@aws-sdk/client-s3' +export function loadEvaluationWith ({ EVALUATION_RESULT_DIR, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, logger, existsSync, writeFileSync, loadEvaluationFromDir, loadEvaluationFromS3 }) { + return async ({ messageId, processId }) => { + logger('Attempting to load evaluation from directory: %s', EVALUATION_RESULT_DIR) + const fileName = path.join(EVALUATION_RESULT_DIR, `${processId}-${messageId}.json`) + const fileExists = existsSync(fileName) + + if (fileExists) { + return loadEvaluationFromDir({ fileName }) + } else { + if (!AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY || !AWS_REGION) { + return 'AWS Credentials not set' + } + return loadEvaluationFromS3({ fileName }).then((file) => { + writeFileSync(fileName, JSON.stringify(file)) + return file + }) + } + } +} + +export function loadEvaluationFromDirWith ({ logger, readFileSync }) { + return ({ fileName }) => { + try { + const file = readFileSync(fileName, 'utf8') + logger('Successfully loaded evaluation from directory: %s', fileName) + return JSON.parse(file) + } catch (e) { + logger('Error reading file %s: %s', fileName, e) + throw new Error(`Error reading file ${fileName}: ${e}`) + } + } +} + +export function loadEvaluationFromS3With ({ EVALUATION_RESULT_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, logger, getFileFromS3With }) { + return async ({ fileName }) => { + const s3Client = new S3Client({ + region: AWS_REGION, + credentials: { + accessKeyId: AWS_ACCESS_KEY_ID, + secretAccessKey: AWS_SECRET_ACCESS_KEY + } + }) + logger('Attempting to load evaluation from S3: %s', fileName) + const getFileFromS3 = getFileFromS3With(EVALUATION_RESULT_BUCKET, s3Client) + const file = await getFileFromS3(fileName) + .then(JSON.parse) + .catch((e) => { + throw new Error(`Error getting file from S3: ${e}`) + }) + logger('Successfully loaded evaluation from S3: %s', fileName) + return file + } +} diff --git a/servers/cu/src/effects/worker/loadEvaluation.test.js b/servers/cu/src/effects/worker/loadEvaluation.test.js new file mode 100644 index 000000000..4c04bc740 --- /dev/null +++ b/servers/cu/src/effects/worker/loadEvaluation.test.js @@ -0,0 +1,151 @@ +/* eslint-disable no-throw-literal */ +import { describe, test } from 'node:test' +import assert from 'node:assert' + +import { createTestLogger } from '../../domain/logger.js' +import { loadEvaluationFromDirWith, loadEvaluationFromS3With, loadEvaluationWith } from './loadEvaluation.js' + +const logger = createTestLogger({ name: 'ao-cu:worker' }) + +describe('loadEvaluation', async () => { + describe('loadEvaluationWith', async () => { + test('Correct file name, file exists', async () => { + let existsCalled = false + let loadFromDirCalled = false + const loadEvaluation = loadEvaluationWith({ + EVALUATION_RESULT_DIR: 'test-directory', + EVALUATION_RESULT_BUCKET: 'test-bucket', + AWS_ACCESS_KEY_ID: 'test-access-key-id', + AWS_SECRET_ACCESS_KEY: 'test-secret-access-key', + AWS_REGION: 'test-region', + logger, + existsSync: (path) => { + existsCalled = true + assert.equal(path, 'test-directory/test-process-id-test-message-id.json') + return true + }, + writeFileSync: () => { + assert.fail('writeFileSync should not be called') + }, + loadEvaluationFromDir: async ({ fileName }) => { + loadFromDirCalled = true + assert.equal(fileName, 'test-directory/test-process-id-test-message-id.json') + return { foo: 'bar' } + }, + loadEvaluationFromS3: async () => { + assert.fail('loadEvaluationFromS3 should not be called') + } + }) + const result = await loadEvaluation({ + messageId: 'test-message-id', + processId: 'test-process-id' + }) + assert.deepStrictEqual(result, { foo: 'bar' }) + assert.strictEqual(existsCalled, true) + assert.strictEqual(loadFromDirCalled, true) + }) + + test('AWS Credentials not set', async () => { + const loadEvaluation = loadEvaluationWith({ + EVALUATION_RESULT_DIR: './test/evaluation-results', + EVALUATION_RESULT_BUCKET: 'test-bucket', + AWS_ACCESS_KEY_ID: null, + AWS_SECRET_ACCESS_KEY: null, + AWS_REGION: null, + existsSync: () => false, + writeFileSync: () => {}, + loadEvaluationFromDir: async () => null, + loadEvaluationFromS3: async () => null, + logger + }) + const result = await loadEvaluation({ + messageId: 'test-message-id', + processId: 'test-process-id' + }) + assert.strictEqual(result, 'AWS Credentials not set') + }) + + test('File does not exist', async () => { + let existsCalled = false + let writeFileSyncCalled = false + const loadEvaluation = loadEvaluationWith({ + EVALUATION_RESULT_DIR: 'test-directory', + EVALUATION_RESULT_BUCKET: 'test-bucket', + AWS_ACCESS_KEY_ID: 'test-access-key-id', + AWS_SECRET_ACCESS_KEY: 'test-secret-access-key', + AWS_REGION: 'test-region', + logger, + existsSync: (path) => { + existsCalled = true + assert.equal(path, 'test-directory/test-process-id-test-message-id.json') + return false + }, + writeFileSync: (path, data) => { + writeFileSyncCalled = true + assert.equal(path, 'test-directory/test-process-id-test-message-id.json') + assert.equal(data, JSON.stringify({ foo: 'bar' })) + }, + loadEvaluationFromDir: async () => { + assert.fail('loadEvaluationFromDir should not be called') + }, + loadEvaluationFromS3: async ({ fileName }) => { + assert.equal(fileName, 'test-directory/test-process-id-test-message-id.json') + return { foo: 'bar' } + } + }) + const result = await loadEvaluation({ + messageId: 'test-message-id', + processId: 'test-process-id' + }) + assert.deepStrictEqual(result, { foo: 'bar' }) + assert.strictEqual(writeFileSyncCalled, true) + assert.strictEqual(existsCalled, true) + }) + }) + + describe('loadEvaluationFromDirWith', async () => { + test('Load from dir', async () => { + let readCalled = false + const loadEvaluationFromDir = loadEvaluationFromDirWith({ + logger, + readFileSync: (path, encoding) => { + readCalled = true + assert.equal(path, 'test-directory/test-process-id-test-message-id.json') + assert.equal(encoding, 'utf8') + return JSON.stringify({ foo: 'bar' }) + } + }) + const result = await loadEvaluationFromDir({ + fileName: 'test-directory/test-process-id-test-message-id.json' + }) + assert.deepStrictEqual(result, { foo: 'bar' }) + assert.strictEqual(readCalled, true) + }) + }) + + describe('loadEvaluationFromS3With', async () => { + test('Load from S3', async () => { + let getFileFromS3Called = false + const loadEvaluationFromS3 = loadEvaluationFromS3With({ + EVALUATION_RESULT_BUCKET: 'test-bucket', + AWS_ACCESS_KEY_ID: 'test-access-key-id', + AWS_SECRET_ACCESS_KEY: 'test-secret-access-key', + AWS_REGION: 'test-region', + logger, + getFileFromS3With: (bucketName, s3Client) => { + getFileFromS3Called = true + assert.equal(bucketName, 'test-bucket') + return async (fileName) => { + assert.equal(fileName, 'test-directory/test-process-id-test-message-id.json') + return JSON.stringify({ foo: 'bar' }) + } + } + }) + const result = await loadEvaluationFromS3({ + fileName: 'test-directory/test-process-id-test-message-id.json' + }) + assert.deepStrictEqual(result, { foo: 'bar' }) + assert.strictEqual(getFileFromS3Called, true) + }) + }) +}) diff --git a/servers/cu/src/effects/worker/loadEvaluation/index.js b/servers/cu/src/effects/worker/loadEvaluation/index.js new file mode 100644 index 000000000..b20a8ba7f --- /dev/null +++ b/servers/cu/src/effects/worker/loadEvaluation/index.js @@ -0,0 +1,53 @@ +import { workerData } from 'node:worker_threads' +import { hostname } from 'node:os' +import fs from 'node:fs' +import { fetch, setGlobalDispatcher, Agent } from 'undici' +import { worker } from 'workerpool' + +import { createLogger } from '../../../domain/logger.js' +import { createApis } from './main.js' + +setGlobalDispatcher(new Agent({ + /** the timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server */ + keepAliveTimeout: 30 * 1000, // 30 seconds + /** the maximum allowed `idleTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. */ + keepAliveMaxTimeout: 10 * 60 * 1000 // 10 mins +})) + +const logger = createLogger({ ...workerData, name: `ao-cu:${hostname()}:worker-${workerData.id}` }) + +const apis = await createApis({ + ...workerData, + fetch, + globSync: fs.globSync, + logger +}) + +const broadcast = new BroadcastChannel(workerData.BROADCAST) + +broadcast.onmessage = (event) => { + const data = event.data + + if (data.type === 'dump-evaluations') { + return apis.dumpEvaluations(data.processId) + } + + logger.warn('Unrecognized event type "%s". Doing nothing...', data.type) +} + +if (workerData.EVALUATION_RESULT_DIR && !fs.existsSync(workerData.EVALUATION_RESULT_DIR)) { + fs.mkdirSync(workerData.EVALUATION_RESULT_DIR, { recursive: true }) +} + +worker({ + loadEvaluation: (...args) => { + return apis.loadEvaluation(...args) + .catch((e) => { + throw new Error(`Error in loadEvaluation worker: ${e}`) + }) + }, + dumpEvaluations: (...args) => apis.dumpEvaluations(...args) + .catch((e) => { + throw new Error(`Error in dumpEvaluations worker: ${e}`) + }) +}) diff --git a/servers/cu/src/effects/worker/loadEvaluation/main.js b/servers/cu/src/effects/worker/loadEvaluation/main.js new file mode 100644 index 000000000..a5dbdf05d --- /dev/null +++ b/servers/cu/src/effects/worker/loadEvaluation/main.js @@ -0,0 +1,64 @@ +import { dumpEvaluationsWith } from '../dumpEvaluations.js' +import { loadEvaluationFromDirWith, loadEvaluationFromS3With, loadEvaluationWith } from '../loadEvaluation.js' +import fs from 'node:fs' +import { GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3' + +export const createApis = async (ctx) => { + const uploadFileToS3With = (s3Client) => { + return async (filePath, bucketName, key) => { + const fileStream = fs.createReadStream(filePath) + const params = { + Bucket: bucketName, + Key: key, + Body: fileStream + } + const command = new PutObjectCommand(params) + await s3Client.send(command) + } + } + + const getFileFromS3With = (EVALUATION_RESULT_BUCKET, s3Client) => { + return async (fileName) => { + const key = fileName.split('/').pop() + const command = new GetObjectCommand({ + Bucket: EVALUATION_RESULT_BUCKET, + Key: key + }) + const response = await s3Client.send(command) + return response.Body.transformToString() + } + } + + const loadEvaluation = loadEvaluationWith({ + EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, + AWS_ACCESS_KEY_ID: ctx.AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY: ctx.AWS_SECRET_ACCESS_KEY, + AWS_REGION: ctx.AWS_REGION, + logger: ctx.logger, + existsSync: fs.existsSync, + writeFileSync: fs.writeFileSync, + loadEvaluationFromDir: loadEvaluationFromDirWith({ + logger: ctx.logger, + readFileSync: fs.readFileSync + }), + loadEvaluationFromS3: loadEvaluationFromS3With({ + EVALUATION_RESULT_BUCKET: ctx.EVALUATION_RESULT_BUCKET, + AWS_ACCESS_KEY_ID: ctx.AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY: ctx.AWS_SECRET_ACCESS_KEY, + AWS_REGION: ctx.AWS_REGION, + logger: ctx.logger, + getFileFromS3With + }) + }) + const dumpEvaluations = dumpEvaluationsWith({ + EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, + EVALUATION_RESULT_BUCKET: ctx.EVALUATION_RESULT_BUCKET, + AWS_ACCESS_KEY_ID: ctx.AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY: ctx.AWS_SECRET_ACCESS_KEY, + AWS_REGION: ctx.AWS_REGION, + globSync: ctx.globSync, + logger: ctx.logger, + uploadFileToS3With + }) + return { loadEvaluation, dumpEvaluations } +} diff --git a/servers/cu/src/effects/worker/saveEvaluation/index.js b/servers/cu/src/effects/worker/saveEvaluation/index.js new file mode 100644 index 000000000..410991638 --- /dev/null +++ b/servers/cu/src/effects/worker/saveEvaluation/index.js @@ -0,0 +1,34 @@ +import { workerData } from 'node:worker_threads' +import { hostname } from 'node:os' +import fs from 'node:fs' +import { fetch, setGlobalDispatcher, Agent } from 'undici' +import { worker } from 'workerpool' + +import { createLogger } from '../../../domain/logger.js' +import { createApis } from './main.js' + +setGlobalDispatcher(new Agent({ + /** the timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server */ + keepAliveTimeout: 30 * 1000, // 30 seconds + /** the maximum allowed `idleTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. */ + keepAliveMaxTimeout: 10 * 60 * 1000 // 10 mins +})) + +const logger = createLogger({ ...workerData, name: `ao-cu:${hostname()}:worker-${workerData.id}` }) + +const apis = await createApis({ + ...workerData, + fetch, + logger +}) + +if (workerData.EVALUATION_RESULT_DIR && !fs.existsSync(workerData.EVALUATION_RESULT_DIR)) { + fs.mkdirSync(workerData.EVALUATION_RESULT_DIR, { recursive: true }) +} + +worker({ + saveEvaluationToDir: (...args) => apis.saveEvaluationToDir(...args) + .catch((e) => { + throw new Error(`Error in saveEvaluation worker: ${e}`) + }) +}) diff --git a/servers/cu/src/effects/worker/saveEvaluation/main.js b/servers/cu/src/effects/worker/saveEvaluation/main.js new file mode 100644 index 000000000..a4a9bf140 --- /dev/null +++ b/servers/cu/src/effects/worker/saveEvaluation/main.js @@ -0,0 +1,11 @@ +import { saveEvaluationToDirWith } from '../saveEvaluationToDir.js' +import fs from 'node:fs' +export const createApis = async (ctx) => { + const saveEvaluationToDir = saveEvaluationToDirWith({ + EVALUATION_RESULT_DIR: ctx.EVALUATION_RESULT_DIR, + existsSync: fs.existsSync, + writeFileSync: fs.writeFileSync, + logger: ctx.logger + }) + return { saveEvaluationToDir } +} diff --git a/servers/cu/src/effects/worker/saveEvaluationToDir.js b/servers/cu/src/effects/worker/saveEvaluationToDir.js new file mode 100644 index 000000000..e64da08dd --- /dev/null +++ b/servers/cu/src/effects/worker/saveEvaluationToDir.js @@ -0,0 +1,21 @@ +import path from 'path' +import { omit } from 'ramda' +export function saveEvaluationToDirWith ({ EVALUATION_RESULT_DIR, existsSync, writeFileSync, logger }) { + return async ({ messageId, processId, output }) => { + const dir = path.join(EVALUATION_RESULT_DIR, `${processId}-${messageId}.json`) + const outputWithoutMemory = omit(['Memory'], output) + + logger('Saving evaluation of message %s to process %s to directory: %s', messageId, processId, dir) + if (!existsSync(dir)) { + try { + writeFileSync(dir, JSON.stringify(outputWithoutMemory)) + } catch (e) { + throw new Error(`Error writing file ${dir}: ${e}`) + } + logger('Successfully saved evaluation of message %s to process %s to directory: %s', messageId, processId, dir) + } else { + logger('Evaluation of message %s to process %s already exists in directory: %s', messageId, processId, dir) + } + return output + } +} diff --git a/servers/cu/src/effects/worker/saveEvaluationToDir.test.js b/servers/cu/src/effects/worker/saveEvaluationToDir.test.js new file mode 100644 index 000000000..2043663a0 --- /dev/null +++ b/servers/cu/src/effects/worker/saveEvaluationToDir.test.js @@ -0,0 +1,65 @@ +/* eslint-disable no-throw-literal */ +import { describe, test } from 'node:test' +import assert from 'node:assert' + +import { createTestLogger } from '../../domain/logger.js' +import { saveEvaluationToDirWith } from './saveEvaluationToDir.js' + +const logger = createTestLogger({ name: 'ao-cu:worker' }) + +describe('saveEvaluationToDir', async () => { + describe('saveEvaluationToDirWith', async () => { + test('File exists (exit)', async () => { + let existsSyncCalled = false + let writeFileSyncCalled = false + const saveEvaluationToDir = saveEvaluationToDirWith({ + EVALUATION_RESULT_DIR: 'test-directory', + existsSync: (path) => { + existsSyncCalled = true + assert.equal(path, 'test-directory/test-process-id-test-message-id.json') + return true + }, + writeFileSync: (path, data) => { + writeFileSyncCalled = true + assert.fail('writeFileSync should not be called') + }, + logger + }) + const result = await saveEvaluationToDir({ + messageId: 'test-message-id', + processId: 'test-process-id', + output: { foo: 'bar' } + }) + assert.deepStrictEqual(result, { foo: 'bar' }) + assert.ok(existsSyncCalled) + assert.ok(!writeFileSyncCalled) + }) + + test('File does not exist (write)', async () => { + let existsSyncCalled = false + let writeFileSyncCalled = false + const saveEvaluationToDir = saveEvaluationToDirWith({ + EVALUATION_RESULT_DIR: 'test-directory', + existsSync: (path) => { + existsSyncCalled = true + assert.equal(path, 'test-directory/test-process-id-test-message-id.json') + return false + }, + writeFileSync: (path, data) => { + writeFileSyncCalled = true + assert.equal(path, 'test-directory/test-process-id-test-message-id.json') + assert.equal(data, JSON.stringify({ foo: 'bar' })) + }, + logger + }) + const result = await saveEvaluationToDir({ + messageId: 'test-message-id', + processId: 'test-process-id', + output: { foo: 'bar' } + }) + assert.deepStrictEqual(result, { foo: 'bar' }) + assert.ok(existsSyncCalled) + assert.ok(writeFileSyncCalled) + }) + }) +})