diff --git a/app/_layout.tsx b/app/_layout.tsx
index 44828bf4..c833144a 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -10,6 +10,7 @@ import { RetryErrorBoundary } from '../components/ErrorBoundary/RetryErrorBounda
import '../global.css'; // NativeWind CSS
import { AnalyticsProvider, ErrorBoundary, OfflineIndicatorProvider } from '../src/components';
import AppLifecycleManager from '../src/components/AppLifecycleManager';
+import { ConflictResolutionModal } from '../src/components/common/ConflictResolutionModal';
import { KeyboardDelegateProvider } from '../src/components/common/KeyboardDelegateProvider';
import { UpdateNotificationModal } from '../src/components/common/UpdateNotificationModal';
import { useAnalytics } from '../src/hooks';
@@ -173,6 +174,7 @@ const RootLayout = () => {
+
diff --git a/package-lock.json b/package-lock.json
index 2687f834..33937ab4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -142,7 +142,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -1530,7 +1529,7 @@
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
- "dev": true
+ "devOptional": true
},
"node_modules/@egjs/hammerjs": {
"version": "2.0.17",
@@ -1581,7 +1580,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"optional": true,
"os": [
"aix"
@@ -1597,7 +1595,6 @@
"cpu": [
"arm"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -1613,7 +1610,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -1629,7 +1625,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -1645,7 +1640,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"darwin"
@@ -1661,7 +1655,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"darwin"
@@ -1677,7 +1670,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -1693,7 +1685,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -1709,7 +1700,6 @@
"cpu": [
"arm"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -1725,7 +1715,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -1741,7 +1730,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -1757,7 +1745,6 @@
"cpu": [
"loong64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -1773,7 +1760,6 @@
"cpu": [
"mips64el"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -1789,7 +1775,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -1805,7 +1790,6 @@
"cpu": [
"riscv64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -1821,7 +1805,6 @@
"cpu": [
"s390x"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -1837,7 +1820,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -1853,7 +1835,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"netbsd"
@@ -1869,7 +1850,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"netbsd"
@@ -1885,7 +1865,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"openbsd"
@@ -1901,7 +1880,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"openbsd"
@@ -1917,7 +1895,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"openharmony"
@@ -1933,7 +1910,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"sunos"
@@ -1949,7 +1925,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -1965,7 +1940,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -1981,7 +1955,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -2905,7 +2878,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
"integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/types": "^29.6.3",
"@types/node": "*",
@@ -2922,7 +2895,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
"integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/console": "^29.7.0",
"@jest/reporters": "^29.7.0",
@@ -2969,7 +2942,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -2984,7 +2957,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/@jest/create-cache-key-function": {
@@ -3002,7 +2975,7 @@
"version": "30.4.0",
"resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz",
"integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
@@ -3025,7 +2998,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
"integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"expect": "^29.7.0",
"jest-snapshot": "^29.7.0"
@@ -3038,7 +3011,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
"integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"jest-get-type": "^29.6.3"
},
@@ -3066,7 +3039,7 @@
"version": "30.1.0",
"resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz",
"integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
@@ -3075,7 +3048,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
"integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/environment": "^29.7.0",
"@jest/expect": "^29.7.0",
@@ -3090,7 +3063,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
"integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@bcoe/v8-coverage": "^0.2.3",
"@jest/console": "^29.7.0",
@@ -3134,7 +3107,7 @@
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -3154,7 +3127,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -3177,7 +3150,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
"integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.18",
"callsites": "^3.0.0",
@@ -3191,7 +3164,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
"integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/console": "^29.7.0",
"@jest/types": "^29.6.3",
@@ -3206,7 +3179,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
"integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/test-result": "^29.7.0",
"graceful-fs": "^4.2.9",
@@ -3329,7 +3302,6 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@@ -3342,7 +3314,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
"engines": {
"node": ">= 8"
}
@@ -3351,7 +3322,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@@ -5259,7 +5229,7 @@
"version": "13.3.3",
"resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-13.3.3.tgz",
"integrity": "sha512-k6Mjsd9dbZgvY4Bl7P1NIpePQNi+dfYtlJ5voi9KQlynxSyQkfOgJmYGCYmw/aSgH/rUcFvG8u5gd4npzgRDyg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"jest-matcher-utils": "^30.0.5",
"picocolors": "^1.1.1",
@@ -5285,7 +5255,7 @@
"version": "30.4.1",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz",
"integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@sinclair/typebox": "^0.34.0"
},
@@ -5297,13 +5267,13 @@
"version": "0.34.49",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz",
"integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==",
- "dev": true
+ "devOptional": true
},
"node_modules/@testing-library/react-native/node_modules/jest-diff": {
"version": "30.4.1",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz",
"integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/diff-sequences": "30.4.0",
"@jest/get-type": "30.1.0",
@@ -5318,7 +5288,7 @@
"version": "30.4.1",
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz",
"integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/get-type": "30.1.0",
"chalk": "^4.1.2",
@@ -5333,7 +5303,7 @@
"version": "30.4.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz",
"integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/schemas": "30.4.1",
"ansi-styles": "^5.2.0",
@@ -5604,7 +5574,7 @@
"version": "19.1.17",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz",
"integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -7160,7 +7130,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "dev": true,
"engines": {
"node": ">=8"
},
@@ -7360,7 +7329,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6"
}
@@ -7380,7 +7349,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
- "dev": true,
"engines": {
"node": ">= 6"
}
@@ -7454,7 +7422,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=10"
}
@@ -7472,7 +7440,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -7496,7 +7463,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -7560,7 +7526,7 @@
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
- "dev": true
+ "devOptional": true
},
"node_modules/cli-cursor": {
"version": "5.0.0",
@@ -7699,7 +7665,7 @@
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
- "dev": true,
+ "devOptional": true,
"engines": {
"iojs": ">= 1.0.0",
"node": ">= 0.12.0"
@@ -7709,7 +7675,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz",
"integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==",
- "dev": true
+ "devOptional": true
},
"node_modules/color": {
"version": "4.2.3",
@@ -7914,7 +7880,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
"integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/types": "^29.6.3",
"chalk": "^4.0.0",
@@ -8008,7 +7974,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "dev": true,
"bin": {
"cssesc": "bin/cssesc"
},
@@ -8044,7 +8009,7 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "dev": true
+ "devOptional": true
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
@@ -8151,7 +8116,7 @@
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz",
"integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==",
- "dev": true,
+ "devOptional": true,
"peerDependencies": {
"babel-plugin-macros": "^3.1.0"
},
@@ -8514,7 +8479,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
"integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=8"
}
@@ -8527,14 +8492,13 @@
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
- "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
- "dev": true
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
},
"node_modules/diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
"integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
@@ -8542,8 +8506,7 @@
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
- "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
- "dev": true
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"node_modules/doctrine": {
"version": "2.1.0",
@@ -8691,7 +8654,7 @@
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
"integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=12"
},
@@ -8769,7 +8732,7 @@
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"is-arrayish": "^0.2.1"
}
@@ -8977,7 +8940,7 @@
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
- "dev": true,
+ "devOptional": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
@@ -9614,7 +9577,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"cross-spawn": "^7.0.3",
"get-stream": "^6.0.0",
@@ -9637,7 +9600,7 @@
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
"integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">= 0.8.0"
}
@@ -9658,7 +9621,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
"integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/expect-utils": "^29.7.0",
"jest-get-type": "^29.6.3",
@@ -10885,7 +10848,6 @@
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@@ -10901,7 +10863,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -10939,7 +10900,6 @@
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
"integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
- "dev": true,
"dependencies": {
"reusify": "^1.0.4"
}
@@ -11338,7 +11298,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=10"
},
@@ -11403,7 +11363,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -11663,7 +11622,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true
+ "devOptional": true
},
"node_modules/http-errors": {
"version": "2.0.1",
@@ -11722,7 +11681,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=10.17.0"
}
@@ -11829,7 +11788,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
"integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"pkg-dir": "^4.2.0",
"resolve-cwd": "^3.0.0"
@@ -11856,7 +11815,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=8"
}
@@ -11947,7 +11906,7 @@
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true
+ "devOptional": true
},
"node_modules/is-async-function": {
"version": "2.1.1",
@@ -11987,7 +11946,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@@ -12123,7 +12081,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -12162,7 +12119,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
"integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6"
}
@@ -12189,7 +12146,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -12355,7 +12311,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=8"
},
@@ -12496,7 +12452,7 @@
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
"integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@babel/core": "^7.23.9",
"@babel/parser": "^7.23.9",
@@ -12512,7 +12468,7 @@
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
+ "devOptional": true,
"bin": {
"semver": "bin/semver.js"
},
@@ -12524,7 +12480,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
"integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"istanbul-lib-coverage": "^3.0.0",
"make-dir": "^4.0.0",
@@ -12538,7 +12494,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
"integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"debug": "^4.1.1",
"istanbul-lib-coverage": "^3.0.0",
@@ -12552,7 +12508,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
"integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"html-escaper": "^2.0.0",
"istanbul-lib-report": "^3.0.0"
@@ -12582,7 +12538,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/core": "^29.7.0",
"@jest/types": "^29.6.3",
@@ -12608,7 +12564,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
"integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"execa": "^5.0.0",
"jest-util": "^29.7.0",
@@ -12622,7 +12578,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
"integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/environment": "^29.7.0",
"@jest/expect": "^29.7.0",
@@ -12653,7 +12609,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -12668,14 +12624,14 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/jest-cli": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
"integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/core": "^29.7.0",
"@jest/test-result": "^29.7.0",
@@ -12708,7 +12664,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
"integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@babel/core": "^7.11.6",
"@jest/test-sequencer": "^29.7.0",
@@ -12754,7 +12710,7 @@
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -12774,7 +12730,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -12786,7 +12742,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -12801,14 +12757,14 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/jest-diff": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
"integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"chalk": "^4.0.0",
"diff-sequences": "^29.6.3",
@@ -12823,7 +12779,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -12838,14 +12794,14 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/jest-docblock": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
"integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"detect-newline": "^3.0.0"
},
@@ -12857,7 +12813,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
"integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/types": "^29.6.3",
"chalk": "^4.0.0",
@@ -12873,7 +12829,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -12888,7 +12844,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/jest-environment-jsdom": {
@@ -13012,7 +12968,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
"integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"jest-get-type": "^29.6.3",
"pretty-format": "^29.7.0"
@@ -13025,7 +12981,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -13040,14 +12996,14 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/jest-matcher-utils": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
"integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"chalk": "^4.0.0",
"jest-diff": "^29.7.0",
@@ -13062,7 +13018,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -13077,7 +13033,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/jest-message-util": {
@@ -13136,7 +13092,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
"integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6"
},
@@ -13161,7 +13117,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
"integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"chalk": "^4.0.0",
"graceful-fs": "^4.2.9",
@@ -13181,7 +13137,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
"integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"jest-regex-util": "^29.6.3",
"jest-snapshot": "^29.7.0"
@@ -13194,7 +13150,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
"integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/console": "^29.7.0",
"@jest/environment": "^29.7.0",
@@ -13226,7 +13182,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
"integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/environment": "^29.7.0",
"@jest/fake-timers": "^29.7.0",
@@ -13260,7 +13216,7 @@
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -13280,7 +13236,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -13292,7 +13248,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
"integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@babel/core": "^7.11.6",
"@babel/generator": "^7.7.2",
@@ -13323,7 +13279,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@jest/schemas": "^29.6.3",
@@ -13338,14 +13294,14 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/jest-snapshot/node_modules/semver": {
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
+ "devOptional": true,
"bin": {
"semver": "bin/semver.js"
},
@@ -13546,7 +13502,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
"integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jest/test-result": "^29.7.0",
"@jest/types": "^29.6.3",
@@ -13598,7 +13554,6 @@
"version": "1.21.7",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
- "dev": true,
"bin": {
"jiti": "bin/jiti.js"
}
@@ -13700,7 +13655,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
- "dev": true
+ "devOptional": true
},
"node_modules/json-rpc-2.0": {
"version": "1.7.1",
@@ -14091,7 +14046,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
- "dev": true,
"engines": {
"node": ">=14"
},
@@ -14409,7 +14363,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
"integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"semver": "^7.5.3"
},
@@ -14424,7 +14378,7 @@
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
+ "devOptional": true,
"bin": {
"semver": "bin/semver.js"
},
@@ -14498,7 +14452,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
"engines": {
"node": ">= 8"
}
@@ -15332,7 +15285,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6"
}
@@ -15353,7 +15306,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=4"
}
@@ -15509,7 +15462,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true
+ "devOptional": true
},
"node_modules/negotiator": {
"version": "1.0.0",
@@ -15638,7 +15591,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"path-key": "^3.0.0"
},
@@ -15691,7 +15644,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
- "dev": true,
"engines": {
"node": ">= 6"
}
@@ -15846,7 +15798,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"mimic-fn": "^2.1.0"
},
@@ -16167,7 +16119,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@@ -16323,7 +16275,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -16340,7 +16291,7 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"find-up": "^4.0.0"
},
@@ -16352,7 +16303,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -16365,7 +16316,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -16377,7 +16328,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"p-try": "^2.0.0"
},
@@ -16392,7 +16343,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -16489,7 +16440,6 @@
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
@@ -16506,7 +16456,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
"integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -16531,7 +16480,6 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -16556,7 +16504,6 @@
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz",
"integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==",
- "dev": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -16583,7 +16530,6 @@
"version": "3.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz",
"integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==",
- "dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -16776,7 +16722,7 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
"integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
- "dev": true,
+ "devOptional": true,
"funding": [
{
"type": "individual",
@@ -16831,7 +16777,6 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -16975,14 +16920,14 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
},
"node_modules/react-is-19": {
"name": "react-is",
"version": "19.2.7",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz",
"integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==",
- "dev": true
+ "devOptional": true
},
"node_modules/react-native": {
"version": "0.81.5",
@@ -17146,7 +17091,6 @@
"version": "0.35.10",
"resolved": "https://registry.npmjs.org/react-native-nitro-modules/-/react-native-nitro-modules-0.35.10.tgz",
"integrity": "sha512-KsySOAIkbSTjNiX2GvXiNT9P5DMeZd99yvtE/+Lw7RXV7Ztxh9Z+YyAbYkBqadhN5J/KXXuPjhRYgyLOIZgsbQ==",
- "dev": true,
"license": "MIT",
"peerDependencies": {
"react": "*",
@@ -17394,7 +17338,6 @@
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
"integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -17470,7 +17413,7 @@
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-19.1.0.tgz",
"integrity": "sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"react-is": "^19.1.0",
"scheduler": "^0.26.0"
@@ -17483,14 +17426,13 @@
"version": "19.2.7",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz",
"integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
- "dev": true,
"dependencies": {
"pify": "^2.3.0"
}
@@ -17499,7 +17441,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -17527,7 +17468,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
"integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"indent-string": "^4.0.0",
"strip-indent": "^3.0.0"
@@ -17540,7 +17481,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"min-indent": "^1.0.0"
},
@@ -17716,7 +17657,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
"integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"resolve-from": "^5.0.0"
},
@@ -17814,7 +17755,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
- "dev": true,
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
@@ -17888,7 +17828,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -18413,7 +18352,7 @@
"version": "0.5.13",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -18669,7 +18608,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
"integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"char-regex": "^1.0.2",
"strip-ansi": "^6.0.0"
@@ -18844,7 +18783,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=8"
}
@@ -18853,7 +18792,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6"
}
@@ -18874,7 +18813,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=8"
},
@@ -18965,7 +18904,6 @@
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
- "dev": true,
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@@ -19002,7 +18940,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
"integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -19390,7 +19327,7 @@
"version": "4.22.4",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz",
"integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"esbuild": "~0.28.0"
},
@@ -19794,8 +19731,7 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/utils-merge": {
"version": "1.0.1",
@@ -19821,7 +19757,7 @@
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
"integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.12",
"@types/istanbul-lib-coverage": "^2.0.1",
@@ -20617,7 +20553,8 @@
"@0no-co/graphql.web": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.3.2.tgz",
- "integrity": "sha512-Q1+pRlLhE31GOY/2c9BAEnFTNxO7Awtc6fhhEDlxyCBQ2N0IhD32cPVvPChrK9mwBNSgRdW/sF1kd2e0ojHj1Q=="
+ "integrity": "sha512-Q1+pRlLhE31GOY/2c9BAEnFTNxO7Awtc6fhhEDlxyCBQ2N0IhD32cPVvPChrK9mwBNSgRdW/sF1kd2e0ojHj1Q==",
+ "requires": {}
},
"@adobe/css-tools": {
"version": "4.5.0",
@@ -20628,8 +20565,7 @@
"@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
- "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
- "dev": true
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="
},
"@babel/code-frame": {
"version": "7.29.7",
@@ -21537,7 +21473,7 @@
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
- "dev": true
+ "devOptional": true
},
"@egjs/hammerjs": {
"version": "2.0.17",
@@ -21582,182 +21518,156 @@
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
"integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
- "dev": true,
"optional": true
},
"@esbuild/android-arm": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
"integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
- "dev": true,
"optional": true
},
"@esbuild/android-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
"integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
- "dev": true,
"optional": true
},
"@esbuild/android-x64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
"integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
- "dev": true,
"optional": true
},
"@esbuild/darwin-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
"integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
- "dev": true,
"optional": true
},
"@esbuild/darwin-x64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
"integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
- "dev": true,
"optional": true
},
"@esbuild/freebsd-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
"integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
- "dev": true,
"optional": true
},
"@esbuild/freebsd-x64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
"integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
- "dev": true,
"optional": true
},
"@esbuild/linux-arm": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
"integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
- "dev": true,
"optional": true
},
"@esbuild/linux-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
"integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
- "dev": true,
"optional": true
},
"@esbuild/linux-ia32": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
"integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
- "dev": true,
"optional": true
},
"@esbuild/linux-loong64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
"integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
- "dev": true,
"optional": true
},
"@esbuild/linux-mips64el": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
"integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
- "dev": true,
"optional": true
},
"@esbuild/linux-ppc64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
"integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
- "dev": true,
"optional": true
},
"@esbuild/linux-riscv64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
"integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
- "dev": true,
"optional": true
},
"@esbuild/linux-s390x": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
"integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
- "dev": true,
"optional": true
},
"@esbuild/linux-x64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
"integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
- "dev": true,
"optional": true
},
"@esbuild/netbsd-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
"integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
- "dev": true,
"optional": true
},
"@esbuild/netbsd-x64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
"integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
- "dev": true,
"optional": true
},
"@esbuild/openbsd-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
"integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
- "dev": true,
"optional": true
},
"@esbuild/openbsd-x64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
"integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
- "dev": true,
"optional": true
},
"@esbuild/openharmony-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
"integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
- "dev": true,
"optional": true
},
"@esbuild/sunos-x64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
"integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
- "dev": true,
"optional": true
},
"@esbuild/win32-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
"integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
- "dev": true,
"optional": true
},
"@esbuild/win32-ia32": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
"integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
- "dev": true,
"optional": true
},
"@esbuild/win32-x64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
"integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
- "dev": true,
"optional": true
},
"@eslint-community/eslint-utils": {
@@ -22294,7 +22204,8 @@
"@expo/vector-icons": {
"version": "15.1.1",
"resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-15.1.1.tgz",
- "integrity": "sha512-Iu2VkcoI5vygbtYngm7jb4ifxElNVXQYdDrYkT7UCEIiKLeWnQY0wf2ZhHZ+Wro6Sc5TaumpKUOqDRpLi5rkvw=="
+ "integrity": "sha512-Iu2VkcoI5vygbtYngm7jb4ifxElNVXQYdDrYkT7UCEIiKLeWnQY0wf2ZhHZ+Wro6Sc5TaumpKUOqDRpLi5rkvw==",
+ "requires": {}
},
"@expo/ws-tunnel": {
"version": "1.0.6",
@@ -22464,7 +22375,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
"integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/types": "^29.6.3",
"@types/node": "*",
@@ -22478,7 +22389,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
"integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/console": "^29.7.0",
"@jest/reporters": "^29.7.0",
@@ -22514,7 +22425,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
@@ -22525,7 +22436,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -22541,7 +22452,7 @@
"version": "30.4.0",
"resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz",
"integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==",
- "dev": true
+ "devOptional": true
},
"@jest/environment": {
"version": "29.7.0",
@@ -22558,7 +22469,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
"integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"expect": "^29.7.0",
"jest-snapshot": "^29.7.0"
@@ -22568,7 +22479,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
"integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"jest-get-type": "^29.6.3"
}
@@ -22590,13 +22501,13 @@
"version": "30.1.0",
"resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz",
"integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==",
- "dev": true
+ "devOptional": true
},
"@jest/globals": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
"integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/environment": "^29.7.0",
"@jest/expect": "^29.7.0",
@@ -22608,7 +22519,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
"integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@bcoe/v8-coverage": "^0.2.3",
"@jest/console": "^29.7.0",
@@ -22640,7 +22551,7 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
+ "devOptional": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -22654,7 +22565,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
+ "devOptional": true,
"requires": {
"brace-expansion": "^1.1.13"
}
@@ -22673,7 +22584,7 @@
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
"integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jridgewell/trace-mapping": "^0.3.18",
"callsites": "^3.0.0",
@@ -22684,7 +22595,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
"integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/console": "^29.7.0",
"@jest/types": "^29.6.3",
@@ -22696,7 +22607,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
"integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/test-result": "^29.7.0",
"graceful-fs": "^4.2.9",
@@ -22799,7 +22710,6 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
"requires": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@@ -22808,14 +22718,12 @@
"@nodelib/fs.stat": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
},
"@nodelib/fs.walk": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
"requires": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@@ -23163,22 +23071,26 @@
"@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
- "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "requires": {}
},
"@radix-ui/react-context": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz",
- "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="
+ "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==",
+ "requires": {}
},
"@radix-ui/react-direction": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.2.tgz",
- "integrity": "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA=="
+ "integrity": "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==",
+ "requires": {}
},
"@radix-ui/react-focus-guards": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.4.tgz",
- "integrity": "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q=="
+ "integrity": "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==",
+ "requires": {}
},
"@radix-ui/react-id": {
"version": "1.1.2",
@@ -23199,7 +23111,8 @@
"@radix-ui/react-use-callback-ref": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz",
- "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="
+ "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==",
+ "requires": {}
},
"@radix-ui/react-use-controllable-state": {
"version": "1.2.3",
@@ -23229,7 +23142,8 @@
"@radix-ui/react-use-layout-effect": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz",
- "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="
+ "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==",
+ "requires": {}
},
"@react-native-async-storage/async-storage": {
"version": "2.2.0",
@@ -23251,7 +23165,8 @@
"@react-native-community/netinfo": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-12.0.1.tgz",
- "integrity": "sha512-P/3caXIvfYSJG8AWJVefukg+ZGRPs+M4Lp3pNJtgcTYoJxCjWrKQGNnCkj/Cz//zWa/avGed0i/wzm0T8vV2IQ=="
+ "integrity": "sha512-P/3caXIvfYSJG8AWJVefukg+ZGRPs+M4Lp3pNJtgcTYoJxCjWrKQGNnCkj/Cz//zWa/avGed0i/wzm0T8vV2IQ==",
+ "requires": {}
},
"@react-native-community/slider": {
"version": "5.2.0",
@@ -23740,7 +23655,8 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-2.0.2.tgz",
"integrity": "sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"@storybook/mcp": {
"version": "0.7.0",
@@ -23758,7 +23674,8 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz",
"integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==",
- "dev": true
+ "dev": true,
+ "requires": {}
}
}
},
@@ -23778,7 +23695,8 @@
"version": "10.4.6",
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.4.6.tgz",
"integrity": "sha512-iGNmKzrq9vgl2PDrYAnZKI+yvac3Ym+lJXXuQaqlFRS23zA5MNm4EBX+rAG7WulqchoK6NaZ0KQOs2mAgEpTMg==",
- "dev": true
+ "dev": true,
+ "requires": {}
}
}
},
@@ -23918,7 +23836,7 @@
"version": "13.3.3",
"resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-13.3.3.tgz",
"integrity": "sha512-k6Mjsd9dbZgvY4Bl7P1NIpePQNi+dfYtlJ5voi9KQlynxSyQkfOgJmYGCYmw/aSgH/rUcFvG8u5gd4npzgRDyg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"jest-matcher-utils": "^30.0.5",
"picocolors": "^1.1.1",
@@ -23930,7 +23848,7 @@
"version": "30.4.1",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz",
"integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@sinclair/typebox": "^0.34.0"
}
@@ -23939,13 +23857,13 @@
"version": "0.34.49",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz",
"integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==",
- "dev": true
+ "devOptional": true
},
"jest-diff": {
"version": "30.4.1",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz",
"integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/diff-sequences": "30.4.0",
"@jest/get-type": "30.1.0",
@@ -23957,7 +23875,7 @@
"version": "30.4.1",
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz",
"integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/get-type": "30.1.0",
"chalk": "^4.1.2",
@@ -23969,7 +23887,7 @@
"version": "30.4.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz",
"integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/schemas": "30.4.1",
"ansi-styles": "^5.2.0",
@@ -23983,7 +23901,8 @@
"version": "14.6.1",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
"integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"@tmcp/adapter-valibot": {
"version": "0.1.6",
@@ -24000,7 +23919,8 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@tmcp/session-manager/-/session-manager-0.2.2.tgz",
"integrity": "sha512-UrCRpTsxh5XnMbplspvftEYboiZWgAiXqqAUbyFTHoHMJ0LoNDy8bQd0+7qtxtT4S5Qsnv650gvs/Nbec5NTCQ==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"@tmcp/transport-http": {
"version": "0.8.6",
@@ -24209,7 +24129,7 @@
"version": "19.1.17",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz",
"integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"csstype": "^3.0.2"
}
@@ -24306,7 +24226,8 @@
"version": "8.62.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz",
"integrity": "sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"@typescript-eslint/type-utils": {
"version": "8.62.0",
@@ -24600,7 +24521,8 @@
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@valibot/to-json-schema/-/to-json-schema-1.7.1.tgz",
"integrity": "sha512-3qkmU6KXWh8GIThEAW3kuRHPQBMjWkKy+Ppz3WkUucx53DTpOa6siMn4xDGSOhlVyMrDaJTCTMLYPZVAIk1P0A==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"@vitest/expect": {
"version": "3.2.4",
@@ -24768,7 +24690,8 @@
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"acorn-walk": {
"version": "8.3.5",
@@ -25295,8 +25218,7 @@
"binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "dev": true
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="
},
"boolbase": {
"version": "1.0.0",
@@ -25423,7 +25345,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true
+ "devOptional": true
},
"camelcase": {
"version": "6.3.0",
@@ -25433,8 +25355,7 @@
"camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
- "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
- "dev": true
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
},
"caniuse-lite": {
"version": "1.0.30001799",
@@ -25477,7 +25398,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
- "dev": true
+ "devOptional": true
},
"check-error": {
"version": "2.1.3",
@@ -25489,7 +25410,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
"requires": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -25505,7 +25425,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
"requires": {
"is-glob": "^4.0.1"
}
@@ -25550,7 +25469,7 @@
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
- "dev": true
+ "devOptional": true
},
"cli-cursor": {
"version": "5.0.0",
@@ -25645,13 +25564,13 @@
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
- "dev": true
+ "devOptional": true
},
"collect-v8-coverage": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz",
"integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==",
- "dev": true
+ "devOptional": true
},
"color": {
"version": "4.2.3",
@@ -25824,7 +25743,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
"integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/types": "^29.6.3",
"chalk": "^4.0.0",
@@ -25896,8 +25815,7 @@
"cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "dev": true
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
},
"cssom": {
"version": "0.5.0",
@@ -25926,7 +25844,7 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "dev": true
+ "devOptional": true
},
"damerau-levenshtein": {
"version": "1.0.8",
@@ -26001,7 +25919,8 @@
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz",
"integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==",
- "dev": true
+ "devOptional": true,
+ "requires": {}
},
"deep-eql": {
"version": "5.0.2",
@@ -26255,7 +26174,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
"integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
- "dev": true
+ "devOptional": true
},
"detect-node-es": {
"version": "1.1.0",
@@ -26265,20 +26184,18 @@
"didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
- "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
- "dev": true
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
},
"diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
"integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
- "dev": true
+ "devOptional": true
},
"dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
- "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
- "dev": true
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"doctrine": {
"version": "2.1.0",
@@ -26381,7 +26298,7 @@
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
"integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
- "dev": true
+ "devOptional": true
},
"emoji-regex": {
"version": "9.2.2",
@@ -26432,7 +26349,7 @@
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"is-arrayish": "^0.2.1"
}
@@ -26604,7 +26521,7 @@
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@esbuild/aix-ppc64": "0.28.1",
"@esbuild/android-arm": "0.28.1",
@@ -26971,7 +26888,8 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
"integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"eslint-scope": {
"version": "8.4.0",
@@ -27067,7 +26985,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"cross-spawn": "^7.0.3",
"get-stream": "^6.0.0",
@@ -27084,7 +27002,7 @@
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
"integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
- "dev": true
+ "devOptional": true
},
"expand-tilde": {
"version": "2.0.2",
@@ -27099,7 +27017,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
"integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/expect-utils": "^29.7.0",
"jest-get-type": "^29.6.3",
@@ -27437,7 +27355,8 @@
"expo-application": {
"version": "7.0.8",
"resolved": "https://registry.npmjs.org/expo-application/-/expo-application-7.0.8.tgz",
- "integrity": "sha512-qFGyxk7VJbrNOQWBbE09XUuGuvkOgFS9QfToaK2FdagM2aQ+x3CvGV2DuVgl/l4ZxPgIf3b/MNh9xHpwSwn74Q=="
+ "integrity": "sha512-qFGyxk7VJbrNOQWBbE09XUuGuvkOgFS9QfToaK2FdagM2aQ+x3CvGV2DuVgl/l4ZxPgIf3b/MNh9xHpwSwn74Q==",
+ "requires": {}
},
"expo-asset": {
"version": "12.0.13",
@@ -27451,7 +27370,8 @@
"expo-av": {
"version": "16.0.8",
"resolved": "https://registry.npmjs.org/expo-av/-/expo-av-16.0.8.tgz",
- "integrity": "sha512-cmVPftGR/ca7XBgs7R6ky36lF3OC0/MM/lpgX/yXqfv0jASTsh7AYX9JxHCwFmF+Z6JEB1vne9FDx4GiLcGreQ=="
+ "integrity": "sha512-cmVPftGR/ca7XBgs7R6ky36lF3OC0/MM/lpgX/yXqfv0jASTsh7AYX9JxHCwFmF+Z6JEB1vne9FDx4GiLcGreQ==",
+ "requires": {}
},
"expo-barcode-scanner": {
"version": "12.0.0",
@@ -27464,7 +27384,8 @@
"expo-battery": {
"version": "55.0.13",
"resolved": "https://registry.npmjs.org/expo-battery/-/expo-battery-55.0.13.tgz",
- "integrity": "sha512-yt2BZCs76aeM9UPbIf8pAGvZJ6AQ7UQ8qjiQcciU3o3q512ZTgsIWObfOqZlTy/abzU2njHW6OikWbtQbeyeZg=="
+ "integrity": "sha512-yt2BZCs76aeM9UPbIf8pAGvZJ6AQ7UQ8qjiQcciU3o3q512ZTgsIWObfOqZlTy/abzU2njHW6OikWbtQbeyeZg==",
+ "requires": {}
},
"expo-build-properties": {
"version": "1.0.10",
@@ -27518,12 +27439,14 @@
"expo-document-picker": {
"version": "14.0.8",
"resolved": "https://registry.npmjs.org/expo-document-picker/-/expo-document-picker-14.0.8.tgz",
- "integrity": "sha512-3tyQKpPqWWFlI8p9RiMX1+T1Zge5mEKeBuXWp1h8PEItFMUDSiOJbQ112sfdC6Hxt8wSxreV9bCRl/NgBdt+fA=="
+ "integrity": "sha512-3tyQKpPqWWFlI8p9RiMX1+T1Zge5mEKeBuXWp1h8PEItFMUDSiOJbQ112sfdC6Hxt8wSxreV9bCRl/NgBdt+fA==",
+ "requires": {}
},
"expo-file-system": {
"version": "19.0.23",
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.23.tgz",
- "integrity": "sha512-MeGkid9OeNILfT/qonaXHp4f2c15xaB28U/bcN7pqZej0Kx0+6+V7e9ZIXpPHm07zVatxA+QkMTPQEGfmvVOxA=="
+ "integrity": "sha512-MeGkid9OeNILfT/qonaXHp4f2c15xaB28U/bcN7pqZej0Kx0+6+V7e9ZIXpPHm07zVatxA+QkMTPQEGfmvVOxA==",
+ "requires": {}
},
"expo-font": {
"version": "14.0.12",
@@ -27536,17 +27459,20 @@
"expo-haptics": {
"version": "15.0.8",
"resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-15.0.8.tgz",
- "integrity": "sha512-lftutojy8Qs8zaDzzjwM3gKHFZ8bOOEZDCkmh2Ddpe95Ra6kt2izeOfOfKuP/QEh0MZ1j9TfqippyHdRd1ZM9g=="
+ "integrity": "sha512-lftutojy8Qs8zaDzzjwM3gKHFZ8bOOEZDCkmh2Ddpe95Ra6kt2izeOfOfKuP/QEh0MZ1j9TfqippyHdRd1ZM9g==",
+ "requires": {}
},
"expo-image": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/expo-image/-/expo-image-3.0.11.tgz",
- "integrity": "sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA=="
+ "integrity": "sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA==",
+ "requires": {}
},
"expo-image-loader": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.0.0.tgz",
- "integrity": "sha512-hVMhXagsO1cSng5s70IEjuJAuHy2hX/inu5MM3T0ecJMf7L/7detKf22molQBRymerbk6Tzu+20h11eU0n/3jQ=="
+ "integrity": "sha512-hVMhXagsO1cSng5s70IEjuJAuHy2hX/inu5MM3T0ecJMf7L/7detKf22molQBRymerbk6Tzu+20h11eU0n/3jQ==",
+ "requires": {}
},
"expo-image-picker": {
"version": "17.0.11",
@@ -27559,19 +27485,22 @@
"expo-image-loader": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-6.0.0.tgz",
- "integrity": "sha512-nKs/xnOGw6ACb4g26xceBD57FKLFkSwEUTDXEDF3Gtcu3MqF3ZIYd3YM+sSb1/z9AKV1dYT7rMSGVNgsveXLIQ=="
+ "integrity": "sha512-nKs/xnOGw6ACb4g26xceBD57FKLFkSwEUTDXEDF3Gtcu3MqF3ZIYd3YM+sSb1/z9AKV1dYT7rMSGVNgsveXLIQ==",
+ "requires": {}
}
}
},
"expo-keep-awake": {
"version": "15.0.8",
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.8.tgz",
- "integrity": "sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ=="
+ "integrity": "sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ==",
+ "requires": {}
},
"expo-linear-gradient": {
"version": "15.0.8",
"resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-15.0.8.tgz",
- "integrity": "sha512-V2d8Wjn0VzhPHO+rrSBtcl+Fo+jUUccdlmQ6OoL9/XQB7Qk3d9lYrqKDJyccwDxmQT10JdST3Tmf2K52NLc3kw=="
+ "integrity": "sha512-V2d8Wjn0VzhPHO+rrSBtcl+Fo+jUUccdlmQ6OoL9/XQB7Qk3d9lYrqKDJyccwDxmQT10JdST3Tmf2K52NLc3kw==",
+ "requires": {}
},
"expo-linking": {
"version": "8.0.12",
@@ -27612,7 +27541,8 @@
"expo-network": {
"version": "8.0.8",
"resolved": "https://registry.npmjs.org/expo-network/-/expo-network-8.0.8.tgz",
- "integrity": "sha512-dgrL8UHAmWofqeY4UEjWskCl/RoQAM0DG6PZR8xz2WZt+6aQEboQgFRXowCfhbKZ71d16sNuKXtwBEsp2DtdNw=="
+ "integrity": "sha512-dgrL8UHAmWofqeY4UEjWskCl/RoQAM0DG6PZR8xz2WZt+6aQEboQgFRXowCfhbKZ71d16sNuKXtwBEsp2DtdNw==",
+ "requires": {}
},
"expo-notifications": {
"version": "0.32.17",
@@ -27682,7 +27612,8 @@
"@radix-ui/react-compose-refs": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz",
- "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="
+ "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==",
+ "requires": {}
},
"@radix-ui/react-presence": {
"version": "1.1.6",
@@ -27751,7 +27682,8 @@
"expo-secure-store": {
"version": "15.0.8",
"resolved": "https://registry.npmjs.org/expo-secure-store/-/expo-secure-store-15.0.8.tgz",
- "integrity": "sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw=="
+ "integrity": "sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw==",
+ "requires": {}
},
"expo-sensors": {
"version": "15.0.8",
@@ -27769,7 +27701,8 @@
"expo-speech-recognition": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/expo-speech-recognition/-/expo-speech-recognition-3.1.3.tgz",
- "integrity": "sha512-vluXqggfY2AJcazYITvnV6YSE2rVe5SId5TpdjMC/m6JPUgHCOODrcHJtAQF6OUYl4f2T7oZSceGw6fJCC86Xw=="
+ "integrity": "sha512-vluXqggfY2AJcazYITvnV6YSE2rVe5SId5TpdjMC/m6JPUgHCOODrcHJtAQF6OUYl4f2T7oZSceGw6fJCC86Xw==",
+ "requires": {}
},
"expo-splash-screen": {
"version": "31.0.13",
@@ -27798,12 +27731,14 @@
"expo-video": {
"version": "3.0.16",
"resolved": "https://registry.npmjs.org/expo-video/-/expo-video-3.0.16.tgz",
- "integrity": "sha512-H1HlxcHGomZItqisGfW3YL/G9BHtNBfVSimDJcLuyxyU87wFnV8loO9tCjuhufkfh/aTa2sW5BYAjLjg9DvnBQ=="
+ "integrity": "sha512-H1HlxcHGomZItqisGfW3YL/G9BHtNBfVSimDJcLuyxyU87wFnV8loO9tCjuhufkfh/aTa2sW5BYAjLjg9DvnBQ==",
+ "requires": {}
},
"expo-web-browser": {
"version": "15.0.11",
"resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-15.0.11.tgz",
- "integrity": "sha512-r2LS4Ro6DgUPZkcaEfgt8mp9eJuoA93x11Jh7S6utFe0FEzvUNn2yFhxg8XVwESaaHGt2k5V8LuK36rsp0BeIw=="
+ "integrity": "sha512-r2LS4Ro6DgUPZkcaEfgt8mp9eJuoA93x11Jh7S6utFe0FEzvUNn2yFhxg8XVwESaaHGt2k5V8LuK36rsp0BeIw==",
+ "requires": {}
},
"exponential-backoff": {
"version": "3.1.3",
@@ -27819,7 +27754,6 @@
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@@ -27832,7 +27766,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
"requires": {
"is-glob": "^4.0.1"
}
@@ -27859,7 +27792,6 @@
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
"integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
- "dev": true,
"requires": {
"reusify": "^1.0.4"
}
@@ -28140,7 +28072,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "dev": true
+ "devOptional": true
},
"get-symbol-description": {
"version": "1.1.0",
@@ -28191,7 +28123,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
"requires": {
"is-glob": "^4.0.3"
}
@@ -28371,7 +28302,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true
+ "devOptional": true
},
"http-errors": {
"version": "2.0.1",
@@ -28416,7 +28347,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
- "dev": true
+ "devOptional": true
},
"husky": {
"version": "9.1.7",
@@ -28478,7 +28409,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
"integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"pkg-dir": "^4.2.0",
"resolve-cwd": "^3.0.0"
@@ -28493,7 +28424,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
- "dev": true
+ "devOptional": true
},
"inflight": {
"version": "1.0.6",
@@ -28565,7 +28496,7 @@
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true
+ "devOptional": true
},
"is-async-function": {
"version": "2.1.1",
@@ -28593,7 +28524,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
"requires": {
"binary-extensions": "^2.0.0"
}
@@ -28676,8 +28606,7 @@
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
},
"is-finalizationregistry": {
"version": "1.1.1",
@@ -28701,7 +28630,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
"integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
- "dev": true
+ "devOptional": true
},
"is-generator-function": {
"version": "1.1.2",
@@ -28719,7 +28648,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
"requires": {
"is-extglob": "^2.1.1"
}
@@ -28818,7 +28746,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true
+ "devOptional": true
},
"is-string": {
"version": "1.1.1",
@@ -28908,7 +28836,7 @@
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
"integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@babel/core": "^7.23.9",
"@babel/parser": "^7.23.9",
@@ -28921,7 +28849,7 @@
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -28929,7 +28857,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
"integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"istanbul-lib-coverage": "^3.0.0",
"make-dir": "^4.0.0",
@@ -28940,7 +28868,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
"integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"debug": "^4.1.1",
"istanbul-lib-coverage": "^3.0.0",
@@ -28951,7 +28879,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
"integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"html-escaper": "^2.0.0",
"istanbul-lib-report": "^3.0.0"
@@ -28975,7 +28903,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/core": "^29.7.0",
"@jest/types": "^29.6.3",
@@ -28987,7 +28915,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
"integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
- "dev": true,
+ "devOptional": true,
"requires": {
"execa": "^5.0.0",
"jest-util": "^29.7.0",
@@ -28998,7 +28926,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
"integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/environment": "^29.7.0",
"@jest/expect": "^29.7.0",
@@ -29026,7 +28954,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
@@ -29037,7 +28965,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -29045,7 +28973,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
"integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/core": "^29.7.0",
"@jest/test-result": "^29.7.0",
@@ -29064,7 +28992,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
"integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@babel/core": "^7.11.6",
"@jest/test-sequencer": "^29.7.0",
@@ -29094,7 +29022,7 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
+ "devOptional": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -29108,7 +29036,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
+ "devOptional": true,
"requires": {
"brace-expansion": "^1.1.13"
}
@@ -29117,7 +29045,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
@@ -29128,7 +29056,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -29136,7 +29064,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
"integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"chalk": "^4.0.0",
"diff-sequences": "^29.6.3",
@@ -29148,7 +29076,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
@@ -29159,7 +29087,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -29167,7 +29095,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
"integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
- "dev": true,
+ "devOptional": true,
"requires": {
"detect-newline": "^3.0.0"
}
@@ -29176,7 +29104,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
"integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/types": "^29.6.3",
"chalk": "^4.0.0",
@@ -29189,7 +29117,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
@@ -29200,7 +29128,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -29291,7 +29219,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
"integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"jest-get-type": "^29.6.3",
"pretty-format": "^29.7.0"
@@ -29301,7 +29229,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
@@ -29312,7 +29240,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -29320,7 +29248,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
"integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
- "dev": true,
+ "devOptional": true,
"requires": {
"chalk": "^4.0.0",
"jest-diff": "^29.7.0",
@@ -29332,7 +29260,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
@@ -29343,7 +29271,7 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -29394,7 +29322,8 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
"integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
- "dev": true
+ "devOptional": true,
+ "requires": {}
},
"jest-regex-util": {
"version": "29.6.3",
@@ -29405,7 +29334,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
"integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"chalk": "^4.0.0",
"graceful-fs": "^4.2.9",
@@ -29422,7 +29351,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
"integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"jest-regex-util": "^29.6.3",
"jest-snapshot": "^29.7.0"
@@ -29432,7 +29361,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
"integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/console": "^29.7.0",
"@jest/environment": "^29.7.0",
@@ -29461,7 +29390,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
"integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/environment": "^29.7.0",
"@jest/fake-timers": "^29.7.0",
@@ -29491,7 +29420,7 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
+ "devOptional": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -29505,7 +29434,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
+ "devOptional": true,
"requires": {
"brace-expansion": "^1.1.13"
}
@@ -29516,7 +29445,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
"integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@babel/core": "^7.11.6",
"@babel/generator": "^7.7.2",
@@ -29544,7 +29473,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
@@ -29555,13 +29484,13 @@
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
},
"semver": {
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -29704,7 +29633,7 @@
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
"integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jest/test-result": "^29.7.0",
"@jest/types": "^29.6.3",
@@ -29745,8 +29674,7 @@
"jiti": {
"version": "1.21.7",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
- "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
- "dev": true
+ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="
},
"js-tokens": {
"version": "4.0.0",
@@ -29815,7 +29743,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
- "dev": true
+ "devOptional": true
},
"json-rpc-2.0": {
"version": "1.7.1",
@@ -30020,8 +29948,7 @@
"lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
- "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
- "dev": true
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="
},
"lines-and-columns": {
"version": "1.2.4",
@@ -30228,7 +30155,8 @@
"lucide-react-native": {
"version": "0.562.0",
"resolved": "https://registry.npmjs.org/lucide-react-native/-/lucide-react-native-0.562.0.tgz",
- "integrity": "sha512-ZF2ok8SzyUaiCIrLGqYh/6SPs+huVzbZOCv0i411L4+oP3tJgQvvKePiVgWCioa7HsT2xaJZSrdd92cuB2/+ew=="
+ "integrity": "sha512-ZF2ok8SzyUaiCIrLGqYh/6SPs+huVzbZOCv0i411L4+oP3tJgQvvKePiVgWCioa7HsT2xaJZSrdd92cuB2/+ew==",
+ "requires": {}
},
"lz-string": {
"version": "1.5.0",
@@ -30249,7 +30177,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
"integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"semver": "^7.5.3"
},
@@ -30258,7 +30186,7 @@
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -30321,8 +30249,7 @@
"merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
},
"metro": {
"version": "0.83.7",
@@ -30998,7 +30925,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
+ "devOptional": true
},
"mimic-function": {
"version": "5.0.1",
@@ -31010,7 +30937,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
- "dev": true
+ "devOptional": true
},
"minimatch": {
"version": "7.4.9",
@@ -31108,7 +31035,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
- "dev": true
+ "devOptional": true
},
"negotiator": {
"version": "1.0.0",
@@ -31203,7 +31130,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"path-key": "^3.0.0"
}
@@ -31243,8 +31170,7 @@
"object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
- "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
- "dev": true
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="
},
"object-inspect": {
"version": "1.13.4",
@@ -31351,7 +31277,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"mimic-fn": "^2.1.0"
}
@@ -31588,7 +31514,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@@ -31693,8 +31619,7 @@
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "dev": true
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
},
"pirates": {
"version": "4.0.7",
@@ -31705,7 +31630,7 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"find-up": "^4.0.0"
},
@@ -31714,7 +31639,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -31724,7 +31649,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
+ "devOptional": true,
"requires": {
"p-locate": "^4.1.0"
}
@@ -31733,7 +31658,7 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
+ "devOptional": true,
"requires": {
"p-try": "^2.0.0"
}
@@ -31742,7 +31667,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
+ "devOptional": true,
"requires": {
"p-limit": "^2.2.0"
}
@@ -31808,7 +31733,6 @@
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
- "dev": true,
"requires": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
@@ -31819,7 +31743,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
"integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
- "dev": true,
"requires": {
"camelcase-css": "^2.0.1"
}
@@ -31828,7 +31751,6 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
- "dev": true,
"requires": {
"postcss-selector-parser": "^6.1.1"
}
@@ -31837,7 +31759,6 @@
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz",
"integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==",
- "dev": true,
"requires": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -31857,13 +31778,13 @@
"prettier": {
"version": "3.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz",
- "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==",
- "dev": true
+ "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q=="
},
"prettier-plugin-tailwindcss": {
"version": "0.5.14",
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.14.tgz",
- "integrity": "sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q=="
+ "integrity": "sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q==",
+ "requires": {}
},
"pretty-bytes": {
"version": "5.6.0",
@@ -31950,7 +31871,7 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
"integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
- "dev": true
+ "devOptional": true
},
"qrcode-terminal": {
"version": "0.11.0",
@@ -31985,8 +31906,7 @@
"queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
},
"range-parser": {
"version": "1.2.1",
@@ -32058,7 +31978,8 @@
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.4.0.tgz",
"integrity": "sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"react-dom": {
"version": "19.1.0",
@@ -32076,7 +31997,8 @@
"react-freeze": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz",
- "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA=="
+ "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==",
+ "requires": {}
},
"react-is": {
"version": "17.0.2",
@@ -32088,13 +32010,13 @@
"version": "npm:react-is@18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
- "dev": true
+ "devOptional": true
},
"react-is-19": {
"version": "npm:react-is@19.2.7",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz",
"integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==",
- "dev": true
+ "devOptional": true
},
"react-native": {
"version": "0.81.5",
@@ -32241,12 +32163,14 @@
"react-native-iap": {
"version": "15.3.3",
"resolved": "https://registry.npmjs.org/react-native-iap/-/react-native-iap-15.3.3.tgz",
- "integrity": "sha512-ClEbc/1rET28VBI0UUaWHzFAbzKIEJcJw/WlKBywWFlnGZoV+CkILI7QCvtQRHZfsmRUm7r1Cg1g0XJvzYQntw=="
+ "integrity": "sha512-ClEbc/1rET28VBI0UUaWHzFAbzKIEJcJw/WlKBywWFlnGZoV+CkILI7QCvtQRHZfsmRUm7r1Cg1g0XJvzYQntw==",
+ "requires": {}
},
"react-native-is-edge-to-edge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.3.1.tgz",
- "integrity": "sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA=="
+ "integrity": "sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==",
+ "requires": {}
},
"react-native-modal-datetime-picker": {
"version": "18.0.0",
@@ -32261,7 +32185,7 @@
"version": "0.35.10",
"resolved": "https://registry.npmjs.org/react-native-nitro-modules/-/react-native-nitro-modules-0.35.10.tgz",
"integrity": "sha512-KsySOAIkbSTjNiX2GvXiNT9P5DMeZd99yvtE/+Lw7RXV7Ztxh9Z+YyAbYkBqadhN5J/KXXuPjhRYgyLOIZgsbQ==",
- "dev": true
+ "requires": {}
},
"react-native-reanimated": {
"version": "4.1.7",
@@ -32282,7 +32206,8 @@
"react-native-safe-area-context": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz",
- "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg=="
+ "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==",
+ "requires": {}
},
"react-native-screens": {
"version": "4.16.0",
@@ -32368,8 +32293,7 @@
"react-refresh": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
- "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
- "dev": true
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="
},
"react-remove-scroll": {
"version": "2.7.2",
@@ -32405,7 +32329,7 @@
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-19.1.0.tgz",
"integrity": "sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw==",
- "dev": true,
+ "devOptional": true,
"requires": {
"react-is": "^19.1.0",
"scheduler": "^0.26.0"
@@ -32415,7 +32339,7 @@
"version": "19.2.7",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz",
"integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==",
- "dev": true
+ "devOptional": true
}
}
},
@@ -32423,7 +32347,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
- "dev": true,
"requires": {
"pify": "^2.3.0"
}
@@ -32432,7 +32355,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
"requires": {
"picomatch": "^2.2.1"
}
@@ -32454,7 +32376,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
"integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"indent-string": "^4.0.0",
"strip-indent": "^3.0.0"
@@ -32464,7 +32386,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"min-indent": "^1.0.0"
}
@@ -32602,7 +32524,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
"integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"resolve-from": "^5.0.0"
}
@@ -32668,8 +32590,7 @@
"reusify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
- "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
- "dev": true
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="
},
"rfdc": {
"version": "1.4.1",
@@ -32718,7 +32639,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
"requires": {
"queue-microtask": "^1.2.2"
}
@@ -33099,7 +33019,7 @@
"version": "0.5.13",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
- "dev": true,
+ "devOptional": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -33289,7 +33209,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
"integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
- "dev": true,
+ "devOptional": true,
"requires": {
"char-regex": "^1.0.2",
"strip-ansi": "^6.0.0"
@@ -33415,13 +33335,13 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true
+ "devOptional": true
},
"strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dev": true
+ "devOptional": true
},
"strip-indent": {
"version": "4.1.1",
@@ -33433,7 +33353,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true
+ "devOptional": true
},
"structured-headers": {
"version": "0.4.1",
@@ -33498,7 +33418,6 @@
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
- "dev": true,
"requires": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@@ -33528,7 +33447,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
"integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
- "dev": true,
"requires": {
"lilconfig": "^3.1.1"
}
@@ -33674,7 +33592,8 @@
"fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "requires": {}
},
"picomatch": {
"version": "4.0.4",
@@ -33751,7 +33670,8 @@
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
"integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"ts-dedent": {
"version": "2.3.0",
@@ -33802,7 +33722,7 @@
"version": "4.22.4",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz",
"integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==",
- "dev": true,
+ "devOptional": true,
"requires": {
"esbuild": "~0.28.0",
"fsevents": "~2.3.3"
@@ -34024,7 +33944,8 @@
"use-latest-callback": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.6.tgz",
- "integrity": "sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg=="
+ "integrity": "sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==",
+ "requires": {}
},
"use-sidecar": {
"version": "1.1.3",
@@ -34038,7 +33959,8 @@
"use-sync-external-store": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
- "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "requires": {}
},
"util": {
"version": "0.12.5",
@@ -34055,8 +33977,7 @@
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"utils-merge": {
"version": "1.0.1",
@@ -34072,7 +33993,7 @@
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
"integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==",
- "dev": true,
+ "devOptional": true,
"requires": {
"@jridgewell/trace-mapping": "^0.3.12",
"@types/istanbul-lib-coverage": "^2.0.1",
@@ -34083,7 +34004,8 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/valibot/-/valibot-1.4.1.tgz",
"integrity": "sha512-klCmFTz2jeDluy9RwX+F884TCiogtdBJ/YaxSx1EOBYXa3NXNWj8kR1jjN8rzluwojJVWWaHJ4r1U5LfICnM3g==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"validate-npm-package-name": {
"version": "5.0.1",
@@ -34106,7 +34028,8 @@
"@radix-ui/react-compose-refs": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz",
- "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="
+ "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==",
+ "requires": {}
},
"@radix-ui/react-dialog": {
"version": "1.1.17",
@@ -34421,7 +34344,8 @@
"ws": {
"version": "8.21.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
- "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="
+ "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
+ "requires": {}
},
"wsl-utils": {
"version": "0.1.0",
@@ -34559,7 +34483,8 @@
"zustand": {
"version": "5.0.14",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz",
- "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g=="
+ "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==",
+ "requires": {}
}
}
}
diff --git a/src/__tests__/components/ConflictResolutionModal.test.tsx b/src/__tests__/components/ConflictResolutionModal.test.tsx
new file mode 100644
index 00000000..2d95b715
--- /dev/null
+++ b/src/__tests__/components/ConflictResolutionModal.test.tsx
@@ -0,0 +1,284 @@
+/**
+ * Tests for #660 — ConflictResolutionModal component
+ */
+import { fireEvent, render, waitFor } from '@testing-library/react-native';
+import React from 'react';
+
+import { ConflictResolutionModal } from '../../components/common/ConflictResolutionModal';
+import { useConflictStore, type ConflictData } from '../../store/conflictStore';
+
+// Mock the conflict store
+jest.mock('../../store/conflictStore', () => {
+ const actual = jest.requireActual('../../store/conflictStore');
+ return {
+ ...actual,
+ useConflictStore: jest.fn(),
+ useActiveConflict: jest.fn(),
+ useConflictModalVisible: jest.fn(),
+ useIsResolvingConflict: jest.fn(),
+ };
+});
+
+// Mock AccessibleModal to simplify testing
+jest.mock('../../components/common/AccessibleModal', () => ({
+ AccessibleModal: ({ visible, children, onClose }: any) => (visible ? children : null),
+}));
+
+const mockResolveConflict = jest.fn();
+const mockHideModal = jest.fn();
+const mockGetPendingCount = jest.fn();
+
+const createMockConflict = (overrides?: Partial): ConflictData => ({
+ id: 'test-conflict-1',
+ entityId: 'note-123',
+ entityType: 'note',
+ localData: {
+ title: 'My Local Title',
+ content: 'Local content here',
+ tags: ['work', 'important'],
+ },
+ serverData: {
+ title: 'Server Title (Updated)',
+ content: 'Server content here',
+ tags: ['work'],
+ },
+ localVersion: 1,
+ serverVersion: 2,
+ clientTimestamp: Date.now() - 60000,
+ serverTimestamp: Date.now(),
+ endpoint: '/api/notes/123',
+ method: 'PUT',
+ detectedAt: Date.now(),
+ ...overrides,
+});
+
+describe('ConflictResolutionModal', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ mockResolveConflict.mockResolvedValue(undefined);
+ mockGetPendingCount.mockReturnValue(1);
+
+ (useConflictStore as unknown as jest.Mock).mockReturnValue({
+ resolveConflict: mockResolveConflict,
+ hideModal: mockHideModal,
+ getPendingCount: mockGetPendingCount,
+ });
+ });
+
+ const setupMocks = (isVisible: boolean, conflict: ConflictData | null, isResolving = false) => {
+ /* eslint-disable @typescript-eslint/no-require-imports */
+ const {
+ useActiveConflict,
+ useConflictModalVisible,
+ useIsResolvingConflict,
+ } = require('../../store/conflictStore');
+ /* eslint-enable @typescript-eslint/no-require-imports */
+ (useActiveConflict as jest.Mock).mockReturnValue(conflict);
+ (useConflictModalVisible as jest.Mock).mockReturnValue(isVisible);
+ (useIsResolvingConflict as jest.Mock).mockReturnValue(isResolving);
+ };
+
+ describe('rendering', () => {
+ it('renders nothing when not visible', () => {
+ setupMocks(false, null);
+ const { queryByText } = render();
+ expect(queryByText('Sync Conflict Detected')).toBeNull();
+ });
+
+ it('renders nothing when no active conflict', () => {
+ setupMocks(true, null);
+ const { queryByText } = render();
+ expect(queryByText('Sync Conflict Detected')).toBeNull();
+ });
+
+ it('renders modal with conflict details', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByText, getAllByText } = render();
+
+ expect(getByText('Sync Conflict Detected')).toBeTruthy();
+ // Check that Note entity type is displayed (may appear in multiple places)
+ expect(getAllByText(/Note/).length).toBeGreaterThan(0);
+ });
+
+ it('shows badge when multiple conflicts pending', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+ mockGetPendingCount.mockReturnValue(3);
+
+ const { getByText } = render();
+
+ expect(getByText('3 conflicts')).toBeTruthy();
+ });
+
+ it('displays version information', () => {
+ const conflict = createMockConflict({
+ localVersion: 5,
+ serverVersion: 8,
+ });
+ setupMocks(true, conflict);
+
+ const { getByText } = render();
+
+ expect(getByText('5')).toBeTruthy();
+ expect(getByText('8')).toBeTruthy();
+ });
+ });
+
+ describe('tabs', () => {
+ it('shows differences tab by default', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByText } = render();
+
+ // Differences tab should be active
+ const diffTab = getByText('Differences');
+ expect(diffTab).toBeTruthy();
+ });
+
+ it('switches to local version tab', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByText } = render();
+
+ const localTab = getByText('Your Version');
+ fireEvent.press(localTab);
+
+ // Should show local data JSON
+ expect(getByText(/My Local Title/)).toBeTruthy();
+ });
+
+ it('switches to server version tab', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByText } = render();
+
+ const serverTab = getByText('Server Version');
+ fireEvent.press(serverTab);
+
+ // Should show server data JSON
+ expect(getByText(/Server Title \(Updated\)/)).toBeTruthy();
+ });
+ });
+
+ describe('diff view', () => {
+ it('shows differing fields with highlighting', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByText } = render();
+
+ // Should show field names
+ expect(getByText('title')).toBeTruthy();
+ expect(getByText('content')).toBeTruthy();
+ });
+
+ it('handles empty data gracefully', () => {
+ const conflict = createMockConflict({
+ localData: {},
+ serverData: {},
+ });
+ setupMocks(true, conflict);
+
+ const { getByText } = render();
+
+ expect(getByText('No data to compare')).toBeTruthy();
+ });
+ });
+
+ describe('resolution actions', () => {
+ it('calls resolveConflict with local choice on Keep Mine button', async () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByText } = render();
+
+ const keepMineButton = getByText('Keep Mine');
+ fireEvent.press(keepMineButton);
+
+ await waitFor(() => {
+ expect(mockResolveConflict).toHaveBeenCalledWith('test-conflict-1', 'local');
+ });
+ });
+
+ it('calls resolveConflict with server choice on Use Server button', async () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByText } = render();
+
+ const useServerButton = getByText('Use Server');
+ fireEvent.press(useServerButton);
+
+ await waitFor(() => {
+ expect(mockResolveConflict).toHaveBeenCalledWith('test-conflict-1', 'server');
+ });
+ });
+
+ it('disables buttons while resolving', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict, true);
+
+ const { getByLabelText } = render();
+
+ const keepMineButton = getByLabelText('Keep your changes');
+ const useServerButton = getByLabelText('Use server version');
+
+ // Check that buttons are disabled via the disabled prop
+ expect(keepMineButton.props.disabled).toBe(true);
+ expect(useServerButton.props.disabled).toBe(true);
+ });
+ });
+
+ describe('accessibility', () => {
+ it('has proper accessibility labels on buttons', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByLabelText } = render();
+
+ expect(getByLabelText('Keep your changes')).toBeTruthy();
+ expect(getByLabelText('Use server version')).toBeTruthy();
+ });
+
+ it('has proper accessibility hints on buttons', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByA11yHint } = render();
+
+ expect(getByA11yHint('Overwrite server data with your local changes')).toBeTruthy();
+ expect(getByA11yHint('Discard your changes and use the server version')).toBeTruthy();
+ });
+
+ it('tabs have proper accessibility roles', () => {
+ const conflict = createMockConflict();
+ setupMocks(true, conflict);
+
+ const { getByText } = render();
+
+ // Check that all three tabs are rendered with proper text
+ expect(getByText('Differences')).toBeTruthy();
+ expect(getByText('Your Version')).toBeTruthy();
+ expect(getByText('Server Version')).toBeTruthy();
+ });
+ });
+
+ describe('entity type formatting', () => {
+ it('capitalizes entity type', () => {
+ const conflict = createMockConflict({ entityType: 'quiz_draft' });
+ setupMocks(true, conflict);
+
+ const { getAllByText } = render();
+
+ // Should find at least one instance of the formatted entity type
+ const matches = getAllByText(/Quiz draft/);
+ expect(matches.length).toBeGreaterThan(0);
+ });
+ });
+});
diff --git a/src/__tests__/services/api/conflictHandling.test.ts b/src/__tests__/services/api/conflictHandling.test.ts
new file mode 100644
index 00000000..727f7f89
--- /dev/null
+++ b/src/__tests__/services/api/conflictHandling.test.ts
@@ -0,0 +1,280 @@
+/**
+ * Tests for #660 — 409 Conflict handling in axios interceptor
+ */
+import { AxiosError, InternalAxiosRequestConfig } from 'axios';
+
+// Store the interceptor handlers for testing
+let responseErrorHandler: ((error: AxiosError) => Promise) | null = null;
+
+// Mock axios before importing the module
+jest.mock('axios', () => {
+ const actualAxios = jest.requireActual('axios');
+ return {
+ ...actualAxios,
+ create: jest.fn(() => ({
+ interceptors: {
+ request: {
+ use: jest.fn(),
+ },
+ response: {
+ use: jest.fn((successHandler, errorHandler) => {
+ responseErrorHandler = errorHandler;
+ }),
+ },
+ },
+ defaults: {
+ headers: {
+ common: {},
+ },
+ },
+ })),
+ isAxiosError: actualAxios.isAxiosError,
+ };
+});
+
+// Mock dependencies
+jest.mock('../../../services/api/cache', () => ({
+ invalidateCacheForBatchRequests: jest.fn(),
+ invalidateCacheForMutation: jest.fn(),
+ invalidateByPattern: jest.fn(),
+}));
+
+jest.mock('../../../services/api/requestQueue', () => ({
+ requestQueue: {
+ addToQueue: jest.fn(),
+ },
+}));
+
+const mockAddConflict = jest.fn();
+jest.mock('../../../store/conflictStore', () => ({
+ useConflictStore: {
+ getState: () => ({
+ addConflict: mockAddConflict,
+ }),
+ },
+}));
+
+jest.mock('../../../config', () => ({
+ getEnv: jest.fn(() => 'https://api.test.com'),
+}));
+
+jest.mock('../../../config/apiCacheConfig', () => ({
+ MUTATION_INVALIDATION_MAP: [],
+}));
+
+jest.mock('../../../config/security', () => ({
+ SSL_PINNING: { bypassEnabled: false },
+}));
+
+jest.mock('../../../store', () => ({
+ useAppStore: {
+ getState: () => ({
+ logout: jest.fn(),
+ }),
+ },
+}));
+
+jest.mock('../../../utils/logger', () => ({
+ appLogger: {
+ warnSync: jest.fn(),
+ errorSync: jest.fn(),
+ },
+}));
+
+jest.mock('../../../utils/performanceTiming', () => ({
+ startTiming: jest.fn(),
+ notifyEntry: jest.fn(),
+}));
+
+jest.mock('../../../services/healthMetrics', () => ({
+ healthMetricsService: {
+ recordApiCall: jest.fn(),
+ },
+}));
+
+jest.mock('../../../services/sentryContext', () => ({
+ sentryContextService: {
+ captureException: jest.fn(),
+ },
+}));
+
+jest.mock('../../../services/secureStorage', () => ({
+ getAccessToken: jest.fn(),
+ getRefreshToken: jest.fn(),
+ saveTokens: jest.fn(),
+}));
+
+// Import the module after mocks are set up to capture the interceptor
+// eslint-disable-next-line @typescript-eslint/no-require-imports
+require('../../../services/api/axios.config');
+
+describe('axios.config — 409 Conflict handling (#660)', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const createAxiosError = (
+ status: number,
+ responseData: any,
+ config: Partial = {}
+ ): AxiosError => {
+ const error = new Error('Request failed') as AxiosError;
+ error.isAxiosError = true;
+ error.response = {
+ status,
+ data: responseData,
+ statusText: status === 409 ? 'Conflict' : 'Error',
+ headers: {},
+ config: config as InternalAxiosRequestConfig,
+ };
+ error.config = {
+ url: '/api/notes/123',
+ method: 'put',
+ data: { title: 'Local Title' },
+ headers: {
+ 'X-Last-Known-Version': '1',
+ 'X-Client-Timestamp': String(Date.now() - 5000),
+ 'X-Entity-Type': 'note',
+ 'X-Entity-Id': 'note-123',
+ },
+ ...config,
+ } as InternalAxiosRequestConfig;
+ return error;
+ };
+
+ describe('409 Conflict response handling', () => {
+ it('adds conflict to store when 409 received', async () => {
+ expect(responseErrorHandler).not.toBeNull();
+
+ const error = createAxiosError(409, {
+ serverVersion: { title: 'Server Title' },
+ serverVersionNumber: 2,
+ entityType: 'note',
+ entityId: 'note-123',
+ message: 'Version conflict detected',
+ });
+
+ await expect(responseErrorHandler!(error)).rejects.toMatchObject({
+ status: 409,
+ code: 'CONFLICT',
+ });
+
+ expect(mockAddConflict).toHaveBeenCalledWith(
+ expect.objectContaining({
+ entityId: 'note-123',
+ entityType: 'note',
+ localData: { title: 'Local Title' },
+ serverData: { title: 'Server Title' },
+ localVersion: 1,
+ serverVersion: 2,
+ endpoint: '/api/notes/123',
+ method: 'PUT',
+ })
+ );
+ });
+
+ it('extracts version metadata from request headers', async () => {
+ expect(responseErrorHandler).not.toBeNull();
+
+ const clientTimestamp = Date.now() - 5000;
+ const error = createAxiosError(
+ 409,
+ { serverVersion: {}, serverVersionNumber: 3 },
+ {
+ headers: {
+ 'X-Last-Known-Version': '2',
+ 'X-Client-Timestamp': String(clientTimestamp),
+ 'X-Entity-Type': 'quiz',
+ 'X-Entity-Id': 'quiz-456',
+ } as any,
+ }
+ );
+
+ await expect(responseErrorHandler!(error)).rejects.toMatchObject({
+ status: 409,
+ });
+
+ expect(mockAddConflict).toHaveBeenCalledWith(
+ expect.objectContaining({
+ localVersion: 2,
+ clientTimestamp,
+ entityType: 'quiz',
+ entityId: 'quiz-456',
+ })
+ );
+ });
+
+ it('uses fallback values when headers are missing', async () => {
+ expect(responseErrorHandler).not.toBeNull();
+
+ const error = createAxiosError(
+ 409,
+ { serverVersion: {}, serverVersionNumber: 1 },
+ {
+ headers: {} as any,
+ }
+ );
+
+ await expect(responseErrorHandler!(error)).rejects.toMatchObject({
+ status: 409,
+ });
+
+ expect(mockAddConflict).toHaveBeenCalledWith(
+ expect.objectContaining({
+ entityType: 'unknown',
+ entityId: '',
+ })
+ );
+ });
+
+ it('generates unique conflict id', async () => {
+ expect(responseErrorHandler).not.toBeNull();
+
+ const error = createAxiosError(409, { serverVersion: {} });
+
+ await expect(responseErrorHandler!(error)).rejects.toBeDefined();
+
+ const calledWith = mockAddConflict.mock.calls[0][0];
+ expect(calledWith.id).toMatch(/^conflict_\d+_[a-z0-9]+$/);
+ });
+
+ it('includes conflict data in rejection', async () => {
+ expect(responseErrorHandler).not.toBeNull();
+
+ const error = createAxiosError(409, {
+ serverVersion: { data: 'server' },
+ message: 'Custom conflict message',
+ });
+
+ await expect(responseErrorHandler!(error)).rejects.toMatchObject({
+ message: 'Custom conflict message',
+ status: 409,
+ code: 'CONFLICT',
+ conflict: expect.objectContaining({
+ serverData: { data: 'server' },
+ }),
+ });
+ });
+
+ it('logs conflict detection', async () => {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const { appLogger } = require('../../../utils/logger');
+ expect(responseErrorHandler).not.toBeNull();
+
+ const error = createAxiosError(409, {
+ serverVersion: {},
+ serverVersionNumber: 2,
+ });
+
+ await expect(responseErrorHandler!(error)).rejects.toBeDefined();
+
+ expect(appLogger.warnSync).toHaveBeenCalledWith(
+ '409 Conflict - mutation conflicts with server state',
+ expect.objectContaining({
+ endpoint: '/api/notes/123',
+ method: 'put',
+ })
+ );
+ });
+ });
+});
diff --git a/src/__tests__/store/conflictStore.test.ts b/src/__tests__/store/conflictStore.test.ts
new file mode 100644
index 00000000..1ea1e9ef
--- /dev/null
+++ b/src/__tests__/store/conflictStore.test.ts
@@ -0,0 +1,407 @@
+/**
+ * Tests for #660 — Conflict store for offline-first conflict resolution
+ */
+import { useConflictStore, type ConflictData } from '../../store/conflictStore';
+
+// Mock axios client
+jest.mock('../../services/api/axios.config', () => ({
+ __esModule: true,
+ default: jest.fn(),
+}));
+
+jest.mock('../../utils/logger', () => ({
+ __esModule: true,
+ default: { error: jest.fn(), warn: jest.fn(), info: jest.fn() },
+ logger: { error: jest.fn(), warn: jest.fn(), info: jest.fn() },
+ appLogger: { errorSync: jest.fn(), warnSync: jest.fn(), infoSync: jest.fn() },
+}));
+
+const createMockConflict = (overrides?: Partial): ConflictData => ({
+ id: `conflict_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
+ entityId: 'note-123',
+ entityType: 'note',
+ localData: { title: 'Local Title', content: 'Local content' },
+ serverData: { title: 'Server Title', content: 'Server content' },
+ localVersion: 1,
+ serverVersion: 2,
+ clientTimestamp: Date.now() - 5000,
+ serverTimestamp: Date.now(),
+ endpoint: '/api/notes/123',
+ method: 'PUT',
+ detectedAt: Date.now(),
+ ...overrides,
+});
+
+describe('conflictStore — offline-first conflict resolution (#660)', () => {
+ beforeEach(() => {
+ // Reset store to initial state
+ useConflictStore.setState({
+ conflicts: [],
+ activeConflict: null,
+ isModalVisible: false,
+ resolutionHistory: [],
+ isResolving: false,
+ });
+ jest.clearAllMocks();
+ });
+
+ describe('addConflict', () => {
+ it('adds a new conflict to the queue', () => {
+ const conflict = createMockConflict();
+
+ useConflictStore.getState().addConflict(conflict);
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(1);
+ expect(state.conflicts[0]).toEqual(conflict);
+ });
+
+ it('auto-shows modal for first conflict when no modal is visible', () => {
+ const conflict = createMockConflict();
+
+ useConflictStore.getState().addConflict(conflict);
+
+ const state = useConflictStore.getState();
+ expect(state.isModalVisible).toBe(true);
+ expect(state.activeConflict).toEqual(conflict);
+ });
+
+ it('replaces existing conflict for same entity', () => {
+ const conflict1 = createMockConflict({ id: 'conflict-1' });
+ const conflict2 = createMockConflict({
+ id: 'conflict-2',
+ localData: { title: 'Updated Local' },
+ });
+
+ useConflictStore.getState().addConflict(conflict1);
+ useConflictStore.getState().addConflict(conflict2);
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(1);
+ expect(state.conflicts[0].id).toBe('conflict-2');
+ expect((state.conflicts[0].localData as any).title).toBe('Updated Local');
+ });
+
+ it('queues multiple conflicts for different entities', () => {
+ const conflict1 = createMockConflict({ entityId: 'note-1' });
+ const conflict2 = createMockConflict({ entityId: 'note-2' });
+
+ useConflictStore.getState().addConflict(conflict1);
+ useConflictStore.getState().addConflict(conflict2);
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(2);
+ });
+ });
+
+ describe('removeConflict', () => {
+ it('removes a conflict by id', () => {
+ const conflict = createMockConflict({ id: 'to-remove' });
+ useConflictStore.setState({ conflicts: [conflict] });
+
+ useConflictStore.getState().removeConflict('to-remove');
+
+ expect(useConflictStore.getState().conflicts).toHaveLength(0);
+ });
+
+ it('clears activeConflict if it matches the removed conflict', () => {
+ const conflict = createMockConflict({ id: 'active-conflict' });
+ useConflictStore.setState({
+ conflicts: [conflict],
+ activeConflict: conflict,
+ isModalVisible: true,
+ });
+
+ useConflictStore.getState().removeConflict('active-conflict');
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(0);
+ expect(state.activeConflict).toBeNull();
+ });
+
+ it('does not affect other conflicts', () => {
+ const conflict1 = createMockConflict({ id: 'keep', entityId: 'note-1' });
+ const conflict2 = createMockConflict({ id: 'remove', entityId: 'note-2' });
+ useConflictStore.setState({ conflicts: [conflict1, conflict2] });
+
+ useConflictStore.getState().removeConflict('remove');
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(1);
+ expect(state.conflicts[0].id).toBe('keep');
+ });
+ });
+
+ describe('clearAllConflicts', () => {
+ it('clears all conflicts and hides modal', () => {
+ const conflicts = [
+ createMockConflict({ entityId: 'note-1' }),
+ createMockConflict({ entityId: 'note-2' }),
+ ];
+ useConflictStore.setState({
+ conflicts,
+ activeConflict: conflicts[0],
+ isModalVisible: true,
+ });
+
+ useConflictStore.getState().clearAllConflicts();
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(0);
+ expect(state.activeConflict).toBeNull();
+ expect(state.isModalVisible).toBe(false);
+ });
+ });
+
+ describe('showModal / hideModal', () => {
+ it('showModal displays the first conflict if none specified', () => {
+ const conflict = createMockConflict();
+ useConflictStore.setState({ conflicts: [conflict] });
+
+ useConflictStore.getState().showModal();
+
+ const state = useConflictStore.getState();
+ expect(state.isModalVisible).toBe(true);
+ expect(state.activeConflict).toEqual(conflict);
+ });
+
+ it('showModal displays a specific conflict when provided', () => {
+ const conflict1 = createMockConflict({ id: 'c1', entityId: 'note-1' });
+ const conflict2 = createMockConflict({ id: 'c2', entityId: 'note-2' });
+ useConflictStore.setState({ conflicts: [conflict1, conflict2] });
+
+ useConflictStore.getState().showModal(conflict2);
+
+ const state = useConflictStore.getState();
+ expect(state.activeConflict?.id).toBe('c2');
+ });
+
+ it('hideModal sets isModalVisible to false', () => {
+ useConflictStore.setState({ isModalVisible: true });
+
+ useConflictStore.getState().hideModal();
+
+ expect(useConflictStore.getState().isModalVisible).toBe(false);
+ });
+ });
+
+ describe('resolveConflict', () => {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const apiClient = require('../../services/api/axios.config').default;
+
+ beforeEach(() => {
+ apiClient.mockReset();
+ apiClient.mockResolvedValue({ data: {} });
+ });
+
+ it('resolves with local choice and sends data to server', async () => {
+ const conflict = createMockConflict({ id: 'resolve-local' });
+ useConflictStore.setState({
+ conflicts: [conflict],
+ activeConflict: conflict,
+ isModalVisible: true,
+ });
+
+ await useConflictStore.getState().resolveConflict('resolve-local', 'local');
+
+ expect(apiClient).toHaveBeenCalledWith(
+ expect.objectContaining({
+ method: 'put',
+ url: '/api/notes/123',
+ data: conflict.localData,
+ headers: expect.objectContaining({
+ 'X-Force-Override': 'true',
+ 'X-Conflict-Resolution': 'local',
+ }),
+ })
+ );
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(0);
+ expect(state.resolutionHistory).toHaveLength(1);
+ expect(state.resolutionHistory[0].choice).toBe('local');
+ });
+
+ it('resolves with server choice without sending request', async () => {
+ const conflict = createMockConflict({ id: 'resolve-server' });
+ useConflictStore.setState({
+ conflicts: [conflict],
+ activeConflict: conflict,
+ isModalVisible: true,
+ });
+
+ await useConflictStore.getState().resolveConflict('resolve-server', 'server');
+
+ // Server choice should NOT call the API (just accept server data)
+ expect(apiClient).not.toHaveBeenCalled();
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(0);
+ expect(state.resolutionHistory[0].choice).toBe('server');
+ });
+
+ it('resolves with merge choice using provided merged data', async () => {
+ const conflict = createMockConflict({ id: 'resolve-merge' });
+ const mergedData = { title: 'Merged Title', content: 'Merged content' };
+ useConflictStore.setState({
+ conflicts: [conflict],
+ activeConflict: conflict,
+ isModalVisible: true,
+ });
+
+ await useConflictStore.getState().resolveConflict('resolve-merge', 'merge', mergedData);
+
+ expect(apiClient).toHaveBeenCalledWith(
+ expect.objectContaining({
+ data: mergedData,
+ headers: expect.objectContaining({
+ 'X-Conflict-Resolution': 'merge',
+ }),
+ })
+ );
+
+ const state = useConflictStore.getState();
+ expect(state.resolutionHistory[0].resolvedData).toEqual(mergedData);
+ });
+
+ it('shows next conflict after resolution', async () => {
+ const conflict1 = createMockConflict({ id: 'c1', entityId: 'note-1' });
+ const conflict2 = createMockConflict({ id: 'c2', entityId: 'note-2' });
+ useConflictStore.setState({
+ conflicts: [conflict1, conflict2],
+ activeConflict: conflict1,
+ isModalVisible: true,
+ });
+
+ await useConflictStore.getState().resolveConflict('c1', 'server');
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(1);
+ expect(state.activeConflict?.id).toBe('c2');
+ expect(state.isModalVisible).toBe(true);
+ });
+
+ it('hides modal when all conflicts are resolved', async () => {
+ const conflict = createMockConflict({ id: 'last-conflict' });
+ useConflictStore.setState({
+ conflicts: [conflict],
+ activeConflict: conflict,
+ isModalVisible: true,
+ });
+
+ await useConflictStore.getState().resolveConflict('last-conflict', 'server');
+
+ const state = useConflictStore.getState();
+ expect(state.conflicts).toHaveLength(0);
+ expect(state.activeConflict).toBeNull();
+ expect(state.isModalVisible).toBe(false);
+ });
+
+ it('sets isResolving during resolution', async () => {
+ const conflict = createMockConflict();
+ useConflictStore.setState({ conflicts: [conflict] });
+
+ // Create a promise we can control
+ let resolveApiCall: () => void;
+ const apiPromise = new Promise(resolve => {
+ resolveApiCall = resolve;
+ });
+ apiClient.mockReturnValue(apiPromise);
+
+ const resolvePromise = useConflictStore.getState().resolveConflict(conflict.id, 'local');
+
+ // Should be resolving
+ expect(useConflictStore.getState().isResolving).toBe(true);
+
+ // Complete the API call
+ resolveApiCall!();
+ await resolvePromise;
+
+ // Should no longer be resolving
+ expect(useConflictStore.getState().isResolving).toBe(false);
+ });
+
+ it('handles API errors gracefully', async () => {
+ const conflict = createMockConflict();
+ useConflictStore.setState({
+ conflicts: [conflict],
+ activeConflict: conflict,
+ isModalVisible: true,
+ });
+ apiClient.mockRejectedValue(new Error('Network error'));
+
+ await expect(
+ useConflictStore.getState().resolveConflict(conflict.id, 'local')
+ ).rejects.toThrow('Network error');
+
+ // Should reset isResolving even on error
+ expect(useConflictStore.getState().isResolving).toBe(false);
+ // Conflict should still be in the queue
+ expect(useConflictStore.getState().conflicts).toHaveLength(1);
+ });
+
+ it('logs warning for non-existent conflict', async () => {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const logger = require('../../utils/logger').logger;
+
+ await useConflictStore.getState().resolveConflict('non-existent', 'local');
+
+ expect(logger.warn).toHaveBeenCalledWith('Conflict not found: non-existent');
+ });
+ });
+
+ describe('getConflictById', () => {
+ it('returns the conflict with matching id', () => {
+ const conflict = createMockConflict({ id: 'find-me' });
+ useConflictStore.setState({ conflicts: [conflict] });
+
+ const found = useConflictStore.getState().getConflictById('find-me');
+
+ expect(found).toEqual(conflict);
+ });
+
+ it('returns undefined for non-existent id', () => {
+ const found = useConflictStore.getState().getConflictById('not-found');
+
+ expect(found).toBeUndefined();
+ });
+ });
+
+ describe('getPendingCount', () => {
+ it('returns the number of pending conflicts', () => {
+ const conflicts = [
+ createMockConflict({ entityId: 'note-1' }),
+ createMockConflict({ entityId: 'note-2' }),
+ createMockConflict({ entityId: 'note-3' }),
+ ];
+ useConflictStore.setState({ conflicts });
+
+ expect(useConflictStore.getState().getPendingCount()).toBe(3);
+ });
+
+ it('returns 0 when no conflicts', () => {
+ expect(useConflictStore.getState().getPendingCount()).toBe(0);
+ });
+ });
+
+ describe('resolutionHistory', () => {
+ it('keeps last 50 resolutions', async () => {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const apiClient = require('../../services/api/axios.config').default;
+ apiClient.mockResolvedValue({ data: {} });
+
+ // Add 55 conflicts and resolve them
+ for (let i = 0; i < 55; i++) {
+ const conflict = createMockConflict({ id: `c-${i}`, entityId: `note-${i}` });
+ useConflictStore.setState({
+ conflicts: [conflict],
+ activeConflict: conflict,
+ });
+ await useConflictStore.getState().resolveConflict(`c-${i}`, 'server');
+ }
+
+ const history = useConflictStore.getState().resolutionHistory;
+ expect(history.length).toBeLessThanOrEqual(50);
+ });
+ });
+});
diff --git a/src/components/common/ConflictResolutionModal.tsx b/src/components/common/ConflictResolutionModal.tsx
new file mode 100644
index 00000000..3f02beb6
--- /dev/null
+++ b/src/components/common/ConflictResolutionModal.tsx
@@ -0,0 +1,508 @@
+import React, { useCallback, useMemo, useState } from 'react';
+import {
+ View,
+ Text,
+ StyleSheet,
+ ScrollView,
+ Pressable,
+ ActivityIndicator,
+ useColorScheme,
+} from 'react-native';
+
+import { AccessibleModal } from './AccessibleModal';
+import {
+ useConflictStore,
+ useActiveConflict,
+ useConflictModalVisible,
+ useIsResolvingConflict,
+ type ConflictResolutionChoice,
+} from '../../store/conflictStore';
+
+interface DiffLine {
+ key: string;
+ localValue: string;
+ serverValue: string;
+ isDifferent: boolean;
+}
+
+/**
+ * Formats a value for display in the diff view
+ */
+function formatValue(value: unknown): string {
+ if (value === null) return 'null';
+ if (value === undefined) return 'undefined';
+ if (typeof value === 'string') return value;
+ if (typeof value === 'number' || typeof value === 'boolean') return String(value);
+ if (value instanceof Date) return value.toISOString();
+ try {
+ return JSON.stringify(value, null, 2);
+ } catch {
+ return String(value);
+ }
+}
+
+/**
+ * Flattens a nested object into dot-notation keys
+ */
+function flattenObject(obj: unknown, prefix = ''): Record {
+ const result: Record = {};
+
+ if (obj === null || obj === undefined || typeof obj !== 'object') {
+ return { [prefix || 'value']: obj };
+ }
+
+ if (Array.isArray(obj)) {
+ obj.forEach((item, index) => {
+ const key = prefix ? `${prefix}[${index}]` : `[${index}]`;
+ Object.assign(result, flattenObject(item, key));
+ });
+ return result;
+ }
+
+ for (const [key, value] of Object.entries(obj as Record)) {
+ const newKey = prefix ? `${prefix}.${key}` : key;
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
+ Object.assign(result, flattenObject(value, newKey));
+ } else {
+ result[newKey] = value;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Computes diff lines between local and server data
+ */
+function computeDiffLines(localData: unknown, serverData: unknown): DiffLine[] {
+ const localFlat = flattenObject(localData);
+ const serverFlat = flattenObject(serverData);
+ const allKeys = new Set([...Object.keys(localFlat), ...Object.keys(serverFlat)]);
+
+ const lines: DiffLine[] = [];
+ for (const key of Array.from(allKeys).sort()) {
+ const localValue = formatValue(localFlat[key]);
+ const serverValue = formatValue(serverFlat[key]);
+ lines.push({
+ key,
+ localValue,
+ serverValue,
+ isDifferent: localValue !== serverValue,
+ });
+ }
+
+ return lines;
+}
+
+/**
+ * Formats entity type for display
+ */
+function formatEntityType(type: string): string {
+ return type.charAt(0).toUpperCase() + type.slice(1).replace(/_/g, ' ');
+}
+
+/**
+ * Formats timestamp for display
+ */
+function formatTimestamp(ts: number): string {
+ return new Date(ts).toLocaleString();
+}
+
+interface ConflictResolutionModalProps {
+ /** Whether to use portal rendering (default: true) */
+ usePortal?: boolean;
+}
+
+export const ConflictResolutionModal: React.FC = ({
+ usePortal = true,
+}) => {
+ const colorScheme = useColorScheme();
+ const isDark = colorScheme === 'dark';
+
+ const isVisible = useConflictModalVisible();
+ const activeConflict = useActiveConflict();
+ const isResolving = useIsResolvingConflict();
+ const { resolveConflict, hideModal, getPendingCount } = useConflictStore();
+
+ const [selectedTab, setSelectedTab] = useState<'diff' | 'local' | 'server'>('diff');
+
+ const diffLines = useMemo(() => {
+ if (!activeConflict) return [];
+ return computeDiffLines(activeConflict.localData, activeConflict.serverData);
+ }, [activeConflict]);
+
+ const handleResolve = useCallback(
+ async (choice: ConflictResolutionChoice) => {
+ if (!activeConflict || isResolving) return;
+ try {
+ await resolveConflict(activeConflict.id, choice);
+ } catch {
+ // Error is logged in store
+ }
+ },
+ [activeConflict, isResolving, resolveConflict]
+ );
+
+ const handleClose = useCallback(() => {
+ if (!isResolving) {
+ hideModal();
+ }
+ }, [hideModal, isResolving]);
+
+ const pendingCount = getPendingCount();
+
+ const styles = createStyles(isDark);
+
+ if (!activeConflict) {
+ return null;
+ }
+
+ return (
+
+
+ Sync Conflict Detected
+ {pendingCount > 1 && {pendingCount} conflicts}
+
+
+
+ Your changes to this {formatEntityType(activeConflict.entityType)} conflict with newer data
+ on the server.
+
+
+
+
+ Entity:
+ {formatEntityType(activeConflict.entityType)}
+
+
+ Your version:
+ {activeConflict.localVersion ?? 'Unknown'}
+
+
+ Server version:
+ {activeConflict.serverVersion ?? 'Unknown'}
+
+
+ Your edit:
+ {formatTimestamp(activeConflict.clientTimestamp)}
+
+
+
+ {/* Tab selector */}
+
+ setSelectedTab('diff')}
+ accessibilityRole="tab"
+ accessibilityState={{ selected: selectedTab === 'diff' }}
+ >
+
+ Differences
+
+
+ setSelectedTab('local')}
+ accessibilityRole="tab"
+ accessibilityState={{ selected: selectedTab === 'local' }}
+ >
+
+ Your Version
+
+
+ setSelectedTab('server')}
+ accessibilityRole="tab"
+ accessibilityState={{ selected: selectedTab === 'server' }}
+ >
+
+ Server Version
+
+
+
+
+ {/* Content area */}
+
+ {selectedTab === 'diff' && (
+
+ {diffLines.length === 0 ? (
+ No data to compare
+ ) : (
+ diffLines.map((line, index) => (
+
+
+ {line.key}
+
+
+
+ Yours:
+
+ {line.localValue}
+
+
+
+ Server:
+
+ {line.serverValue}
+
+
+
+
+ ))
+ )}
+
+ )}
+
+ {selectedTab === 'local' && (
+
+ {formatValue(activeConflict.localData)}
+
+ )}
+
+ {selectedTab === 'server' && (
+
+ {formatValue(activeConflict.serverData)}
+
+ )}
+
+
+ {/* Action buttons */}
+
+ handleResolve('local')}
+ disabled={isResolving}
+ accessibilityRole="button"
+ accessibilityLabel="Keep your changes"
+ accessibilityHint="Overwrite server data with your local changes"
+ >
+ {isResolving ? (
+
+ ) : (
+ Keep Mine
+ )}
+
+
+ handleResolve('server')}
+ disabled={isResolving}
+ accessibilityRole="button"
+ accessibilityLabel="Use server version"
+ accessibilityHint="Discard your changes and use the server version"
+ >
+ Use Server
+
+
+
+
+ Choose which version to keep. "Keep Mine" will upload your version to the server.
+ "Use Server" will discard your local changes.
+
+
+ );
+};
+
+const createStyles = (isDark: boolean) =>
+ StyleSheet.create({
+ modalContainer: {
+ maxWidth: 500,
+ width: '95%',
+ maxHeight: '90%',
+ backgroundColor: isDark ? '#1a1a1a' : '#ffffff',
+ },
+ header: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ marginBottom: 8,
+ },
+ title: {
+ fontSize: 20,
+ fontWeight: '700',
+ color: isDark ? '#ffffff' : '#11181C',
+ },
+ badge: {
+ fontSize: 12,
+ fontWeight: '600',
+ color: '#ffffff',
+ backgroundColor: '#e53935',
+ paddingHorizontal: 8,
+ paddingVertical: 4,
+ borderRadius: 12,
+ overflow: 'hidden',
+ },
+ subtitle: {
+ fontSize: 14,
+ color: isDark ? '#9BA1A6' : '#687076',
+ marginBottom: 16,
+ lineHeight: 20,
+ },
+ metaContainer: {
+ backgroundColor: isDark ? '#252525' : '#f5f5f5',
+ borderRadius: 8,
+ padding: 12,
+ marginBottom: 16,
+ },
+ metaRow: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 4,
+ },
+ metaLabel: {
+ fontSize: 12,
+ color: isDark ? '#9BA1A6' : '#687076',
+ },
+ metaValue: {
+ fontSize: 12,
+ fontWeight: '600',
+ color: isDark ? '#ffffff' : '#11181C',
+ },
+ tabContainer: {
+ flexDirection: 'row',
+ borderBottomWidth: 1,
+ borderBottomColor: isDark ? '#333' : '#e0e0e0',
+ marginBottom: 12,
+ },
+ tab: {
+ flex: 1,
+ paddingVertical: 10,
+ alignItems: 'center',
+ },
+ tabActive: {
+ borderBottomWidth: 2,
+ borderBottomColor: '#0a7ea4',
+ },
+ tabText: {
+ fontSize: 13,
+ fontWeight: '500',
+ color: isDark ? '#9BA1A6' : '#687076',
+ },
+ tabTextActive: {
+ color: '#0a7ea4',
+ fontWeight: '600',
+ },
+ contentContainer: {
+ maxHeight: 250,
+ marginBottom: 16,
+ },
+ diffContainer: {
+ gap: 8,
+ },
+ diffRow: {
+ borderRadius: 6,
+ padding: 10,
+ backgroundColor: isDark ? '#252525' : '#fafafa',
+ },
+ diffRowEven: {
+ backgroundColor: isDark ? '#1f1f1f' : '#f5f5f5',
+ },
+ diffRowChanged: {
+ borderLeftWidth: 3,
+ borderLeftColor: '#ff9800',
+ },
+ diffKey: {
+ fontSize: 12,
+ fontWeight: '600',
+ color: isDark ? '#ffffff' : '#11181C',
+ marginBottom: 6,
+ },
+ diffValues: {
+ flexDirection: 'row',
+ gap: 8,
+ },
+ diffValue: {
+ flex: 1,
+ padding: 6,
+ borderRadius: 4,
+ },
+ diffValueLocal: {
+ backgroundColor: isDark ? 'rgba(76, 175, 80, 0.15)' : 'rgba(76, 175, 80, 0.1)',
+ },
+ diffValueServer: {
+ backgroundColor: isDark ? 'rgba(33, 150, 243, 0.15)' : 'rgba(33, 150, 243, 0.1)',
+ },
+ diffLabel: {
+ fontSize: 10,
+ fontWeight: '600',
+ color: isDark ? '#9BA1A6' : '#687076',
+ marginBottom: 2,
+ },
+ diffText: {
+ fontSize: 12,
+ color: isDark ? '#ffffff' : '#11181C',
+ },
+ diffTextLocal: {
+ color: '#4caf50',
+ },
+ diffTextServer: {
+ color: '#2196f3',
+ },
+ emptyText: {
+ fontSize: 14,
+ color: isDark ? '#9BA1A6' : '#687076',
+ textAlign: 'center',
+ padding: 20,
+ },
+ jsonContainer: {
+ backgroundColor: isDark ? '#252525' : '#f5f5f5',
+ borderRadius: 8,
+ padding: 12,
+ },
+ jsonText: {
+ fontSize: 12,
+ fontFamily: 'monospace',
+ color: isDark ? '#ffffff' : '#11181C',
+ },
+ buttonContainer: {
+ flexDirection: 'row',
+ gap: 12,
+ marginBottom: 12,
+ },
+ button: {
+ flex: 1,
+ paddingVertical: 14,
+ borderRadius: 8,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ buttonLocal: {
+ backgroundColor: '#4caf50',
+ },
+ buttonServer: {
+ backgroundColor: '#2196f3',
+ },
+ buttonText: {
+ fontSize: 15,
+ fontWeight: '600',
+ color: '#ffffff',
+ },
+ helpText: {
+ fontSize: 12,
+ color: isDark ? '#9BA1A6' : '#687076',
+ textAlign: 'center',
+ lineHeight: 18,
+ },
+ });
+
+export default ConflictResolutionModal;
diff --git a/src/services/api/axios.config.ts b/src/services/api/axios.config.ts
index 8b2663b4..38ad8c7c 100644
--- a/src/services/api/axios.config.ts
+++ b/src/services/api/axios.config.ts
@@ -12,17 +12,22 @@
import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios';
-import { invalidateCacheForBatchRequests, invalidateCacheForMutation, invalidateByPattern } from './cache';
+import {
+ invalidateCacheForBatchRequests,
+ invalidateCacheForMutation,
+ invalidateByPattern,
+} from './cache';
import { requestQueue } from './requestQueue';
import { getEnv } from '../../config';
import { MUTATION_INVALIDATION_MAP } from '../../config/apiCacheConfig';
import { SSL_PINNING } from '../../config/security';
import { useAppStore } from '../../store';
+import { useConflictStore, type ConflictData } from '../../store/conflictStore';
import { appLogger } from '../../utils/logger';
import { startTiming, notifyEntry } from '../../utils/performanceTiming';
import { healthMetricsService } from '../healthMetrics';
-import { sentryContextService } from '../sentryContext';
import { getAccessToken, getRefreshToken, saveTokens } from '../secureStorage';
+import { sentryContextService } from '../sentryContext';
// ─── Helpers ────────────────────────────────────────────────────────────────
@@ -259,17 +264,14 @@ apiClient.interceptors.response.use(
isCertPinFailure(error)
) {
// Report to Sentry — endpoint and method only; no token, headers, or body
- sentryContextService.captureException(
- new Error('SSL certificate pin validation failed'),
- {
- tags: { 'security.event': 'ssl_pin_failure' },
- extra: {
- endpoint: originalRequest?.url,
- method: originalRequest?.method?.toUpperCase(),
- },
- fingerprint: ['ssl-pin-failure'],
- }
- );
+ sentryContextService.captureException(new Error('SSL certificate pin validation failed'), {
+ tags: { 'security.event': 'ssl_pin_failure' },
+ extra: {
+ endpoint: originalRequest?.url,
+ method: originalRequest?.method?.toUpperCase(),
+ },
+ fingerprint: ['ssl-pin-failure'],
+ });
appLogger.errorSync('SSL pin validation failed — possible MITM attack', undefined, {
endpoint: originalRequest?.url,
@@ -280,7 +282,8 @@ apiClient.interceptors.response.use(
useAppStore.getState().logout();
return Promise.reject({
- message: 'Secure connection could not be established. Please check your network and try again.',
+ message:
+ 'Secure connection could not be established. Please check your network and try again.',
code: 'SSL_PIN_FAILURE',
status: 0,
});
@@ -358,6 +361,69 @@ apiClient.interceptors.response.use(
});
}
+ // ─── 409: Conflict — offline mutation conflicts with server state ─────
+ //
+ // Server returns 409 when the client's lastKnownVersion is behind the
+ // server's current version. The response body contains:
+ // - serverVersion: the current server data
+ // - serverVersionNumber: the current version number
+ // - localVersion: echoed back from client headers (if provided)
+ // - entityType: type of entity (note, quiz, profile, etc.)
+ // - entityId: identifier of the conflicting entity
+
+ if (status === 409) {
+ const responseData = error.response?.data as
+ | {
+ serverVersion?: unknown;
+ serverVersionNumber?: number;
+ localVersion?: unknown;
+ entityType?: string;
+ entityId?: string;
+ message?: string;
+ }
+ | undefined;
+
+ // Extract version metadata from request headers
+ const clientVersionHeader = originalRequest.headers?.['X-Last-Known-Version'];
+ const clientTimestampHeader = originalRequest.headers?.['X-Client-Timestamp'];
+ const entityTypeHeader = originalRequest.headers?.['X-Entity-Type'];
+ const entityIdHeader = originalRequest.headers?.['X-Entity-Id'];
+
+ const conflictData: ConflictData = {
+ id: `conflict_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
+ entityId: responseData?.entityId ?? String(entityIdHeader ?? ''),
+ entityType: responseData?.entityType ?? String(entityTypeHeader ?? 'unknown'),
+ localData: originalRequest.data,
+ serverData: responseData?.serverVersion,
+ localVersion: clientVersionHeader ? Number(clientVersionHeader) : undefined,
+ serverVersion: responseData?.serverVersionNumber,
+ clientTimestamp: clientTimestampHeader ? Number(clientTimestampHeader) : Date.now(),
+ serverTimestamp: Date.now(),
+ endpoint: originalRequest.url ?? '',
+ method: (originalRequest.method ?? 'UNKNOWN').toUpperCase(),
+ detectedAt: Date.now(),
+ };
+
+ appLogger.warnSync('409 Conflict - mutation conflicts with server state', {
+ endpoint: originalRequest.url,
+ method: originalRequest.method,
+ entityType: conflictData.entityType,
+ entityId: conflictData.entityId,
+ localVersion: conflictData.localVersion,
+ serverVersion: conflictData.serverVersion,
+ });
+
+ // Add to conflict store for UI resolution
+ useConflictStore.getState().addConflict(conflictData);
+
+ return Promise.reject({
+ message: responseData?.message ?? 'Your changes conflict with newer server data',
+ status: 409,
+ code: 'CONFLICT',
+ conflict: conflictData,
+ });
+ }
+
// ─── 429: Rate limit (exponential backoff) ─────────────────────────────
if (status === 429) {
@@ -446,7 +512,8 @@ apiClient.interceptors.response.use(
// ─── ECONNABORTED: Timeout — user-friendly message ──────────────────────
if (error.code === 'ECONNABORTED') {
- const isUpload = originalRequest.method?.toUpperCase() === 'POST' &&
+ const isUpload =
+ originalRequest.method?.toUpperCase() === 'POST' &&
originalRequest.data instanceof FormData;
return Promise.reject({
message: isUpload
diff --git a/src/services/api/requestQueue.ts b/src/services/api/requestQueue.ts
index 5592f4e8..519e57b4 100644
--- a/src/services/api/requestQueue.ts
+++ b/src/services/api/requestQueue.ts
@@ -3,8 +3,7 @@ import { InternalAxiosRequestConfig } from 'axios';
import * as Network from 'expo-network';
import { useAppStore } from '../../store';
-import logger from '../../utils/logger';
-import { healthMetricsService } from '../healthMetrics';
+import { logger } from '../../utils/logger';
import { mobileAnalyticsService } from '../mobileAnalytics';
import { isSessionValid, refreshAccessToken } from '../secureStorage';
@@ -19,6 +18,14 @@ export interface QueuedRequest {
priority: RequestPriority;
endpoint: string;
method: string;
+ /** Version of the entity when mutation was created (for conflict detection) */
+ lastKnownVersion?: number;
+ /** Timestamp when mutation was created on client (for last-write-wins resolution) */
+ clientTimestamp?: number;
+ /** Entity type for conflict resolution (e.g., 'note', 'quiz', 'profile') */
+ entityType?: string;
+ /** Entity ID for conflict resolution */
+ entityId?: string;
}
interface BatchGroup {
@@ -65,6 +72,11 @@ class RequestQueue {
async addToQueue(
config: InternalAxiosRequestConfig,
priority: RequestPriority = 'normal',
+ versionMetadata?: {
+ lastKnownVersion?: number;
+ entityType?: string;
+ entityId?: string;
+ }
): Promise {
try {
const queue = await this.getQueue();
@@ -80,6 +92,10 @@ class RequestQueue {
priority,
endpoint,
method,
+ lastKnownVersion: versionMetadata?.lastKnownVersion,
+ clientTimestamp: Date.now(),
+ entityType: versionMetadata?.entityType,
+ entityId: versionMetadata?.entityId,
};
queue.push(queuedRequest);
@@ -91,9 +107,7 @@ class RequestQueue {
this.metrics.byMethod[method] = (this.metrics.byMethod[method] ?? 0) + 1;
await this.persistMetrics();
- logger.info(
- `Added request to queue: [${priority}] ${method} ${endpoint}`,
- );
+ logger.info(`Added request to queue: [${priority}] ${method} ${endpoint}`);
this.notifyListeners(queue.length);
mobileAnalyticsService.trackEvent('request_queued' as any, {
@@ -123,18 +137,18 @@ class RequestQueue {
async removeFromQueue(id: string): Promise {
try {
const queue = await this.getQueue();
- const removed = queue.find((req) => req.id === id);
- const filtered = queue.filter((req) => req.id !== id);
+ const removed = queue.find(req => req.id === id);
+ const filtered = queue.filter(req => req.id !== id);
if (removed) {
this.metrics.totalQueued = Math.max(0, this.metrics.totalQueued - 1);
this.metrics.byPriority[removed.priority] = Math.max(
0,
- this.metrics.byPriority[removed.priority] - 1,
+ this.metrics.byPriority[removed.priority] - 1
);
this.metrics.byMethod[removed.method] = Math.max(
0,
- (this.metrics.byMethod[removed.method] ?? 1) - 1,
+ (this.metrics.byMethod[removed.method] ?? 1) - 1
);
}
@@ -148,7 +162,7 @@ class RequestQueue {
async incrementRetry(id: string): Promise {
try {
const queue = await this.getQueue();
- const request = queue.find((req) => req.id === id);
+ const request = queue.find(req => req.id === id);
if (request) {
request.retries += 1;
this.metrics.totalRetries++;
@@ -197,7 +211,7 @@ class RequestQueue {
.setTokens(
refreshedTokens.accessToken,
refreshedTokens.refreshToken,
- refreshedTokens.expiresAt,
+ refreshedTokens.expiresAt
);
logger.info('RequestQueue: session refreshed before replaying queued requests');
} catch (error) {
@@ -205,23 +219,19 @@ class RequestQueue {
useAppStore.getState().logout();
logger.warn(
'RequestQueue: queued requests cleared after session refresh failed; re-authentication required',
- error,
+ error
);
return;
}
}
- const validRequests = queue.filter(
- (req) => req.retries < req.maxRetries,
- );
- const expiredRequests = queue.filter(
- (req) => req.retries >= req.maxRetries,
- );
+ const validRequests = queue.filter(req => req.retries < req.maxRetries);
+ const expiredRequests = queue.filter(req => req.retries >= req.maxRetries);
for (const expired of expiredRequests) {
await this.removeFromQueue(expired.id);
logger.warn(
- `Request ${expired.id} [${expired.priority}] ${expired.method} ${expired.endpoint} dropped after ${expired.maxRetries} retries`,
+ `Request ${expired.id} [${expired.priority}] ${expired.method} ${expired.endpoint} dropped after ${expired.maxRetries} retries`
);
}
@@ -246,12 +256,10 @@ class RequestQueue {
this.restoreMetrics();
const pending = this.getQueue();
pending
- .then((q) => {
+ .then(q => {
this.notifyListeners(q.length);
if (q.length > 0) {
- logger.info(
- `RequestQueue: ${q.length} pending requests restored from storage`,
- );
+ logger.info(`RequestQueue: ${q.length} pending requests restored from storage`);
mobileAnalyticsService.trackEvent('queue_resumed' as any, {
pendingCount: q.length,
});
@@ -344,7 +352,7 @@ class RequestQueue {
onPendingCountChange(listener: (count: number) => void): () => void {
this.listeners.push(listener);
return () => {
- this.listeners = this.listeners.filter((l) => l !== listener);
+ this.listeners = this.listeners.filter(l => l !== listener);
};
}
@@ -376,7 +384,9 @@ class RequestQueue {
if (requests.length === 1) {
const req = requests[0];
try {
- await client(req.config);
+ // Add version metadata headers for conflict detection
+ const configWithVersion = this.addVersionHeaders(req);
+ await client(configWithVersion);
await this.removeFromQueue(req.id);
mobileAnalyticsService.trackEvent('request_dequeued' as any, {
priority: req.priority,
@@ -384,19 +394,17 @@ class RequestQueue {
endpoint: req.endpoint,
batched: false,
});
- } catch (error) {
+ } catch {
await this.incrementRetry(req.id);
}
return;
}
- logger.info(
- `RequestQueue: Batching ${requests.length} ${method} requests to ${endpoint}`,
- );
+ logger.info(`RequestQueue: Batching ${requests.length} ${method} requests to ${endpoint}`);
if (method === 'GET') {
const results = await Promise.allSettled(
- requests.map((req) => client(req.config).catch(() => {})),
+ requests.map(req => client(req.config).catch(() => {}))
);
for (let i = 0; i < requests.length; i++) {
if (results[i].status === 'fulfilled') {
@@ -409,7 +417,7 @@ class RequestQueue {
}
if (method === 'PUT' || method === 'PATCH') {
- const payloads = requests.map((req) => req.config.data);
+ const payloads = requests.map(req => req.config.data);
try {
const mergedPayload = this.mergePayloads(payloads);
const batchConfig = {
@@ -428,7 +436,7 @@ class RequestQueue {
batchSize: requests.length,
});
return;
- } catch (error) {
+ } catch {
for (const req of requests) {
await this.incrementRetry(req.id);
}
@@ -440,7 +448,7 @@ class RequestQueue {
try {
await client(req.config);
await this.removeFromQueue(req.id);
- } catch (error) {
+ } catch {
await this.incrementRetry(req.id);
}
}
@@ -468,7 +476,7 @@ class RequestQueue {
}
private notifyListeners(count: number): void {
- this.listeners.forEach((listener) => listener(count));
+ this.listeners.forEach(listener => listener(count));
}
private async checkConnectivity(): Promise {
@@ -482,9 +490,8 @@ class RequestQueue {
private async listenForNetworkChanges(): Promise {
try {
- const listener = Network.addNetworkStateListener((state) => {
- const online =
- (state.isConnected && state.isInternetReachable) ?? false;
+ const listener = Network.addNetworkStateListener(state => {
+ const online = (state.isConnected && state.isInternetReachable) ?? false;
if (online) {
logger.info('RequestQueue: Network became available, processing queue');
void this.processQueue(this.apiClient!);
@@ -492,19 +499,13 @@ class RequestQueue {
});
this.networkListener = () => listener.remove();
} catch (error) {
- logger.error(
- 'RequestQueue: Failed to listen for network changes:',
- error,
- );
+ logger.error('RequestQueue: Failed to listen for network changes:', error);
}
}
private async persistMetrics(): Promise {
try {
- await AsyncStorage.setItem(
- QUEUE_METRICS_KEY,
- JSON.stringify(this.metrics),
- );
+ await AsyncStorage.setItem(QUEUE_METRICS_KEY, JSON.stringify(this.metrics));
} catch (error) {
logger.error('Error persisting queue metrics:', error);
}
@@ -525,6 +526,27 @@ class RequestQueue {
}
}
+ private addVersionHeaders(request: QueuedRequest): InternalAxiosRequestConfig {
+ const config = { ...request.config };
+ config.headers = config.headers || {};
+
+ // Add version metadata as headers for conflict detection
+ if (request.lastKnownVersion !== undefined) {
+ config.headers['X-Last-Known-Version'] = String(request.lastKnownVersion);
+ }
+ if (request.clientTimestamp !== undefined) {
+ config.headers['X-Client-Timestamp'] = String(request.clientTimestamp);
+ }
+ if (request.entityType) {
+ config.headers['X-Entity-Type'] = request.entityType;
+ }
+ if (request.entityId) {
+ config.headers['X-Entity-Id'] = request.entityId;
+ }
+
+ return config;
+ }
+
private generateId(): string {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
diff --git a/src/store/conflictStore.ts b/src/store/conflictStore.ts
new file mode 100644
index 00000000..0928e91e
--- /dev/null
+++ b/src/store/conflictStore.ts
@@ -0,0 +1,247 @@
+import { create } from 'zustand';
+import { subscribeWithSelector } from 'zustand/middleware';
+
+import apiClient from '../services/api/axios.config';
+import { logger } from '../utils/logger';
+
+/**
+ * Resolution strategy for conflicts
+ */
+export type ConflictResolutionChoice = 'local' | 'server' | 'merge';
+
+/**
+ * Data structure representing a detected conflict
+ */
+export interface ConflictData {
+ /** Unique identifier for this conflict instance */
+ id: string;
+ /** Entity identifier (e.g., note ID, quiz ID) */
+ entityId: string;
+ /** Entity type (e.g., 'note', 'quiz', 'profile') */
+ entityType: string;
+ /** Local (client) data that was being sent */
+ localData: unknown;
+ /** Server's current data */
+ serverData: unknown;
+ /** Client's last known version number */
+ localVersion?: number;
+ /** Server's current version number */
+ serverVersion?: number;
+ /** Timestamp when client mutation was created */
+ clientTimestamp: number;
+ /** Timestamp when server data was fetched */
+ serverTimestamp: number;
+ /** API endpoint that triggered the conflict */
+ endpoint: string;
+ /** HTTP method (PUT, PATCH, POST, DELETE) */
+ method: string;
+ /** When the conflict was detected */
+ detectedAt: number;
+}
+
+/**
+ * Result of resolving a conflict
+ */
+export interface ConflictResolution {
+ conflictId: string;
+ choice: ConflictResolutionChoice;
+ resolvedData: unknown;
+ resolvedAt: number;
+}
+
+interface ConflictStoreState {
+ /** Queue of unresolved conflicts */
+ conflicts: ConflictData[];
+ /** Currently displayed conflict (for modal) */
+ activeConflict: ConflictData | null;
+ /** Whether the resolution modal is visible */
+ isModalVisible: boolean;
+ /** History of resolved conflicts for debugging/analytics */
+ resolutionHistory: ConflictResolution[];
+ /** Whether a resolution is in progress */
+ isResolving: boolean;
+
+ // Actions
+ addConflict: (conflict: ConflictData) => void;
+ removeConflict: (conflictId: string) => void;
+ clearAllConflicts: () => void;
+ showModal: (conflict?: ConflictData) => void;
+ hideModal: () => void;
+ resolveConflict: (
+ conflictId: string,
+ choice: ConflictResolutionChoice,
+ mergedData?: unknown
+ ) => Promise;
+ getConflictById: (conflictId: string) => ConflictData | undefined;
+ getPendingCount: () => number;
+}
+
+export const useConflictStore = create()(
+ subscribeWithSelector((set, get) => ({
+ conflicts: [],
+ activeConflict: null,
+ isModalVisible: false,
+ resolutionHistory: [],
+ isResolving: false,
+
+ addConflict: (conflict: ConflictData) => {
+ const prevState = get();
+ const shouldShowModal = !prevState.isModalVisible && prevState.conflicts.length === 0;
+
+ set(state => {
+ // Avoid duplicates for the same entity
+ const exists = state.conflicts.some(
+ c => c.entityId === conflict.entityId && c.entityType === conflict.entityType
+ );
+ if (exists) {
+ // Replace existing conflict with newer one
+ return {
+ conflicts: state.conflicts.map(c =>
+ c.entityId === conflict.entityId && c.entityType === conflict.entityType
+ ? conflict
+ : c
+ ),
+ };
+ }
+ return { conflicts: [...state.conflicts, conflict] };
+ });
+
+ // Auto-show modal for the first conflict if not already showing
+ if (shouldShowModal) {
+ set({ activeConflict: conflict, isModalVisible: true });
+ }
+
+ logger.info(`Conflict added: ${conflict.entityType}/${conflict.entityId}`);
+ },
+
+ removeConflict: (conflictId: string) => {
+ set(state => ({
+ conflicts: state.conflicts.filter(c => c.id !== conflictId),
+ activeConflict: state.activeConflict?.id === conflictId ? null : state.activeConflict,
+ }));
+ },
+
+ clearAllConflicts: () => {
+ set({
+ conflicts: [],
+ activeConflict: null,
+ isModalVisible: false,
+ });
+ },
+
+ showModal: (conflict?: ConflictData) => {
+ const state = get();
+ const conflictToShow = conflict ?? state.conflicts[0] ?? null;
+ set({
+ activeConflict: conflictToShow,
+ isModalVisible: conflictToShow !== null,
+ });
+ },
+
+ hideModal: () => {
+ set({ isModalVisible: false });
+ },
+
+ resolveConflict: async (
+ conflictId: string,
+ choice: ConflictResolutionChoice,
+ mergedData?: unknown
+ ) => {
+ const state = get();
+ const conflict = state.conflicts.find(c => c.id === conflictId);
+
+ if (!conflict) {
+ logger.warn(`Conflict not found: ${conflictId}`);
+ return;
+ }
+
+ set({ isResolving: true });
+
+ try {
+ // Determine which data to send
+ let resolvedData: unknown;
+ switch (choice) {
+ case 'local':
+ resolvedData = conflict.localData;
+ break;
+ case 'server':
+ resolvedData = conflict.serverData;
+ break;
+ case 'merge':
+ resolvedData = mergedData ?? conflict.localData;
+ break;
+ }
+
+ // If user chose server version, no need to sync - just accept it
+ if (choice !== 'server') {
+ // Re-send the mutation with force flag to override server version
+ await apiClient({
+ method: conflict.method.toLowerCase(),
+ url: conflict.endpoint,
+ data: resolvedData,
+ headers: {
+ 'X-Force-Override': 'true',
+ 'X-Conflict-Resolution': choice,
+ 'X-Server-Version': String(conflict.serverVersion ?? 0),
+ },
+ });
+ }
+
+ // Record resolution
+ const resolution: ConflictResolution = {
+ conflictId,
+ choice,
+ resolvedData,
+ resolvedAt: Date.now(),
+ };
+
+ set(state => ({
+ conflicts: state.conflicts.filter(c => c.id !== conflictId),
+ resolutionHistory: [...state.resolutionHistory.slice(-49), resolution],
+ isResolving: false,
+ }));
+
+ // Show next conflict if any
+ const remainingConflicts = get().conflicts;
+ if (remainingConflicts.length > 0) {
+ set({
+ activeConflict: remainingConflicts[0],
+ isModalVisible: true,
+ });
+ } else {
+ set({
+ activeConflict: null,
+ isModalVisible: false,
+ });
+ }
+
+ logger.info(
+ `Conflict resolved: ${conflict.entityType}/${conflict.entityId} with choice: ${choice}`
+ );
+ } catch (error) {
+ logger.error('Failed to resolve conflict:', error);
+ set({ isResolving: false });
+ throw error;
+ }
+ },
+
+ getConflictById: (conflictId: string) => {
+ return get().conflicts.find(c => c.id === conflictId);
+ },
+
+ getPendingCount: () => {
+ return get().conflicts.length;
+ },
+ }))
+);
+
+// Selector hooks for common use cases
+export const useActiveConflict = () => useConflictStore(state => state.activeConflict);
+
+export const useConflictModalVisible = () => useConflictStore(state => state.isModalVisible);
+
+export const usePendingConflictsCount = () => useConflictStore(state => state.conflicts.length);
+
+export const useIsResolvingConflict = () => useConflictStore(state => state.isResolving);
+
+export default useConflictStore;