diff --git a/policy-site/.gitignore b/policy-site/.gitignore new file mode 100644 index 00000000..e314bcb5 --- /dev/null +++ b/policy-site/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +# Built by scripts/build-policy-wasm.sh from crates/didbot-policy-check. +public/wasm/ diff --git a/policy-site/.npmrc b/policy-site/.npmrc new file mode 100644 index 00000000..dc043ffe --- /dev/null +++ b/policy-site/.npmrc @@ -0,0 +1,2 @@ +# No dependency runs code at install time. +ignore-scripts=true diff --git a/policy-site/index.html b/policy-site/index.html new file mode 100644 index 00000000..1fd23751 --- /dev/null +++ b/policy-site/index.html @@ -0,0 +1,85 @@ + + + + + + + + + + + + +
+

+

+ +
+ +
+ + +
+

Sign in

+
+ + + +

+ +
+
+ + + + + + + + + +
+

Examples

+

+ +
+
+ + diff --git a/policy-site/package-lock.json b/policy-site/package-lock.json new file mode 100644 index 00000000..091ca77d --- /dev/null +++ b/policy-site/package-lock.json @@ -0,0 +1,1865 @@ +{ + "name": "policy-site", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "policy-site", + "version": "0.0.1", + "dependencies": { + "@atproto/common-web": "0.5.12", + "@atproto/oauth-client-browser": "0.5.7", + "@atproto/syntax": "0.7.6", + "@github/combobox-nav": "3.0.2" + }, + "devDependencies": { + "@atproto/oauth-types": "0.7.6", + "linkedom": "0.18.13", + "typescript": "7.0.2", + "vite": "8.3.0" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@atproto-labs/did-resolver": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.3.9.tgz", + "integrity": "sha512-KBgUdQQ76XL/xjQbuMfVBOEa5jaragR9NPq1uMoM4iXKnW5GGcEMHMPu+WrTxYNBku0b7MhPUfCs1pKtyLBP6g==", + "license": "MIT", + "dependencies": { + "@atproto-labs/fetch": "^0.3.6", + "@atproto-labs/pipe": "^0.2.4", + "@atproto-labs/simple-store": "^0.5.1", + "@atproto-labs/simple-store-memory": "^0.2.6", + "@atproto/did": "^0.5.5", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto-labs/fetch": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.3.6.tgz", + "integrity": "sha512-ro/J/cnqIp4bnSzDl8N3SkruvQcE3Pze+1H70mKdFfyFcdhPr+7O4lggl4VCbhI84Yqgz47c0xC6F/Yl39SxDA==", + "license": "MIT", + "dependencies": { + "@atproto-labs/pipe": "^0.2.4" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto-labs/handle-resolver": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver/-/handle-resolver-0.4.9.tgz", + "integrity": "sha512-I2xrVsgw0M1Gp5qAWQwX8aoq6E79PnNHuWX+mg7zMRpuazA5DWiHoJOyBZHPeEtbtYO3F8g3le7PRL8imaWljw==", + "license": "MIT", + "dependencies": { + "@atproto-labs/simple-store": "^0.5.1", + "@atproto-labs/simple-store-memory": "^0.2.6", + "@atproto/did": "^0.5.5", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto-labs/identity-resolver": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@atproto-labs/identity-resolver/-/identity-resolver-0.4.9.tgz", + "integrity": "sha512-dVfbDHybOlDimEdUSbDfCQpMbmf25LNpffxCqSE//hLD86wiUzJQwJn2sHfskZTfwGD896wyY8musfrR3vDnOA==", + "license": "MIT", + "dependencies": { + "@atproto-labs/did-resolver": "^0.3.9", + "@atproto-labs/handle-resolver": "^0.4.9" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto-labs/pipe": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.2.4.tgz", + "integrity": "sha512-n67jCcrC+ouAeO10cWkpPzzLMlDi/lDCU30Us+LGqhOPhT6c4t5ASdBLQi9W3jUQtRzQBt3G9zipF+xKWNvVbw==", + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto-labs/simple-store": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.5.1.tgz", + "integrity": "sha512-vfvoDhu6ds6BT3Pqe+d2/LBU1WpDRtt69y6zltlfMCoucPm82m1j50/Wb6xTvv+oZ+2j0JyZVXsmXQ12SWYQJg==", + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto-labs/simple-store-memory": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.2.6.tgz", + "integrity": "sha512-DD1v7MEfYF3BAcEpMTTpzfBLWLoI2HuyBhku2YpjHoqPrRrhCtE1alkxfPHjtjJVIjuU/XNU0cF/wB3+5FiKsA==", + "license": "MIT", + "dependencies": { + "@atproto-labs/simple-store": "^0.5.1", + "lru-cache": "^10.2.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/common-web": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.5.12.tgz", + "integrity": "sha512-FumNnUOUsk68gP+jU0sXY2wY2piqFV9/qU2YaoQq/IZknhJBlLYCzjQXXCN0FCzxAZWD0YUzgydeyuRvFoAxfA==", + "license": "MIT", + "dependencies": { + "@atproto/lex-data": "^0.1.7", + "@atproto/lex-json": "^0.1.6", + "@atproto/syntax": "^0.7.6", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/did": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.5.5.tgz", + "integrity": "sha512-ibCxTPsPQtrtX+iidnE0bkMBHuoWFBdVfPFfCAlUKY6HfkqdUUxItzvQu9AMucYDhP6vJ15HLVj+QhNY2jtIvw==", + "license": "MIT", + "dependencies": { + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/jwk": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@atproto/jwk/-/jwk-0.7.4.tgz", + "integrity": "sha512-tq7TUDmNfe1yDfpRgdGQMJdl9TUlJmREQNCag9yg5w8Evu+TOiFiLgiOCbo7X4ouRPSgd1DpOzXbUa8UyKKMZA==", + "license": "MIT", + "dependencies": { + "multiformats": "^13.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/jwk-jose": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@atproto/jwk-jose/-/jwk-jose-0.2.4.tgz", + "integrity": "sha512-gzDoA0JTwnc0ZJOBLM7WX9xFxtynRS2K1Bofb8epzoMWDQvyvfbPcfkdPrKFM7NXCFUVpGpBnsCB8KFPTf1rCg==", + "license": "MIT", + "dependencies": { + "@atproto/jwk": "^0.7.4", + "jose": "^5.2.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/jwk-webcrypto": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@atproto/jwk-webcrypto/-/jwk-webcrypto-0.3.4.tgz", + "integrity": "sha512-UsFIUozqnRecXPo6HgKV4PW4FqYHxX1V3iAe0rRV6Q2RSfYD8V2mZ89pv8NpvJynnaqJArBQ7HZlcg0F4tRYhA==", + "license": "MIT", + "dependencies": { + "@atproto/jwk": "^0.7.4", + "@atproto/jwk-jose": "^0.2.4", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/lex-data": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.1.7.tgz", + "integrity": "sha512-kW/dPLqo/WgCLV+XESR4JKwV6c1rZWJGOfuPupZGTjEDAKoBbKXdaEzX9/1vKQYbZ9U3j0DS/n7OFFK7wBugyQ==", + "license": "MIT", + "dependencies": { + "multiformats": "^13.0.0", + "tslib": "^2.8.1", + "unicode-segmenter": "^0.14.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/lex-json": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.1.6.tgz", + "integrity": "sha512-mvrAd0lbyuecIHjyld8QN6MN6CBf4j0GCxLzegsvLh0SvDf+GbYWklkcQqmITL44yFQOwmA/QNIQj0Uvh7+R/g==", + "license": "MIT", + "dependencies": { + "@atproto/lex-data": "^0.1.7", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/lexicon": { + "version": "0.7.14", + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.7.14.tgz", + "integrity": "sha512-hDG2+61JTrvxDFtoojDFxKmZkFVl/lR/jFi82gSN0zNDjr+dlpUGKlG9MoPBkUKK83+Pi+I2jZ55xXboJq6JTA==", + "license": "MIT", + "dependencies": { + "@atproto/common-web": "^0.5.12", + "@atproto/syntax": "^0.7.6", + "multiformats": "^13.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/oauth-client": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/@atproto/oauth-client/-/oauth-client-0.8.7.tgz", + "integrity": "sha512-DbWMFcy6t2HyyPKVEhKeh8xNNwM6sokPl/Qk7ctZLvEu+FT4GKsUppgf2hkWqeUyVc7nacu+nE7dCkYwPZmBKA==", + "license": "MIT", + "dependencies": { + "@atproto-labs/did-resolver": "^0.3.9", + "@atproto-labs/fetch": "^0.3.6", + "@atproto-labs/handle-resolver": "^0.4.9", + "@atproto-labs/identity-resolver": "^0.4.9", + "@atproto-labs/simple-store": "^0.5.1", + "@atproto-labs/simple-store-memory": "^0.2.6", + "@atproto/did": "^0.5.5", + "@atproto/jwk": "^0.7.4", + "@atproto/oauth-types": "^0.7.6", + "@atproto/xrpc": "^0.8.13", + "core-js": "^3.50.0", + "multiformats": "^13.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/oauth-client-browser": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@atproto/oauth-client-browser/-/oauth-client-browser-0.5.7.tgz", + "integrity": "sha512-AvCl5Of2NDW/yUhDSyOufmhmGnfa9ScNPSzOVYLDrJvm2t/5DdHoRu4gmtwwe2L6rf6hbUUFziA80drJm+0D0w==", + "license": "MIT", + "dependencies": { + "@atproto-labs/did-resolver": "^0.3.9", + "@atproto-labs/handle-resolver": "^0.4.9", + "@atproto-labs/simple-store": "^0.5.1", + "@atproto/did": "^0.5.5", + "@atproto/jwk": "^0.7.4", + "@atproto/jwk-webcrypto": "^0.3.4", + "@atproto/oauth-client": "^0.8.7", + "@atproto/oauth-types": "^0.7.6", + "core-js": "^3.50.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/oauth-types": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@atproto/oauth-types/-/oauth-types-0.7.6.tgz", + "integrity": "sha512-FgFe2mJOUgJSQBxKBwLWv+lFyDxBnlqA+wfSLabCNnabtNuSO3UQI0CGI72K7/OQRQvdwkcYQMoUuw3Rqex/mg==", + "license": "MIT", + "dependencies": { + "@atproto/did": "^0.5.5", + "@atproto/jwk": "^0.7.4", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/syntax": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.7.6.tgz", + "integrity": "sha512-luKQTcWw1H1jLmYvX34ldBqNjz2orF082njmcF9fxbWTqVhsO+TpY0FHRKVPoV7dwL0Y3L16DNz1ma0LFGH25g==", + "license": "MIT", + "dependencies": { + "iso-datestring-validator": "^2.2.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@atproto/xrpc": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.8.13.tgz", + "integrity": "sha512-htpi93/Zqyk5WsT+RrWemxMcJFJx2M4VT8i8avuWJTyr7AZ42qvPsIdVsaPdOOS3rZwodq/SdZXJFgspJ4yc5g==", + "license": "MIT", + "dependencies": { + "@atproto/lexicon": "^0.7.14", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@github/combobox-nav": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-3.0.2.tgz", + "integrity": "sha512-txEw3G7oK2b7G5olc5+xDEUJcHvyp9N+yx+LIjAwKsfOq22CGAx5zUL/6yVsHiCF/tRZlKMuFTAgpOHoCrNrzA==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.149.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.149.0.tgz", + "integrity": "sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.8.tgz", + "integrity": "sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.8.tgz", + "integrity": "sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.8.tgz", + "integrity": "sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.8.tgz", + "integrity": "sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.8.tgz", + "integrity": "sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.8.tgz", + "integrity": "sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.8.tgz", + "integrity": "sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.8.tgz", + "integrity": "sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.8.tgz", + "integrity": "sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.8.tgz", + "integrity": "sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.8.tgz", + "integrity": "sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.8.tgz", + "integrity": "sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.8.tgz", + "integrity": "sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.8.tgz", + "integrity": "sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.8.tgz", + "integrity": "sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/boolbase": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-2.0.0.tgz", + "integrity": "sha512-DkVaaQHymRhpYEYo9x1oo7Q7B0Y6KJUsjm3c9eTyFDby4MHLBTwZ6ZDWBel5zrYxj1WsZgC5oLpiz+93MluXeA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/core-js": { + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.50.0.tgz", + "integrity": "sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==", + "hasInstallScript": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/css-select": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-7.0.0.tgz", + "integrity": "sha512-snmjEVXy+1LnwXdxhYvTMj1d9tOh4HxkA1YmoayVBeeyR2C14Pum7fcxJIm4SswYspVy866eYNwlH6xC3/VH5g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^2.0.0", + "css-what": "^8.0.0", + "domhandler": "^6.0.1", + "domutils": "^4.0.2", + "nth-check": "^3.0.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-8.0.0.tgz", + "integrity": "sha512-DH0Bqq3DNp5tdOReuNyAA+Ev4Y2GS5FMbZpeTLP6C4CDi0h5nL0BmUPChXw3o/qbHLDWHl49sbNqQVY7bMSDdw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz", + "integrity": "sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0", + "entities": "^8.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz", + "integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/domhandler": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz", + "integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^3.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz", + "integrity": "sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^3.0.0", + "domelementtype": "^3.0.0", + "domhandler": "^6.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/htmlparser2/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/iso-datestring-validator": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", + "license": "MIT" + }, + "node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/linkedom": { + "version": "0.18.13", + "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.13.tgz", + "integrity": "sha512-ES/o9qotMpzpN2MHs+Iq/JcVoOj8Fa5wiQYrTdFpvAnwXL0g66XHHUc9WUMk6nAlBtGsFQ24ne+SYnvnaQ2FSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "css-select": "^7.0.0", + "cssom": "^0.5.0", + "html-escaper": "^3.0.3", + "htmlparser2": "^10.1.0", + "uhyphen": "^0.2.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "canvas": ">= 2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/multiformats": { + "version": "13.4.2", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.4.2.tgz", + "integrity": "sha512-eh6eHCrRi1+POZ3dA+Dq1C6jhP1GNtr9CRINMb67OKzqW9I5DUuZM/3jLPlzhgpGeiNUlEGEbkCYChXMCc/8DQ==", + "license": "Apache-2.0 OR MIT" + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nth-check": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-3.0.1.tgz", + "integrity": "sha512-GX0gsdbGVCgnRgbeGaubfjpBXyYRWOOCVeYh08bSQvDZqxz5ndXs1OTfAt/h36G1xvI94YIspsI0sVFqAV9+RQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^2.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rolldown": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.8.tgz", + "integrity": "sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.149.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.8", + "@rolldown/binding-android-arm64": "1.2.8", + "@rolldown/binding-darwin-arm64": "1.2.8", + "@rolldown/binding-darwin-x64": "1.2.8", + "@rolldown/binding-freebsd-x64": "1.2.8", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.8", + "@rolldown/binding-linux-arm64-gnu": "1.2.8", + "@rolldown/binding-linux-arm64-musl": "1.2.8", + "@rolldown/binding-linux-ppc64-gnu": "1.2.8", + "@rolldown/binding-linux-s390x-gnu": "1.2.8", + "@rolldown/binding-linux-x64-gnu": "1.2.8", + "@rolldown/binding-linux-x64-musl": "1.2.8", + "@rolldown/binding-openharmony-arm64": "1.2.8", + "@rolldown/binding-win32-arm64-msvc": "1.2.8", + "@rolldown/binding-win32-x64-msvc": "1.2.8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/uhyphen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", + "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", + "dev": true, + "license": "ISC" + }, + "node_modules/unicode-segmenter": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.5.tgz", + "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz", + "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.6", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/policy-site/package.json b/policy-site/package.json new file mode 100644 index 00000000..7badeeab --- /dev/null +++ b/policy-site/package.json @@ -0,0 +1,27 @@ +{ + "name": "policy-site", + "private": true, + "type": "module", + "version": "0.0.1", + "engines": { + "node": ">=22.18.0" + }, + "scripts": { + "dev": "vite", + "build": "tsc --noEmit && vite build", + "preview": "vite preview", + "test": "node --test \"tests/check-*.mjs\"" + }, + "dependencies": { + "@atproto/common-web": "0.5.12", + "@atproto/oauth-client-browser": "0.5.7", + "@atproto/syntax": "0.7.6", + "@github/combobox-nav": "3.0.2" + }, + "devDependencies": { + "@atproto/oauth-types": "0.7.6", + "linkedom": "0.18.13", + "typescript": "7.0.2", + "vite": "8.3.0" + } +} diff --git a/policy-site/src/checks.ts b/policy-site/src/checks.ts new file mode 100644 index 00000000..5da97168 --- /dev/null +++ b/policy-site/src/checks.ts @@ -0,0 +1,96 @@ +// The one door to the policy checks: `didbot-policy-check`, compiled to wasm +// by scripts/build-policy-wasm.sh into public/wasm/. Every judgment this page +// shows about a record comes through here, from the same Rust the servers +// run. Nothing in TypeScript decides whether a record is valid. +// +// Loaded with a dynamic import() of a URL Vite leaves alone. The deployed +// script-src grants 'wasm-unsafe-eval' and not 'unsafe-eval', so the module +// must be fetched as a script, never evaluated from a string. The URL is made +// absolute because the dev server rewrites a path-only import() to ask for +// `?import`, which it refuses for files in public/. + +const MODULE_URL = "/wasm/policy-check.js"; + +export type Severity = "error" | "warning"; + +export interface Problem { + severity: Severity; + path: string; + message: string; +} + +export interface Report { + ok: boolean; + problems: Problem[]; +} + +export interface RevisionProblem extends Problem { + collection: string; + rkey: string; +} + +export interface RevisionReport { + digest: string | null; + problems: RevisionProblem[]; +} + +export interface Vocabulary { + actions: string[]; + engines: string[]; + includes: string[]; +} + +export interface Example { + id: string; + title: string; + collection: "bot.did.policy" | "bot.did.policyBinding"; + rkey: string; + record: Record; +} + +/** A record as `com.atproto.repo.listRecords` returns it, minus its URI. */ +export interface Entry { + rkey: string; + value: unknown; +} + +interface Module { + default: () => Promise; + checkPolicy(record: string): string; + checkBinding(record: string, repo: string): string; + checkRevision(policies: string, bindings: string, repo: string): string; + vocabulary(): string; + examples(): string; +} + +export interface Checks { + checkPolicy(record: unknown): Report; + checkBinding(record: unknown, repo: string): Report; + checkRevision(policies: Entry[], bindings: Entry[], repo: string): RevisionReport; + vocabulary: Vocabulary; + examples: Example[]; +} + +let loading: Promise | undefined; + +export function loadChecks(): Promise { + loading ??= load(); + return loading; +} + +async function load(): Promise { + const url = new URL(MODULE_URL, document.baseURI).href; + const mod = (await import(/* @vite-ignore */ url)) as Module; + await mod.default(); + return { + checkPolicy: (record) => JSON.parse(mod.checkPolicy(JSON.stringify(record))) as Report, + checkBinding: (record, repo) => + JSON.parse(mod.checkBinding(JSON.stringify(record), repo)) as Report, + checkRevision: (policies, bindings, repo) => + JSON.parse( + mod.checkRevision(JSON.stringify(policies), JSON.stringify(bindings), repo), + ) as RevisionReport, + vocabulary: JSON.parse(mod.vocabulary()) as Vocabulary, + examples: JSON.parse(mod.examples()) as Example[], + }; +} diff --git a/policy-site/src/copy.ts b/policy-site/src/copy.ts new file mode 100644 index 00000000..b742daad --- /dev/null +++ b/policy-site/src/copy.ts @@ -0,0 +1,23 @@ +// Every sentence of prose on this page, and the only place any of it lives. +// +// Per CLAUDE.md's Copywriting section, an agent does not write brand voice or +// explanatory prose. Each string here is invented filler in ⟦…⟧ until a human +// writes the real line: real but obscure words, so it never reads as a draft. +// +// grep -rn 'PH(' policy-site/src/ # in the source +// grep -rn '⟦' policy-site/dist # in the build +// +// Short functional labels ("Sign in", "Save") are written where they are used. +export const PH = (text: string) => `⟦${text}⟧`; + +export const copy = { + title: PH("quillwort"), + clientName: PH("quillwort"), + lede: PH("marram tussock over a shingle spit"), + signInHint: PH("fescue and sea-holly, handle or did"), + serversIntro: PH("lighthouse keepers compare the lamp log"), + policiesIntro: PH("samphire gathered at the neap tide"), + bindingsIntro: PH("halyard, cleat and bowline"), + examplesIntro: PH("specimen drawers in the herbarium"), + lastWriteIntro: PH("the tide table, read back"), +} as const; diff --git a/policy-site/src/dom.ts b/policy-site/src/dom.ts new file mode 100644 index 00000000..26597825 --- /dev/null +++ b/policy-site/src/dom.ts @@ -0,0 +1,66 @@ +// Element construction for every view on the page. Strings become text nodes, +// never markup: record contents, handles and server responses are untrusted, +// and this is the only path by which any of them reaches the document. + +type Child = Node | string | null | undefined | false; + +export interface Props { + class?: string; + id?: string; + hidden?: boolean; + attrs?: Record; + on?: { [K in keyof HTMLElementEventMap]?: (event: HTMLElementEventMap[K]) => void }; +} + +export function h( + tag: K, + props: Props = {}, + ...children: Child[] +): HTMLElementTagNameMap[K] { + const el = document.createElement(tag); + if (props.class) el.className = props.class; + if (props.id) el.id = props.id; + if (props.hidden) el.hidden = true; + for (const [name, value] of Object.entries(props.attrs ?? {})) el.setAttribute(name, value); + for (const [type, listener] of Object.entries(props.on ?? {})) { + el.addEventListener(type, listener as EventListener); + } + append(el, ...children); + return el; +} + +export function append(parent: Node, ...children: Child[]): void { + for (const child of children) { + if (child === null || child === undefined || child === false) continue; + parent.appendChild(typeof child === "string" ? document.createTextNode(child) : child); + } +} + +export function replaceChildren(parent: Element, ...children: Child[]): void { + parent.replaceChildren(); + append(parent, ...children); +} + +export function byId(id: string): T { + const el = document.getElementById(id); + if (!el) throw new Error(`index.html has no #${id}`); + return el as T; +} + +/** The message an error carries, for showing verbatim. */ +export function errorText(error: unknown): string { + if (error instanceof Error) { + const name = "error" in error && typeof error.error === "string" ? error.error : error.name; + return name && name !== "Error" && !error.message.startsWith(name) + ? `${name}: ${error.message}` + : error.message; + } + return String(error); +} + +let nextId = 0; +/** A document-unique id for wiring labels and ARIA references. */ +export function uid(prefix: string): string { + nextId += 1; + return `${prefix}-${nextId}`; +} diff --git a/policy-site/src/main.ts b/policy-site/src/main.ts new file mode 100644 index 00000000..0d25efed --- /dev/null +++ b/policy-site/src/main.ts @@ -0,0 +1,51 @@ +import { loadChecks } from "./checks.ts"; +import { byId, errorText } from "./dom.ts"; +import { resume, signIn, signOut, type OAuthSession } from "./session.ts"; +import { renderExamples } from "./ui/examples.ts"; + +function showError(id: string, error: unknown): void { + const el = byId(id); + el.textContent = errorText(error); + el.hidden = false; +} + +function showAccount(session: OAuthSession): void { + byId("sign-in").hidden = true; + byId("account").hidden = false; + byId("account-did").textContent = session.did; + byId("sign-out").addEventListener("click", async () => { + try { + await signOut(session); + } finally { + location.reload(); + } + }); +} + +async function main(): Promise { + byId("sign-in-form").addEventListener("submit", async (event) => { + event.preventDefault(); + byId("sign-in-error").hidden = true; + const input = byId("sign-in-input"); + try { + await signIn(input.value); + } catch (error) { + showError("sign-in-error", error); + } + }); + + loadChecks().then( + (loaded) => renderExamples(byId("example-list"), loaded.examples, null), + (error: unknown) => showError("page-status", `The policy checks did not load: ${errorText(error)}`), + ); + + let session: OAuthSession | undefined; + try { + session = await resume(); + } catch (error) { + showError("sign-in-error", error); + } + if (session) showAccount(session); +} + +void main(); diff --git a/policy-site/src/oauth-config.ts b/policy-site/src/oauth-config.ts new file mode 100644 index 00000000..09d0404a --- /dev/null +++ b/policy-site/src/oauth-config.ts @@ -0,0 +1,29 @@ +// The one place this client's OAuth identity is written down. The served +// /client-metadata.json (see vite.config.ts), the scope the page requests and +// the redirect it expects are all read from here, so they cannot disagree. + +import type { OAuthClientMetadataInput } from "@atproto/oauth-client-browser"; +import { copy } from "./copy.ts"; + +export const ORIGIN = "https://policy.did.bot"; + +export const CLIENT_ID = `${ORIGIN}/client-metadata.json`; + +export const REDIRECT_URI = `${ORIGIN}/`; + +// Writes to the two collections this page edits. Reads of the operator's +// repository are public and need no scope. +export const SCOPE = "atproto repo:bot.did.policy repo:bot.did.policyBinding"; + +export const clientMetadata: OAuthClientMetadataInput = { + client_id: CLIENT_ID, + client_name: copy.clientName, + client_uri: ORIGIN, + application_type: "web", + token_endpoint_auth_method: "none", + dpop_bound_access_tokens: true, + grant_types: ["authorization_code", "refresh_token"], + response_types: ["code"], + scope: SCOPE, + redirect_uris: [REDIRECT_URI], +}; diff --git a/policy-site/src/session.ts b/policy-site/src/session.ts new file mode 100644 index 00000000..c37c8c19 --- /dev/null +++ b/policy-site/src/session.ts @@ -0,0 +1,70 @@ +// Signing in: a public OAuth client with DPoP and PKCE, its keys and tokens in +// the library's own IndexedDB store. + +import { + AtprotoDohHandleResolver, + BrowserOAuthClient, + buildAtprotoLoopbackClientMetadata, + isLoopbackHost, + type HandleResolver, + type OAuthSession, +} from "@atproto/oauth-client-browser"; +import { isValidDid } from "@atproto/syntax"; +import { clientMetadata, SCOPE } from "./oauth-config.ts"; + +export type { OAuthSession }; + +// Who sees what at sign-in: +// - A handle is resolved over DNS-over-HTTPS at Cloudflare (1.1.1.1), which +// sees the name `_atproto.` and the browser's address. The handle's +// own domain is asked for /.well-known/atproto-did at the same time. +// - A DID skips both. The OAuth library would otherwise look up the handle in +// the DID document to verify it, so it is given a resolver that never +// answers; the page shows the handle the operator's own PDS reports instead. +// - Every sign-in then resolves the DID document: at plc.directory for +// did:plc, or at the DID's own domain for did:web. +const DOH_ENDPOINT = "https://cloudflare-dns.com/dns-query"; + +const handles = new AtprotoDohHandleResolver({ dohEndpoint: DOH_ENDPOINT }); + +const unverifiedHandles: HandleResolver = { resolve: async () => null }; + +// Served from a loopback address, the page signs in as the atproto loopback +// client: the same scope, redirected back to wherever the page is. +// See https://atproto.com/specs/oauth#localhost-client-development. +function metadataForThisOrigin() { + if (!isLoopbackHost(location.hostname)) return clientMetadata; + const port = location.port ? `:${location.port}` : ""; + return buildAtprotoLoopbackClientMetadata({ + scope: SCOPE, + redirect_uris: [`http://127.0.0.1${port}/`], + }); +} + +export const client = new BrowserOAuthClient({ + clientMetadata: metadataForThisOrigin(), + handleResolver: unverifiedHandles, +}); + +/** Restores a stored session, or completes a sign-in the URL is returning from. */ +export async function resume(): Promise { + return (await client.init())?.session; +} + +/** Leaves the page for the operator's authorization server. */ +export async function signIn(identity: string): Promise { + const input = identity.trim().replace(/^@/, ""); + const did = isValidDid(input) ? input : await handles.resolve(input.toLowerCase()); + if (!did) throw new Error(`${input} does not resolve to a DID`); + await client.signIn(did, { scope: SCOPE }); +} + +/** Revokes the session's tokens at its authorization server and forgets it. */ +export async function signOut(session: OAuthSession): Promise { + await session.signOut(); +} + +/** The PDS the session's tokens are for. */ +export async function pdsUrl(session: OAuthSession): Promise { + return (await session.getTokenInfo()).aud; +} diff --git a/policy-site/src/style.css b/policy-site/src/style.css new file mode 100644 index 00000000..cbdfe20b --- /dev/null +++ b/policy-site/src/style.css @@ -0,0 +1,321 @@ +:root { + color-scheme: light dark; + --fg: #1b1d21; + --muted: #5d6470; + --bg: #fbfbfa; + --panel: #ffffff; + --line: #d9dbe0; + --accent: #2657c9; + --ok: #1d7a3b; + --warn: #8a5a00; + --error: #b3261e; + font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + line-height: 1.45; + color: var(--fg); + background: var(--bg); +} + +@media (prefers-color-scheme: dark) { + :root { + --fg: #e6e7ea; + --muted: #a0a6b1; + --bg: #15171b; + --panel: #1d2026; + --line: #353a44; + --accent: #8fb0ff; + --ok: #6fd08f; + --warn: #e5b451; + --error: #ff8a80; + } +} + +body { + margin: 0; + background: var(--bg); +} + +.masthead, +main { + max-width: 64rem; + margin: 0 auto; + padding: 1rem 1.25rem; +} + +.masthead { + border-bottom: 1px solid var(--line); +} + +h1 { + font-size: 1.5rem; + margin: 0.5rem 0; +} + +h2 { + font-size: 1.2rem; + margin: 2rem 0 0.5rem; +} + +h3 { + font-size: 1rem; + margin: 0 0 0.5rem; + overflow-wrap: anywhere; +} + +.mono, +code, +pre, +textarea.mono { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 0.85rem; +} + +.mono, +code { + overflow-wrap: anywhere; +} + +.hint { + color: var(--muted); + font-size: 0.9rem; +} + +.account { + display: flex; + flex-wrap: wrap; + gap: 1rem; + align-items: center; + justify-content: space-between; +} + +dl { + display: grid; + grid-template-columns: max-content 1fr; + gap: 0.2rem 0.75rem; + margin: 0.5rem 0; +} + +dt { + color: var(--muted); +} + +dd { + margin: 0; +} + +.cards { + list-style: none; + padding: 0; + display: grid; + gap: 0.75rem; +} + +.card, +.editor { + background: var(--panel); + border: 1px solid var(--line); + border-radius: 6px; + padding: 0.75rem 1rem; +} + +.editor { + display: grid; + gap: 0.75rem; + margin: 0.75rem 0; + border-color: var(--accent); +} + +.field { + display: grid; + gap: 0.25rem; +} + +label { + font-weight: 600; +} + +input:not([type="checkbox"]), +select, +textarea { + font: inherit; + color: inherit; + background: var(--bg); + border: 1px solid var(--line); + border-radius: 4px; + padding: 0.35rem 0.5rem; + width: 100%; + box-sizing: border-box; +} + +input[readonly] { + color: var(--muted); +} + +fieldset.checkboxes { + border: 1px solid var(--line); + border-radius: 4px; + display: flex; + flex-wrap: wrap; + gap: 0.25rem 1rem; +} + +fieldset.checkboxes label { + font-weight: normal; +} + +#sign-in-form { + display: grid; + gap: 0.5rem; + max-width: 28rem; +} + +button { + font: inherit; + padding: 0.3rem 0.8rem; + border: 1px solid var(--line); + border-radius: 4px; + background: var(--panel); + color: inherit; + cursor: pointer; +} + +button[type="submit"] { + border-color: var(--accent); + color: var(--accent); +} + +button.danger { + border-color: var(--error); + color: var(--error); +} + +button.link { + border: none; + background: none; + padding: 0 0.25rem; +} + +button:disabled { + opacity: 0.5; + cursor: default; +} + +.buttons { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; +} + +.status { + margin: 0.25rem 0; + font-weight: 600; +} + +.status.ok, +.status.matching { + color: var(--ok); +} + +.status.behind, +.status.checking { + color: var(--warn); +} + +.status.error, +.status.unreachable { + color: var(--error); +} + +.problems { + margin: 0.25rem 0; + padding-left: 1.25rem; +} + +.problems .severity { + font-weight: 600; + text-transform: uppercase; + font-size: 0.75rem; +} + +.problems .error .severity { + color: var(--error); +} + +.problems .warning .severity { + color: var(--warn); +} + +pre.verbatim { + background: var(--bg); + border: 1px solid var(--line); + border-radius: 4px; + padding: 0.5rem; + overflow-x: auto; + white-space: pre-wrap; + overflow-wrap: anywhere; + max-height: 24rem; +} + +.picker { + display: grid; + gap: 0.25rem; + position: relative; +} + +.chips { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-wrap: wrap; + gap: 0.25rem; +} + +.chip { + border: 1px solid var(--line); + border-radius: 999px; + padding: 0.1rem 0.25rem 0.1rem 0.6rem; + background: var(--bg); +} + +.detail { + color: var(--muted); + font-size: 0.85rem; +} + +.suggestions { + list-style: none; + margin: 0; + padding: 0; + border: 1px solid var(--line); + border-radius: 4px; + background: var(--panel); + max-height: 16rem; + overflow-y: auto; +} + +.suggestions [role="option"] { + padding: 0.3rem 0.5rem; + cursor: pointer; + overflow-wrap: anywhere; +} + +.suggestions [aria-selected="true"], +.suggestions [data-combobox-option-default="true"] { + background: var(--accent); + color: var(--panel); +} + +.suggestions [aria-selected="true"] .detail, +.suggestions [data-combobox-option-default="true"] .detail { + color: inherit; +} + +:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +[hidden] { + display: none !important; +} diff --git a/policy-site/src/ui/examples.ts b/policy-site/src/ui/examples.ts new file mode 100644 index 00000000..a75dd8db --- /dev/null +++ b/policy-site/src/ui/examples.ts @@ -0,0 +1,79 @@ +// The check module's examples, each copyable and usable as a starting point. + +import type { Example } from "../checks.ts"; +import { h, replaceChildren } from "../dom.ts"; + +// Examples name their repository with the first DID, inside AT-URIs, and a +// subject with the second. +const EXAMPLE_OPERATOR = "did:example:operator"; +const EXAMPLE_SUBJECT = "did:example:subject"; + +/** + * An example record as a starting point for the operator `did`: their DID + * in place of the example operator's, and no example subject left in + * `subjects` or `excludes`. A binding then starts with no subjects, which + * checkBinding refuses until the operator names one. + */ +export function asOperator(record: Record, did: string): Record { + const out = JSON.parse(JSON.stringify(record).replaceAll(EXAMPLE_OPERATOR, did)) as Record; + for (const field of ["subjects", "excludes"]) { + const dids = out[field]; + if (Array.isArray(dids)) out[field] = dids.filter((d) => d !== EXAMPLE_SUBJECT); + } + return out; +} + +export function renderExamples( + into: HTMLElement, + examples: Example[], + use: ((example: Example) => void) | null, +): void { + replaceChildren( + into, + ...examples.map((example) => { + const json = JSON.stringify(example.record, null, 2); + const copied = h("span", { class: "hint", attrs: { role: "status" } }); + return h( + "li", + { class: "card" }, + h("h3", {}, example.title), + h( + "dl", + {}, + h("dt", {}, "collection"), + h("dd", { class: "mono" }, example.collection), + h("dt", {}, "rkey"), + h("dd", { class: "mono" }, example.rkey), + ), + h("pre", { class: "verbatim" }, json), + h( + "div", + { class: "buttons" }, + h( + "button", + { + attrs: { type: "button" }, + on: { + click: () => + navigator.clipboard.writeText(json).then( + () => (copied.textContent = "Copied"), + (error: unknown) => (copied.textContent = `Copy failed: ${String(error)}`), + ), + }, + }, + "Copy JSON", + ), + h( + "button", + { + attrs: use ? { type: "button" } : { type: "button", disabled: "" }, + on: { click: () => use?.(example) }, + }, + example.collection === "bot.did.policy" ? "Use as new policy" : "Use as new binding", + ), + copied, + ), + ); + }), + ); +} diff --git a/policy-site/tests/check-client-metadata.mjs b/policy-site/tests/check-client-metadata.mjs new file mode 100644 index 00000000..c2dac91e --- /dev/null +++ b/policy-site/tests/check-client-metadata.mjs @@ -0,0 +1,66 @@ +// The client metadata a PDS fetches is valid, is served where its client_id +// says, and asks for exactly what the page asks for. +// +// An authorization server refuses a request whose scope or redirect_uri the +// metadata does not cover, so a mismatch is a sign-in that fails for every +// operator. A scope wider than the page needs is worse: it is a token that +// could write anything in an operator's repository. +import { readFileSync } from "node:fs"; +import { isDeepStrictEqual } from "node:util"; +import { join } from "node:path"; +import { assertOAuthDiscoverableClientId, oauthClientMetadataSchema } from "@atproto/oauth-types"; +import { clientMetadata, REDIRECT_URI, SCOPE } from "../src/oauth-config.ts"; +import { distDir, fail, report, requireDist } from "./lib.mjs"; + +// Pinned here, apart from src/oauth-config.ts, so widening it takes two edits. +const EXPECTED_SCOPE = "atproto repo:bot.did.policy repo:bot.did.policyBinding"; + +requireDist(); + +let served; +try { + served = JSON.parse(readFileSync(join(distDir, "client-metadata.json"), "utf8")); +} catch (error) { + fail(`dist/client-metadata.json is missing or not JSON: ${error.message}`); +} + +if (served) { + const parsed = oauthClientMetadataSchema.safeParse(served); + if (!parsed.success) fail(`client-metadata.json is not valid client metadata: ${parsed.error.message}`); + + try { + assertOAuthDiscoverableClientId(served.client_id); + } catch (error) { + fail(`client_id ${served.client_id} is not a discoverable client id: ${error.message}`); + } + const clientId = new URL(served.client_id); + if (clientId.pathname !== "/client-metadata.json") { + fail(`client_id ${served.client_id} is not where the build serves the file`); + } + for (const uri of served.redirect_uris ?? []) { + if (new URL(uri).origin !== clientId.origin) fail(`redirect_uri ${uri} is not on ${clientId.origin}`); + } + + if (!isDeepStrictEqual(served, JSON.parse(JSON.stringify(clientMetadata)))) { + fail("dist/client-metadata.json differs from src/oauth-config.ts's clientMetadata; rebuild"); + } + if (served.scope !== SCOPE) fail(`served scope "${served.scope}" is not the scope the page requests, "${SCOPE}"`); + if (!served.redirect_uris?.includes(REDIRECT_URI)) { + fail(`served redirect_uris ${JSON.stringify(served.redirect_uris)} lack the page's ${REDIRECT_URI}`); + } + if (served.scope !== EXPECTED_SCOPE) fail(`scope is "${served.scope}", expected exactly "${EXPECTED_SCOPE}"`); + + const required = { + application_type: "web", + token_endpoint_auth_method: "none", + dpop_bound_access_tokens: true, + }; + for (const [key, value] of Object.entries(required)) { + if (served[key] !== value) fail(`${key} is ${JSON.stringify(served[key])}, expected ${JSON.stringify(value)}`); + } + for (const grant of ["authorization_code", "refresh_token"]) { + if (!served.grant_types?.includes(grant)) fail(`grant_types lacks ${grant}`); + } +} + +report("client-metadata"); diff --git a/policy-site/tests/check-html.mjs b/policy-site/tests/check-html.mjs new file mode 100644 index 00000000..09bd95b2 --- /dev/null +++ b/policy-site/tests/check-html.mjs @@ -0,0 +1,56 @@ +// The built pages hold nothing the deployed CSP would refuse and load nothing +// from any origin but their own. +// +// script-src and style-src grant 'self' and no 'unsafe-inline': an inline +//