feat: upgrade Remix to 3.0.0-alpha.4 and resync package docs#64
feat: upgrade Remix to 3.0.0-alpha.4 and resync package docs#64kentcdodds wants to merge 7 commits intomainfrom
Conversation
createElement(type, props) merges variadic rest children onto props; with no rest args that becomes [] and wipes props.children, producing a blank UI. Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
|
🔎 Preview deployed: https://epicflare-pr-64.kentcdodds.workers.dev Worker: Mocks:
|
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
|
@cursoragent please check CI and fix issues. |
|
Bugbot Autofix prepared a fix for the issue found in the latest run.
Preview (03cfc173b6)diff --git a/bun.lock b/bun.lock
--- a/bun.lock
+++ b/bun.lock
@@ -13,7 +13,7 @@
"@modelcontextprotocol/sdk": "1.26.0",
"agents": "^0.7.6",
"get-port": "^7.1.0",
- "remix": "3.0.0-alpha.3",
+ "remix": "3.0.0-alpha.4",
"workers-ai-provider": "^3.1.2",
"zod": "^4.3.6",
},
@@ -306,35 +306,45 @@
"@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="],
- "@remix-run/async-context-middleware": ["@remix-run/async-context-middleware@0.1.3", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0" } }, "sha512-z2hTnQspCWsKka1QxAu5qf+IRbMYwTBmwL28zCk8nIPcYxG19rDbBeRmfQZ9hxHes72XVDu3Fd3ArLTy8bivdw=="],
+ "@remix-run/async-context-middleware": ["@remix-run/async-context-middleware@0.2.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0" } }, "sha512-ebnPM2OFM/BO3GSoRKs2i5+lgY/fIunzpL8/RhuEuP9a04cQYXMbEzbdae+qBHTgtsVdPrqeGr/lwlF5FNnQZA=="],
- "@remix-run/component": ["@remix-run/component@0.5.0", "", { "dependencies": { "@remix-run/interaction": "^0.5.0" } }, "sha512-xRLOcgwWKZxFdj63bWi3/snC2uxkm978B49EGEv1/G43iBFksYjS4ADanfYxREvQjMlaCqSUH0ZTZsJbGsz3PQ=="],
+ "@remix-run/auth": ["@remix-run/auth@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/session": "^0.4.1" } }, "sha512-TFsK40jymT7iZyGWOjoqlFFmILdDXy3jYBoI1mohjdqrp8SVWbm7htiSbdd+j8DGBUEbxMTvKcaVG0bPA1M8VA=="],
- "@remix-run/compression-middleware": ["@remix-run/compression-middleware@0.1.3", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0", "@remix-run/mime": "^0.4.0", "@remix-run/response": "^0.3.2" } }, "sha512-pKsKtIzW/yjCbftSQRYfvnKKdq65Nu+YEhbQooiXtmd1Ub4inHSrzeA/vca3sQkBnBr/eRA9uujxIszRe+6LAg=="],
+ "@remix-run/auth-middleware": ["@remix-run/auth-middleware@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/session": "^0.4.1" } }, "sha512-QZlI4MyU8VTVI6WB4T4MWWqtbYrIUiiBG3mgdJUXjEYMBBoxVAkT/6rT3YErmhX+SVARP2zC46qzujrbYcQLCQ=="],
+ "@remix-run/component": ["@remix-run/component@0.6.0", "", { "dependencies": { "@types/dom-navigation": "^1.0.7" } }, "sha512-hmIUdPnBtONeOH8bbLuOVGSgFD/MAUNvpH0Xwgo27GByBIjQhOm25Tt9R04HQslDhHAzwNVnWeQkr3j9eyXYhQ=="],
+
+ "@remix-run/compression-middleware": ["@remix-run/compression-middleware@0.1.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/mime": "^0.4.0", "@remix-run/response": "^0.3.2" } }, "sha512-vE+d4a04Tq9fO5KBmNtMD5GKJEt6U++B1yufEdBMNe5ROBQkpPWSuKlhkRcZyoV8SPEI2HbkKMtQ8QXfGJR11g=="],
+
"@remix-run/cookie": ["@remix-run/cookie@0.5.1", "", { "dependencies": { "@remix-run/headers": "^0.19.0" } }, "sha512-gbeZfVd1AKRlFj3IJWcIcR6zqVGz2XGJhR+mcqYiWnYt6KM8oUGtc82dsc4qZnWWA1f0nM4/He9wrU4GjB0pag=="],
- "@remix-run/data-schema": ["@remix-run/data-schema@0.1.0", "", { "dependencies": { "@standard-schema/spec": "^1.1.0" } }, "sha512-PzpYP9P19cb8bS7Y9+MSxyAWGy0n13sx1lYOMoKI+iEx7pdB2ZLaiidwcn1l6AeR6gjVIpuzyM6/UONG3bzboA=="],
+ "@remix-run/cop-middleware": ["@remix-run/cop-middleware@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0" } }, "sha512-5rMNrBswTJJLPsu1nUfXubgVf7XBTRfPNfd/4QQHaXRdhXVWVxU9HbNJrOjd7iNbBWuZeaJxSdDepbQLnuBkTg=="],
- "@remix-run/data-table": ["@remix-run/data-table@0.1.0", "", { "dependencies": { "@remix-run/data-schema": "^0.1.0" } }, "sha512-u70INxiF9pThV2LcP7y37G7eq16OXWrA+HXS8qNplQgJJd1zhwZrODOtfF9U+PKLiWQkBj+bEeh7QhOlvGsVDg=="],
+ "@remix-run/cors-middleware": ["@remix-run/cors-middleware@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/headers": "^0.19.0" } }, "sha512-x5nyxDhWEOnznqetDhtj5WYoHrKpbiZ8K6BCGQ2+DFjLYHoyWCayciUNrqOVSIWc5XdV6KlKHcg1TyQvW/cS4w=="],
- "@remix-run/data-table-mysql": ["@remix-run/data-table-mysql@0.1.0", "", { "dependencies": { "@remix-run/data-table": "^0.1.0" }, "peerDependencies": { "mysql2": "^3.15.3" }, "optionalPeers": ["mysql2"] }, "sha512-lzghxTYZDHODNisIJWkq4IkcGsb1pUrp9WGtlNEVWrXWC6aArOiojR3PW4arFwNG5ddKJPRfwot+ySc2YZy5RQ=="],
+ "@remix-run/csrf-middleware": ["@remix-run/csrf-middleware@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/session": "^0.4.1" } }, "sha512-EZAV9UZrx3qdYu3ov9NgFVJchw6kOLYI2uRB+e3WBVW1phruHKc42EpdJIZ6fU0J9Of0Lfo4X4VTM7O1/u1cWw=="],
- "@remix-run/data-table-postgres": ["@remix-run/data-table-postgres@0.1.0", "", { "dependencies": { "@remix-run/data-table": "^0.1.0" }, "peerDependencies": { "pg": "^8.16.3" }, "optionalPeers": ["pg"] }, "sha512-mqzARY5tOFVLjFAArryuLQ93M8IdjVnLKnD1VQyJHFbHQF7Zbr8+exv7Hp2hZ1TIGAhZiepGjjC8Re631yJ1Jw=="],
+ "@remix-run/data-schema": ["@remix-run/data-schema@0.2.0", "", { "dependencies": { "@standard-schema/spec": "^1.1.0" } }, "sha512-bXgdWcWZS6TkbVO9Piwn035iw0LczfSTFrCW4Vd1k57x/4j3eWZwfHBr3lXdw5whefacs3m3pVZ8RUiu8NtBeA=="],
- "@remix-run/data-table-sqlite": ["@remix-run/data-table-sqlite@0.1.0", "", { "dependencies": { "@remix-run/data-table": "^0.1.0" }, "peerDependencies": { "better-sqlite3": "^12.4.1" }, "optionalPeers": ["better-sqlite3"] }, "sha512-hFtmz9haMr3p/aFWL5D1zpJsgAnNdLVCF6HvXBmtK4m3NdLmM1eNhblpB/SfPGNEODsmx4Jvcaof57dfaLuKgA=="],
+ "@remix-run/data-table": ["@remix-run/data-table@0.2.0", "", {}, "sha512-6QbdlKER0F0rX3XkTHC7ZPBgu/dyGvvgrKRbraBD9iGHTE7AED1emo3/YWsDJUeFzy7INNJaUIpNm9jNcgL8Kg=="],
+ "@remix-run/data-table-mysql": ["@remix-run/data-table-mysql@0.2.0", "", { "dependencies": { "@remix-run/data-table": "^0.2.0" }, "peerDependencies": { "mysql2": "^3.15.3" }, "optionalPeers": ["mysql2"] }, "sha512-voTcYUjG12PWlrWIYSqoa+C3WK9Q6TVKPrqnvGKY9s6EVBil9e+glaP2JOM8BbzL0hGuKPifdbrDGfGpGitdQw=="],
+
+ "@remix-run/data-table-postgres": ["@remix-run/data-table-postgres@0.2.0", "", { "dependencies": { "@remix-run/data-table": "^0.2.0" }, "peerDependencies": { "pg": "^8.16.3" }, "optionalPeers": ["pg"] }, "sha512-03v+zpWL4lMJr34PZ76PWFpjIa4K6Uq3NLBqyvN4ojPpm6J7TGsFwPy6sInRm3HjoS4gPSCEmmQ7dUovWa58EA=="],
+
+ "@remix-run/data-table-sqlite": ["@remix-run/data-table-sqlite@0.2.0", "", { "dependencies": { "@remix-run/data-table": "^0.2.0" }, "peerDependencies": { "better-sqlite3": "^12.4.1" }, "optionalPeers": ["better-sqlite3"] }, "sha512-TAQ0u8YnmO3WdnuZULPhc+fgZbKZZPutXiPLf8c9NDhXSC41FlcvfQHTKUxBPVhqdi9mhGiblHHNp6BsVu11dg=="],
+
"@remix-run/fetch-proxy": ["@remix-run/fetch-proxy@0.7.1", "", { "dependencies": { "@remix-run/headers": "^0.19.0" } }, "sha512-rPLfOpAaCXtm1dLI45uIPKERNbXbrh0P9AJc1sliz8pWd/McaFYjdr5KzB4QrFSfPvEt/Wmy6F2521qB1kK0ug=="],
- "@remix-run/fetch-router": ["@remix-run/fetch-router@0.17.0", "", { "dependencies": { "@remix-run/route-pattern": "^0.19.0", "@remix-run/session": "^0.4.1" } }, "sha512-3FeJGrTqrKKCvZdQWijbCXTEHKcdttkLFbI2ogfpZ+iDYSNZ9036wgDXuuoZqg6d+D0E8Unhk5ZwrLKDCd/hOw=="],
+ "@remix-run/fetch-router": ["@remix-run/fetch-router@0.18.0", "", { "dependencies": { "@remix-run/route-pattern": "^0.20.0" } }, "sha512-9Z4JgLH9/jD8jiVvAY9LZR+VoZxPJOQ7pENTBJoSo91PZOkPXfCxQWhPhAwYCP8z+/0FV4ZWSg1DmPPoU2f0UQ=="],
"@remix-run/file-storage": ["@remix-run/file-storage@0.13.3", "", { "dependencies": { "@remix-run/fs": "^0.4.2", "@remix-run/lazy-file": "^5.0.2" } }, "sha512-HBDz9RRsFRvI6EoeasklxH/NleGy0QZBXBcA4gQBW8ueucop21TQI4wvGlhZmXcnJ3nP4RkhdF2Gff2/HD5eiA=="],
"@remix-run/file-storage-s3": ["@remix-run/file-storage-s3@0.1.0", "", { "dependencies": { "@remix-run/file-storage": "^0.13.3", "aws4fetch": "^1.0.20" } }, "sha512-r80An7nSFidK/0xn9O9/HxfUcgxVpM4kprnTGr6pGhKdgbaTCEtA+U5ETYGfeedFxhDcT+7ue+4Fv/VxeIvFwQ=="],
- "@remix-run/form-data-middleware": ["@remix-run/form-data-middleware@0.1.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0", "@remix-run/form-data-parser": "^0.15.0" } }, "sha512-WZfP1U6lDoipkfjcd0V39HJeTPMTX2WyaPcOBTbBHS0kapIZiHYm6RpGLhE8U58652i3TBh/zzvAczJIbFV2AA=="],
+ "@remix-run/form-data-middleware": ["@remix-run/form-data-middleware@0.2.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/form-data-parser": "^0.16.0" } }, "sha512-oaBcYvyP/U2GrDdFyQUh2pNCshvKwPFBoUup1Bz3NHnMzym4OkZJgaBi4o7HIJC33QudnqI7Mp7TW1pRg0CvIA=="],
- "@remix-run/form-data-parser": ["@remix-run/form-data-parser@0.15.0", "", { "dependencies": { "@remix-run/multipart-parser": "^0.14.2" } }, "sha512-sQP4r9218TWmow6Nt252VjKE674dRi4Z8WTnWUxJJG8I/qNfnGZubZ8LgyE0dR9z1gfaEpkd19MfYMLiOTOkJQ=="],
+ "@remix-run/form-data-parser": ["@remix-run/form-data-parser@0.16.0", "", { "dependencies": { "@remix-run/multipart-parser": "^0.15.0" } }, "sha512-k4QCgCyPURpqe+9Rual1GBJ8Ab6ri82Clfh5ooxq03jKQ1TyQZT5xh+XP0R05+Bqg5bzAZl7r2BpmDEvfoRTWw=="],
"@remix-run/fs": ["@remix-run/fs@0.4.2", "", { "dependencies": { "@remix-run/lazy-file": "^5.0.2", "@remix-run/mime": "^0.4.0" } }, "sha512-z3W2L+iUwgZ7i0S379SYQ8veOe2Weqs+JajmyTCqSVzbmMUniH3qQ6SAYr3FjbrKtLLWHN3SpK4XtFv57VzbLA=="],
@@ -342,33 +352,31 @@
"@remix-run/html-template": ["@remix-run/html-template@0.3.0", "", {}, "sha512-aAMx68udtIk0fmCpCXHYscVeCDsRVEmEgh4XvtusPr3vkHu3jn4gx5oAxgsPXPdDmmD/d75SYyI0m/F+aLz5iQ=="],
- "@remix-run/interaction": ["@remix-run/interaction@0.5.0", "", {}, "sha512-Z2ja9/7TfMHt/wzWq425GI2xj6QxW4E3OHZ8In81uytZKIuWaI6Pn3v8qyMrInwnBEaLcfcbeQVCiExgHU8D4A=="],
-
"@remix-run/lazy-file": ["@remix-run/lazy-file@5.0.2", "", { "dependencies": { "@remix-run/mime": "^0.4.0" } }, "sha512-52Bo5dTV+EDwrUMS3mjeR+Sly85aHeN3fnNTeaflqzlCMWJwr2pX+y6/3mTDtRdxmTWF1MGQAoeayzfPb4zZJg=="],
- "@remix-run/logger-middleware": ["@remix-run/logger-middleware@0.1.3", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0" } }, "sha512-6gxz1XC2lJYQS3Oz1pZzxpuoLowwd2PSpimMaQnkk0fZ7hHYxx7uV+FSs2Z3fue6kYvZ+IxSUe8Wy52V2r4LxA=="],
+ "@remix-run/logger-middleware": ["@remix-run/logger-middleware@0.1.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0" } }, "sha512-gFDyYn8o5ddjQoEbrc8CK6PPq3lzrOX6BCqlvM+QVTMJ5/2aHgfMbQdXnWtbeiWJJrpODpABrSNfjLkHjaK4og=="],
- "@remix-run/method-override-middleware": ["@remix-run/method-override-middleware@0.1.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0" } }, "sha512-gYFsdY0eIStTpsqGnF/22YracUmS8cZlef6KsBKOVf1nOI9wwwbRrj/DWLMQsWt22YSBMuPYZW5NLKEmXvJRZw=="],
+ "@remix-run/method-override-middleware": ["@remix-run/method-override-middleware@0.1.5", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0" } }, "sha512-HYQG6YR4l0rYMJ0MXth9SWfeSCwnqHIrWgMQpZHKS/MT7roX7EAfknrl9vzjQWsVfkwl6RrSD30SrLPONj3tsg=="],
"@remix-run/mime": ["@remix-run/mime@0.4.0", "", {}, "sha512-O6TcTL6CtuX82Q8BHqAere5O+0hYcrzSgY9whsDOBuqbW753Rczprs2jYw3qCDSo0kLxykW4ys3qgZcdgZ+chw=="],
- "@remix-run/multipart-parser": ["@remix-run/multipart-parser@0.14.2", "", { "dependencies": { "@remix-run/headers": "^0.19.0" } }, "sha512-yDq9ql4Xz92bRG/Sgl4cg2dRlxxC6A40XBy/oyDhy76hJtTQvgyzx9sfPXYPxcfL1BtqljC+sYHE0PvjmQhSfw=="],
+ "@remix-run/multipart-parser": ["@remix-run/multipart-parser@0.15.0", "", { "dependencies": { "@remix-run/headers": "^0.19.0" } }, "sha512-/Ugo6k2bN7gh7Ybyhe7R/NrkD075fHrEfVf17P+NNC9rlWHBQCOSyJ4V8n4wtoG8umeImagU/AuHuUuTwzvHww=="],
"@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.13.0", "", {}, "sha512-1EsNo0ZpgXu/90AWoRZf/oE3RVTUS80tiTUpt+hv5pjtAkw7icN4WskDwz/KdAw5ARbJLMhZBrO1NqThmy/McA=="],
"@remix-run/response": ["@remix-run/response@0.3.2", "", { "dependencies": { "@remix-run/headers": "^0.19.0", "@remix-run/html-template": "^0.3.0", "@remix-run/mime": "^0.4.0" } }, "sha512-GkFqVq5E7Do6rMKTBjgoNyJlrsLrqYg+TDlCDrXoZ3v8O2RlSI14+bCF8lGQHy15DWX2pizVj6R0e6NmjcuLuA=="],
- "@remix-run/route-pattern": ["@remix-run/route-pattern@0.19.0", "", {}, "sha512-RXKaIJ2Lx01uyZc0iw+yLzowFCa1/NuB8jN7QTo4QUe2CaUGtvPGdhgrTUp75lyNNCSJIrM9SaAJ6c1pjZdmoA=="],
+ "@remix-run/route-pattern": ["@remix-run/route-pattern@0.20.0", "", {}, "sha512-TEdJ5eFn40St26oyaRYGI1FWeXDvlEzkbvollM8Xit0qIuDFyFG03Okvvfc1s8KgR9sYULDPjxJIzk6xvIRR9A=="],
"@remix-run/session": ["@remix-run/session@0.4.1", "", {}, "sha512-Bm6aKYgutb/raHZ3laloz8g/Qu7f3CeK3o4gUVDMxtEiAdWCzJamwHoTpGOc5+g1Kuy7z85v4M6nGrF06MFDSg=="],
- "@remix-run/session-middleware": ["@remix-run/session-middleware@0.1.4", "", { "dependencies": { "@remix-run/cookie": "^0.5.1", "@remix-run/fetch-router": "^0.17.0", "@remix-run/session": "^0.4.1" } }, "sha512-qqLmf7mG88h+Ge8pWiJMO8+t9nfQMuO/Zx2W68IwB7Cpt+b6PDpB++i3dd/KLlsjJ43XPMoT2ydmo+eQMgBX3g=="],
+ "@remix-run/session-middleware": ["@remix-run/session-middleware@0.2.0", "", { "dependencies": { "@remix-run/cookie": "^0.5.1", "@remix-run/fetch-router": "^0.18.0", "@remix-run/session": "^0.4.1" } }, "sha512-kRAr0inELyXeOnUhlaM/eKIe9x1RZsOGbglb7Keb1+Lf6KxWxNMwv1ymmHBfae2ossjQTN/QUaABwQgX/uk3DQ=="],
"@remix-run/session-storage-memcache": ["@remix-run/session-storage-memcache@0.1.0", "", { "dependencies": { "@remix-run/session": "^0.4.1" } }, "sha512-k853rpHncdTJUwdk0hqd+gZ2OONZLNdOUJBKdJB+MehxrVv1TtacDnA+Xs3kh+IVwUrsTmBhED+GHSUocMATUg=="],
"@remix-run/session-storage-redis": ["@remix-run/session-storage-redis@0.1.0", "", { "dependencies": { "@remix-run/session": "^0.4.1" }, "peerDependencies": { "redis": "^5.10.0" }, "optionalPeers": ["redis"] }, "sha512-MovUS1E98wDHP8zsESJGm3ySB7iiOhd+3usxyXXM2sbF9gIe6r1bdAXXirGIoC8AEq1v8IiFE5u5ipo7PX0UHQ=="],
- "@remix-run/static-middleware": ["@remix-run/static-middleware@0.4.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0", "@remix-run/fs": "^0.4.2", "@remix-run/html-template": "^0.3.0", "@remix-run/mime": "^0.4.0", "@remix-run/response": "^0.3.2" } }, "sha512-aL5ngFG57uPXTEDaH0uP/cKDpYkLMTtmPjK+SR1ugS654ORk8WTD4Ajf56QekMykCvCnO6PkgFAruUyKkwDNMg=="],
+ "@remix-run/static-middleware": ["@remix-run/static-middleware@0.4.5", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/fs": "^0.4.2", "@remix-run/html-template": "^0.3.0", "@remix-run/mime": "^0.4.0", "@remix-run/response": "^0.3.2" } }, "sha512-jxEbrQMDWcUgmv/2NSv4pahvaW3I4jxqZUMDQ7/VfkcgeJKLFk15vgp4BJ5vLDKKMvwVcgM0Ccbw4Y3Ev2zemA=="],
"@remix-run/tar-parser": ["@remix-run/tar-parser@0.7.0", "", {}, "sha512-PW8JxEUzaGcnqxC5hBI8L9lK/Qz3oad6IGKZ+NExI3L7urVJUux+yCBrsme79DMBgS6hL+lgd/5LPFA5fSwF9A=="],
@@ -396,6 +404,8 @@
"@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
+ "@types/dom-navigation": ["@types/dom-navigation@1.0.7", "", {}, "sha512-Di4W+i2faYquHUnyWUg3bBQp5pTNvjDDA7mIYfD/1WlLgan6sKkeVjGbdL78K0CuNEk5Pfc/c0rfelwkz10mnQ=="],
+
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
@@ -952,7 +962,7 @@
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
- "remix": ["remix@3.0.0-alpha.3", "", { "dependencies": { "@remix-run/async-context-middleware": "^0.1.3", "@remix-run/component": "^0.5.0", "@remix-run/compression-middleware": "^0.1.3", "@remix-run/cookie": "^0.5.1", "@remix-run/data-schema": "^0.1.0", "@remix-run/data-table": "^0.1.0", "@remix-run/data-table-mysql": "^0.1.0", "@remix-run/data-table-postgres": "^0.1.0", "@remix-run/data-table-sqlite": "^0.1.0", "@remix-run/fetch-proxy": "^0.7.1", "@remix-run/fetch-router": "^0.17.0", "@remix-run/file-storage": "^0.13.3", "@remix-run/file-storage-s3": "^0.1.0", "@remix-run/form-data-middleware": "^0.1.4", "@remix-run/form-data-parser": "^0.15.0", "@remix-run/fs": "^0.4.2", "@remix-run/headers": "^0.19.0", "@remix-run/html-template": "^0.3.0", "@remix-run/interaction": "^0.5.0", "@remix-run/lazy-file": "^5.0.2", "@remix-run/logger-middleware": "^0.1.3", "@remix-run/method-override-middleware": "^0.1.4", "@remix-run/mime": "^0.4.0", "@remix-run/multipart-parser": "^0.14.2", "@remix-run/node-fetch-server": "^0.13.0", "@remix-run/response": "^0.3.2", "@remix-run/route-pattern": "^0.19.0", "@remix-run/session": "^0.4.1", "@remix-run/session-middleware": "^0.1.4", "@remix-run/session-storage-memcache": "^0.1.0", "@remix-run/session-storage-redis": "^0.1.0", "@remix-run/static-middleware": "^0.4.4", "@remix-run/tar-parser": "^0.7.0" } }, "sha512-RIctAYR7OW3oYzAGclLhgltrRtKviIdnCVwoLcPDicOjV4I2mJ9AEi8YXl2+hGPupzNNEUcrDtoICd7xNuMptg=="],
+ "remix": ["remix@3.0.0-alpha.4", "", { "dependencies": { "@remix-run/async-context-middleware": "^0.2.0", "@remix-run/auth": "^0.1.0", "@remix-run/auth-middleware": "^0.1.0", "@remix-run/component": "^0.6.0", "@remix-run/compression-middleware": "^0.1.4", "@remix-run/cookie": "^0.5.1", "@remix-run/cop-middleware": "^0.1.0", "@remix-run/cors-middleware": "^0.1.0", "@remix-run/csrf-middleware": "^0.1.0", "@remix-run/data-schema": "^0.2.0", "@remix-run/data-table": "^0.2.0", "@remix-run/data-table-mysql": "^0.2.0", "@remix-run/data-table-postgres": "^0.2.0", "@remix-run/data-table-sqlite": "^0.2.0", "@remix-run/fetch-proxy": "^0.7.1", "@remix-run/fetch-router": "^0.18.0", "@remix-run/file-storage": "^0.13.3", "@remix-run/file-storage-s3": "^0.1.0", "@remix-run/form-data-middleware": "^0.2.0", "@remix-run/form-data-parser": "^0.16.0", "@remix-run/fs": "^0.4.2", "@remix-run/headers": "^0.19.0", "@remix-run/html-template": "^0.3.0", "@remix-run/lazy-file": "^5.0.2", "@remix-run/logger-middleware": "^0.1.4", "@remix-run/method-override-middleware": "^0.1.5", "@remix-run/mime": "^0.4.0", "@remix-run/multipart-parser": "^0.15.0", "@remix-run/node-fetch-server": "^0.13.0", "@remix-run/response": "^0.3.2", "@remix-run/route-pattern": "^0.20.0", "@remix-run/session": "^0.4.1", "@remix-run/session-middleware": "^0.2.0", "@remix-run/session-storage-memcache": "^0.1.0", "@remix-run/session-storage-redis": "^0.1.0", "@remix-run/static-middleware": "^0.4.5", "@remix-run/tar-parser": "^0.7.0" } }, "sha512-fvYHPm8QbrbL09wmmddrnknyntB+fl4bLmpECpK32cOoJW3pMoMvESBB1qRAQe+yWgTexVjp5G3BGOmmtYOX0A=="],
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
diff --git a/client/app.tsx b/client/app.tsx
--- a/client/app.tsx
+++ b/client/app.tsx
@@ -1,199 +1,184 @@
-import { type Handle } from 'remix/component'
-import { clientRoutes } from './routes/index.tsx'
-import {
- getPathname,
- listenToRouterNavigation,
- Router,
-} from './client-router.tsx'
-import {
- fetchSessionInfo,
- type SessionInfo,
- type SessionStatus,
-} from './session.ts'
-import { buildAuthLink } from './auth-links.ts'
-import { colors, mq, spacing, typography } from './styles/tokens.ts'
-
+import { css, type Handle } from 'remix/component';
+import { clientRoutes } from './routes/index.tsx';
+import { getPathname, listenToRouterNavigation, Router, } from './client-router.tsx';
+import { fetchSessionInfo, type SessionInfo, type SessionStatus, } from './session.ts';
+import { buildAuthLink } from './auth-links.ts';
+import { colors, mq, spacing, typography } from './styles/tokens.ts';
export function App(handle: Handle) {
- let session: SessionInfo | null = null
- let sessionStatus: SessionStatus = 'idle'
- let sessionRefreshInFlight = false
- let sessionRefreshQueued = false
- let currentPathname = getPathname()
-
- function queueSessionRefresh() {
- sessionRefreshQueued = true
- if (sessionRefreshInFlight) return
-
- // Preserve current nav state during refreshes after first load.
- if (sessionStatus === 'idle') {
- sessionStatus = 'loading'
- handle.update()
- }
-
- sessionRefreshQueued = false
- sessionRefreshInFlight = true
- handle.queueTask(async (signal) => {
- const nextSession = await fetchSessionInfo(signal)
- sessionRefreshInFlight = false
- if (signal.aborted) return
- session = nextSession
- sessionStatus = 'ready'
- handle.update()
- if (sessionRefreshQueued) {
- queueSessionRefresh()
- }
- })
- if (sessionStatus !== 'loading') {
- handle.update()
- }
- }
-
- handle.queueTask(() => {
- queueSessionRefresh()
- })
- listenToRouterNavigation(handle, () => {
- currentPathname = getPathname()
- queueSessionRefresh()
- handle.update()
- })
-
- const navLinkCss = {
- color: colors.primaryText,
- fontWeight: typography.fontWeight.medium,
- textDecoration: 'none',
- '&:hover': {
- textDecoration: 'underline',
- },
- }
-
- const navHomeLinkCss = {
- ...navLinkCss,
- display: 'flex',
- alignItems: 'center',
- lineHeight: 0,
- '&:hover': {
- textDecoration: 'none',
- opacity: 0.85,
- },
- }
-
- const logOutButtonCss = {
- padding: `${spacing.xs} ${spacing.md}`,
- borderRadius: '999px',
- border: `1px solid ${colors.border}`,
- backgroundColor: 'transparent',
- color: colors.text,
- fontWeight: typography.fontWeight.medium,
- cursor: 'pointer',
- }
-
- return () => {
- const isChatLayout = currentPathname.startsWith('/chat')
- const sessionEmail = session?.email ?? ''
- const isSessionReady = sessionStatus === 'ready'
- const isLoggedIn = isSessionReady && Boolean(sessionEmail)
- const showAuthLinks = isSessionReady && !isLoggedIn
- const oauthRedirectTo =
- typeof window !== 'undefined' && currentPathname === '/oauth/authorize'
- ? `${currentPathname}${window.location.search}`
- : null
- const loginHref = buildAuthLink('/login', oauthRedirectTo)
- const signupHref = buildAuthLink('/signup', oauthRedirectTo)
-
- return (
- <main
- css={{
- maxWidth: isChatLayout ? 'none' : '52rem',
- width: '100%',
- margin: isChatLayout ? 0 : '0 auto',
- padding: isChatLayout
- ? `${spacing.lg} ${spacing.xl} ${spacing.sm}`
- : spacing['2xl'],
- minHeight: isChatLayout ? '100vh' : undefined,
- fontFamily: typography.fontFamily,
- boxSizing: 'border-box',
- [mq.tablet]: {
- padding: isChatLayout
- ? `${spacing.sm} ${spacing.sm} 0`
- : spacing.md,
- },
- }}
- >
- <nav
- css={{
- display: 'flex',
- alignItems: 'center',
- gap: spacing.md,
- flexWrap: 'wrap',
- marginBottom: isChatLayout ? spacing.lg : spacing.xl,
- [mq.tablet]: {
- gap: spacing.sm,
- marginBottom: isChatLayout ? spacing.sm : spacing.md,
- },
- }}
- >
- <a href="/" css={navHomeLinkCss} aria-label="Home">
- <img
- src="/logo.png"
- alt=""
- width={112}
- height={28}
- css={{
- display: 'block',
- height: '1.35em',
- width: 'auto',
- }}
- />
+ let session: SessionInfo | null = null;
+ let sessionStatus: SessionStatus = 'idle';
+ let sessionRefreshInFlight = false;
+ let sessionRefreshQueued = false;
+ let currentPathname = getPathname();
+ function queueSessionRefresh() {
+ sessionRefreshQueued = true;
+ if (sessionRefreshInFlight)
+ return;
+ // Preserve current nav state during refreshes after first load.
+ if (sessionStatus === 'idle') {
+ sessionStatus = 'loading';
+ handle.update();
+ }
+ sessionRefreshQueued = false;
+ sessionRefreshInFlight = true;
+ handle.queueTask(async (signal) => {
+ const nextSession = await fetchSessionInfo(signal);
+ sessionRefreshInFlight = false;
+ if (signal.aborted)
+ return;
+ session = nextSession;
+ sessionStatus = 'ready';
+ handle.update();
+ if (sessionRefreshQueued) {
+ queueSessionRefresh();
+ }
+ });
+ if (sessionStatus !== 'loading') {
+ handle.update();
+ }
+ }
+ handle.queueTask(() => {
+ queueSessionRefresh();
+ });
+ listenToRouterNavigation(handle, () => {
+ currentPathname = getPathname();
+ queueSessionRefresh();
+ handle.update();
+ });
+ const navLinkCss = {
+ color: colors.primaryText,
+ fontWeight: typography.fontWeight.medium,
+ textDecoration: 'none',
+ '&:hover': {
+ textDecoration: 'underline',
+ },
+ };
+ const navHomeLinkCss = {
+ ...navLinkCss,
+ display: 'flex',
+ alignItems: 'center',
+ lineHeight: 0,
+ '&:hover': {
+ textDecoration: 'none',
+ opacity: 0.85,
+ },
+ };
+ const logOutButtonCss = {
+ padding: `${spacing.xs} ${spacing.md}`,
+ borderRadius: '999px',
+ border: `1px solid ${colors.border}`,
+ backgroundColor: 'transparent',
+ color: colors.text,
+ fontWeight: typography.fontWeight.medium,
+ cursor: 'pointer',
+ };
+ return () => {
+ const isChatLayout = currentPathname.startsWith('/chat');
+ const sessionEmail = session?.email ?? '';
+ const isSessionReady = sessionStatus === 'ready';
+ const isLoggedIn = isSessionReady && Boolean(sessionEmail);
+ const showAuthLinks = isSessionReady && !isLoggedIn;
+ const oauthRedirectTo = typeof window !== 'undefined' && currentPathname === '/oauth/authorize'
+ ? `${currentPathname}${window.location.search}`
+ : null;
+ const loginHref = buildAuthLink('/login', oauthRedirectTo);
+ const signupHref = buildAuthLink('/signup', oauthRedirectTo);
+ return (<main mix={[
+ css({
+ maxWidth: isChatLayout ? 'none' : '52rem',
+ width: '100%',
+ margin: isChatLayout ? 0 : '0 auto',
+ padding: isChatLayout
+ ? `${spacing.lg} ${spacing.xl} ${spacing.sm}`
+ : spacing['2xl'],
+ minHeight: isChatLayout ? '100vh' : undefined,
+ fontFamily: typography.fontFamily,
+ boxSizing: 'border-box',
+ [mq.tablet]: {
+ padding: isChatLayout
+ ? `${spacing.sm} ${spacing.sm} 0`
+ : spacing.md,
+ },
+ })
+ ]}>
+ <nav mix={[
+ css({
+ display: 'flex',
+ alignItems: 'center',
+ gap: spacing.md,
+ flexWrap: 'wrap',
+ marginBottom: isChatLayout ? spacing.lg : spacing.xl,
+ [mq.tablet]: {
+ gap: spacing.sm,
+ marginBottom: isChatLayout ? spacing.sm : spacing.md,
+ },
+ })
+ ]}>
+ <a href="/" aria-label="Home" mix={[
+ css(navHomeLinkCss)
+ ]}>
+ <img src="/logo.png" alt="" width={112} height={28} mix={[
+ css({
+ display: 'block',
+ height: '1.35em',
+ width: 'auto',
+ })
+ ]}/>
</a>
- {showAuthLinks ? (
- <>
- <a href={loginHref} css={navLinkCss}>
+ {showAuthLinks ? (<>
+ <a href={loginHref} mix={[
+ css(navLinkCss)
+ ]}>
Login
</a>
- <a href={signupHref} css={navLinkCss}>
+ <a href={signupHref} mix={[
+ css(navLinkCss)
+ ]}>
Signup
</a>
- </>
- ) : null}
- {isLoggedIn ? (
- <>
- <a href="/chat" css={navLinkCss}>
+ </>) : null}
+ {isLoggedIn ? (<>
+ <a href="/chat" mix={[
+ css(navLinkCss)
+ ]}>
Chat
</a>
- <a href="/account" css={navLinkCss}>
+ <a href="/account" mix={[
+ css(navLinkCss)
+ ]}>
{sessionEmail}
</a>
- <form method="post" action="/logout" css={{ margin: 0 }}>
- <button type="submit" css={logOutButtonCss}>
+ <form method="post" action="/logout" mix={[
+ css({ margin: 0 })
+ ]}>
+ <button type="submit" mix={[
+ css(logOutButtonCss)
+ ]}>
Log out
</button>
</form>
- </>
- ) : null}
+ </>) : null}
</nav>
- <Router
- setup={{
- routes: clientRoutes,
- fallback: (
- <section>
- <h2
- css={{
- fontSize: typography.fontSize.lg,
- fontWeight: typography.fontWeight.semibold,
- marginBottom: spacing.sm,
- color: colors.text,
- }}
- >
+ <Router setup={{
+ routes: clientRoutes,
+ fallback: (<section>
+ <h2 mix={[
+ css({
+ fontSize: typography.fontSize.lg,
+ fontWeight: typography.fontWeight.semibold,
+ marginBottom: spacing.sm,
+ color: colors.text,
+ })
+ ]}>
Not Found
</h2>
- <p css={{ color: colors.textMuted }}>
+ <p mix={[
+ css({ color: colors.textMuted })
+ ]}>
That route does not exist.
</p>
- </section>
- ),
- }}
- />
- </main>
- )
- }
+ </section>),
+ }}/>
+ </main>);
+ };
}
diff --git a/client/client-router.tsx b/client/client-router.tsx
--- a/client/client-router.tsx
+++ b/client/client-router.tsx
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { addEventListeners, type Handle } from 'remix/component'
type RouterSetup = {
routes: Record<string, JSX.Element>
@@ -251,7 +251,7 @@
export function listenToRouterNavigation(handle: Handle, listener: () => void) {
ensureRouter()
- handle.on(routerEvents, {
+ addEventListeners(routerEvents, handle.signal, {
navigate: () => listener(),
})
}
diff --git a/client/counter.tsx b/client/counter.tsx
--- a/client/counter.tsx
+++ b/client/counter.tsx
@@ -1,49 +1,36 @@
-import { type Handle } from 'remix/component'
-import {
- colors,
- radius,
- spacing,
- transitions,
- typography,
-} from './styles/tokens.ts'
-
+import { css, type Handle, on } from 'remix/component';
+import { colors, radius, spacing, transitions, typography, } from './styles/tokens.ts';
type CounterSetup = {
- initial?: number
-}
-
+ initial?: number;
+};
export function Counter(handle: Handle, setup: CounterSetup = {}) {
- let count = setup.initial ?? 0
-
- function increment() {
- count += 1
- handle.update()
- }
-
- return () => (
- <button
- type="button"
- css={{
- padding: `${spacing.sm} ${spacing.lg}`,
- borderRadius: radius.full,
- border: `1px solid ${colors.border}`,
- backgroundColor: colors.primary,
- color: colors.onPrimary,
- fontSize: typography.fontSize.base,
- fontWeight: typography.fontWeight.semibold,
- cursor: 'pointer',
- transition: `transform ${transitions.fast}, background-color ${transitions.normal}`,
- '&:hover': {
- backgroundColor: colors.primaryHover,
- transform: 'translateY(-1px)',
- },
- '&:active': {
- backgroundColor: colors.primaryActive,
- transform: 'translateY(0)',
- },
- }}
- on={{ click: increment }}
- >
+ let count = setup.initial ?? 0;
+ function increment() {
+ count += 1;
+ handle.update();
+ }
+ return () => (<button type="button" mix={[
+ css({
+ padding: `${spacing.sm} ${spacing.lg}`,
+ borderRadius: radius.full,
+ border: `1px solid ${colors.border}`,
+ backgroundColor: colors.primary,
+ color: colors.onPrimary,
+ fontSize: typography.fontSize.base,
+ fontWeight: typography.fontWeight.semibold,
+ cursor: 'pointer',
+ transition: `transform ${transitions.fast}, background-color ${transitions.normal}`,
+ '&:hover': {
+ backgroundColor: colors.primaryHover,
+ transform: 'translateY(-1px)',
+ },
+ '&:active': {
+ backgroundColor: colors.primaryActive,
+ transform: 'translateY(0)',
+ },
+ }),
+ on("click", increment)
+ ]}>
Count: {count}
- </button>
- )
+ </button>);
}
diff --git a/client/double-check.ts b/client/double-check.ts
--- a/client/double-check.ts
+++ b/client/double-check.ts
@@ -1,26 +1,14 @@
import { type Handle } from 'remix/component'
-type BlurHandler = (event: FocusEvent) => void
-type ClickHandler = (event: MouseEvent) => void
-
type ButtonLikeProps = {
+ mix?: Array<unknown>
+ onConfirm?: (event: MouseEvent) => void
on?: {
- blur?: BlurHandler
- click?: ClickHandler
+ click?: (event: MouseEvent) => void
}
[key: string]: unknown
}
-function callAll<Event>(
- ...handlers: Array<((event: Event) => void) | undefined>
-) {
- return (event: Event) => {
- for (const handler of handlers) {
- handler?.(event)
- }
- }
-}
-
export function createDoubleCheck(handle: Handle) {
let doubleCheck = false
@@ -39,29 +27,36 @@
},
getButtonProps<Props extends ButtonLikeProps>(props?: Props): Props {
const buttonProps = props ?? ({} as Props)
+ const {
+ mix: inputMix,
+ onConfirm,
+ on: onOverrides,
+ ...rest
+ } = buttonProps as ButtonLikeProps
+ const mix = [...(inputMix ?? [])]
+ const confirmHandler = onConfirm ?? onOverrides?.click
- const onBlur: BlurHandler = () => {
- setDoubleCheck(false)
- }
+ mix.push({
+ handleEvent(handle) {
+ handle.addEventListener('blur', () => {
+ setDoubleCheck(false)
+ })
- const onClick: ClickHandler = (event) => {
- if (!doubleCheck) {
- event.preventDefault()
- setDoubleCheck(true)
- return
- }
+ handle.addEventListener('click', (event) => {
+ if (!doubleCheck) {
+ event.preventDefault()
+ setDoubleCheck(true)
+ return
+ }
+ setDoubleCheck(false)
+ confirmHandler?.(event)
+ })
+ },
+ })
- buttonProps.on?.click?.(event)
- setDoubleCheck(false)
- }
-
return {
- ...buttonProps,
- on: {
- ...buttonProps.on,
- blur: callAll(onBlur, buttonProps.on?.blur),
- click: onClick,
- },
+ ...rest,
+ mix,
}
},
}
diff --git a/client/editable-text.tsx b/client/editable-text.tsx
--- a/client/editable-text.tsx
+++ b/client/editable-text.tsx
@@ -1,171 +1,148 @@
-import { type Handle } from 'remix/component'
-
+import { css, type Handle, on } from 'remix/component';
type EditableTextProps = {
- id: string
- ariaLabel: string
- value: string
- emptyText?: string
- buttonCss?: Record<string, unknown>
- inputCss?: Record<string, unknown>
- onSave: (value: string) => Promise<boolean> | boolean
-}
-
+ id: string;
+ ariaLabel: string;
+ value: string;
+ emptyText?: string;
+ buttonCss?: Record<string, unknown>;
+ inputCss?: Record<string, unknown>;
+ onSave: (value: string) => Promise<boolean> | boolean;
+};
const inheritTextStyles = {
- fontSize: 'inherit',
- fontStyle: 'inherit',
- fontWeight: 'inherit',
- fontFamily: 'inherit',
- textAlign: 'inherit',
- lineHeight: 'inherit',
- color: 'inherit',
-} as const
-
+ fontSize: 'inherit',
+ fontStyle: 'inherit',
+ fontWeight: 'inherit',
+ fontFamily: 'inherit',
+ textAlign: 'inherit',
+ lineHeight: 'inherit',
+ color: 'inherit',
+} as const;
export function EditableText(handle: Handle) {
- let isEditing = false
- let draftValue = ''
- let isSaving = false
-
- function focusInput(inputId: string) {
- void handle.queueTask(async () => {
- const input = document.getElementById(inputId)
- if (!(input instanceof HTMLInputElement)) return
- input.focus()
- input.select()
- })
- }
-
- function focusButton(buttonId: string) {
- void handle.queueTask(async () => {
- const button = document.getElementById(buttonId)
- if (!(button instanceof HTMLButtonElement)) return
- button.focus()
- })
- }
-
- return (props: EditableTextProps) => {
- const buttonId = `${props.id}-button`
-
- function startEditing() {
- if (isSaving) return
- draftValue = props.value
- isEditing = true
- handle.update()
- focusInput(props.id)
- }
-
- function cancelEditing() {
- if (isSaving) return
- draftValue = props.value
- isEditing = false
- handle.update()
- focusButton(buttonId)
- }
-
- async function submitEditing(event: SubmitEvent) {
- event.preventDefault()
- if (isSaving) return
- const nextValue = draftValue.trim()
- if (!nextValue) return
-
- isSaving = true
- handle.update()
- let didSave = false
- try {
- didSave = await props.onSave(nextValue)
- } catch (error) {
- isSaving = false
- handle.update()
- throw error
- }
- isSaving = false
- if (!didSave) {
- handle.update()
- return
- }
-
- isEditing = false
- handle.update()
... diff truncated: showing 800 of 27929 lines |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Destructured
onproperty shadows importedonfunction- Renamed the destructured
onproperty to avoid shadowing the importedonhelper, preventing runtime crashes when noonoverride is provided.
- Renamed the destructured
Preview (724bba772f)
diff --git a/bun.lock b/bun.lock
--- a/bun.lock
+++ b/bun.lock
@@ -13,7 +13,7 @@
"@modelcontextprotocol/sdk": "1.26.0",
"agents": "^0.7.6",
"get-port": "^7.1.0",
- "remix": "3.0.0-alpha.3",
+ "remix": "3.0.0-alpha.4",
"workers-ai-provider": "^3.1.2",
"zod": "^4.3.6",
},
@@ -306,35 +306,45 @@
"@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="],
- "@remix-run/async-context-middleware": ["@remix-run/async-context-middleware@0.1.3", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0" } }, "sha512-z2hTnQspCWsKka1QxAu5qf+IRbMYwTBmwL28zCk8nIPcYxG19rDbBeRmfQZ9hxHes72XVDu3Fd3ArLTy8bivdw=="],
+ "@remix-run/async-context-middleware": ["@remix-run/async-context-middleware@0.2.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0" } }, "sha512-ebnPM2OFM/BO3GSoRKs2i5+lgY/fIunzpL8/RhuEuP9a04cQYXMbEzbdae+qBHTgtsVdPrqeGr/lwlF5FNnQZA=="],
- "@remix-run/component": ["@remix-run/component@0.5.0", "", { "dependencies": { "@remix-run/interaction": "^0.5.0" } }, "sha512-xRLOcgwWKZxFdj63bWi3/snC2uxkm978B49EGEv1/G43iBFksYjS4ADanfYxREvQjMlaCqSUH0ZTZsJbGsz3PQ=="],
+ "@remix-run/auth": ["@remix-run/auth@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/session": "^0.4.1" } }, "sha512-TFsK40jymT7iZyGWOjoqlFFmILdDXy3jYBoI1mohjdqrp8SVWbm7htiSbdd+j8DGBUEbxMTvKcaVG0bPA1M8VA=="],
- "@remix-run/compression-middleware": ["@remix-run/compression-middleware@0.1.3", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0", "@remix-run/mime": "^0.4.0", "@remix-run/response": "^0.3.2" } }, "sha512-pKsKtIzW/yjCbftSQRYfvnKKdq65Nu+YEhbQooiXtmd1Ub4inHSrzeA/vca3sQkBnBr/eRA9uujxIszRe+6LAg=="],
+ "@remix-run/auth-middleware": ["@remix-run/auth-middleware@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/session": "^0.4.1" } }, "sha512-QZlI4MyU8VTVI6WB4T4MWWqtbYrIUiiBG3mgdJUXjEYMBBoxVAkT/6rT3YErmhX+SVARP2zC46qzujrbYcQLCQ=="],
+ "@remix-run/component": ["@remix-run/component@0.6.0", "", { "dependencies": { "@types/dom-navigation": "^1.0.7" } }, "sha512-hmIUdPnBtONeOH8bbLuOVGSgFD/MAUNvpH0Xwgo27GByBIjQhOm25Tt9R04HQslDhHAzwNVnWeQkr3j9eyXYhQ=="],
+
+ "@remix-run/compression-middleware": ["@remix-run/compression-middleware@0.1.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/mime": "^0.4.0", "@remix-run/response": "^0.3.2" } }, "sha512-vE+d4a04Tq9fO5KBmNtMD5GKJEt6U++B1yufEdBMNe5ROBQkpPWSuKlhkRcZyoV8SPEI2HbkKMtQ8QXfGJR11g=="],
+
"@remix-run/cookie": ["@remix-run/cookie@0.5.1", "", { "dependencies": { "@remix-run/headers": "^0.19.0" } }, "sha512-gbeZfVd1AKRlFj3IJWcIcR6zqVGz2XGJhR+mcqYiWnYt6KM8oUGtc82dsc4qZnWWA1f0nM4/He9wrU4GjB0pag=="],
- "@remix-run/data-schema": ["@remix-run/data-schema@0.1.0", "", { "dependencies": { "@standard-schema/spec": "^1.1.0" } }, "sha512-PzpYP9P19cb8bS7Y9+MSxyAWGy0n13sx1lYOMoKI+iEx7pdB2ZLaiidwcn1l6AeR6gjVIpuzyM6/UONG3bzboA=="],
+ "@remix-run/cop-middleware": ["@remix-run/cop-middleware@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0" } }, "sha512-5rMNrBswTJJLPsu1nUfXubgVf7XBTRfPNfd/4QQHaXRdhXVWVxU9HbNJrOjd7iNbBWuZeaJxSdDepbQLnuBkTg=="],
- "@remix-run/data-table": ["@remix-run/data-table@0.1.0", "", { "dependencies": { "@remix-run/data-schema": "^0.1.0" } }, "sha512-u70INxiF9pThV2LcP7y37G7eq16OXWrA+HXS8qNplQgJJd1zhwZrODOtfF9U+PKLiWQkBj+bEeh7QhOlvGsVDg=="],
+ "@remix-run/cors-middleware": ["@remix-run/cors-middleware@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/headers": "^0.19.0" } }, "sha512-x5nyxDhWEOnznqetDhtj5WYoHrKpbiZ8K6BCGQ2+DFjLYHoyWCayciUNrqOVSIWc5XdV6KlKHcg1TyQvW/cS4w=="],
- "@remix-run/data-table-mysql": ["@remix-run/data-table-mysql@0.1.0", "", { "dependencies": { "@remix-run/data-table": "^0.1.0" }, "peerDependencies": { "mysql2": "^3.15.3" }, "optionalPeers": ["mysql2"] }, "sha512-lzghxTYZDHODNisIJWkq4IkcGsb1pUrp9WGtlNEVWrXWC6aArOiojR3PW4arFwNG5ddKJPRfwot+ySc2YZy5RQ=="],
+ "@remix-run/csrf-middleware": ["@remix-run/csrf-middleware@0.1.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/session": "^0.4.1" } }, "sha512-EZAV9UZrx3qdYu3ov9NgFVJchw6kOLYI2uRB+e3WBVW1phruHKc42EpdJIZ6fU0J9Of0Lfo4X4VTM7O1/u1cWw=="],
- "@remix-run/data-table-postgres": ["@remix-run/data-table-postgres@0.1.0", "", { "dependencies": { "@remix-run/data-table": "^0.1.0" }, "peerDependencies": { "pg": "^8.16.3" }, "optionalPeers": ["pg"] }, "sha512-mqzARY5tOFVLjFAArryuLQ93M8IdjVnLKnD1VQyJHFbHQF7Zbr8+exv7Hp2hZ1TIGAhZiepGjjC8Re631yJ1Jw=="],
+ "@remix-run/data-schema": ["@remix-run/data-schema@0.2.0", "", { "dependencies": { "@standard-schema/spec": "^1.1.0" } }, "sha512-bXgdWcWZS6TkbVO9Piwn035iw0LczfSTFrCW4Vd1k57x/4j3eWZwfHBr3lXdw5whefacs3m3pVZ8RUiu8NtBeA=="],
- "@remix-run/data-table-sqlite": ["@remix-run/data-table-sqlite@0.1.0", "", { "dependencies": { "@remix-run/data-table": "^0.1.0" }, "peerDependencies": { "better-sqlite3": "^12.4.1" }, "optionalPeers": ["better-sqlite3"] }, "sha512-hFtmz9haMr3p/aFWL5D1zpJsgAnNdLVCF6HvXBmtK4m3NdLmM1eNhblpB/SfPGNEODsmx4Jvcaof57dfaLuKgA=="],
+ "@remix-run/data-table": ["@remix-run/data-table@0.2.0", "", {}, "sha512-6QbdlKER0F0rX3XkTHC7ZPBgu/dyGvvgrKRbraBD9iGHTE7AED1emo3/YWsDJUeFzy7INNJaUIpNm9jNcgL8Kg=="],
+ "@remix-run/data-table-mysql": ["@remix-run/data-table-mysql@0.2.0", "", { "dependencies": { "@remix-run/data-table": "^0.2.0" }, "peerDependencies": { "mysql2": "^3.15.3" }, "optionalPeers": ["mysql2"] }, "sha512-voTcYUjG12PWlrWIYSqoa+C3WK9Q6TVKPrqnvGKY9s6EVBil9e+glaP2JOM8BbzL0hGuKPifdbrDGfGpGitdQw=="],
+
+ "@remix-run/data-table-postgres": ["@remix-run/data-table-postgres@0.2.0", "", { "dependencies": { "@remix-run/data-table": "^0.2.0" }, "peerDependencies": { "pg": "^8.16.3" }, "optionalPeers": ["pg"] }, "sha512-03v+zpWL4lMJr34PZ76PWFpjIa4K6Uq3NLBqyvN4ojPpm6J7TGsFwPy6sInRm3HjoS4gPSCEmmQ7dUovWa58EA=="],
+
+ "@remix-run/data-table-sqlite": ["@remix-run/data-table-sqlite@0.2.0", "", { "dependencies": { "@remix-run/data-table": "^0.2.0" }, "peerDependencies": { "better-sqlite3": "^12.4.1" }, "optionalPeers": ["better-sqlite3"] }, "sha512-TAQ0u8YnmO3WdnuZULPhc+fgZbKZZPutXiPLf8c9NDhXSC41FlcvfQHTKUxBPVhqdi9mhGiblHHNp6BsVu11dg=="],
+
"@remix-run/fetch-proxy": ["@remix-run/fetch-proxy@0.7.1", "", { "dependencies": { "@remix-run/headers": "^0.19.0" } }, "sha512-rPLfOpAaCXtm1dLI45uIPKERNbXbrh0P9AJc1sliz8pWd/McaFYjdr5KzB4QrFSfPvEt/Wmy6F2521qB1kK0ug=="],
- "@remix-run/fetch-router": ["@remix-run/fetch-router@0.17.0", "", { "dependencies": { "@remix-run/route-pattern": "^0.19.0", "@remix-run/session": "^0.4.1" } }, "sha512-3FeJGrTqrKKCvZdQWijbCXTEHKcdttkLFbI2ogfpZ+iDYSNZ9036wgDXuuoZqg6d+D0E8Unhk5ZwrLKDCd/hOw=="],
+ "@remix-run/fetch-router": ["@remix-run/fetch-router@0.18.0", "", { "dependencies": { "@remix-run/route-pattern": "^0.20.0" } }, "sha512-9Z4JgLH9/jD8jiVvAY9LZR+VoZxPJOQ7pENTBJoSo91PZOkPXfCxQWhPhAwYCP8z+/0FV4ZWSg1DmPPoU2f0UQ=="],
"@remix-run/file-storage": ["@remix-run/file-storage@0.13.3", "", { "dependencies": { "@remix-run/fs": "^0.4.2", "@remix-run/lazy-file": "^5.0.2" } }, "sha512-HBDz9RRsFRvI6EoeasklxH/NleGy0QZBXBcA4gQBW8ueucop21TQI4wvGlhZmXcnJ3nP4RkhdF2Gff2/HD5eiA=="],
"@remix-run/file-storage-s3": ["@remix-run/file-storage-s3@0.1.0", "", { "dependencies": { "@remix-run/file-storage": "^0.13.3", "aws4fetch": "^1.0.20" } }, "sha512-r80An7nSFidK/0xn9O9/HxfUcgxVpM4kprnTGr6pGhKdgbaTCEtA+U5ETYGfeedFxhDcT+7ue+4Fv/VxeIvFwQ=="],
- "@remix-run/form-data-middleware": ["@remix-run/form-data-middleware@0.1.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0", "@remix-run/form-data-parser": "^0.15.0" } }, "sha512-WZfP1U6lDoipkfjcd0V39HJeTPMTX2WyaPcOBTbBHS0kapIZiHYm6RpGLhE8U58652i3TBh/zzvAczJIbFV2AA=="],
+ "@remix-run/form-data-middleware": ["@remix-run/form-data-middleware@0.2.0", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/form-data-parser": "^0.16.0" } }, "sha512-oaBcYvyP/U2GrDdFyQUh2pNCshvKwPFBoUup1Bz3NHnMzym4OkZJgaBi4o7HIJC33QudnqI7Mp7TW1pRg0CvIA=="],
- "@remix-run/form-data-parser": ["@remix-run/form-data-parser@0.15.0", "", { "dependencies": { "@remix-run/multipart-parser": "^0.14.2" } }, "sha512-sQP4r9218TWmow6Nt252VjKE674dRi4Z8WTnWUxJJG8I/qNfnGZubZ8LgyE0dR9z1gfaEpkd19MfYMLiOTOkJQ=="],
+ "@remix-run/form-data-parser": ["@remix-run/form-data-parser@0.16.0", "", { "dependencies": { "@remix-run/multipart-parser": "^0.15.0" } }, "sha512-k4QCgCyPURpqe+9Rual1GBJ8Ab6ri82Clfh5ooxq03jKQ1TyQZT5xh+XP0R05+Bqg5bzAZl7r2BpmDEvfoRTWw=="],
"@remix-run/fs": ["@remix-run/fs@0.4.2", "", { "dependencies": { "@remix-run/lazy-file": "^5.0.2", "@remix-run/mime": "^0.4.0" } }, "sha512-z3W2L+iUwgZ7i0S379SYQ8veOe2Weqs+JajmyTCqSVzbmMUniH3qQ6SAYr3FjbrKtLLWHN3SpK4XtFv57VzbLA=="],
@@ -342,33 +352,31 @@
"@remix-run/html-template": ["@remix-run/html-template@0.3.0", "", {}, "sha512-aAMx68udtIk0fmCpCXHYscVeCDsRVEmEgh4XvtusPr3vkHu3jn4gx5oAxgsPXPdDmmD/d75SYyI0m/F+aLz5iQ=="],
- "@remix-run/interaction": ["@remix-run/interaction@0.5.0", "", {}, "sha512-Z2ja9/7TfMHt/wzWq425GI2xj6QxW4E3OHZ8In81uytZKIuWaI6Pn3v8qyMrInwnBEaLcfcbeQVCiExgHU8D4A=="],
-
"@remix-run/lazy-file": ["@remix-run/lazy-file@5.0.2", "", { "dependencies": { "@remix-run/mime": "^0.4.0" } }, "sha512-52Bo5dTV+EDwrUMS3mjeR+Sly85aHeN3fnNTeaflqzlCMWJwr2pX+y6/3mTDtRdxmTWF1MGQAoeayzfPb4zZJg=="],
- "@remix-run/logger-middleware": ["@remix-run/logger-middleware@0.1.3", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0" } }, "sha512-6gxz1XC2lJYQS3Oz1pZzxpuoLowwd2PSpimMaQnkk0fZ7hHYxx7uV+FSs2Z3fue6kYvZ+IxSUe8Wy52V2r4LxA=="],
+ "@remix-run/logger-middleware": ["@remix-run/logger-middleware@0.1.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0" } }, "sha512-gFDyYn8o5ddjQoEbrc8CK6PPq3lzrOX6BCqlvM+QVTMJ5/2aHgfMbQdXnWtbeiWJJrpODpABrSNfjLkHjaK4og=="],
- "@remix-run/method-override-middleware": ["@remix-run/method-override-middleware@0.1.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0" } }, "sha512-gYFsdY0eIStTpsqGnF/22YracUmS8cZlef6KsBKOVf1nOI9wwwbRrj/DWLMQsWt22YSBMuPYZW5NLKEmXvJRZw=="],
+ "@remix-run/method-override-middleware": ["@remix-run/method-override-middleware@0.1.5", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0" } }, "sha512-HYQG6YR4l0rYMJ0MXth9SWfeSCwnqHIrWgMQpZHKS/MT7roX7EAfknrl9vzjQWsVfkwl6RrSD30SrLPONj3tsg=="],
"@remix-run/mime": ["@remix-run/mime@0.4.0", "", {}, "sha512-O6TcTL6CtuX82Q8BHqAere5O+0hYcrzSgY9whsDOBuqbW753Rczprs2jYw3qCDSo0kLxykW4ys3qgZcdgZ+chw=="],
- "@remix-run/multipart-parser": ["@remix-run/multipart-parser@0.14.2", "", { "dependencies": { "@remix-run/headers": "^0.19.0" } }, "sha512-yDq9ql4Xz92bRG/Sgl4cg2dRlxxC6A40XBy/oyDhy76hJtTQvgyzx9sfPXYPxcfL1BtqljC+sYHE0PvjmQhSfw=="],
+ "@remix-run/multipart-parser": ["@remix-run/multipart-parser@0.15.0", "", { "dependencies": { "@remix-run/headers": "^0.19.0" } }, "sha512-/Ugo6k2bN7gh7Ybyhe7R/NrkD075fHrEfVf17P+NNC9rlWHBQCOSyJ4V8n4wtoG8umeImagU/AuHuUuTwzvHww=="],
"@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.13.0", "", {}, "sha512-1EsNo0ZpgXu/90AWoRZf/oE3RVTUS80tiTUpt+hv5pjtAkw7icN4WskDwz/KdAw5ARbJLMhZBrO1NqThmy/McA=="],
"@remix-run/response": ["@remix-run/response@0.3.2", "", { "dependencies": { "@remix-run/headers": "^0.19.0", "@remix-run/html-template": "^0.3.0", "@remix-run/mime": "^0.4.0" } }, "sha512-GkFqVq5E7Do6rMKTBjgoNyJlrsLrqYg+TDlCDrXoZ3v8O2RlSI14+bCF8lGQHy15DWX2pizVj6R0e6NmjcuLuA=="],
- "@remix-run/route-pattern": ["@remix-run/route-pattern@0.19.0", "", {}, "sha512-RXKaIJ2Lx01uyZc0iw+yLzowFCa1/NuB8jN7QTo4QUe2CaUGtvPGdhgrTUp75lyNNCSJIrM9SaAJ6c1pjZdmoA=="],
+ "@remix-run/route-pattern": ["@remix-run/route-pattern@0.20.0", "", {}, "sha512-TEdJ5eFn40St26oyaRYGI1FWeXDvlEzkbvollM8Xit0qIuDFyFG03Okvvfc1s8KgR9sYULDPjxJIzk6xvIRR9A=="],
"@remix-run/session": ["@remix-run/session@0.4.1", "", {}, "sha512-Bm6aKYgutb/raHZ3laloz8g/Qu7f3CeK3o4gUVDMxtEiAdWCzJamwHoTpGOc5+g1Kuy7z85v4M6nGrF06MFDSg=="],
- "@remix-run/session-middleware": ["@remix-run/session-middleware@0.1.4", "", { "dependencies": { "@remix-run/cookie": "^0.5.1", "@remix-run/fetch-router": "^0.17.0", "@remix-run/session": "^0.4.1" } }, "sha512-qqLmf7mG88h+Ge8pWiJMO8+t9nfQMuO/Zx2W68IwB7Cpt+b6PDpB++i3dd/KLlsjJ43XPMoT2ydmo+eQMgBX3g=="],
+ "@remix-run/session-middleware": ["@remix-run/session-middleware@0.2.0", "", { "dependencies": { "@remix-run/cookie": "^0.5.1", "@remix-run/fetch-router": "^0.18.0", "@remix-run/session": "^0.4.1" } }, "sha512-kRAr0inELyXeOnUhlaM/eKIe9x1RZsOGbglb7Keb1+Lf6KxWxNMwv1ymmHBfae2ossjQTN/QUaABwQgX/uk3DQ=="],
"@remix-run/session-storage-memcache": ["@remix-run/session-storage-memcache@0.1.0", "", { "dependencies": { "@remix-run/session": "^0.4.1" } }, "sha512-k853rpHncdTJUwdk0hqd+gZ2OONZLNdOUJBKdJB+MehxrVv1TtacDnA+Xs3kh+IVwUrsTmBhED+GHSUocMATUg=="],
"@remix-run/session-storage-redis": ["@remix-run/session-storage-redis@0.1.0", "", { "dependencies": { "@remix-run/session": "^0.4.1" }, "peerDependencies": { "redis": "^5.10.0" }, "optionalPeers": ["redis"] }, "sha512-MovUS1E98wDHP8zsESJGm3ySB7iiOhd+3usxyXXM2sbF9gIe6r1bdAXXirGIoC8AEq1v8IiFE5u5ipo7PX0UHQ=="],
- "@remix-run/static-middleware": ["@remix-run/static-middleware@0.4.4", "", { "dependencies": { "@remix-run/fetch-router": "^0.17.0", "@remix-run/fs": "^0.4.2", "@remix-run/html-template": "^0.3.0", "@remix-run/mime": "^0.4.0", "@remix-run/response": "^0.3.2" } }, "sha512-aL5ngFG57uPXTEDaH0uP/cKDpYkLMTtmPjK+SR1ugS654ORk8WTD4Ajf56QekMykCvCnO6PkgFAruUyKkwDNMg=="],
+ "@remix-run/static-middleware": ["@remix-run/static-middleware@0.4.5", "", { "dependencies": { "@remix-run/fetch-router": "^0.18.0", "@remix-run/fs": "^0.4.2", "@remix-run/html-template": "^0.3.0", "@remix-run/mime": "^0.4.0", "@remix-run/response": "^0.3.2" } }, "sha512-jxEbrQMDWcUgmv/2NSv4pahvaW3I4jxqZUMDQ7/VfkcgeJKLFk15vgp4BJ5vLDKKMvwVcgM0Ccbw4Y3Ev2zemA=="],
"@remix-run/tar-parser": ["@remix-run/tar-parser@0.7.0", "", {}, "sha512-PW8JxEUzaGcnqxC5hBI8L9lK/Qz3oad6IGKZ+NExI3L7urVJUux+yCBrsme79DMBgS6hL+lgd/5LPFA5fSwF9A=="],
@@ -396,6 +404,8 @@
"@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
+ "@types/dom-navigation": ["@types/dom-navigation@1.0.7", "", {}, "sha512-Di4W+i2faYquHUnyWUg3bBQp5pTNvjDDA7mIYfD/1WlLgan6sKkeVjGbdL78K0CuNEk5Pfc/c0rfelwkz10mnQ=="],
+
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
@@ -952,7 +962,7 @@
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
- "remix": ["remix@3.0.0-alpha.3", "", { "dependencies": { "@remix-run/async-context-middleware": "^0.1.3", "@remix-run/component": "^0.5.0", "@remix-run/compression-middleware": "^0.1.3", "@remix-run/cookie": "^0.5.1", "@remix-run/data-schema": "^0.1.0", "@remix-run/data-table": "^0.1.0", "@remix-run/data-table-mysql": "^0.1.0", "@remix-run/data-table-postgres": "^0.1.0", "@remix-run/data-table-sqlite": "^0.1.0", "@remix-run/fetch-proxy": "^0.7.1", "@remix-run/fetch-router": "^0.17.0", "@remix-run/file-storage": "^0.13.3", "@remix-run/file-storage-s3": "^0.1.0", "@remix-run/form-data-middleware": "^0.1.4", "@remix-run/form-data-parser": "^0.15.0", "@remix-run/fs": "^0.4.2", "@remix-run/headers": "^0.19.0", "@remix-run/html-template": "^0.3.0", "@remix-run/interaction": "^0.5.0", "@remix-run/lazy-file": "^5.0.2", "@remix-run/logger-middleware": "^0.1.3", "@remix-run/method-override-middleware": "^0.1.4", "@remix-run/mime": "^0.4.0", "@remix-run/multipart-parser": "^0.14.2", "@remix-run/node-fetch-server": "^0.13.0", "@remix-run/response": "^0.3.2", "@remix-run/route-pattern": "^0.19.0", "@remix-run/session": "^0.4.1", "@remix-run/session-middleware": "^0.1.4", "@remix-run/session-storage-memcache": "^0.1.0", "@remix-run/session-storage-redis": "^0.1.0", "@remix-run/static-middleware": "^0.4.4", "@remix-run/tar-parser": "^0.7.0" } }, "sha512-RIctAYR7OW3oYzAGclLhgltrRtKviIdnCVwoLcPDicOjV4I2mJ9AEi8YXl2+hGPupzNNEUcrDtoICd7xNuMptg=="],
+ "remix": ["remix@3.0.0-alpha.4", "", { "dependencies": { "@remix-run/async-context-middleware": "^0.2.0", "@remix-run/auth": "^0.1.0", "@remix-run/auth-middleware": "^0.1.0", "@remix-run/component": "^0.6.0", "@remix-run/compression-middleware": "^0.1.4", "@remix-run/cookie": "^0.5.1", "@remix-run/cop-middleware": "^0.1.0", "@remix-run/cors-middleware": "^0.1.0", "@remix-run/csrf-middleware": "^0.1.0", "@remix-run/data-schema": "^0.2.0", "@remix-run/data-table": "^0.2.0", "@remix-run/data-table-mysql": "^0.2.0", "@remix-run/data-table-postgres": "^0.2.0", "@remix-run/data-table-sqlite": "^0.2.0", "@remix-run/fetch-proxy": "^0.7.1", "@remix-run/fetch-router": "^0.18.0", "@remix-run/file-storage": "^0.13.3", "@remix-run/file-storage-s3": "^0.1.0", "@remix-run/form-data-middleware": "^0.2.0", "@remix-run/form-data-parser": "^0.16.0", "@remix-run/fs": "^0.4.2", "@remix-run/headers": "^0.19.0", "@remix-run/html-template": "^0.3.0", "@remix-run/lazy-file": "^5.0.2", "@remix-run/logger-middleware": "^0.1.4", "@remix-run/method-override-middleware": "^0.1.5", "@remix-run/mime": "^0.4.0", "@remix-run/multipart-parser": "^0.15.0", "@remix-run/node-fetch-server": "^0.13.0", "@remix-run/response": "^0.3.2", "@remix-run/route-pattern": "^0.20.0", "@remix-run/session": "^0.4.1", "@remix-run/session-middleware": "^0.2.0", "@remix-run/session-storage-memcache": "^0.1.0", "@remix-run/session-storage-redis": "^0.1.0", "@remix-run/static-middleware": "^0.4.5", "@remix-run/tar-parser": "^0.7.0" } }, "sha512-fvYHPm8QbrbL09wmmddrnknyntB+fl4bLmpECpK32cOoJW3pMoMvESBB1qRAQe+yWgTexVjp5G3BGOmmtYOX0A=="],
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
diff --git a/client/app.tsx b/client/app.tsx
--- a/client/app.tsx
+++ b/client/app.tsx
@@ -1,199 +1,184 @@
-import { type Handle } from 'remix/component'
-import { clientRoutes } from './routes/index.tsx'
-import {
- getPathname,
- listenToRouterNavigation,
- Router,
-} from './client-router.tsx'
-import {
- fetchSessionInfo,
- type SessionInfo,
- type SessionStatus,
-} from './session.ts'
-import { buildAuthLink } from './auth-links.ts'
-import { colors, mq, spacing, typography } from './styles/tokens.ts'
-
+import { css, type Handle } from 'remix/component';
+import { clientRoutes } from './routes/index.tsx';
+import { getPathname, listenToRouterNavigation, Router, } from './client-router.tsx';
+import { fetchSessionInfo, type SessionInfo, type SessionStatus, } from './session.ts';
+import { buildAuthLink } from './auth-links.ts';
+import { colors, mq, spacing, typography } from './styles/tokens.ts';
export function App(handle: Handle) {
- let session: SessionInfo | null = null
- let sessionStatus: SessionStatus = 'idle'
- let sessionRefreshInFlight = false
- let sessionRefreshQueued = false
- let currentPathname = getPathname()
-
- function queueSessionRefresh() {
- sessionRefreshQueued = true
- if (sessionRefreshInFlight) return
-
- // Preserve current nav state during refreshes after first load.
- if (sessionStatus === 'idle') {
- sessionStatus = 'loading'
- handle.update()
- }
-
- sessionRefreshQueued = false
- sessionRefreshInFlight = true
- handle.queueTask(async (signal) => {
- const nextSession = await fetchSessionInfo(signal)
- sessionRefreshInFlight = false
- if (signal.aborted) return
- session = nextSession
- sessionStatus = 'ready'
- handle.update()
- if (sessionRefreshQueued) {
- queueSessionRefresh()
- }
- })
- if (sessionStatus !== 'loading') {
- handle.update()
- }
- }
-
- handle.queueTask(() => {
- queueSessionRefresh()
- })
- listenToRouterNavigation(handle, () => {
- currentPathname = getPathname()
- queueSessionRefresh()
- handle.update()
- })
-
- const navLinkCss = {
- color: colors.primaryText,
- fontWeight: typography.fontWeight.medium,
- textDecoration: 'none',
- '&:hover': {
- textDecoration: 'underline',
- },
- }
-
- const navHomeLinkCss = {
- ...navLinkCss,
- display: 'flex',
- alignItems: 'center',
- lineHeight: 0,
- '&:hover': {
- textDecoration: 'none',
- opacity: 0.85,
- },
- }
-
- const logOutButtonCss = {
- padding: `${spacing.xs} ${spacing.md}`,
- borderRadius: '999px',
- border: `1px solid ${colors.border}`,
- backgroundColor: 'transparent',
- color: colors.text,
- fontWeight: typography.fontWeight.medium,
- cursor: 'pointer',
- }
-
- return () => {
- const isChatLayout = currentPathname.startsWith('/chat')
- const sessionEmail = session?.email ?? ''
- const isSessionReady = sessionStatus === 'ready'
- const isLoggedIn = isSessionReady && Boolean(sessionEmail)
- const showAuthLinks = isSessionReady && !isLoggedIn
- const oauthRedirectTo =
- typeof window !== 'undefined' && currentPathname === '/oauth/authorize'
- ? `${currentPathname}${window.location.search}`
- : null
- const loginHref = buildAuthLink('/login', oauthRedirectTo)
- const signupHref = buildAuthLink('/signup', oauthRedirectTo)
-
- return (
- <main
- css={{
- maxWidth: isChatLayout ? 'none' : '52rem',
- width: '100%',
- margin: isChatLayout ? 0 : '0 auto',
- padding: isChatLayout
- ? `${spacing.lg} ${spacing.xl} ${spacing.sm}`
- : spacing['2xl'],
- minHeight: isChatLayout ? '100vh' : undefined,
- fontFamily: typography.fontFamily,
- boxSizing: 'border-box',
- [mq.tablet]: {
- padding: isChatLayout
- ? `${spacing.sm} ${spacing.sm} 0`
- : spacing.md,
- },
- }}
- >
- <nav
- css={{
- display: 'flex',
- alignItems: 'center',
- gap: spacing.md,
- flexWrap: 'wrap',
- marginBottom: isChatLayout ? spacing.lg : spacing.xl,
- [mq.tablet]: {
- gap: spacing.sm,
- marginBottom: isChatLayout ? spacing.sm : spacing.md,
- },
- }}
- >
- <a href="/" css={navHomeLinkCss} aria-label="Home">
- <img
- src="/logo.png"
- alt=""
- width={112}
- height={28}
- css={{
- display: 'block',
- height: '1.35em',
- width: 'auto',
- }}
- />
+ let session: SessionInfo | null = null;
+ let sessionStatus: SessionStatus = 'idle';
+ let sessionRefreshInFlight = false;
+ let sessionRefreshQueued = false;
+ let currentPathname = getPathname();
+ function queueSessionRefresh() {
+ sessionRefreshQueued = true;
+ if (sessionRefreshInFlight)
+ return;
+ // Preserve current nav state during refreshes after first load.
+ if (sessionStatus === 'idle') {
+ sessionStatus = 'loading';
+ handle.update();
+ }
+ sessionRefreshQueued = false;
+ sessionRefreshInFlight = true;
+ handle.queueTask(async (signal) => {
+ const nextSession = await fetchSessionInfo(signal);
+ sessionRefreshInFlight = false;
+ if (signal.aborted)
+ return;
+ session = nextSession;
+ sessionStatus = 'ready';
+ handle.update();
+ if (sessionRefreshQueued) {
+ queueSessionRefresh();
+ }
+ });
+ if (sessionStatus !== 'loading') {
+ handle.update();
+ }
+ }
+ handle.queueTask(() => {
+ queueSessionRefresh();
+ });
+ listenToRouterNavigation(handle, () => {
+ currentPathname = getPathname();
+ queueSessionRefresh();
+ handle.update();
+ });
+ const navLinkCss = {
+ color: colors.primaryText,
+ fontWeight: typography.fontWeight.medium,
+ textDecoration: 'none',
+ '&:hover': {
+ textDecoration: 'underline',
+ },
+ };
+ const navHomeLinkCss = {
+ ...navLinkCss,
+ display: 'flex',
+ alignItems: 'center',
+ lineHeight: 0,
+ '&:hover': {
+ textDecoration: 'none',
+ opacity: 0.85,
+ },
+ };
+ const logOutButtonCss = {
+ padding: `${spacing.xs} ${spacing.md}`,
+ borderRadius: '999px',
+ border: `1px solid ${colors.border}`,
+ backgroundColor: 'transparent',
+ color: colors.text,
+ fontWeight: typography.fontWeight.medium,
+ cursor: 'pointer',
+ };
+ return () => {
+ const isChatLayout = currentPathname.startsWith('/chat');
+ const sessionEmail = session?.email ?? '';
+ const isSessionReady = sessionStatus === 'ready';
+ const isLoggedIn = isSessionReady && Boolean(sessionEmail);
+ const showAuthLinks = isSessionReady && !isLoggedIn;
+ const oauthRedirectTo = typeof window !== 'undefined' && currentPathname === '/oauth/authorize'
+ ? `${currentPathname}${window.location.search}`
+ : null;
+ const loginHref = buildAuthLink('/login', oauthRedirectTo);
+ const signupHref = buildAuthLink('/signup', oauthRedirectTo);
+ return (<main mix={[
+ css({
+ maxWidth: isChatLayout ? 'none' : '52rem',
+ width: '100%',
+ margin: isChatLayout ? 0 : '0 auto',
+ padding: isChatLayout
+ ? `${spacing.lg} ${spacing.xl} ${spacing.sm}`
+ : spacing['2xl'],
+ minHeight: isChatLayout ? '100vh' : undefined,
+ fontFamily: typography.fontFamily,
+ boxSizing: 'border-box',
+ [mq.tablet]: {
+ padding: isChatLayout
+ ? `${spacing.sm} ${spacing.sm} 0`
+ : spacing.md,
+ },
+ })
+ ]}>
+ <nav mix={[
+ css({
+ display: 'flex',
+ alignItems: 'center',
+ gap: spacing.md,
+ flexWrap: 'wrap',
+ marginBottom: isChatLayout ? spacing.lg : spacing.xl,
+ [mq.tablet]: {
+ gap: spacing.sm,
+ marginBottom: isChatLayout ? spacing.sm : spacing.md,
+ },
+ })
+ ]}>
+ <a href="/" aria-label="Home" mix={[
+ css(navHomeLinkCss)
+ ]}>
+ <img src="/logo.png" alt="" width={112} height={28} mix={[
+ css({
+ display: 'block',
+ height: '1.35em',
+ width: 'auto',
+ })
+ ]}/>
</a>
- {showAuthLinks ? (
- <>
- <a href={loginHref} css={navLinkCss}>
+ {showAuthLinks ? (<>
+ <a href={loginHref} mix={[
+ css(navLinkCss)
+ ]}>
Login
</a>
- <a href={signupHref} css={navLinkCss}>
+ <a href={signupHref} mix={[
+ css(navLinkCss)
+ ]}>
Signup
</a>
- </>
- ) : null}
- {isLoggedIn ? (
- <>
- <a href="/chat" css={navLinkCss}>
+ </>) : null}
+ {isLoggedIn ? (<>
+ <a href="/chat" mix={[
+ css(navLinkCss)
+ ]}>
Chat
</a>
- <a href="/account" css={navLinkCss}>
+ <a href="/account" mix={[
+ css(navLinkCss)
+ ]}>
{sessionEmail}
</a>
- <form method="post" action="/logout" css={{ margin: 0 }}>
- <button type="submit" css={logOutButtonCss}>
+ <form method="post" action="/logout" mix={[
+ css({ margin: 0 })
+ ]}>
+ <button type="submit" mix={[
+ css(logOutButtonCss)
+ ]}>
Log out
</button>
</form>
- </>
- ) : null}
+ </>) : null}
</nav>
- <Router
- setup={{
- routes: clientRoutes,
- fallback: (
- <section>
- <h2
- css={{
- fontSize: typography.fontSize.lg,
- fontWeight: typography.fontWeight.semibold,
- marginBottom: spacing.sm,
- color: colors.text,
- }}
- >
+ <Router setup={{
+ routes: clientRoutes,
+ fallback: (<section>
+ <h2 mix={[
+ css({
+ fontSize: typography.fontSize.lg,
+ fontWeight: typography.fontWeight.semibold,
+ marginBottom: spacing.sm,
+ color: colors.text,
+ })
+ ]}>
Not Found
</h2>
- <p css={{ color: colors.textMuted }}>
+ <p mix={[
+ css({ color: colors.textMuted })
+ ]}>
That route does not exist.
</p>
- </section>
- ),
- }}
- />
- </main>
- )
- }
+ </section>),
+ }}/>
+ </main>);
+ };
}
diff --git a/client/client-router.tsx b/client/client-router.tsx
--- a/client/client-router.tsx
+++ b/client/client-router.tsx
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { addEventListeners, type Handle } from 'remix/component'
type RouterSetup = {
routes: Record<string, JSX.Element>
@@ -251,7 +251,7 @@
export function listenToRouterNavigation(handle: Handle, listener: () => void) {
ensureRouter()
- handle.on(routerEvents, {
+ addEventListeners(routerEvents, handle.signal, {
navigate: () => listener(),
})
}
diff --git a/client/counter.tsx b/client/counter.tsx
--- a/client/counter.tsx
+++ b/client/counter.tsx
@@ -1,49 +1,36 @@
-import { type Handle } from 'remix/component'
-import {
- colors,
- radius,
- spacing,
- transitions,
- typography,
-} from './styles/tokens.ts'
-
+import { css, type Handle, on } from 'remix/component';
+import { colors, radius, spacing, transitions, typography, } from './styles/tokens.ts';
type CounterSetup = {
- initial?: number
-}
-
+ initial?: number;
+};
export function Counter(handle: Handle, setup: CounterSetup = {}) {
- let count = setup.initial ?? 0
-
- function increment() {
- count += 1
- handle.update()
- }
-
- return () => (
- <button
- type="button"
- css={{
- padding: `${spacing.sm} ${spacing.lg}`,
- borderRadius: radius.full,
- border: `1px solid ${colors.border}`,
- backgroundColor: colors.primary,
- color: colors.onPrimary,
- fontSize: typography.fontSize.base,
- fontWeight: typography.fontWeight.semibold,
- cursor: 'pointer',
- transition: `transform ${transitions.fast}, background-color ${transitions.normal}`,
- '&:hover': {
- backgroundColor: colors.primaryHover,
- transform: 'translateY(-1px)',
- },
- '&:active': {
- backgroundColor: colors.primaryActive,
- transform: 'translateY(0)',
- },
- }}
- on={{ click: increment }}
- >
+ let count = setup.initial ?? 0;
+ function increment() {
+ count += 1;
+ handle.update();
+ }
+ return () => (<button type="button" mix={[
+ css({
+ padding: `${spacing.sm} ${spacing.lg}`,
+ borderRadius: radius.full,
+ border: `1px solid ${colors.border}`,
+ backgroundColor: colors.primary,
+ color: colors.onPrimary,
+ fontSize: typography.fontSize.base,
+ fontWeight: typography.fontWeight.semibold,
+ cursor: 'pointer',
+ transition: `transform ${transitions.fast}, background-color ${transitions.normal}`,
+ '&:hover': {
+ backgroundColor: colors.primaryHover,
+ transform: 'translateY(-1px)',
+ },
+ '&:active': {
+ backgroundColor: colors.primaryActive,
+ transform: 'translateY(0)',
+ },
+ }),
+ on("click", increment)
+ ]}>
Count: {count}
- </button>
- )
+ </button>);
}
diff --git a/client/double-check.ts b/client/double-check.ts
--- a/client/double-check.ts
+++ b/client/double-check.ts
@@ -1,26 +1,14 @@
-import { type Handle } from 'remix/component'
+import { on, type Handle } from 'remix/component'
-type BlurHandler = (event: FocusEvent) => void
-type ClickHandler = (event: MouseEvent) => void
-
type ButtonLikeProps = {
+ mix?: Array<unknown>
+ onConfirm?: (event: MouseEvent) => void
on?: {
- blur?: BlurHandler
- click?: ClickHandler
+ click?: (event: MouseEvent) => void
}
[key: string]: unknown
}
-function callAll<Event>(
- ...handlers: Array<((event: Event) => void) | undefined>
-) {
- return (event: Event) => {
- for (const handler of handlers) {
- handler?.(event)
- }
- }
-}
-
export function createDoubleCheck(handle: Handle) {
let doubleCheck = false
@@ -39,29 +27,36 @@
},
getButtonProps<Props extends ButtonLikeProps>(props?: Props): Props {
const buttonProps = props ?? ({} as Props)
+ const {
+ mix: inputMix,
+ onConfirm,
+ on: onOverrides,
+ ...rest
+ } = buttonProps as ButtonLikeProps
+ const mix = [...(inputMix ?? [])]
+ const confirmHandler = onConfirm ?? onOverrides?.click
- const onBlur: BlurHandler = () => {
- setDoubleCheck(false)
- }
+ mix.push(
+ on<HTMLButtonElement, 'blur'>('blur', () => {
+ setDoubleCheck(false)
+ }),
+ )
- const onClick: ClickHandler = (event) => {
- if (!doubleCheck) {
- event.preventDefault()
- setDoubleCheck(true)
- return
- }
+ mix.push(
+ on<HTMLButtonElement, 'click'>('click', (event) => {
+ if (!doubleCheck) {
+ event.preventDefault()
+ setDoubleCheck(true)
+ return
+ }
+ setDoubleCheck(false)
+ confirmHandler?.(event)
+ }),
+ )
- buttonProps.on?.click?.(event)
- setDoubleCheck(false)
- }
-
return {
- ...buttonProps,
- on: {
- ...buttonProps.on,
- blur: callAll(onBlur, buttonProps.on?.blur),
- click: onClick,
- },
+ ...rest,
+ mix,
}
},
}
diff --git a/client/editable-text.tsx b/client/editable-text.tsx
--- a/client/editable-text.tsx
+++ b/client/editable-text.tsx
@@ -1,171 +1,148 @@
-import { type Handle } from 'remix/component'
-
+import { css, type Handle, on } from 'remix/component';
type EditableTextProps = {
- id: string
- ariaLabel: string
- value: string
- emptyText?: string
- buttonCss?: Record<string, unknown>
- inputCss?: Record<string, unknown>
- onSave: (value: string) => Promise<boolean> | boolean
-}
-
+ id: string;
+ ariaLabel: string;
+ value: string;
+ emptyText?: string;
+ buttonCss?: Record<string, unknown>;
+ inputCss?: Record<string, unknown>;
+ onSave: (value: string) => Promise<boolean> | boolean;
+};
const inheritTextStyles = {
- fontSize: 'inherit',
- fontStyle: 'inherit',
- fontWeight: 'inherit',
- fontFamily: 'inherit',
- textAlign: 'inherit',
- lineHeight: 'inherit',
- color: 'inherit',
-} as const
-
+ fontSize: 'inherit',
+ fontStyle: 'inherit',
+ fontWeight: 'inherit',
+ fontFamily: 'inherit',
+ textAlign: 'inherit',
+ lineHeight: 'inherit',
+ color: 'inherit',
+} as const;
export function EditableText(handle: Handle) {
- let isEditing = false
- let draftValue = ''
- let isSaving = false
-
- function focusInput(inputId: string) {
- void handle.queueTask(async () => {
- const input = document.getElementById(inputId)
- if (!(input instanceof HTMLInputElement)) return
- input.focus()
- input.select()
- })
- }
-
- function focusButton(buttonId: string) {
- void handle.queueTask(async () => {
- const button = document.getElementById(buttonId)
- if (!(button instanceof HTMLButtonElement)) return
- button.focus()
- })
- }
-
- return (props: EditableTextProps) => {
- const buttonId = `${props.id}-button`
-
- function startEditing() {
- if (isSaving) return
- draftValue = props.value
- isEditing = true
- handle.update()
- focusInput(props.id)
- }
-
- function cancelEditing() {
- if (isSaving) return
- draftValue = props.value
- isEditing = false
- handle.update()
- focusButton(buttonId)
- }
-
- async function submitEditing(event: SubmitEvent) {
- event.preventDefault()
- if (isSaving) return
- const nextValue = draftValue.trim()
- if (!nextValue) return
-
- isSaving = true
- handle.update()
- let didSave = false
- try {
- didSave = await props.onSave(nextValue)
- } catch (error) {
- isSaving = false
- handle.update()
- throw error
- }
- isSaving = false
- if (!didSave) {
- handle.update()
- return
- }
-
- isEditing = false
... diff truncated: showing 800 of 27873 linesCo-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
| setDoubleCheck(false) | ||
| }) | ||
| }, | ||
| }) |
There was a problem hiding this comment.
Undocumented handleEvent mixin breaks double-check event handling
High Severity
The handleEvent method pushed into the mix array is not a recognized Remix component mix-in interface. The documented mix-in APIs are on(), css(), ref(), and createMixin() — none of which use a raw handleEvent method. This means the framework will never call handleEvent, so the blur and click event listeners are never attached to the delete button. As a result, clicking the button does nothing (no double-check confirmation, no deletion), completely breaking the thread-delete flow. The fix is to use on('blur', ...) and on('click', ...) calls pushed into the mix array instead.





Summary
remix@3.0.0-alpha.4fetch-routercontroller/handlerAPIs and the newdata-tabletable/column APIsremix/componentruntimeDatabaseAdaptercontractdocs/agents/remixfrom the upstream alpha.4 package markdown with one directory per package and upstreamREADME.mdfilesTesting
bun run lintbun run typecheckbun run build/,/login, and unauthenticated/chatredirectWalkthrough
Home page after default runtime migration
Login page after default runtime migration
Chat redirect after default runtime migration