diff --git a/package-lock.json b/package-lock.json index 8ec25e36..20441f56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,6 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "json": "^11.0.0", - "ksuid": "^3.0.0", "next": "^14", "prettier": "3.3.3", "react": "18.3.1", @@ -4165,8 +4164,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -4194,8 +4192,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -4225,8 +4222,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -5944,8 +5940,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -5973,8 +5968,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6004,8 +5998,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6138,8 +6131,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6167,8 +6159,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6198,8 +6189,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6275,8 +6265,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6304,8 +6293,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6335,8 +6323,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6437,8 +6424,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6466,8 +6452,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -6497,8 +6482,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -11355,8 +11339,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -11384,8 +11367,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -11415,8 +11397,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -14605,8 +14586,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -14634,8 +14614,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -14665,8 +14644,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -15567,8 +15545,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -15596,8 +15573,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -15627,8 +15603,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -19342,8 +19317,7 @@ "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/feross" }, @@ -20572,8 +20546,7 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/feross" }, @@ -21177,8 +21150,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -21233,8 +21205,7 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/feross" }, @@ -21458,8 +21429,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001625.tgz", "integrity": "sha512-4KE9N2gcRH+HQhpeiRZXd+1niLB/XNLAhSy4z7fI8EzcbcPoAqjNInxVHTiTwWfTIV4w096XG8OtCOCQQKPv3w==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -24561,8 +24531,7 @@ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz", "integrity": "sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" }, @@ -24617,8 +24586,7 @@ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/jimmywarting" }, @@ -26153,8 +26121,7 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/feross" }, @@ -27829,8 +27796,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -27858,8 +27824,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -27889,8 +27854,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -30209,8 +30173,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -30238,8 +30201,7 @@ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -30269,8 +30231,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -33717,12 +33678,10 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "funding": [{ + "type": "github", + "url": "https://github.com/sponsors/ai" + }], "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" @@ -33911,8 +33870,7 @@ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/jimmywarting" }, @@ -35362,8 +35320,7 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/postcss/" }, @@ -35391,8 +35348,7 @@ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/postcss/" }, @@ -35734,8 +35690,7 @@ "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "individual", "url": "https://github.com/sponsors/dubzzz" }, @@ -35778,8 +35733,7 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/feross" }, @@ -36648,8 +36602,7 @@ "resolved": "https://registry.npmjs.org/release-it/-/release-it-17.3.0.tgz", "integrity": "sha512-7t9a2WEwqQKCdteshZUrO/3RX60plS5CzYAFr5+4Zj8qvRx1pFOFVglJSz4BeFAEd2yejpPxfI60+qRUzLEDZw==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/webpro" }, @@ -36977,8 +36930,7 @@ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/feross" }, @@ -37037,8 +36989,7 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/feross" }, @@ -37936,8 +37887,7 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "github", "url": "https://github.com/sponsors/feross" }, @@ -39564,8 +39514,7 @@ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "dev": true, - "funding": [ - { + "funding": [{ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, @@ -40567,4 +40516,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index c7f558ee..ebb68e0d 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,6 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "json": "^11.0.0", - "ksuid": "^3.0.0", "next": "^14", "prettier": "3.3.3", "react": "18.3.1", @@ -138,4 +137,4 @@ "@commitlint/config-conventional" ] } -} +} \ No newline at end of file diff --git a/react.ts b/react.ts index 91726192..5989aea9 100644 --- a/react.ts +++ b/react.ts @@ -1,4 +1,5 @@ export * from './src/react/Computed'; +export * from './src/react/$'; export * from './src/react/For'; export { usePauseProvider } from './src/react/usePauseProvider'; export * from './src/react/Memo'; diff --git a/src/config/enableReactTracking.ts b/src/config/enableReactTracking.ts index 122ff920..32bcb61b 100644 --- a/src/config/enableReactTracking.ts +++ b/src/config/enableReactTracking.ts @@ -8,29 +8,36 @@ import { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED as ReactInternals } interface ReactTrackingOptions { auto?: boolean; // Make all get() calls act as useSelector() hooks warnUnobserved?: boolean; // Warn if get() is used outside of an observer + warnGet?: boolean; // Warn if get() is used in a component } -export function enableReactTracking({ auto, warnUnobserved }: ReactTrackingOptions) { +export function enableReactTracking({ auto, warnUnobserved, warnGet }: ReactTrackingOptions) { const { get } = internal; - if (auto || (process.env.NODE_ENV === 'development' && warnUnobserved)) { + if (auto || (process.env.NODE_ENV === 'development' && (warnUnobserved || warnGet))) { const ReactRenderContext = createContext(0); + const isInRender = () => { + // If we're already tracking then we definitely don't need useSelector + try { + // If there's no dispatcher we're definitely not in React + // This is an optimization to not need to run useContext. If in a future React version + // this works differently we can change it or just remove it. + const dispatcher = ReactInternals.ReactCurrentDispatcher.current; + if (dispatcher) { + // If there's a dispatcher then we may be inside of a hook. + // Attempt a useContext hook, which will throw an error if outside of render. + useContext(ReactRenderContext); + return true; + } + } catch {} // eslint-disable-line no-empty + return false; + }; + const needsSelector = () => { // If we're already tracking then we definitely don't need useSelector if (!tracking.current) { - try { - // If there's no dispatcher we're definitely not in React - // This is an optimization to not need to run useContext. If in a future React version - // this works differently we can change it or just remove it. - const dispatcher = ReactInternals.ReactCurrentDispatcher.current; - if (dispatcher) { - // If there's a dispatcher then we may be inside of a hook. - // Attempt a useContext hook, which will throw an error if outside of render. - useContext(ReactRenderContext); - return true; - } - } catch {} // eslint-disable-line no-empty + return isInRender(); } return false; }; @@ -38,12 +45,18 @@ export function enableReactTracking({ auto, warnUnobserved }: ReactTrackingOptio configureLegendState({ observableFunctions: { get: (node: NodeInfo, options?: TrackingType | (GetOptions & UseSelectorOptions)) => { - if (needsSelector()) { + if (process.env.NODE_ENV === 'development' && warnUnobserved) { + if (isInRender()) { + console.warn( + '[legend-state] Detected a `get()` call in a React component. It is recommended to use the `use$` hook instead to be compatible with React Compiler: https://legendapp.com/open-source/state/v3/react/react-api/#use$', + ); + } + } else if (needsSelector()) { if (auto) { return useSelector(() => get(node, options), isObject(options) ? options : undefined); } else if (process.env.NODE_ENV === 'development' && warnUnobserved) { console.warn( - '[legend-state] Detected a `get()` call in an unobserved component. You may want to wrap it in observer: https://legendapp.com/open-source/state/react-api/#observer-hoc', + '[legend-state] Detected a `get()` call in an unobserved component. You may want to wrap it in observer: https://legendapp.com/open-source/state/v3/react/react-api/#observer', ); } } diff --git a/src/helpers.ts b/src/helpers.ts index 78a9c891..02e48d74 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -12,16 +12,21 @@ import { isPrimitive, isSet, } from './is'; -import type { Change, ObserveEvent, OpaqueObject, Selector, TypeAtPath } from './observableInterfaces'; +import type { Change, GetOptions, ObserveEvent, OpaqueObject, Selector, TypeAtPath } from './observableInterfaces'; import type { ObservableParam } from './observableTypes'; -export function computeSelector(selector: Selector, e?: ObserveEvent, retainObservable?: boolean): T { +export function computeSelector( + selector: Selector, + getOptions?: GetOptions, + e?: ObserveEvent, + retainObservable?: boolean, +): T { let c = selector as any; if (!isObservable(c) && isFunction(c)) { c = e ? c(e) : c(); } - return isObservable(c) && !retainObservable ? c.get() : c; + return isObservable(c) && !retainObservable ? c.get(getOptions) : c; } export function getObservableIndex(value$: ObservableParam): number { diff --git a/src/observableInterfaces.ts b/src/observableInterfaces.ts index ef1b1ead..88a32bee 100644 --- a/src/observableInterfaces.ts +++ b/src/observableInterfaces.ts @@ -4,7 +4,7 @@ import type { Observable, ObservableParam } from './observableTypes'; export type TrackingType = undefined | true | symbol; // true === shallow export interface GetOptions { - shallow: boolean; + shallow?: boolean; } export type OpaqueObject = T & { [symbolOpaque]: true }; diff --git a/src/observe.ts b/src/observe.ts index 10e20fb7..771c167e 100644 --- a/src/observe.ts +++ b/src/observe.ts @@ -47,7 +47,11 @@ export function observe( // Dispose listeners from previous run dispose?.(); - const { dispose: _dispose, value, nodes } = trackSelector(selectorOrRun as Selector, update, e, options); + const { + dispose: _dispose, + value, + nodes, + } = trackSelector(selectorOrRun as Selector, update, undefined, e, options); dispose = _dispose; e.value = value; diff --git a/src/react/$.tsx b/src/react/$.tsx new file mode 100644 index 00000000..e9a6e76e --- /dev/null +++ b/src/react/$.tsx @@ -0,0 +1,59 @@ +import { ComponentProps, memo, NamedExoticComponent, ReactElement } from 'react'; +import { Computed } from './Computed'; +import { isEmpty, isFunction } from '@legendapp/state'; +import { createElement, FC, forwardRef } from 'react'; +import { ReactiveFnBinders, ReactiveFns } from './configureReactive'; +import { reactive } from './reactive-observer'; +import { IReactive } from '@legendapp/state/react'; + +type ComputedWithMemo = (params: { + children: ComponentProps['children']; + scoped?: boolean; +}) => ReactElement; + +const Memo = memo(Computed as ComputedWithMemo, (prev, next) => + next.scoped ? prev.children === next.children : true, +) as NamedExoticComponent<{ + children: any; + scoped?: boolean; +}>; + +type ReactiveProxy = typeof Memo & IReactive; + +const setReactProps = new Set([ + '$$typeof', + 'defaultProps', + 'propTypes', + 'tag', + 'PropTypes', + 'displayName', + 'getDefaultProps', + 'type', + 'compare', +]); + +const reactives: Record = {}; + +export const $: ReactiveProxy = new Proxy(Memo as any, { + get(target: Record, p: string) { + if (Object.hasOwn(target, p) || setReactProps.has(p)) { + return target[p]; + } + if (!reactives[p]) { + const Component = ReactiveFns.get(p) || p; + + // Create a wrapper around createElement with the string so we can proxy it + // eslint-disable-next-line react/display-name + const render = forwardRef((props, ref) => { + const propsOut = { ...props } as any; + if (ref && (isFunction(ref) || !isEmpty(ref))) { + propsOut.ref = ref; + } + return createElement(Component, propsOut); + }); + + reactives[p] = reactive(render, [], ReactiveFnBinders.get(p)); + } + return reactives[p]; + }, +}) as unknown as ReactiveProxy; diff --git a/src/react/Memo.tsx b/src/react/Memo.tsx index c28261d6..0811cabf 100644 --- a/src/react/Memo.tsx +++ b/src/react/Memo.tsx @@ -1,4 +1,14 @@ -import { memo } from 'react'; +import { memo, ReactElement, NamedExoticComponent, ComponentProps } from 'react'; import { Computed } from './Computed'; -export const Memo = memo(Computed, () => true); +type ComputedWithMemo = (params: { + children: ComponentProps['children']; + scoped?: boolean; +}) => ReactElement; + +export const Memo = memo(Computed as ComputedWithMemo, (prev, next) => + next.scoped ? prev.children === next.children : true, +) as NamedExoticComponent<{ + children: any; + scoped?: boolean; +}>; diff --git a/src/react/reactInterfaces.ts b/src/react/reactInterfaces.ts index 6d3995ea..a9708786 100644 --- a/src/react/reactInterfaces.ts +++ b/src/react/reactInterfaces.ts @@ -1,4 +1,4 @@ -import type { Observable, Selector } from '@legendapp/state'; +import type { GetOptions, Observable, Selector } from '@legendapp/state'; import type { FC, LegacyRef, ReactNode } from 'react'; export type ShapeWithNew$ = Partial> & { @@ -29,7 +29,7 @@ export type FCReactive = P & } >; -export interface UseSelectorOptions { +export interface UseSelectorOptions extends GetOptions { suspense?: boolean; skipCheck?: boolean; } diff --git a/src/react/useObserve.ts b/src/react/useObserve.ts index 3ce610bd..899be088 100644 --- a/src/react/useObserve.ts +++ b/src/react/useObserve.ts @@ -59,7 +59,7 @@ export function useObserve( if (!ref.current.dispose) { ref.current.dispose = observe( - ((e: ObserveEventCallback) => computeSelector(ref.current.selector, e)) as any, + ((e: ObserveEventCallback) => computeSelector(ref.current.selector, undefined, e)) as any, (e) => ref.current.reaction?.(e), options, ); diff --git a/src/react/useSelector.ts b/src/react/useSelector.ts index 34eeffe7..dd2d414e 100644 --- a/src/react/useSelector.ts +++ b/src/react/useSelector.ts @@ -42,7 +42,7 @@ function createSelectorFunctions( value, dispose: _dispose, resubscribe: _resubscribe, - } = trackSelector(_selector, _update, undefined, undefined, /*createResubscribe*/ true); + } = trackSelector(_selector, _update, options, undefined, undefined, /*createResubscribe*/ true); dispose = _dispose; resubscribe = _resubscribe; @@ -116,7 +116,7 @@ export function useSelector(selector: Selector, options?: UseSelectorOptio // Short-circuit to skip creating the hook if selector is an observable // and running in an observer. If selector is a function it needs to run in its own context. if (reactGlobals.inObserver && isObservable(selector) && !options?.suspense) { - return computeSelector(selector); + return computeSelector(selector, options); } let value; @@ -163,3 +163,5 @@ export function useSelector(selector: Selector, options?: UseSelectorOptio return value; } + +export { useSelector as use$ }; diff --git a/src/sync-plugins/keel.ts b/src/sync-plugins/keel.ts index 342fe92c..6b84e95a 100644 --- a/src/sync-plugins/keel.ts +++ b/src/sync-plugins/keel.ts @@ -18,7 +18,6 @@ import { WaitForSetCrudFnParams, syncedCrud, } from '@legendapp/state/sync-plugins/crud'; -import ksuid from 'ksuid'; // Keel types export interface KeelObjectBase { @@ -49,10 +48,6 @@ type Result = NonNullable | Err>; type SubscribeFn = (params: SyncedGetSetSubscribeBaseParams) => () => void; -export function generateKeelId() { - return ksuid.randomSync().string; -} - export interface KeelGetParams {} export interface KeelListParams { @@ -99,7 +94,7 @@ interface SyncedKeelPropsManyWhere< CrudResult< APIResult<{ results: TRemote[]; - pageInfo: any; + pageInfo?: any; }> > >; @@ -111,7 +106,7 @@ interface SyncedKeelPropsManyNoWhere > >; @@ -150,7 +145,7 @@ export interface SyncedKeelPropsBase { client?: KeelClient; create?: (i: NoInfer>) => Promise>>; - update?: (params: { where: any; values?: Partial }) => Promise>; + update?: (params: { where: any; values?: NoInfer> }) => Promise>>; delete?: (params: { id: string }) => Promise>; realtime?: { path?: (action: string, inputs: any) => string | Promise; @@ -275,7 +270,7 @@ async function getAllPages( listFn: (params: KeelListParams) => Promise< APIResult<{ results: TRemote[]; - pageInfo: any; + pageInfo?: any; }> >, params: KeelListParams, @@ -584,7 +579,6 @@ export function syncedKeel< changesSince, updatePartial: true, subscribe, - generateId: generateKeelId, get, }) as SyncedCrudReturnType; } diff --git a/src/trackSelector.ts b/src/trackSelector.ts index 2d05038f..aec9117d 100644 --- a/src/trackSelector.ts +++ b/src/trackSelector.ts @@ -1,11 +1,12 @@ import { computeSelector } from './helpers'; -import type { ObserveOptions, ListenerParams, ObserveEvent, Selector } from './observableInterfaces'; +import type { ObserveOptions, ListenerParams, ObserveEvent, Selector, GetOptions } from './observableInterfaces'; import { setupTracking } from './setupTracking'; import { beginTracking, endTracking, tracking } from './tracking'; export function trackSelector( selector: Selector, update: (params: ListenerParams) => void, + getOptions?: GetOptions, observeEvent?: ObserveEvent, observeOptions?: ObserveOptions, createResubscribe?: boolean, @@ -15,7 +16,9 @@ export function trackSelector( let updateFn = update; beginTracking(); - const value = selector ? computeSelector(selector, observeEvent, observeOptions?.fromComputed) : selector; + const value = selector + ? computeSelector(selector, getOptions, observeEvent, observeOptions?.fromComputed) + : selector; const tracker = tracking.current; const nodes = tracker!.nodes; endTracking(); diff --git a/src/when.ts b/src/when.ts index 87ad7516..da7fc902 100644 --- a/src/when.ts +++ b/src/when.ts @@ -1,6 +1,6 @@ import { isObservable } from './globals'; import { computeSelector, isObservableValueReady } from './helpers'; -import { isArray, isPromise } from './is'; +import { isArray, isFunction, isPromise } from './is'; import type { ObserveEvent, Selector } from './observableInterfaces'; import { observe } from './observe'; @@ -29,10 +29,13 @@ function _when(predicate: Selector | Selector[], effect?: (value: T let isOk: any = true; if (isArray(ret)) { for (let i = 0; i < ret.length; i++) { - if (isObservable(ret[i])) { - ret[i] = computeSelector(ret[i]); + let item = ret[i]; + if (isObservable(item)) { + item = computeSelector(item); + } else if (isFunction(item)) { + item = item(); } - isOk = isOk && !!(checkReady ? isObservableValueReady(ret[i]) : ret[i]); + isOk = isOk && !!(checkReady ? isObservableValueReady(item) : item); } } else { isOk = checkReady ? isObservableValueReady(ret) : ret; diff --git a/tests/react.test.tsx b/tests/react.test.tsx index 326f213b..4e30b5f9 100644 --- a/tests/react.test.tsx +++ b/tests/react.test.tsx @@ -16,6 +16,7 @@ import { useObserveEffect } from '../src/react/useObserveEffect'; import { useSelector } from '../src/react/useSelector'; import { getNode } from '../src/globals'; import { Memo } from '../src/react/Memo'; +import { $ } from '../src/react/$'; import { GlobalRegistrator } from '@happy-dom/global-registrator'; import { useComputed } from '../src/react/useComputed'; import { when } from '../src/when'; @@ -1755,6 +1756,45 @@ describe('Reactive', () => { // expect(items[0].id).toEqual('1'); }); }); +describe('$', () => { + test('$.div $className', () => { + const obs$ = observable('hi'); + let num = 0; + const Test = function Test() { + return ( + <$.div + $className={() => { + num++; + return obs$.get(); + }} + /> + ); + }; + function App() { + return createElement(Test); + } + render(createElement(App)); + + expect(num).toEqual(1); + const { container } = render(createElement(Test)); + + let items = container.querySelectorAll('div'); + expect(items.length).toEqual(1); + expect(items[0].className).toEqual('hi'); + + act(() => { + obs$.set('hello'); + }); + + items = container.querySelectorAll('div'); + + expect(items[0].className).toEqual('hello'); + + // items = container.querySelectorAll('li'); + // expect(items.length).toEqual(2); + // expect(items[0].id).toEqual('1'); + }); +}); describe('Memo', () => { test('Memo works with function returning function', () => { let num = 0; @@ -1796,7 +1836,7 @@ describe('Memo', () => { const Test = function Test() { return (
- {obs$.test} + <$>{obs$.test}
); }; diff --git a/tsup.config.ts b/tsup.config.ts index ed3feef9..55d33c1f 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -27,7 +27,6 @@ const external = [ '@legendapp/state/react-reactive/enableReactive', 'firebase/auth', 'firebase/database', - 'ksuid', ]; const keys = pkg['lsexports']