diff --git a/.gitignore b/.gitignore index ed7805a1..d5965e31 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,10 @@ .dev/ .mint +# Node / pnpm +node_modules/ +.pnpm-store/ + # Swift / Xcode / SwiftPM .build/ .swiftpm/ diff --git a/dev.yml b/dev.yml index 0ca0c43d..51bfbb8a 100644 --- a/dev.yml +++ b/dev.yml @@ -2,7 +2,6 @@ name: checkout-kit up: - packages: - - quicktype - rover - mint - xcbeautify @@ -56,6 +55,7 @@ up: version: v22.14.0 package_manager: pnpm@10.33.1 packages: + - protocol - platforms/react-native - platforms/web - custom: @@ -105,6 +105,9 @@ commands: update-upstream: desc: "Update vendored UCP snapshot files from upstream" run: ./protocol/scripts/update_ucp_snapshot.sh "$@" + check-tools: + desc: "Verify protocol codegen tools match protocol/package.json" + run: ./protocol/scripts/check_codegen_tools.sh "$@" apollo: subcommands: download_schema: diff --git a/protocol/package.json b/protocol/package.json new file mode 100644 index 00000000..cbdcc13b --- /dev/null +++ b/protocol/package.json @@ -0,0 +1,17 @@ +{ + "name": "@shopify/checkout-kit-protocol-tooling", + "version": "0.0.0", + "private": true, + "license": "MIT", + "description": "Tooling for generating Shopify Checkout Kit protocol models.", + "packageManager": "pnpm@10.33.1+sha512.05ba3c1d5d1c18f68df06470d74055e62d41fc110a0c660db1b2dfb2785327f04cf0f68345d4609bc52089e7fa0343c31593b2f9594e2c5d5da426230acc9820", + "scripts": { + "check-tools": "./scripts/check_codegen_tools.sh", + "codegen:kotlin": "./scripts/generate_models.sh --lang kotlin", + "codegen:swift": "./scripts/generate_models.sh --lang swift", + "codegen:typescript": "./scripts/generate_models.sh --lang typescript" + }, + "devDependencies": { + "quicktype": "23.2.6" + } +} diff --git a/protocol/pnpm-lock.yaml b/protocol/pnpm-lock.yaml new file mode 100644 index 00000000..0491d303 --- /dev/null +++ b/protocol/pnpm-lock.yaml @@ -0,0 +1,838 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + quicktype: + specifier: 23.2.6 + version: 23.2.6 + +packages: + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@glideapps/ts-necessities@2.2.3': + resolution: {integrity: sha512-gXi0awOZLHk3TbW55GZLCPP6O+y/b5X1pBXKBVckFONSwF1z1E5ND2BGJsghQFah+pW7pkkyFb2VhUQI2qhL5w==} + + '@glideapps/ts-necessities@2.4.0': + resolution: {integrity: sha512-mDC+qosuNa4lxR3ioMBb6CD0XLRsQBplU+zRPUYiMLXKeVPZ6UYphdNG/EGReig0YyfnVlBKZEXl1wzTotYmPA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@mark.probst/typescript-json-schema@0.55.0': + resolution: {integrity: sha512-jI48mSnRgFQxXiE/UTUCVCpX8lK3wCFKLF1Ss2aEreboKNuLQGt3e0/YFqWVHe/WENxOaqiJvwOz+L/SrN2+qQ==} + hasBin: true + + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@16.18.126': + resolution: {integrity: sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + array-back@3.1.0: + resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} + engines: {node: '>=6'} + + array-back@6.2.3: + resolution: {integrity: sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw==} + engines: {node: '>=12.17'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + browser-or-node@3.0.0: + resolution: {integrity: sha512-iczIdVJzGEYhP5DqQxYM9Hh7Ztpqqi+CXZpSmX8ALFs9ecXkQIeqRyM6TfxEfMVpwhl3dSuDvxdzzo9sUOIVBQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + chalk-template@0.4.0: + resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} + engines: {node: '>=12'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + collection-utils@1.0.1: + resolution: {integrity: sha512-LA2YTIlR7biSpXkKYwwuzGjwL5rjWEZVOSnvdUc7gObvWe4WkjxOpfrdhoP7Hs09YWDVfg0Mal9BpAqLfVEzQg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + command-line-args@5.2.1: + resolution: {integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==} + engines: {node: '>=4.0.0'} + + command-line-usage@7.0.4: + resolution: {integrity: sha512-85UdvzTNx/+s5CkSgBm/0hzP80RFHAa7PsfeADE5ezZF3uHz3/Tqj9gIKGT9PTtpycc3Ua64T0oVulGfKxzfqg==} + engines: {node: '>=12.20.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} + + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + engines: {node: '>=0.3.1'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + find-replace@3.0.0: + resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==} + engines: {node: '>=4.0.0'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + graphql@0.11.7: + resolution: {integrity: sha512-x7uDjyz8Jx+QPbpCFCMQ8lltnQa4p4vSYHx6ADe8rVYRTdsyhCJbvSty5DAsLVmU6cGakl+r8HQYolKHxk/tiw==} + deprecated: 'No longer supported; please update to a newer version. Details: https://github.com/graphql/graphql-js#version-support' + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + + iterall@1.1.3: + resolution: {integrity: sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==} + + js-base64@3.7.8: + resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + path-equal@1.2.5: + resolution: {integrity: sha512-i73IctDr3F2W+bsOWDyyVm/lqsXO47aY9nsFZUjTT/aljSbkxHxxCoyZ9UUrM8jK0JVod+An+rl48RCsvWM+9g==} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + quicktype-core@23.2.6: + resolution: {integrity: sha512-asfeSv7BKBNVb9WiYhFRBvBZHcRutPRBwJMxW0pefluK4kkKu4lv0IvZBwFKvw2XygLcL1Rl90zxWDHYgkwCmA==} + + quicktype-graphql-input@23.2.6: + resolution: {integrity: sha512-jHQ8XrEaccZnWA7h/xqUQhfl+0mR5o91T6k3I4QhlnZSLdVnbycrMq4FHa9EaIFcai783JKwSUl1+koAdJq4pg==} + + quicktype-typescript-input@23.2.6: + resolution: {integrity: sha512-dCNMxR+7PGs9/9Tsth9H6LOQV1G+Tv4sUGT8ZUfDRJ5Hq371qOYLma5BnLX6VxkPu8JT7mAMpQ9VFlxstX6Qaw==} + + quicktype@23.2.6: + resolution: {integrity: sha512-rlD1jF71bOmDn6SQ/ToLuuRkMQ7maxo5oVTn5dPCl11ymqoJCFCvl7FzRfh+fkDFmWt2etl+JiIEdWImLxferA==} + engines: {node: '>=18.12.0'} + hasBin: true + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + stream-chain@2.2.5: + resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} + + stream-json@1.8.0: + resolution: {integrity: sha512-HZfXngYHUAr1exT4fxlbc1IOce1RYxp2ldeaf97LYCOPSoOqY/1Psp7iGvpb+6JIOgkra9zDYnPX01hGAHzEPw==} + + string-to-stream@3.0.1: + resolution: {integrity: sha512-Hl092MV3USJuUCC6mfl9sPzGloA3K5VwdIeJjYIkXY/8K+mUvaeEabWJgArp+xXrsWxCajeT2pc4axbVhIZJyg==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + table-layout@4.1.1: + resolution: {integrity: sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==} + engines: {node: '>=12.17'} + + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + typescript@4.9.4: + resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + typical@4.0.0: + resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==} + engines: {node: '>=8'} + + typical@7.3.0: + resolution: {integrity: sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==} + engines: {node: '>=12.17'} + + unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + + urijs@1.19.11: + resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wordwrapjs@5.1.1: + resolution: {integrity: sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==} + engines: {node: '>=12.17'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + +snapshots: + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@glideapps/ts-necessities@2.2.3': {} + + '@glideapps/ts-necessities@2.4.0': {} + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mark.probst/typescript-json-schema@0.55.0': + dependencies: + '@types/json-schema': 7.0.15 + '@types/node': 16.18.126 + glob: 7.2.3 + path-equal: 1.2.5 + safe-stable-stringify: 2.5.0 + ts-node: 10.9.2(@types/node@16.18.126)(typescript@4.9.4) + typescript: 4.9.4 + yargs: 17.7.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + + '@tsconfig/node10@1.0.12': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@16.18.126': {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn-walk@8.3.5: + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + arg@4.1.3: {} + + array-back@3.1.0: {} + + array-back@6.2.3: {} + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + browser-or-node@3.0.0: {} + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + chalk-template@0.4.0: + dependencies: + chalk: 4.1.2 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + collection-utils@1.0.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + command-line-args@5.2.1: + dependencies: + array-back: 3.1.0 + find-replace: 3.0.0 + lodash.camelcase: 4.3.0 + typical: 4.0.0 + + command-line-usage@7.0.4: + dependencies: + array-back: 6.2.3 + chalk-template: 0.4.0 + table-layout: 4.1.1 + typical: 7.3.0 + + concat-map@0.0.1: {} + + create-require@1.1.1: {} + + cross-fetch@4.1.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + diff@4.0.4: {} + + emoji-regex@8.0.0: {} + + escalade@3.2.0: {} + + event-target-shim@5.0.1: {} + + events@3.3.0: {} + + find-replace@3.0.0: + dependencies: + array-back: 3.1.0 + + fs.realpath@1.0.0: {} + + get-caller-file@2.0.5: {} + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + graphql@0.11.7: + dependencies: + iterall: 1.1.3 + + has-flag@4.0.0: {} + + ieee754@1.2.1: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-fullwidth-code-point@3.0.0: {} + + is-url@1.2.4: {} + + iterall@1.1.3: {} + + js-base64@3.7.8: {} + + lodash.camelcase@4.3.0: {} + + lodash@4.18.1: {} + + make-error@1.3.6: {} + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + moment@2.30.1: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + pako@0.2.9: {} + + pako@1.0.11: {} + + path-equal@1.2.5: {} + + path-is-absolute@1.0.1: {} + + pluralize@8.0.0: {} + + process@0.11.10: {} + + quicktype-core@23.2.6: + dependencies: + '@glideapps/ts-necessities': 2.2.3 + browser-or-node: 3.0.0 + collection-utils: 1.0.1 + cross-fetch: 4.1.0 + is-url: 1.2.4 + js-base64: 3.7.8 + lodash: 4.18.1 + pako: 1.0.11 + pluralize: 8.0.0 + readable-stream: 4.5.2 + unicode-properties: 1.4.1 + urijs: 1.19.11 + wordwrap: 1.0.0 + yaml: 2.9.0 + transitivePeerDependencies: + - encoding + + quicktype-graphql-input@23.2.6: + dependencies: + collection-utils: 1.0.1 + graphql: 0.11.7 + quicktype-core: 23.2.6 + transitivePeerDependencies: + - encoding + + quicktype-typescript-input@23.2.6: + dependencies: + '@mark.probst/typescript-json-schema': 0.55.0 + quicktype-core: 23.2.6 + typescript: 4.9.5 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - encoding + + quicktype@23.2.6: + dependencies: + '@glideapps/ts-necessities': 2.4.0 + chalk: 4.1.2 + collection-utils: 1.0.1 + command-line-args: 5.2.1 + command-line-usage: 7.0.4 + cross-fetch: 4.1.0 + graphql: 0.11.7 + lodash: 4.18.1 + moment: 2.30.1 + quicktype-core: 23.2.6 + quicktype-graphql-input: 23.2.6 + quicktype-typescript-input: 23.2.6 + readable-stream: 4.7.0 + stream-json: 1.8.0 + string-to-stream: 3.0.1 + typescript: 5.8.3 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - encoding + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-stream@4.5.2: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + require-directory@2.1.1: {} + + safe-buffer@5.2.1: {} + + safe-stable-stringify@2.5.0: {} + + stream-chain@2.2.5: {} + + stream-json@1.8.0: + dependencies: + stream-chain: 2.2.5 + + string-to-stream@3.0.1: + dependencies: + readable-stream: 3.6.2 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + table-layout@4.1.1: + dependencies: + array-back: 6.2.3 + wordwrapjs: 5.1.1 + + tiny-inflate@1.0.3: {} + + tr46@0.0.3: {} + + ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 16.18.126 + acorn: 8.16.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 4.9.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + typescript@4.9.4: {} + + typescript@4.9.5: {} + + typescript@5.8.3: {} + + typical@4.0.0: {} + + typical@7.3.0: {} + + unicode-properties@1.4.1: + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + + urijs@1.19.11: {} + + util-deprecate@1.0.2: {} + + v8-compile-cache-lib@3.0.1: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + wordwrap@1.0.0: {} + + wordwrapjs@5.1.1: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + y18n@5.0.8: {} + + yaml@2.9.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yn@3.1.1: {} diff --git a/protocol/scripts/check_codegen_tools.mjs b/protocol/scripts/check_codegen_tools.mjs new file mode 100755 index 00000000..87567a3e --- /dev/null +++ b/protocol/scripts/check_codegen_tools.mjs @@ -0,0 +1,72 @@ +#!/usr/bin/env node +/* + * MIT License + * + * Copyright 2023-present, Shopify Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import {requireQuicktype} from "./codegen_tools.mjs"; + +function usage() { + console.error(`Usage: check_codegen_tools.sh [--quiet] + +Verifies that protocol codegen tools are installed from the repo-local pnpm +dependencies and match the exact versions in protocol/package.json.`); +} + +function parseArgs(argv) { + let quiet = false; + + for (let index = 0; index < argv.length;) { + const arg = argv[index]; + switch (arg) { + case "--quiet": + quiet = true; + index += 1; + break; + case "-h": + case "--help": + usage(); + process.exit(0); + break; + default: + console.error(`Unknown argument: ${arg}`); + usage(); + process.exit(2); + } + } + + return {quiet}; +} + +async function main() { + const {quiet} = parseArgs(process.argv.slice(2)); + + await requireQuicktype(); + + if (!quiet) { + console.log("Protocol codegen tools are installed and match protocol/package.json."); + } +} + +main().catch((error) => { + console.error(error.message); + process.exit(1); +}); diff --git a/protocol/scripts/check_codegen_tools.sh b/protocol/scripts/check_codegen_tools.sh new file mode 100755 index 00000000..b44e090b --- /dev/null +++ b/protocol/scripts/check_codegen_tools.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec node "${SCRIPT_DIR}/check_codegen_tools.mjs" "$@" diff --git a/protocol/scripts/codegen_tools.mjs b/protocol/scripts/codegen_tools.mjs new file mode 100644 index 00000000..7be4d804 --- /dev/null +++ b/protocol/scripts/codegen_tools.mjs @@ -0,0 +1,108 @@ +/* + * MIT License + * + * Copyright 2023-present, Shopify Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import {spawn} from "node:child_process"; +import {constants as fsConstants} from "node:fs"; +import fs from "node:fs/promises"; +import path from "node:path"; +import {fileURLToPath} from "node:url"; + +export const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url)); +export const PROTOCOL_DIR = path.resolve(SCRIPT_DIR, ".."); +export const REPO_ROOT = path.resolve(PROTOCOL_DIR, ".."); +export const PACKAGE_JSON = path.join(PROTOCOL_DIR, "package.json"); +export const QUICKTYPE_BIN = path.join(PROTOCOL_DIR, "node_modules", ".bin", "quicktype"); + +export function run(command, args, options = {}) { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd: options.cwd ?? REPO_ROOT, + stdio: options.capture ? ["ignore", "pipe", "pipe"] : "inherit", + }); + + let stdout = ""; + let stderr = ""; + if (options.capture) { + child.stdout.setEncoding("utf8"); + child.stderr.setEncoding("utf8"); + child.stdout.on("data", (chunk) => { + stdout += chunk; + }); + child.stderr.on("data", (chunk) => { + stderr += chunk; + }); + } + + child.on("error", reject); + child.on("close", (code) => { + if (code === 0) { + resolve({stdout, stderr}); + } else { + const error = new Error(`${command} ${args.join(" ")} exited with ${code}`); + error.code = code; + error.stdout = stdout; + error.stderr = stderr; + reject(error); + } + }); + }); +} + +export async function readJson(file) { + return JSON.parse(await fs.readFile(file, "utf8")); +} + +async function fileIsExecutable(file) { + try { + await fs.access(file, fsConstants.X_OK); + return true; + } catch { + return false; + } +} + +export async function requireQuicktype() { + if (!(await fileIsExecutable(QUICKTYPE_BIN))) { + throw new Error(`quicktype is required at ${QUICKTYPE_BIN}. Run dev up or (cd protocol && pnpm install).`); + } + + const packageJson = await readJson(PACKAGE_JSON); + const expected = packageJson.devDependencies?.quicktype ?? ""; + if (expected === "") { + throw new Error(`Missing quicktype version in ${PACKAGE_JSON}`); + } + + if (!/^[0-9]+[.][0-9]+[.][0-9]+(-[0-9A-Za-z.-]+)?$/.test(expected)) { + throw new Error(`quicktype must use an exact version in ${PACKAGE_JSON}; found ${expected}.`); + } + + const {stdout} = await run(QUICKTYPE_BIN, ["--version"], {capture: true}); + const actual = stdout.match(/^quicktype version (\S+)/m)?.[1] ?? ""; + if (actual === "") { + throw new Error(`Unable to determine quicktype version from ${QUICKTYPE_BIN}`); + } + + if (actual !== expected) { + throw new Error(`Unsupported quicktype version: ${actual}. Expected ${expected} from ${PACKAGE_JSON}.`); + } +} diff --git a/protocol/scripts/generate_models.mjs b/protocol/scripts/generate_models.mjs new file mode 100755 index 00000000..80ce5da3 --- /dev/null +++ b/protocol/scripts/generate_models.mjs @@ -0,0 +1,362 @@ +#!/usr/bin/env node +/* + * MIT License + * + * Copyright 2023-present, Shopify Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; + +import { + PROTOCOL_DIR, + QUICKTYPE_BIN, + REPO_ROOT, + readJson, + requireQuicktype, + run, +} from "./codegen_tools.mjs"; + +const SCHEMA_SOURCE_DIR = path.join(PROTOCOL_DIR, "schemas"); +const SERVICES_DIR = path.join(PROTOCOL_DIR, "services", "shopping"); + +function usage() { + console.error("Usage: generate_models.sh --lang [--output ]"); +} + +function parseArgs(argv) { + let lang = ""; + let output = ""; + + for (let index = 0; index < argv.length;) { + const arg = argv[index]; + switch (arg) { + case "--lang": + if (index + 1 >= argv.length) { + throw new Error("Missing value for --lang"); + } + lang = normalizeLang(argv[index + 1]); + index += 2; + break; + case "--output": + if (index + 1 >= argv.length) { + throw new Error("Missing value for --output"); + } + output = argv[index + 1]; + index += 2; + break; + default: + throw new Error(`Unknown argument: ${arg}`); + } + } + + if (lang === "") { + usage(); + process.exit(1); + } + + return {lang, output}; +} + +function normalizeLang(lang) { + switch (lang) { + case "kotlin": + case "swift": + case "typescript": + return lang; + case "ts": + return "typescript"; + default: + throw new Error(`Unsupported language: ${lang}. Use kotlin, swift, or typescript.`); + } +} + +async function writeJson(file, value) { + await fs.mkdir(path.dirname(file), {recursive: true}); + await fs.writeFile(file, `${JSON.stringify(value, null, 2)}\n`); +} + +function rewriteRefs(value) { + if (Array.isArray(value)) { + for (const item of value) { + rewriteRefs(item); + } + return; + } + + if (value === null || typeof value !== "object") { + return; + } + + if (typeof value.$ref === "string") { + value.$ref = value.$ref.replaceAll("../../schemas/shopping/", ""); + } + + for (const item of Object.values(value)) { + rewriteRefs(item); + } +} + +function ensureObjectPath(root, pathSegments) { + let target = root; + for (const segment of pathSegments) { + if (target[segment] === undefined) { + target[segment] = {}; + } + target = target[segment]; + } + return target; +} + +async function prepareCodegenSchemas(tempDir) { + const schemaDir = path.join(tempDir, "schemas"); + await fs.cp(SCHEMA_SOURCE_DIR, schemaDir, {recursive: true}); + return path.join(schemaDir, "shopping"); +} + +async function extractResultSchema(specDir, methodName, outputFile, rootTitle, checkoutTitle, paymentTitle) { + const service = await readJson(path.join(SERVICES_DIR, "embedded.openrpc.json")); + const method = service.methods.find((candidate) => candidate.name === methodName); + if (method === undefined) { + throw new Error(`Missing OpenRPC method ${methodName}`); + } + + const schema = structuredClone(method.result.schema); + schema.title = rootTitle; + ensureObjectPath(schema, ["properties", "checkout"]).title = checkoutTitle; + ensureObjectPath(schema, ["properties", "checkout", "properties", "payment"]).title = paymentTitle; + rewriteRefs(schema); + ensureObjectPath(schema, ["properties", "checkout", "properties", "payment", "properties"]).instruments = { + $ref: "payment.json#/properties/instruments", + }; + schema.components = service.components; + + await writeJson(path.join(specDir, outputFile), schema); +} + +async function typeSchemas(specDir) { + const typesDir = path.join(specDir, "types"); + const names = (await fs.readdir(typesDir)) + .filter((name) => name.endsWith(".json")) + .sort(); + return names.map((name) => path.join(typesDir, name)); +} + +async function runQuicktype(args) { + await run(QUICKTYPE_BIN, args); +} + +async function replaceInFile(file, transform) { + const source = await fs.readFile(file, "utf8"); + await fs.writeFile(file, transform(source)); +} + +function replaceRequired(source, regex, replacement, description) { + const matches = Array.from(source.matchAll(regex.global ? regex : new RegExp(regex.source, `${regex.flags}g`))).length; + if (matches === 0) { + throw new Error(`${description}: expected at least one match.`); + } + + return source.replace(regex.global ? regex : new RegExp(regex.source, `${regex.flags}g`), replacement); +} + +function replaceExactlyOnce(source, search, replacement, description) { + const matches = source.split(search).length - 1; + if (matches !== 1) { + throw new Error(`${description}: expected exactly one match, found ${matches}.`); + } + + return source.replace(search, replacement); +} + +function replaceIfPresent(source, regex, replacement) { + return source.replace(regex.global ? regex : new RegExp(regex.source, `${regex.flags}g`), replacement); +} + +async function generateKotlin(specDir, output) { + await fs.mkdir(path.dirname(output), {recursive: true}); + await runQuicktype([ + "--lang", + "kotlin", + "--src-lang", + "schema", + "--framework", + "kotlinx", + "--src", + path.join(specDir, "checkout.json"), + ...((await typeSchemas(specDir)).flatMap((file) => ["--src", file])), + "--src", + path.join(specDir, "payment.json"), + "--src", + path.join(specDir, "order.json"), + "--src", + path.join(specDir, "instruments_change_result.json"), + "--src", + path.join(specDir, "credential_result.json"), + "--package", + "com.shopify.checkoutkit", + "-o", + output, + ]); + + await replaceInFile(output, (source) => { + let result = source + .replace(/^data class /gm, "public data class ") + .replace(/^sealed class /gm, "public sealed class ") + .replace(/^enum class /gm, "public enum class ") + .replace(/^typealias /gm, "public typealias ") + .replace(/^ class /gm, " public class ") + .replace(/^ val /gm, " public val ") + .replace(/\(val value: /g, "(public val value: "); + + result = replaceExactlyOnce(result, "public data class Binding (", "public data class TokenBinding (", "Kotlin TokenBinding class declaration"); + result = replaceIfPresent(result, /: Binding$/gm, ": TokenBinding"); + result = replaceRequired(result, /Binding\.serializer\(\)/g, "TokenBinding.serializer()", "Kotlin TokenBinding serializer references"); + result = replaceExactlyOnce(result, "public enum class ColorScheme(", "public enum class EmbeddedColorScheme(", "Kotlin EmbeddedColorScheme declaration"); + result = replaceRequired(result, /List/g, "List", "Kotlin EmbeddedColorScheme list references"); + result = replaceIfPresent(result, /ColorScheme\.serializer\(\)/g, "EmbeddedColorScheme.serializer()"); + result = replaceExactlyOnce(result, "typealias Totals = JsonArray", "typealias Totals = List", "Kotlin Totals collection type"); + + return result; + }); +} + +async function generateSwift(specDir, output) { + await fs.mkdir(path.dirname(output), {recursive: true}); + await runQuicktype([ + "--lang", + "swift", + "--swift-5-support", + "--access-level", + "public", + "--sendable", + "--src-lang", + "schema", + "--src", + path.join(specDir, "checkout.json"), + ...((await typeSchemas(specDir)).flatMap((file) => ["--src", file])), + "--src", + path.join(specDir, "payment.json"), + "--src", + path.join(specDir, "order.json"), + "--src", + path.join(specDir, "instruments_change_result.json"), + "--src", + path.join(specDir, "credential_result.json"), + "-o", + output, + ]); + + await replaceInFile(output, (source) => source + .replace(/\bBinding\b/g, "TokenBinding") + .replace(/\bColorScheme\b/g, "EmbeddedColorScheme")); +} + +async function generateTypescript(specDir, output) { + await fs.mkdir(path.dirname(output), {recursive: true}); + await runQuicktype([ + "--lang", + "ts", + "--src-lang", + "schema", + "--just-types", + "--prefer-unions", + "--nice-property-names", + "--acronym-style", + "camel", + "--no-date-times", + "--src", + path.join(specDir, "checkout.json"), + ...((await typeSchemas(specDir)).flatMap((file) => ["--src", file])), + "--src", + path.join(specDir, "payment.json"), + "--src", + path.join(specDir, "order.json"), + "--src", + path.join(specDir, "instruments_change_result.json"), + "--src", + path.join(specDir, "credential_result.json"), + "-o", + output, + ]); + + await replaceInFile(output, (source) => source + .replace(/^type /gm, "export type ") + .replace(/\bBinding\b/g, "TokenBinding") + .replace(/\bColorScheme\b/g, "EmbeddedColorScheme")); +} + +async function main() { + const {lang, output} = parseArgs(process.argv.slice(2)); + await requireQuicktype(); + + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "checkout-kit-protocol-codegen-")); + try { + const specDir = await prepareCodegenSchemas(tempDir); + + await extractResultSchema( + specDir, + "ec.payment.instruments_change_request", + "instruments_change_result.json", + "InstrumentsChangeResult", + "InstrumentsChangeCheckout", + "InstrumentsChangePayment", + ); + await extractResultSchema( + specDir, + "ec.payment.credential_request", + "credential_result.json", + "CredentialResult", + "CredentialCheckout", + "CredentialPayment", + ); + + switch (lang) { + case "kotlin": { + const target = output || path.join(REPO_ROOT, "platforms", "android", "lib", "src", "main", "java", "com", "shopify", "checkoutkit", "Models.kt"); + await generateKotlin(specDir, target); + console.log(`Generated ${target}`); + break; + } + case "swift": { + const target = output || path.join(PROTOCOL_DIR, "languages", "swift", "Sources", "ShopifyCheckoutProtocol", "Generated", "Models.swift"); + await generateSwift(specDir, target); + console.log(`Generated ${target}`); + break; + } + case "typescript": { + const target = output || path.join(PROTOCOL_DIR, "languages", "typescript", "src", "generated", "Models.ts"); + await generateTypescript(specDir, target); + console.log(`Generated ${target}`); + break; + } + default: + throw new Error(`Unsupported language: ${lang}. Use kotlin, swift, or typescript.`); + } + } finally { + await fs.rm(tempDir, {recursive: true, force: true}); + } +} + +main().catch((error) => { + console.error(error.message); + process.exit(1); +}); diff --git a/protocol/scripts/generate_models.sh b/protocol/scripts/generate_models.sh index 4668656e..7dbcb0af 100755 --- a/protocol/scripts/generate_models.sh +++ b/protocol/scripts/generate_models.sh @@ -1,189 +1,5 @@ #!/bin/bash set -euo pipefail -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -SPEC_DIR="${REPO_ROOT}/protocol/schemas/shopping" -SERVICES_DIR="${REPO_ROOT}/protocol/services/shopping" - -LANG="" -while [[ $# -gt 0 ]]; do - case "$1" in - --lang) LANG="$2"; shift 2 ;; - *) echo "Unknown argument: $1"; exit 1 ;; - esac -done - -if [[ -z "$LANG" ]]; then - echo "Usage: $0 --lang " - exit 1 -fi - -TEMP_SCHEMAS=() -cleanup() { rm -f "${TEMP_SCHEMAS[@]}"; } -trap cleanup EXIT - -# quicktype generates non-deterministic color-based names (e.g. PurplePayment, FluffyPayment) -# when inline objects collide with existing type names. Injecting "title" fields into the -# extracted result schemas gives quicktype deterministic naming hints. -# -# We also rewrite $ref paths from "../../schemas/shopping/" to "" so that refs resolve -# correctly when the temp file is placed alongside the main schemas in SPEC_DIR. The -# openrpc doc's `components` section is copied into the temp file so internal -# `#/components/schemas/X` refs in the extracted result schema resolve locally. -extract_result_schema() { - local method_name="$1" - local output_file="$2" - local root_title="$3" - local checkout_title="$4" - local payment_title="$5" - jq --arg method "$method_name" \ - --arg root_title "$root_title" \ - --arg checkout_title "$checkout_title" \ - --arg payment_title "$payment_title" \ - ' - . as $root - | .methods[] | select(.name == $method) | .result.schema - | .title = $root_title - | .properties.checkout.title = $checkout_title - | .properties.checkout.properties.payment.title = $payment_title - | walk(if type == "object" and has("$ref") then - .["$ref"] |= gsub("../../schemas/shopping/"; "") - else . end) - | .properties.checkout.properties.payment.properties.instruments = - {"$ref": "payment.json#/properties/instruments"} - | . + { components: $root.components } - ' \ - "${SERVICES_DIR}/embedded.openrpc.json" > "$output_file" - TEMP_SCHEMAS+=("$output_file") -} - -extract_result_schema "ec.payment.instruments_change_request" \ - "${SPEC_DIR}/instruments_change_result.json" \ - "InstrumentsChangeResult" "InstrumentsChangeCheckout" "InstrumentsChangePayment" - -extract_result_schema "ec.payment.credential_request" \ - "${SPEC_DIR}/credential_result.json" \ - "CredentialResult" "CredentialCheckout" "CredentialPayment" - -case "$LANG" in - kotlin) - OUTPUT="${REPO_ROOT}/platforms/android/lib/src/main/java/com/shopify/checkoutkit/Models.kt" - quicktype \ - --lang kotlin \ - --src-lang schema \ - --framework kotlinx \ - --src "${SPEC_DIR}/checkout.json" \ - --src "${SPEC_DIR}/types/"*.json \ - --src "${SPEC_DIR}/payment.json" \ - --src "${SPEC_DIR}/order.json" \ - --src "${SPEC_DIR}/instruments_change_result.json" \ - --src "${SPEC_DIR}/credential_result.json" \ - --package "com.shopify.checkoutkit" \ - -o "${OUTPUT}" - - # Post-process for -Xexplicit-api=strict: every top-level and member declaration that - # is part of the public API surface needs an explicit 'public' modifier. - # - # quicktype Kotlin does not support --access-level, so we add 'public' via sed. - # Patterns: top-level classes/aliases, 4-space-indented constructor properties, - # inner classes inside sealed classes, and inline constructor params in enum/sealed. - sed -i '' \ - -e 's/^data class /public data class /' \ - -e 's/^sealed class /public sealed class /' \ - -e 's/^enum class /public enum class /' \ - -e 's/^typealias /public typealias /' \ - -e 's/^ class / public class /' \ - -e 's/^ val / public val /' \ - -e 's/(val value: /(public val value: /' \ - "${OUTPUT}" - - # Rename types that conflict with platform or Kotlin stdlib names. - # quicktype emits 'data class Binding (' (with space before paren). - # Apply the same renames in Swift and React Native generators for consistency. - # ColorScheme is renamed to avoid collision with the hand-written sealed class - # in ColorScheme.kt used by the dialog-based checkout presentation API. - sed -i '' \ - -e 's/public data class Binding (/public data class TokenBinding (/' \ - -e 's/: Binding$/: TokenBinding/' \ - -e 's/Binding\.serializer()/TokenBinding.serializer()/' \ - -e 's/public enum class ColorScheme(/public enum class EmbeddedColorScheme(/' \ - -e 's/List/List/g' \ - -e 's/ColorScheme\.serializer()/EmbeddedColorScheme.serializer()/g' \ - "${OUTPUT}" - - # quicktype emits `typealias Totals = JsonArray`, but - # kotlinx.serialization.json.JsonArray is not generic. Rewrite to a plain List. - sed -i '' \ - -e 's/typealias Totals = JsonArray/typealias Totals = List/' \ - "${OUTPUT}" - - - echo "Generated ${OUTPUT}" - ;; - - swift) - OUTPUT="${REPO_ROOT}/protocol/languages/swift/Sources/ShopifyCheckoutProtocol/Generated/Models.swift" - quicktype \ - --lang swift \ - --swift-5-support \ - --access-level public \ - --sendable \ - --src-lang schema \ - --src "${SPEC_DIR}/checkout.json" \ - --src "${SPEC_DIR}/types/"*.json \ - --src "${SPEC_DIR}/payment.json" \ - --src "${SPEC_DIR}/order.json" \ - --src "${SPEC_DIR}/instruments_change_result.json" \ - --src "${SPEC_DIR}/credential_result.json" \ - -o "${OUTPUT}" - - # Rename types that conflict with platform or Swift stdlib names. - # Apply the same renames as in the Kotlin generator for consistency. - # Use BSD word-boundary anchors so all identifier sites match — quicktype - # emits `struct Binding:` (no whitespace before `:`), which previous - # space-anchored patterns missed. - sed -i '' -E \ - -e 's/[[:<:]]Binding[[:>:]]/TokenBinding/g' \ - -e 's/[[:<:]]ColorScheme[[:>:]]/EmbeddedColorScheme/g' \ - "${OUTPUT}" - - - echo "Generated ${OUTPUT}" - ;; - - typescript|ts) - OUTPUT="${REPO_ROOT}/protocol/languages/typescript/src/generated/Models.ts" - mkdir -p "$(dirname "${OUTPUT}")" - quicktype \ - --lang ts \ - --src-lang schema \ - --just-types \ - --prefer-unions \ - --nice-property-names \ - --acronym-style camel \ - --no-date-times \ - --src "${SPEC_DIR}/checkout.json" \ - --src "${SPEC_DIR}/types/"*.json \ - --src "${SPEC_DIR}/payment.json" \ - --src "${SPEC_DIR}/order.json" \ - --src "${SPEC_DIR}/instruments_change_result.json" \ - --src "${SPEC_DIR}/credential_result.json" \ - -o "${OUTPUT}" - - # Keep all schema-derived aliases available to package consumers, and apply - # the cross-platform generated model renames used by Swift and Kotlin. - sed -i '' -E \ - -e 's/^type /export type /' \ - -e 's/[[:<:]]Binding[[:>:]]/TokenBinding/g' \ - -e 's/[[:<:]]ColorScheme[[:>:]]/EmbeddedColorScheme/g' \ - "${OUTPUT}" - - - echo "Generated ${OUTPUT}" - ;; - - *) - echo "Unsupported language: $LANG. Use kotlin, swift, or typescript." - exit 1 - ;; -esac +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec node "${SCRIPT_DIR}/generate_models.mjs" "$@"