diff --git a/README.md b/README.md index c6bd9e7..46de575 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,9 @@ Options: -p, --process-id [processId] Specify process Id of existing process. --build-only Bundle the contract into a single file and store it in the process-dist directory. --out-dir [outDir] Used with --build-only to output the single bundle contract file to a specified directory. + --gateway-url [url] Custom Gateway URL to connect to. (default: "https://arweave.net") + --cu-url [url] Custom Compute Unit (CU) URL to connect to. (default: "https://cu.ao-testnet.xyz") + --mu-url [url] Custom Messenger Unit (MU) URL to connect to. (default: "https://mu.ao-testnet.xyz") --concurrency [limit] Concurrency limit for deploying multiple processes. (default: 5) --sqlite Use sqlite aos module when spawning new process. --retry-count [count] Number of retries for deploying contract. (default: 10) diff --git a/package.json b/package.json index 91fa1cf..81d8c4c 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,7 @@ "prepack": "pnpm build" }, "dependencies": { - "@permaweb/aoconnect": "^0.0.57", - "ardb": "^1.1.10", + "@permaweb/aoconnect": "^0.0.58", "arweave": "^1.15.1", "chalk": "^5.3.0", "commander": "^12.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1559f3..347f956 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,11 +9,8 @@ importers: .: dependencies: '@permaweb/aoconnect': - specifier: ^0.0.57 - version: 0.0.57 - ardb: - specifier: ^1.1.10 - version: 1.1.10 + specifier: ^0.0.58 + version: 0.0.58 arweave: specifier: ^1.15.1 version: 1.15.1 @@ -1253,20 +1250,20 @@ packages: fastq: 1.17.1 dev: true - /@permaweb/ao-scheduler-utils@0.0.20: - resolution: {integrity: sha512-bJkcmnQm/rCGqklJt46q5TnHfWkFzSBcSf9Z3uy8ylHRAheS9NyR1BJMAj3EXDjHCpg7JfnLRo6Uc3Xdw1lmOA==} - engines: {node: '>=18', yarn: please-use-npm} + /@permaweb/ao-scheduler-utils@0.0.23: + resolution: {integrity: sha512-RtcWdHnrmvHbDVtqDX41P7ccCPA8IfYTXml4U/IgoaXIU/iUY0BfcMPwLSu2VxQCEfgLCfVASZxG0kZnh3xs3g==} + engines: {node: '>=18'} dependencies: - lru-cache: 10.4.3 + lru-cache: 10.2.2 ramda: 0.30.1 zod: 3.23.8 dev: false - /@permaweb/aoconnect@0.0.57: - resolution: {integrity: sha512-l1+47cZuQ8pOIMOdRXymcegCmefXjqR8Bc2MY6jIzWv9old/tG6mfCue2W1QviGyhjP3zEVQgr7YofkY2lq35Q==} - engines: {node: '>=18', yarn: please-use-npm} + /@permaweb/aoconnect@0.0.58: + resolution: {integrity: sha512-vVxTdsXaWNzM+iGFIAnq7+/yktMO7HCiS5ss8s4+4bolUwh2zVWTKDjM4JsGNQXqlhyNU21hhy0V9DwaunuRjw==} + engines: {node: '>=18'} dependencies: - '@permaweb/ao-scheduler-utils': 0.0.20 + '@permaweb/ao-scheduler-utils': 0.0.23 buffer: 6.0.3 debug: 4.3.6 hyper-async: 1.1.2 @@ -1910,10 +1907,6 @@ packages: resolution: {integrity: sha512-hvuhBYYDe+b1G8KHxsQ0diDqDMA8D9laxWZhNAjE83VZb5UDaXl9Xnz7cGdDSyiHM90qqI/CyGMcpBpiDy6VVQ==} dev: true - /@weavery/clarity@0.1.5: - resolution: {integrity: sha512-0ms2/sBx+uyW3EmXte5otIzNVAXpfJ3lBl6FS8JuLdWmPU6SxiAoGTMUT0N0SL3Ogiz2PZt6NV+mfApbSvYBaQ==} - dev: false - /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2004,27 +1997,12 @@ packages: picomatch: 2.3.1 dev: true - /arconnect@0.2.9: - resolution: {integrity: sha512-Us49eN/+8l6BrkAPdXnJVPwWlxxUPR7QaBjA0j3OBAcioIFRpwTdoPN9FxtwDGN91lgM6ebOudTXJToRiNizoA==} - dependencies: - arweave: 1.15.1 - dev: false - /arconnect@0.4.2: resolution: {integrity: sha512-Jkpd4QL3TVqnd3U683gzXmZUVqBUy17DdJDuL/3D9rkysLgX6ymJ2e+sR+xyZF5Rh42CBqDXWNMmCjBXeP7Gbw==} dependencies: arweave: 1.15.1 dev: false - /ardb@1.1.10: - resolution: {integrity: sha512-LtDa0T6FXiNG9D9kcyboEFfmvVJ9Y+W8404UTQS3BkvgqHHKsgFUuftft3RepAZVvR2bc+z5z+/koqgUe7LIUA==} - dependencies: - arweave: 1.15.1 - blockweave: 1.0.18 - transitivePeerDependencies: - - debug - dev: false - /are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} @@ -2039,15 +2017,6 @@ packages: engines: {node: '>=8'} dev: true - /arweave-multihost@0.1.0: - resolution: {integrity: sha512-biIkzQ3oc4RLV1MORQnqWz51IazP++K/8SsYMjUokK0cUfBLqom4pufKFCjTkGQIZMWWanXxnZqL66hHPgTCgA==} - dependencies: - arweave: 1.15.1 - axios: 0.21.4 - transitivePeerDependencies: - - debug - dev: false - /arweave@1.15.1: resolution: {integrity: sha512-rT7FOwqdudd5npqp4xOYdDT2035LtpcqePjwirh4wjRiEtVsz1FZkRiM2Yj+fOAwYzOm/hNG0GDOipDSaiEGGQ==} engines: {node: '>=18'} @@ -2087,21 +2056,6 @@ packages: postcss-value-parser: 4.2.0 dev: true - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - dependencies: - possible-typed-array-names: 1.0.0 - dev: false - - /axios@0.21.4: - resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} - dependencies: - follow-redirects: 1.15.6 - transitivePeerDependencies: - - debug - dev: false - /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -2124,18 +2078,6 @@ packages: engines: {node: '>=8'} dev: true - /blockweave@1.0.18: - resolution: {integrity: sha512-EEv7phLUTXkHIHdBFjMgqYTzKtRX0jASC974G64oe9CgYHkiSXUCjtbngzcpRroivErD+Y7+WCc2Vggtqklq/w==} - dependencies: - arconnect: 0.2.9 - axios: 0.21.4 - bignumber.js: 9.1.2 - redstone-smartweave: 0.3.10-alpha.19 - util: 0.12.5 - transitivePeerDependencies: - - debug - dev: false - /bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} dev: false @@ -2175,24 +2117,6 @@ packages: update-browserslist-db: 1.0.15(browserslist@4.23.0) dev: true - /bson@4.7.2: - resolution: {integrity: sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==} - engines: {node: '>=6.9.0'} - dependencies: - buffer: 5.7.1 - dev: false - - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: false - - /buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - dev: false - /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: @@ -2242,17 +2166,6 @@ packages: engines: {node: '>=8'} dev: true - /call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - dev: false - /call-me-maybe@1.0.2: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} dev: true @@ -2639,15 +2552,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - dev: false - /defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} dev: true @@ -2746,18 +2650,6 @@ packages: is-arrayish: 0.2.1 dev: true - /es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.4 - dev: false - - /es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - dev: false - /es-module-lexer@1.5.4: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} dev: true @@ -3447,22 +3339,6 @@ packages: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true - /follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false - - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - dependencies: - is-callable: 1.2.7 - dev: false - /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -3505,6 +3381,7 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: true /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} @@ -3525,17 +3402,6 @@ packages: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true - /get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - dev: false - /get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} @@ -3661,12 +3527,6 @@ packages: unicorn-magic: 0.1.0 dev: true - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - dependencies: - get-intrinsic: 1.2.4 - dev: false - /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true @@ -3686,34 +3546,12 @@ packages: engines: {node: '>=8'} dev: true - /has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - dependencies: - es-define-property: 1.0.0 - dev: false - - /has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - dev: false - - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - dev: false - - /has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - dev: false - /hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} dependencies: function-bind: 1.1.2 + dev: true /hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} @@ -3780,14 +3618,6 @@ packages: is-decimal: 1.0.4 dev: true - /is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - dev: false - /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true @@ -3806,11 +3636,6 @@ packages: builtin-modules: 3.3.0 dev: true - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: false - /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: @@ -3843,13 +3668,6 @@ packages: get-east-asian-width: 1.2.0 dev: true - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.2 - dev: false - /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -3886,13 +3704,6 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true - /is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - dependencies: - which-typed-array: 1.1.15 - dev: false - /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -3948,11 +3759,6 @@ packages: hasBin: true dev: true - /json-beautify@1.1.1: - resolution: {integrity: sha512-17j+Hk2lado0xqKtUcyAjK0AtoHnPSIgktWRsEXgdFQFG9UnaGw6CHa0J7xsvulxRpFl6CrkDFHght1p5ZJc4A==} - hasBin: true - dev: false - /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} dev: true @@ -4115,11 +3921,6 @@ packages: /lru-cache@10.2.2: resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} engines: {node: 14 || >=16.14} - dev: true - - /lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - dev: false /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -4621,11 +4422,6 @@ packages: hasBin: true dev: true - /possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - dev: false - /postcss-calc@10.0.0(postcss@8.4.38): resolution: {integrity: sha512-OmjhudoNTP0QleZCwl1i6NeBwN+5MZbY5ersLZz69mjJiDVv/p57RjRuKDkHeDWr4T+S97wQfsqRTNoDHB2e3g==} engines: {node: ^18.12 || ^20.9 || >=22.0} @@ -5029,23 +4825,6 @@ packages: picomatch: 2.3.1 dev: true - /redstone-smartweave@0.3.10-alpha.19: - resolution: {integrity: sha512-QL4OtSh0R+ip7D+obkoZm0Uvp1UKhk+DbbxlFVcdEV3QI3fwD/0wkClbFq6XZueJdOWkcPp6xA2VgUcmAVQ7qQ==} - engines: {node: '>=12'} - deprecated: 'Package has been renamed to warp-contracts. Following is no longer supported. To get the latest version please install warp-contracts: https://www.npmjs.com/package/warp-contracts.' - dependencies: - '@weavery/clarity': 0.1.5 - arweave: 1.15.1 - arweave-multihost: 0.1.0 - axios: 0.21.4 - bignumber.js: 9.1.2 - bson: 4.7.2 - json-beautify: 1.1.1 - tslog: 3.3.4 - transitivePeerDependencies: - - debug - dev: false - /refa@0.12.1: resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -5223,18 +5002,6 @@ packages: hasBin: true dev: true - /set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - dev: false - /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -5310,18 +5077,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - dev: false - - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: false - /spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: @@ -5559,13 +5314,6 @@ packages: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} dev: true - /tslog@3.3.4: - resolution: {integrity: sha512-N0HHuHE0e/o75ALfkioFObknHR5dVchUad4F0XyFf3gXJYB++DewEzwGI/uIOM216E5a43ovnRNEeQIq9qgm4Q==} - engines: {node: '>=10'} - dependencies: - source-map-support: 0.5.21 - dev: false - /tsx@4.16.5: resolution: {integrity: sha512-ArsiAQHEW2iGaqZ8fTA1nX0a+lN5mNTyuGRRO6OW3H/Yno1y9/t1f9YOI1Cfoqz63VAthn++ZYcbDP7jPflc+A==} engines: {node: '>=18.0.0'} @@ -5717,16 +5465,6 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - dependencies: - inherits: 2.0.4 - is-arguments: 1.1.1 - is-generator-function: 1.0.10 - is-typed-array: 1.1.13 - which-typed-array: 1.1.15 - dev: false - /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -5882,17 +5620,6 @@ packages: undici: 5.28.4 dev: false - /which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - dev: false - /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} diff --git a/src/cli.ts b/src/cli.ts index e2fd427..34901f2 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -12,7 +12,7 @@ import type { BundleResult, DeployResult, Tag } from './types' import { Logger } from './lib/logger' import { BuildError, DeployError } from './lib/error' import { loadAndBundleContracts } from './lib/loader' -import { clearBuildOutDir } from './lib/utils' +import { clearBuildOutDir, isLuaFile, parseToInt, parseUrl } from './lib/utils' const PKG_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '../') @@ -35,14 +35,6 @@ function getPackageJson() { return packageJson } -function parseToInt(value: string, defaultValue: number) { - const parsedValue = Number.parseInt(value) - if (Number.isNaN(parsedValue)) { - return defaultValue - } - return parsedValue -} - function logDeploymentDetails(result: DeployResult) { const { messageId, processId, isNewProcess, configName } = result const processUrl = chalk.green(`https://ao_marton.g8way.io/#/process/${processId}`) @@ -89,6 +81,9 @@ program .option('-p, --process-id [processId]', 'Specify process Id of an existing process.') .option('--build-only', 'Bundle the contract into a single file and store it in the process-dist directory.') .option('--out-dir [outDir]', 'Used with --build-only to output the single bundle contract file to a specified directory.') + .option('--gateway-url [url]', 'Custom Gateway URL to connect to.', parseUrl, 'https://arweave.net') + .option('--cu-url [url]', 'Custom Compute Unit (CU) URL to connect to.', parseUrl, 'https://cu.ao-testnet.xyz') + .option('--mu-url [url]', 'Custom Messenger Unit (MU) URL to connect to.', parseUrl, 'https://mu.ao-testnet.xyz') .option('--concurrency [limit]', 'Concurrency limit for deploying multiple processes.', parseToInt, 5) .option('--sqlite', 'Use sqlite aos module when spawning new process') .option('--retry-count [count]', 'Number of retries for deploying contract.', parseToInt, 10) @@ -98,7 +93,7 @@ program.parse(process.argv) const options = program.opts() const contractOrConfigPath = program.args[0] -const isContractPath = contractOrConfigPath.endsWith('.lua') +const isContractPath = isLuaFile(contractOrConfigPath) const isBuildOnly = options.buildOnly const outDir = options.outDir || './process-dist' @@ -133,6 +128,11 @@ async function deploymentHandler() { configName: options.name, processId: options.processId, sqlite: options.sqlite, + services: { + gatewayUrl: options.gatewayUrl, + cuUrl: options.cuUrl, + muUrl: options.muUrl, + }, }, ) logDeploymentDetails(result) diff --git a/src/lib/config.ts b/src/lib/config.ts index cf704cd..b8625b2 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -3,28 +3,35 @@ import process from 'node:process' import { fileURLToPath } from 'node:url' import createJITI from 'jiti' import type { Config, DeployConfig } from '../types' +import { isArweaveAddress, isCronPattern, isLuaFile, isUrl, jsonStringify } from './utils' +import { defaultLogger } from './logger' const __filename = fileURLToPath(import.meta.url) - const jiti = createJITI(__filename) export class ConfigManager { #config: Config = {} constructor(configPath: string) { - const loadedConfig = this.#load(configPath) - if (ConfigManager.isValidConfig(loadedConfig)) { - this.#config = loadedConfig - } - else { - throw new Error('Invalid config file loaded.') - } + this.#loadConfig(configPath) } - #load(configPath: string) { - const fullPath = path.join(process.cwd(), configPath) - const configs = jiti(fullPath) - return configs.default ?? configs + #loadConfig(configPath: string) { + try { + const fullPath = path.join(process.cwd(), configPath) + const loadedConfig = jiti(fullPath) + const config = loadedConfig.default ?? loadedConfig + + if (!ConfigManager.isValidConfig(config)) { + throw new Error('Invalid config file.') + } + + this.#config = config + } + catch (error: any) { + defaultLogger.error(error) + throw new Error('Failed to load a valid config file. Please check the logs for more details.') + } } static #isNonEmptyString(value: any): boolean { @@ -35,46 +42,94 @@ export class ConfigManager { return typeof value === 'string' } - static #validateTags(tags?: DeployConfig['tags']): boolean { - return tags === undefined || (Array.isArray(tags) && tags.length === 0) || (Array.isArray(tags) && tags.every(tag => + static #validateTags(tags: DeployConfig['tags'], keyName: string): boolean { + const isValid = tags === undefined || (Array.isArray(tags) && tags.length === 0) || (Array.isArray(tags) && tags.every(tag => tag && typeof tag === 'object' && this.#isNonEmptyString(tag.name) && this.#isNonEmptyString(tag.value), )) + + if (!isValid) { + throw new Error(`Invalid tags configuration for "${keyName}": \n${jsonStringify(tags)}`) + } + + return true } - static #validateRetry(retry?: DeployConfig['retry']): boolean { - return retry === undefined || ( + static #validateRetry(retry: DeployConfig['retry'], keyName: string): boolean { + const isValid = retry === undefined || ( typeof retry === 'object' && (retry.count === undefined || (typeof retry.count === 'number' && retry.count >= 0)) && (retry.delay === undefined || (typeof retry.delay === 'number' && retry.delay >= 0)) ) + + if (!isValid) { + throw new Error(`Invalid retry configuration for "${keyName}": \n${jsonStringify(retry)}`) + } + + return true + } + + static #validateServices(services: DeployConfig['services'], keyName: string): boolean { + const isValid = services === undefined || ( + typeof services === 'object' + && (services.gatewayUrl === undefined || isUrl(services?.gatewayUrl)) + && (services.cuUrl === undefined || isUrl(services?.cuUrl)) + && (services.muUrl === undefined || isUrl(services?.muUrl)) + ) + + if (!isValid) { + throw new Error(`Invalid services configuration for "${keyName}": \n${jsonStringify(services)}`) + } + + return true + } + + static #validateOptionalProps(deployConfig: DeployConfig, keyName: string): void { + const optionalAddressProps: (keyof DeployConfig)[] = ['module', 'scheduler', 'processId'] + const optionalStringProps: (keyof DeployConfig)[] = ['name', 'configName', 'luaPath', 'wallet', 'outDir'] + + optionalAddressProps.forEach((prop) => { + if (deployConfig[prop] && !isArweaveAddress(deployConfig[prop])) { + throw new Error(`Invalid optional property "${prop}" in configuration for "${keyName}": ${jsonStringify(deployConfig[prop])}`) + } + }) + + optionalStringProps.forEach((prop) => { + if (deployConfig[prop] && !this.#isString(deployConfig[prop])) { + throw new Error(`Invalid optional property "${prop}" in configuration for "${keyName}": ${jsonStringify(deployConfig[prop])}`) + } + }) } static isValidConfig(config: Config): boolean { // Check if config exists, is an object, and is not empty if (!config || typeof config !== 'object' || Object.keys(config).length === 0) { - return false + throw new Error('Config is missing or invalid.') } // Check if every entry in the object values has a 'contractPath' - return Object.values(config).every((deployConfig) => { - if (!deployConfig || typeof deployConfig !== 'object') { - return false + return Object.entries(config).every(([name, deployConfig]) => { + if (!deployConfig || typeof deployConfig !== 'object' || Object.keys(deployConfig).length === 0) { + throw new Error(`Invalid configuration for "${name}": \n${jsonStringify(deployConfig)}`) } - const requiredStringProps: (keyof DeployConfig)[] = ['contractPath', 'name'] - const optionalStringProps: (keyof DeployConfig)[] = ['module', 'scheduler', 'cron', 'luaPath', 'wallet', 'configName', 'processId', 'outDir'] + if (!isLuaFile(deployConfig.contractPath)) { + throw new Error(`A "*.lua" file is required for "contractPath" in configuration for "${name}".`) + } - const hasRequiredStrings = requiredStringProps.every(prop => this.#isNonEmptyString(deployConfig[prop])) - const hasOptionalStrings = optionalStringProps.every(prop => !deployConfig[prop] || this.#isString(deployConfig[prop])) + this.#validateOptionalProps(deployConfig, name) + this.#validateTags(deployConfig.tags, name) + this.#validateRetry(deployConfig.retry, name) + this.#validateServices(deployConfig.services, name) - const tagsValid = this.#validateTags(deployConfig.tags) - const retryValid = this.#validateRetry(deployConfig.retry) + if (deployConfig.cron && !isCronPattern(deployConfig.cron)) { + throw new Error(`Invalid cron value in configuration for "${name}": ${jsonStringify(deployConfig.cron)}`) + } - // Validate other types - const concurrencyValid = deployConfig.concurrency === undefined || Number.isInteger(deployConfig.concurrency) - const sqliteValid = deployConfig.sqlite === undefined || typeof deployConfig.sqlite === 'boolean' + if (deployConfig.sqlite !== undefined && typeof deployConfig.sqlite !== 'boolean') { + throw new Error(`Invalid sqlite value in configuration for "${name}": ${jsonStringify(deployConfig.sqlite)}`) + } - return hasRequiredStrings && hasOptionalStrings && tagsValid && retryValid && concurrencyValid && sqliteValid + return true }) } @@ -93,11 +148,12 @@ export class ConfigManager { getDeployConfigs(deploy: string) { const configNames = (deploy ?? '').split(',').map((name: string) => name.trim()).filter(Boolean) const config = this.getConfigFromNames(configNames) + if (Object.keys(config).length === 0) { - throw new Error(`Config file doesn't have names from ${deploy}`) + throw new Error(`No matching configurations found for "${deploy}". Please verify the configuration names.`) } - const deployConfigs = Object.entries(config).map(([name, config]) => ({ ...config, configName: name })) - return deployConfigs + + return Object.entries(config).map(([name, config]) => ({ ...config, configName: name })) } } @@ -109,7 +165,8 @@ export class ConfigManager { */ export function defineConfig(config: Config) { if (!ConfigManager.isValidConfig(config)) { - throw new Error('Invalid config file loaded.') + throw new Error('Invalid config file loaded. Please check the logs for more details.') } + return config } diff --git a/src/lib/deploy.ts b/src/lib/deploy.ts index 85fb0cc..c832fa1 100644 --- a/src/lib/deploy.ts +++ b/src/lib/deploy.ts @@ -1,14 +1,20 @@ -import { - createDataItemSigner, - message, - result, - spawn, -} from '@permaweb/aoconnect' +import * as aoconnect from '@permaweb/aoconnect' import pLimit from 'p-limit' -import type { AosConfig, DeployConfig, DeployResult } from '../types' +import type { AosConfig, DeployConfig, DeployResult, Services } from '../types' import { Wallet } from './wallet' import { LuaProjectLoader } from './loader' -import { ardb, isArweaveAddress, retryWithDelay, sleep } from './utils' +import { + AOS_QUERY, + APP_NAME, + defaultServices, + getArweave, + isArweaveAddress, + isCronPattern, + isUrl, + parseToInt, + retryWithDelay, + sleep, +} from './utils' import { Logger } from './logger' /** @@ -17,7 +23,36 @@ import { Logger } from './logger' export class DeploymentsManager { #cachedAosConfig: AosConfig | null = null - async #getAosDetails() { + #validateServices(services?: Services) { + // Validate and use provided URLs or fall back to defaults + const { gatewayUrl, cuUrl, muUrl } = services ?? {} + + services = { + gatewayUrl: isUrl(gatewayUrl) ? gatewayUrl : defaultServices.gatewayUrl, + cuUrl: isUrl(cuUrl) ? cuUrl : defaultServices.cuUrl, + muUrl: isUrl(muUrl) ? muUrl : defaultServices.muUrl, + } + + return services + } + + #getAoInstance(services: Services) { + if ( + (!services.cuUrl || services.cuUrl === defaultServices.cuUrl) + && (!services.gatewayUrl || services.gatewayUrl === defaultServices.gatewayUrl) + && (!services.muUrl || services.muUrl === defaultServices.muUrl) + ) { + return aoconnect + } + + return aoconnect.connect({ + GATEWAY_URL: services.gatewayUrl, + MU_URL: services.muUrl, + CU_URL: services.cuUrl, + }) + } + + async #getAosConfig() { if (this.#cachedAosConfig) { return this.#cachedAosConfig } @@ -26,6 +61,7 @@ export class DeploymentsManager { module: 'cNlipBptaF9JeFAf4wUmpi43EojNanIBos3EfNrEOWo', sqliteModule: 'u1Ju_X8jiuq4rX9Nh-ZGRQuYQZgV2MKLMT3CZsykk54', scheduler: '_GQ33BkPtZrqxA84vM8Zk-N2aO0toNNu_C-l-rawrBA', + authority: 'fcoN_xJeisVsPXA-trzVAuIiqO3ydLQxM-L4XbrQKzY', } try { @@ -35,6 +71,7 @@ export class DeploymentsManager { module: config?.module || defaultDetails.module, sqliteModule: config?.sqliteModule || defaultDetails.sqliteModule, scheduler: config?.scheduler || defaultDetails.scheduler, + authority: defaultDetails.authority, } return this.#cachedAosConfig } @@ -43,28 +80,30 @@ export class DeploymentsManager { } } - async #findProcess(name: string, owner: string, retry: DeployConfig['retry']) { - const tx = await retryWithDelay( - () => ardb - .search('transactions') - .from(owner) - .only('id') - .tags([ - { name: 'Data-Protocol', values: ['ao'] }, - { name: 'Type', values: ['Process'] }, - { name: 'Name', values: [name] }, - ]) - .findOne(), + async #findProcess(name: string, owner: string, retry: DeployConfig['retry'], gateway: string) { + const processId = await retryWithDelay( + async () => { + const res = await getArweave(gateway) + .api + .post('/graphql', { + query: AOS_QUERY, + variables: { owners: [owner], names: [name] }, + }) + if (!res.ok || res?.data?.data === null) { + throw new Error(`(${res.status}) ${res.statusText} - GraphQL ERROR`) + } + return res?.data?.data?.transactions?.edges?.[0]?.node?.id + }, retry?.count, retry?.delay, ) - return tx?.id + return processId } #validateCron(cron: string) { - const cronRegex = /^\d+-(?:Second|second|Minute|minute|Hour|hour|Day|day|Month|month|Year|year|Block|block)s?$/ - if (!cronRegex.test(cron)) { + const isCronValid = isCronPattern(cron) + if (!isCronValid) { throw new Error('Invalid cron flag!') } } @@ -74,25 +113,26 @@ export class DeploymentsManager { * @param {DeployConfig} deployConfig - Configuration options for the deployment. * @returns {Promise} The result of the deployment. */ - async deployContract({ name, wallet, contractPath, tags, cron, module, scheduler, retry, luaPath, configName, processId, sqlite }: DeployConfig): Promise { + async deployContract({ name, wallet, contractPath, tags, cron, module, scheduler, retry, luaPath, configName, processId, sqlite, services }: DeployConfig): Promise { name = name || 'default' configName = configName || name - retry = { - count: typeof retry?.count === 'number' && retry.count >= 0 ? retry.count : 10, - delay: typeof retry?.delay === 'number' && retry.delay >= 0 ? retry.delay : 3000, - } + retry = { count: parseToInt(retry?.count, 10), delay: parseToInt(retry?.delay, 3000) } const logger = new Logger(configName) - const aosDetails = await this.#getAosDetails() - module = isArweaveAddress(module) ? module! : sqlite ? aosDetails.sqliteModule : aosDetails.module - scheduler = isArweaveAddress(scheduler) ? scheduler! : aosDetails.scheduler + const aosConfig = await this.#getAosConfig() + module = isArweaveAddress(module) ? module! : sqlite ? aosConfig.sqliteModule : aosConfig.module + scheduler = isArweaveAddress(scheduler) ? scheduler! : aosConfig.scheduler const walletInstance = await Wallet.load(wallet) const owner = await walletInstance.getAddress() - const signer = createDataItemSigner(walletInstance.jwk) + const signer = aoconnect.createDataItemSigner(walletInstance.jwk) + services = this.#validateServices(services) + + // Initialize the AO instance with validated URLs + const aoInstance = this.#getAoInstance(services) if (!processId || (processId && !isArweaveAddress(processId))) { - processId = await this.#findProcess(name, owner, retry) + processId = await this.#findProcess(name, owner, retry, services.gatewayUrl!) } const isNewProcess = !processId @@ -101,10 +141,10 @@ export class DeploymentsManager { logger.log('Spawning new process...', false, true) tags = Array.isArray(tags) ? tags : [] tags = [ - { name: 'App-Name', value: 'ao-deploy' }, + { name: 'App-Name', value: APP_NAME }, { name: 'Name', value: name }, { name: 'aos-Version', value: 'REPLACE-AO-DEPLOY-VERSION' }, - { name: 'Authority', value: 'fcoN_xJeisVsPXA-trzVAuIiqO3ydLQxM-L4XbrQKzY' }, + { name: 'Authority', value: aosConfig.authority }, ...tags, ] @@ -115,7 +155,7 @@ export class DeploymentsManager { const data = '1984' processId = await retryWithDelay( - () => spawn({ module, signer, tags, data, scheduler }), + () => aoInstance.spawn({ module, signer, tags, data, scheduler }), retry.count, retry.delay, ) @@ -132,7 +172,7 @@ export class DeploymentsManager { // Load contract to process const messageId = await retryWithDelay( async () => - message({ + aoInstance.message({ process: processId, tags: [{ name: 'Action', value: 'Eval' }], data: contractSrc, @@ -143,7 +183,7 @@ export class DeploymentsManager { ) const { Output, Error: error } = await retryWithDelay( - async () => result({ + async () => aoInstance.result({ process: processId, message: messageId, }), diff --git a/src/lib/loader.ts b/src/lib/loader.ts index f703233..ba873de 100644 --- a/src/lib/loader.ts +++ b/src/lib/loader.ts @@ -178,7 +178,7 @@ export class LuaProjectLoader { return line.trim() } else { - throw new Error(chalk.red('It requires a *.lua file')) + throw new Error(chalk.red('A `*.lua` file is required.')) } } diff --git a/src/lib/logger.ts b/src/lib/logger.ts index 3c5a432..66f5040 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -1,4 +1,5 @@ import chalk from 'chalk' +import { APP_NAME } from './utils' export class Logger { static #instances: Map = new Map() @@ -49,3 +50,5 @@ export class Logger { this.#getInstance(name).error(message, prefixNewLine, suffixNewLine) } } + +export const defaultLogger = Logger.init(APP_NAME) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 7ac3e01..5de8b56 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,16 +2,50 @@ import path from 'node:path' import process from 'node:process' import { writeFile } from 'node:fs/promises' import { existsSync, mkdirSync, rmSync } from 'node:fs' +import { URL } from 'node:url' import Arweave from 'arweave' -import Ardb from 'ardb' +export const APP_NAME = 'ao-deploy' + +/** + * Initializes a default Arweave instance. + */ export const arweave = Arweave.init({ host: 'arweave.net', port: 443, protocol: 'https', }) -export const ardb: Ardb = new ((Ardb as any)?.default ?? Ardb)(arweave) +/** + * Parses a gateway URL and returns an object containing the host, port, and protocol. + * + * @param url - The gateway URL to be parsed. + * @returns An object with the host, port, and protocol of the URL. + */ +function parseGatewayUrl(url: string): { host: string, port: number, protocol: string } { + const parsedUrl = new URL(url) + return { + host: parsedUrl.hostname, + port: parsedUrl.port ? Number.parseInt(parsedUrl.port, 10) : 443, + protocol: parsedUrl.protocol.replace(':', ''), + } +} + +/** + * Initializes an Arweave instance with a custom gateway. + * + * @param gateway - The gateway URL to connect to. + * @returns An Arweave instance configured with the provided gateway. + */ +export function getArweave(gateway: string) { + try { + const { host, port, protocol } = parseGatewayUrl(gateway) + return Arweave.init({ host, port, protocol }) + } + catch { + return arweave + } +} export function isArweaveAddress(address: any): boolean { return typeof address === 'string' && /^[\w-]{43}$/.test(address) @@ -83,3 +117,109 @@ export async function clearBuildOutDir(outDir: string) { throw new Error(`Failed to clear ${outDir}`) } } + +/** + * Checks if a string is a valid URL. + * + * @param url - The string to be checked. + * @returns True if the string is a valid URL, false otherwise. + */ +export function isUrl(url?: string): boolean { + try { + if (!url || typeof url !== 'string') { + return false + } + // eslint-disable-next-line no-new + new URL(url) + return true + } + catch { + return false + } +} + +/** + * Parses a string to an integer. + * If parsing fails (i.e., the value is NaN), it returns the specified default value. + * + * @param value - The string to be parsed. + * @param defaultValue - The default value to return if parsing fails. + * @returns The parsed integer or the default value if parsing fails. + */ +export function parseToInt(value: string | number | undefined, defaultValue: number): number { + if (value === undefined) { + return defaultValue + } + const parsedValue = Number.parseInt(value.toString()) + if (Number.isNaN(parsedValue)) { + return defaultValue + } + return parsedValue +} + +/** + * Validates a URL string. + * If the URL is not valid, it returns the specified default value. + * + * @param value - The URL string to be validated. + * @param defaultValue - The default value to return if the URL is not valid. + * @returns The URL if valid, or the default value if the URL is not valid. + */ +export function parseUrl(value: string | undefined, defaultValue: string): string { + if (value === undefined) { + return defaultValue + } + const urlValid = isUrl(value) + if (!urlValid) { + return defaultValue + } + return value +} + +export const defaultServices = { + gatewayUrl: 'https://arweave.net', + cuUrl: 'https://cu.ao-testnet.xyz', + muUrl: 'https://mu.ao-testnet.xyz', +} + +export function jsonStringify(value?: any): string { + try { + return JSON.stringify(value, null, 2) + } + catch { + return value + } +} + +export function isLuaFile(fileName: string): boolean { + if (!fileName) { + return false + } + return fileName.toLowerCase().endsWith('.lua') +} + +export function isCronPattern(cron: string): boolean { + if (!cron) { + return false + } + const cronRegex = /^\d+-(?:Second|second|Minute|minute|Hour|hour|Day|day|Month|month|Year|year|Block|block)s?$/ + return cronRegex.test(cron) +} + +export const AOS_QUERY = `query ($owners: [String!]!, $names: [String!]!) { + transactions( + first: 1, + owners: $owners, + tags: [ + { name: "Data-Protocol", values: ["ao"] }, + { name: "Type", values: ["Process"]}, + { name: "Name", values: $names} + ] + ) { + edges { + node { + id + } + } + } + }` diff --git a/src/types/index.ts b/src/types/index.ts index e019090..ef4df04 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -4,34 +4,65 @@ export type ConfigName = string export interface Tag { name: string, value: string } +export interface Services { + /** + * The URL of the desired Gateway. + * @default "https://arweave.net" + */ + gatewayUrl?: string + + /** + * The URL of the desired AO Compute Unit. + * @default "https://cu.ao-testnet.xyz" + */ + cuUrl?: string + + /** + * The URL of the desired AO Messenger Unit. + * @default "https://mu.ao-testnet.xyz" + */ + muUrl?: string +} + export interface DeployConfig { /** * Process name to spawn * @default "default" */ name?: string + + /** + * Config name used for logging + */ + configName?: string + /** * Path to contract main file */ contractPath: string + /** * The module source to use to spin up Process * @default "Fetches from `https://raw.githubusercontent.com/pawanpaudel93/ao-deploy-config/main/config.json`" */ module?: string + /** * Scheduler to use for Process * @default "_GQ33BkPtZrqxA84vM8Zk-N2aO0toNNu_C-l-rawrBA" */ scheduler?: string + /** * Additional tags to use for spawning Process */ tags?: Tag[] + /** * Cron interval to use for Process i.e (1-minute, 5-minutes) */ cron?: string + /** * Wallet path or JWK itself */ @@ -42,11 +73,6 @@ export interface DeployConfig { */ luaPath?: string - /** - * Config name used for logging - */ - configName?: string - /** * Retry options */ @@ -62,22 +88,27 @@ export interface DeployConfig { */ delay?: number } - /** - * Concurrency limit to deploy multiple processes - */ - concurrency?: number + /** * Process Id of an existing process */ processId?: string + /** * Output directory of bundle */ outDir?: string + /** * Use sqlite aos module when spawning new process + * @default false */ sqlite?: boolean + + /** + * Configuration for various AO services + */ + services?: Services } export type Config = Record @@ -110,4 +141,5 @@ export interface AosConfig { module: string sqliteModule: string scheduler: string + authority: string }