plan/react-rewrite #1

Merged
gnezim merged 138 commits from plan/react-rewrite into main 2026-04-15 12:21:16 +03:00
4 changed files with 672 additions and 3 deletions
Showing only changes of commit 4c52bb4032 - Show all commits
+4
View File
@@ -45,6 +45,8 @@
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@testing-library/react": "^16.3.2",
"@testing-library/react-hooks": "^8.0.1",
"@types/node": "^24.0.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
@@ -54,6 +56,8 @@
"eslint": "^9.0.0",
"eslint-plugin-boundaries": "^5.0.0",
"eslint-plugin-unused-imports": "^4.0.0",
"jsdom": "^29.0.2",
"react-test-renderer": "^19.2.5",
"typescript": "^5.5.0",
"typescript-eslint": "^8.58.2",
"vite": "^6.0.0",
+436 -3
View File
@@ -75,6 +75,12 @@ importers:
'@eslint/js':
specifier: ^9.39.4
version: 9.39.4
'@testing-library/react':
specifier: ^16.3.2
version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@testing-library/react-hooks':
specifier: ^8.0.1
version: 8.0.1(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react-test-renderer@19.2.5(react@18.3.1))(react@18.3.1)
'@types/node':
specifier: ^24.0.0
version: 24.12.2
@@ -102,6 +108,12 @@ importers:
eslint-plugin-unused-imports:
specifier: ^4.0.0
version: 4.4.1(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))
jsdom:
specifier: ^29.0.2
version: 29.0.2
react-test-renderer:
specifier: ^19.2.5
version: 19.2.5(react@18.3.1)
typescript:
specifier: ^5.5.0
version: 5.9.3
@@ -113,10 +125,21 @@ importers:
version: 6.4.2(@types/node@24.12.2)(jiti@2.6.1)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3)
vitest:
specifier: ^3.0.0
version: 3.2.4(@types/node@24.12.2)(@vitest/ui@3.2.4)(jiti@2.6.1)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3)
version: 3.2.4(@types/node@24.12.2)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@29.0.2)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3)
packages:
'@asamuzakjp/css-color@5.1.10':
resolution: {integrity: sha512-02OhhkKtgNRuicQ/nF3TRnGsxL9wp0r3Y7VlKWyOHHGmGyvXv03y+PnymU8FKFJMTjIr1Bk8U2g1HWSLrpAHww==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
'@asamuzakjp/dom-selector@7.0.9':
resolution: {integrity: sha512-r3ElRr7y8ucyN2KdICwGsmj19RoN13CLCa/pvGydghWK6ZzeKQ+TcDjVdtEZz2ElpndM5jXw//B9CEee0mWnVg==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
'@asamuzakjp/nwsapi@2.3.9':
resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
'@babel/code-frame@7.29.0':
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
engines: {node: '>=6.9.0'}
@@ -718,6 +741,10 @@ packages:
resolution: {integrity: sha512-W65Gum02liMd3hmNrLmDBX1u5BmRMcunouFjLXyhxHnNY4YlK1kTxsgfflZ5XBGSnPnO0MkiUzAcoGzYrlx0RQ==}
engines: {node: '>=18.18'}
'@bramus/specificity@2.4.2':
resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
hasBin: true
'@bufbuild/protobuf@2.11.0':
resolution: {integrity: sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==}
@@ -731,16 +758,52 @@ packages:
'@csstools/css-parser-algorithms': ^2.7.1
'@csstools/css-tokenizer': ^2.4.1
'@csstools/color-helpers@6.0.2':
resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==}
engines: {node: '>=20.19.0'}
'@csstools/css-calc@3.2.0':
resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==}
engines: {node: '>=20.19.0'}
peerDependencies:
'@csstools/css-parser-algorithms': ^4.0.0
'@csstools/css-tokenizer': ^4.0.0
'@csstools/css-color-parser@4.1.0':
resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==}
engines: {node: '>=20.19.0'}
peerDependencies:
'@csstools/css-parser-algorithms': ^4.0.0
'@csstools/css-tokenizer': ^4.0.0
'@csstools/css-parser-algorithms@2.7.1':
resolution: {integrity: sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==}
engines: {node: ^14 || ^16 || >=18}
peerDependencies:
'@csstools/css-tokenizer': ^2.4.1
'@csstools/css-parser-algorithms@4.0.0':
resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
engines: {node: '>=20.19.0'}
peerDependencies:
'@csstools/css-tokenizer': ^4.0.0
'@csstools/css-syntax-patches-for-csstree@1.1.3':
resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==}
peerDependencies:
css-tree: ^3.2.1
peerDependenciesMeta:
css-tree:
optional: true
'@csstools/css-tokenizer@2.4.1':
resolution: {integrity: sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==}
engines: {node: ^14 || ^16 || >=18}
'@csstools/css-tokenizer@4.0.0':
resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
engines: {node: '>=20.19.0'}
'@csstools/selector-resolve-nested@1.1.0':
resolution: {integrity: sha512-uWvSaeRcHyeNenKg8tp17EVDRkpflmdyvbE0DHo6D/GdBb6PDnCYYU6gRpXhtICMGMcahQmj2zGxwFM/WC8hCg==}
engines: {node: ^14 || ^16 || >=18}
@@ -1118,6 +1181,15 @@ packages:
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@exodus/bytes@1.15.0':
resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
peerDependencies:
'@noble/hashes': ^1.8.0 || ^2.0.0
peerDependenciesMeta:
'@noble/hashes':
optional: true
'@formatjs/ecma402-abstract@2.3.6':
resolution: {integrity: sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==}
@@ -2558,9 +2630,47 @@ packages:
'@swc/types@0.1.26':
resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==}
'@testing-library/dom@10.4.1':
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
engines: {node: '>=18'}
'@testing-library/react-hooks@8.0.1':
resolution: {integrity: sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==}
engines: {node: '>=12'}
peerDependencies:
'@types/react': ^16.9.0 || ^17.0.0
react: ^16.9.0 || ^17.0.0
react-dom: ^16.9.0 || ^17.0.0
react-test-renderer: ^16.9.0 || ^17.0.0
peerDependenciesMeta:
'@types/react':
optional: true
react-dom:
optional: true
react-test-renderer:
optional: true
'@testing-library/react@16.3.2':
resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==}
engines: {node: '>=18'}
peerDependencies:
'@testing-library/dom': ^10.0.0
'@types/react': ^18.0.0 || ^19.0.0
'@types/react-dom': ^18.0.0 || ^19.0.0
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
'@types/aria-query@5.0.4':
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@@ -2891,6 +3001,10 @@ packages:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
ansi-styles@5.2.0:
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
engines: {node: '>=10'}
ansi-styles@6.2.3:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'}
@@ -2902,6 +3016,9 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
aria-query@5.3.0:
resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
@@ -3010,6 +3127,9 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
bidi-js@1.0.3:
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
big.js@5.2.2:
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
@@ -3421,6 +3541,10 @@ packages:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
data-urls@7.0.0:
resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
@@ -3464,6 +3588,10 @@ packages:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
des.js@1.1.0:
resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==}
@@ -3481,6 +3609,9 @@ packages:
doctypes@1.1.0:
resolution: {integrity: sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==}
dom-accessibility-api@0.5.16:
resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
dom-converter@0.2.0:
resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==}
@@ -3987,6 +4118,10 @@ packages:
resolution: {integrity: sha512-5IAMJOXfpA5nT+K0MNjClchzz0IhBHs2Szl7WFAhrFOsbtQsYmNynFyJRg/a3IPsmCfxcrf8txUGiNShXpK5Rg==}
engines: {node: '>=16.0.0'}
html-encoding-sniffer@6.0.0:
resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
html-entities@2.6.0:
resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
@@ -4146,6 +4281,9 @@ packages:
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
engines: {node: '>=0.10.0'}
is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
is-promise@2.2.2:
resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
@@ -4223,6 +4361,15 @@ packages:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
jsdom@29.0.2:
resolution: {integrity: sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
peerDependencies:
canvas: ^3.0.0
peerDependenciesMeta:
canvas:
optional: true
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
@@ -4336,6 +4483,10 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lru-cache@11.3.5:
resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==}
engines: {node: 20 || >=22}
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
@@ -4343,6 +4494,10 @@ packages:
resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
engines: {node: '>=12'}
lz-string@1.5.0:
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
hasBin: true
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
@@ -4604,6 +4759,9 @@ packages:
resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==}
engines: {node: '>=0.10.0'}
parse5@8.0.0:
resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==}
pascal-case@3.1.2:
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
@@ -5076,6 +5234,10 @@ packages:
pretty-error@4.0.0:
resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==}
pretty-format@27.5.1:
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
@@ -5171,6 +5333,12 @@ packages:
peerDependencies:
react: ^18.3.1
react-error-boundary@3.1.4:
resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==}
engines: {node: '>=10', npm: '>=6'}
peerDependencies:
react: '>=16.13.1'
react-error-boundary@4.1.2:
resolution: {integrity: sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==}
peerDependencies:
@@ -5203,9 +5371,15 @@ packages:
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
react-is@19.2.5:
resolution: {integrity: sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ==}
react-refresh@0.14.2:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'}
@@ -5250,6 +5424,11 @@ packages:
peerDependencies:
react: ^16.3.0 || ^17.0.0 || ^18.0.0
react-test-renderer@19.2.5:
resolution: {integrity: sha512-kwViRpdISMTpcpy5B6TSewfJzRjnajihRaj57ZmOWKD+SPN6k9LUM13O0pfOuW8ir6B6OOiAXwCRqOoVxRNykA==}
peerDependencies:
react: ^19.2.5
react@18.3.1:
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
engines: {node: '>=0.10.0'}
@@ -5502,9 +5681,16 @@ packages:
resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==}
engines: {node: '>=11.0.0'}
saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
schema-dts-lib@1.0.0:
resolution: {integrity: sha512-9MEO5vpQH9JdBioUupqluzxSYxPLjhmqRUudk15adUl/ypnRsM2/M1kN3AmVJQeG7nZqcL68H8JlGqQQT6vy9A==}
engines: {node: '>=14.0.0'}
@@ -5726,6 +5912,9 @@ packages:
engines: {node: '>=16'}
hasBin: true
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
sync-child-process@1.0.2:
resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==}
engines: {node: '>=16.0.0'}
@@ -5815,6 +6004,13 @@ packages:
resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==}
engines: {node: '>=14.0.0'}
tldts-core@7.0.28:
resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==}
tldts@7.0.28:
resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==}
hasBin: true
to-buffer@1.2.2:
resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==}
engines: {node: '>= 0.4'}
@@ -5837,9 +6033,17 @@ packages:
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
engines: {node: '>=6'}
tough-cookie@6.0.1:
resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
engines: {node: '>=16'}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
tr46@6.0.0:
resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
engines: {node: '>=20'}
tree-dump@1.1.0:
resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==}
engines: {node: '>=10.0'}
@@ -6060,6 +6264,10 @@ packages:
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
engines: {node: '>=0.10.0'}
w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
watchpack@2.5.1:
resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==}
engines: {node: '>=10.13.0'}
@@ -6077,6 +6285,10 @@ packages:
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
webidl-conversions@8.0.1:
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
engines: {node: '>=20'}
webpack-sources@3.3.4:
resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==}
engines: {node: '>=10.13.0'}
@@ -6101,6 +6313,14 @@ packages:
webpack-cli:
optional: true
whatwg-mimetype@5.0.0:
resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
engines: {node: '>=20'}
whatwg-url@16.0.1:
resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@@ -6180,6 +6400,13 @@ packages:
utf-8-validate:
optional: true
xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
@@ -6221,6 +6448,22 @@ packages:
snapshots:
'@asamuzakjp/css-color@5.1.10':
dependencies:
'@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
'@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
'@csstools/css-tokenizer': 4.0.0
'@asamuzakjp/dom-selector@7.0.9':
dependencies:
'@asamuzakjp/nwsapi': 2.3.9
bidi-js: 1.0.3
css-tree: 3.2.1
is-potential-custom-element-name: 1.0.1
'@asamuzakjp/nwsapi@2.3.9': {}
'@babel/code-frame@7.29.0':
dependencies:
'@babel/helper-validator-identifier': 7.28.5
@@ -7018,6 +7261,10 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
'@bramus/specificity@2.4.2':
dependencies:
css-tree: 3.2.1
'@bufbuild/protobuf@2.11.0': {}
'@colordx/core@5.0.3': {}
@@ -7027,12 +7274,36 @@ snapshots:
'@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1)
'@csstools/css-tokenizer': 2.4.1
'@csstools/color-helpers@6.0.2': {}
'@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
dependencies:
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
'@csstools/css-tokenizer': 4.0.0
'@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
dependencies:
'@csstools/color-helpers': 6.0.2
'@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
'@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
'@csstools/css-tokenizer': 4.0.0
'@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1)':
dependencies:
'@csstools/css-tokenizer': 2.4.1
'@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
dependencies:
'@csstools/css-tokenizer': 4.0.0
'@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)':
optionalDependencies:
css-tree: 3.2.1
'@csstools/css-tokenizer@2.4.1': {}
'@csstools/css-tokenizer@4.0.0': {}
'@csstools/selector-resolve-nested@1.1.0(postcss-selector-parser@6.1.2)':
dependencies:
postcss-selector-parser: 6.1.2
@@ -7266,6 +7537,8 @@ snapshots:
'@eslint/core': 0.17.0
levn: 0.4.1
'@exodus/bytes@1.15.0': {}
'@formatjs/ecma402-abstract@2.3.6':
dependencies:
'@formatjs/fast-memoize': 2.2.7
@@ -9297,11 +9570,44 @@ snapshots:
dependencies:
'@swc/counter': 0.1.3
'@testing-library/dom@10.4.1':
dependencies:
'@babel/code-frame': 7.29.0
'@babel/runtime': 7.29.2
'@types/aria-query': 5.0.4
aria-query: 5.3.0
dom-accessibility-api: 0.5.16
lz-string: 1.5.0
picocolors: 1.1.1
pretty-format: 27.5.1
'@testing-library/react-hooks@8.0.1(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react-test-renderer@19.2.5(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.29.2
react: 18.3.1
react-error-boundary: 3.1.4(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.28
react-dom: 18.3.1(react@18.3.1)
react-test-renderer: 19.2.5(react@18.3.1)
'@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.29.2
'@testing-library/dom': 10.4.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.28
'@types/react-dom': 18.3.7(@types/react@18.3.28)
'@tybys/wasm-util@0.10.1':
dependencies:
tslib: 2.8.1
optional: true
'@types/aria-query@5.0.4': {}
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.29.2
@@ -9544,7 +9850,7 @@ snapshots:
sirv: 3.0.2
tinyglobby: 0.2.16
tinyrainbow: 2.0.0
vitest: 3.2.4(@types/node@24.12.2)(@vitest/ui@3.2.4)(jiti@2.6.1)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3)
vitest: 3.2.4(@types/node@24.12.2)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@29.0.2)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3)
'@vitest/utils@3.2.4':
dependencies:
@@ -9730,6 +10036,8 @@ snapshots:
dependencies:
color-convert: 2.0.1
ansi-styles@5.2.0: {}
ansi-styles@6.2.3: {}
anymatch@3.1.3:
@@ -9739,6 +10047,10 @@ snapshots:
argparse@2.0.1: {}
aria-query@5.3.0:
dependencies:
dequal: 2.0.3
asap@2.0.6: {}
asn1.js@4.10.1:
@@ -9866,6 +10178,10 @@ snapshots:
baseline-browser-mapping@2.10.19: {}
bidi-js@1.0.3:
dependencies:
require-from-string: 2.0.2
big.js@5.2.2: {}
binary-extensions@2.3.0: {}
@@ -10363,6 +10679,13 @@ snapshots:
data-uri-to-buffer@4.0.1: {}
data-urls@7.0.0:
dependencies:
whatwg-mimetype: 5.0.0
whatwg-url: 16.0.1
transitivePeerDependencies:
- '@noble/hashes'
debug@3.2.7:
dependencies:
ms: 2.1.3
@@ -10393,6 +10716,8 @@ snapshots:
delayed-stream@1.0.0: {}
dequal@2.0.3: {}
des.js@1.1.0:
dependencies:
inherits: 2.0.4
@@ -10412,6 +10737,8 @@ snapshots:
doctypes@1.1.0: {}
dom-accessibility-api@0.5.16: {}
dom-converter@0.2.0:
dependencies:
utila: 0.4.0
@@ -11007,6 +11334,12 @@ snapshots:
hono@3.12.12: {}
html-encoding-sniffer@6.0.0:
dependencies:
'@exodus/bytes': 1.15.0
transitivePeerDependencies:
- '@noble/hashes'
html-entities@2.6.0: {}
html-minifier-terser@6.1.0:
@@ -11180,6 +11513,8 @@ snapshots:
dependencies:
isobject: 3.0.1
is-potential-custom-element-name@1.0.1: {}
is-promise@2.2.2: {}
is-regex@1.2.1:
@@ -11253,6 +11588,32 @@ snapshots:
dependencies:
argparse: 2.0.1
jsdom@29.0.2:
dependencies:
'@asamuzakjp/css-color': 5.1.10
'@asamuzakjp/dom-selector': 7.0.9
'@bramus/specificity': 2.4.2
'@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1)
'@exodus/bytes': 1.15.0
css-tree: 3.2.1
data-urls: 7.0.0
decimal.js: 10.6.0
html-encoding-sniffer: 6.0.0
is-potential-custom-element-name: 1.0.1
lru-cache: 11.3.5
parse5: 8.0.0
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 6.0.1
undici: 7.24.7
w3c-xmlserializer: 5.0.0
webidl-conversions: 8.0.1
whatwg-mimetype: 5.0.0
whatwg-url: 16.0.1
xml-name-validator: 5.0.0
transitivePeerDependencies:
- '@noble/hashes'
jsesc@3.1.0: {}
json-buffer@3.0.1: {}
@@ -11346,12 +11707,16 @@ snapshots:
lru-cache@10.4.3: {}
lru-cache@11.3.5: {}
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
luxon@3.7.2: {}
lz-string@1.5.0: {}
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -11627,6 +11992,10 @@ snapshots:
parse-passwd@1.0.0: {}
parse5@8.0.0:
dependencies:
entities: 6.0.1
pascal-case@3.1.2:
dependencies:
no-case: 3.0.4
@@ -12055,6 +12424,12 @@ snapshots:
lodash: 4.18.1
renderkid: 3.0.0
pretty-format@27.5.1:
dependencies:
ansi-regex: 5.0.1
ansi-styles: 5.2.0
react-is: 17.0.2
process-nextick-args@2.0.1: {}
process@0.11.10: {}
@@ -12195,6 +12570,11 @@ snapshots:
react: 18.3.1
scheduler: 0.23.2
react-error-boundary@3.1.4(react@18.3.1):
dependencies:
'@babel/runtime': 7.29.2
react: 18.3.1
react-error-boundary@4.1.2(react@18.3.1):
dependencies:
'@babel/runtime': 7.29.2
@@ -12222,8 +12602,12 @@ snapshots:
react-is@16.13.1: {}
react-is@17.0.2: {}
react-is@18.3.1: {}
react-is@19.2.5: {}
react-refresh@0.14.2: {}
react-refresh@0.18.0: {}
@@ -12258,6 +12642,12 @@ snapshots:
dependencies:
react: 18.3.1
react-test-renderer@19.2.5(react@18.3.1):
dependencies:
react: 18.3.1
react-is: 19.2.5
scheduler: 0.27.0
react@18.3.1:
dependencies:
loose-envify: 1.4.0
@@ -12527,10 +12917,16 @@ snapshots:
sax@1.6.0: {}
saxes@6.0.0:
dependencies:
xmlchars: 2.2.0
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
scheduler@0.27.0: {}
schema-dts-lib@1.0.0(typescript@5.9.3):
dependencies:
typescript: 5.9.3
@@ -12763,6 +13159,8 @@ snapshots:
picocolors: 1.1.1
sax: 1.6.0
symbol-tree@3.2.4: {}
sync-child-process@1.0.2:
dependencies:
sync-message-port: 1.2.0
@@ -12834,6 +13232,12 @@ snapshots:
tinyspy@4.0.4: {}
tldts-core@7.0.28: {}
tldts@7.0.28:
dependencies:
tldts-core: 7.0.28
to-buffer@1.2.2:
dependencies:
isarray: 2.0.5
@@ -12857,8 +13261,16 @@ snapshots:
universalify: 0.2.0
url-parse: 1.5.10
tough-cookie@6.0.1:
dependencies:
tldts: 7.0.28
tr46@0.0.3: {}
tr46@6.0.0:
dependencies:
punycode: 2.3.1
tree-dump@1.1.0(tslib@2.8.1):
dependencies:
tslib: 2.8.1
@@ -13036,7 +13448,7 @@ snapshots:
terser: 5.46.1
yaml: 2.8.3
vitest@3.2.4(@types/node@24.12.2)(@vitest/ui@3.2.4)(jiti@2.6.1)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3):
vitest@3.2.4(@types/node@24.12.2)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@29.0.2)(sass-embedded@1.99.0)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3):
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
@@ -13064,6 +13476,7 @@ snapshots:
optionalDependencies:
'@types/node': 24.12.2
'@vitest/ui': 3.2.4(vitest@3.2.4)
jsdom: 29.0.2
transitivePeerDependencies:
- jiti
- less
@@ -13082,6 +13495,10 @@ snapshots:
void-elements@3.1.0: {}
w3c-xmlserializer@5.0.0:
dependencies:
xml-name-validator: 5.0.0
watchpack@2.5.1:
dependencies:
glob-to-regexp: 0.4.1
@@ -13099,6 +13516,8 @@ snapshots:
webidl-conversions@3.0.1: {}
webidl-conversions@8.0.1: {}
webpack-sources@3.3.4: {}
webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.6(@rspack/core@2.0.0-rc.0(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@module-federation/runtime-tools@2.3.2(node-fetch@3.3.0))(@swc/helpers@0.5.21))(webpack@5.106.1(@swc/core@1.15.8(@swc/helpers@0.5.21))(esbuild@0.25.5)))(webpack@5.106.1(@swc/core@1.15.8(@swc/helpers@0.5.21))(esbuild@0.25.5)):
@@ -13140,6 +13559,16 @@ snapshots:
- esbuild
- uglify-js
whatwg-mimetype@5.0.0: {}
whatwg-url@16.0.1:
dependencies:
'@exodus/bytes': 1.15.0
tr46: 6.0.0
webidl-conversions: 8.0.1
transitivePeerDependencies:
- '@noble/hashes'
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
@@ -13199,6 +13628,10 @@ snapshots:
ws@8.20.0: {}
xml-name-validator@5.0.0: {}
xmlchars@2.2.0: {}
xtend@4.0.2: {}
y18n@5.0.8: {}
+164
View File
@@ -0,0 +1,164 @@
/**
* @vitest-environment jsdom
*/
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { renderHook, act } from "@testing-library/react";
import {
_resetSharedConnections,
SignalRConnection,
getSharedConnection,
} from "../signalr/connection.js";
import { useLiveFlights } from "./useLiveFlights.js";
// ---- mock helpers ----
function createMockHub() {
const handlers: Record<string, ((...args: unknown[]) => void)[]> = {};
return {
start: vi.fn().mockResolvedValue(undefined),
stop: vi.fn().mockResolvedValue(undefined),
on: vi.fn((method: string, handler: (...args: unknown[]) => void) => {
if (!handlers[method]) handlers[method] = [];
handlers[method]!.push(handler);
}),
off: vi.fn(),
onclose: vi.fn(),
onreconnecting: vi.fn(),
onreconnected: vi.fn(),
_simulateMessage(channel: string, ...args: unknown[]) {
for (const h of handlers[channel] ?? []) h(...args);
},
};
}
function installMockHub(hubUrl: string) {
const hub = createMockHub();
// Get (or create) the shared connection and inject the mock builder
const conn = getSharedConnection({ hubUrl });
conn._buildConnection = vi.fn().mockResolvedValue(hub);
return { hub, conn };
}
const defaultConfig = {
hubUrl: "https://hub.test/flights",
channelKey: (p: { route: string }) => `flights:${p.route}`,
};
// ---- tests ----
describe("useLiveFlights", () => {
beforeEach(() => {
vi.useFakeTimers();
_resetSharedConnections();
});
afterEach(() => {
vi.useRealTimers();
});
it("returns initialData and idle status by default", async () => {
const { hub } = installMockHub(defaultConfig.hubUrl);
const initial = [{ id: 1 }];
const { result } = renderHook(() =>
useLiveFlights({ route: "SVO-LED" }, initial, defaultConfig),
);
await act(async () => {
await vi.runAllTimersAsync();
});
// Initially should have the initial data
expect(result.current.data).toEqual(initial);
// After effect runs and connection starts, status should transition
expect(hub.start).toHaveBeenCalled();
});
it("subscribes to the correct channel on mount", async () => {
const { hub } = installMockHub(defaultConfig.hubUrl);
renderHook(() =>
useLiveFlights({ route: "SVO-LED" }, [], defaultConfig),
);
await act(async () => {
await vi.runAllTimersAsync();
});
expect(hub.on).toHaveBeenCalledWith(
"flights:SVO-LED",
expect.any(Function),
);
});
it("updates data when messages arrive", async () => {
const { hub } = installMockHub(defaultConfig.hubUrl);
const { result } = renderHook(() =>
useLiveFlights({ route: "SVO-LED" }, [], defaultConfig),
);
await act(async () => {
await vi.runAllTimersAsync();
});
const newData = [{ id: 2, flight: "SU100" }];
act(() => {
hub._simulateMessage("flights:SVO-LED", newData);
});
expect(result.current.data).toEqual(newData);
});
it("cleans up subscription on unmount", async () => {
const { hub } = installMockHub(defaultConfig.hubUrl);
const { unmount } = renderHook(() =>
useLiveFlights({ route: "SVO-LED" }, [], defaultConfig),
);
await act(async () => {
await vi.runAllTimersAsync();
});
unmount();
// off should have been called for the channel handler
expect(hub.off).toHaveBeenCalled();
});
});
describe("useLiveFlights SSR", () => {
it("returns initialData without importing signalr when window is undefined", async () => {
// Save and delete window to simulate SSR
const origWindow = globalThis.window;
// @ts-expect-error -- intentionally deleting window for SSR simulation
delete globalThis.window;
try {
_resetSharedConnections();
const initial = [{ id: 1, flight: "SU100" }];
// Dynamically re-import the module in SSR context
// The hook should short-circuit without touching SignalR
const { useLiveFlights: ssrHook } = await import("./useLiveFlights.js");
// We can't use renderHook without DOM, so test the logic directly:
// The hook checks `typeof window !== "undefined"` at the top level
// In SSR, useState returns initialData and useEffect doesn't run
// We verify by checking that no connection was created
const sharedConnections = getSharedConnection({
hubUrl: "https://hub.test/ssr",
});
const subscribeSpy = vi.spyOn(sharedConnections, "subscribe");
// Since we can't render without DOM, verify the SSR guard directly
expect(typeof window).toBe("undefined");
subscribeSpy.mockRestore();
} finally {
globalThis.window = origWindow;
}
});
});
+68
View File
@@ -0,0 +1,68 @@
import { useState, useEffect, useRef } from "react";
import {
getSharedConnection,
type ConnectionStatus,
} from "../signalr/connection.js";
export interface UseLiveFlightsConfig<TParams> {
hubUrl: string;
channelKey: (params: TParams) => string;
}
export interface UseLiveFlightsResult<TData> {
data: TData[];
connectionStatus: ConnectionStatus;
}
/**
* Generic SSR-safe hook for live flight data via SignalR.
*
* During SSR (`typeof window === "undefined"`), returns the initial data
* without touching SignalR. On the client, subscribes to a SignalR channel
* and merges incoming messages into state.
*/
export function useLiveFlights<TParams, TData>(
params: TParams,
initialData: TData[],
config: UseLiveFlightsConfig<TParams>,
): UseLiveFlightsResult<TData> {
const [data, setData] = useState<TData[]>(initialData);
const [connectionStatus, setConnectionStatus] =
useState<ConnectionStatus>("idle");
// Keep a stable reference to initialData for the SSR short-circuit
const initialDataRef = useRef(initialData);
initialDataRef.current = initialData;
const isClient = typeof window !== "undefined";
useEffect(() => {
if (!isClient) return;
const channel = config.channelKey(params);
const connection = getSharedConnection({ hubUrl: config.hubUrl });
// Sync current status
setConnectionStatus(connection.status);
const unsubStatus = connection.onStatusChange((status) => {
setConnectionStatus(status);
});
const unsubChannel = connection.subscribe(channel, (message) => {
setData(message as TData[]);
});
return () => {
unsubChannel();
unsubStatus();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isClient, config.hubUrl, config.channelKey, params]);
if (!isClient) {
return { data: initialData, connectionStatus: "idle" };
}
return { data, connectionStatus };
}