+
+
+ {t('footer.copyright', { year: new Date().getFullYear() })}
+
+
+ {t('footer.madeWith')}
+
+ {t('footer.andPride')}
+
+
Date: Mon, 15 Sep 2025 20:36:49 +0100
Subject: [PATCH 07/15] Sync branchs (#15)
* feat: add payload cms
* Update Footer.tsx
* Update pt.json
* Update en.json
* Update fr.json
* Update es.json
* Update 002_update_homepage_features_custom_emojis.sql
* Delete cms directory
* chore: clinning up supabase old migrations
* Add schemaa
* chore: types justs
* Revert "Login pages corrections, and othes things"
---------
Co-authored-by: Marcelo Santos
---
.gitignore | 4 +
cms/.env.example | 3 +
cms/package-lock.json | 6908 +++++++++++++++++
cms/package.json | 29 +
cms/payload.config.ts | 28 +
cms/src/collections/Media.ts | 12 +
cms/src/collections/Posts.ts | 71 +
cms/src/collections/Users.ts | 13 +
cms/tsconfig.json | 13 +
src/components/Footer.tsx | 2 +-
src/locales/en.json | 2 +-
src/locales/es.json | 2 +-
src/locales/fr.json | 2 +-
src/locales/pt.json | 2 +-
src/pages/Blog.tsx | 81 +-
...update_homepage_features_custom_emojis.sql | 27 +-
...20250731120000-create-blog-posts-table.sql | 19 +
17 files changed, 7123 insertions(+), 95 deletions(-)
create mode 100644 cms/.env.example
create mode 100644 cms/package-lock.json
create mode 100644 cms/package.json
create mode 100644 cms/payload.config.ts
create mode 100644 cms/src/collections/Media.ts
create mode 100644 cms/src/collections/Posts.ts
create mode 100644 cms/src/collections/Users.ts
create mode 100644 cms/tsconfig.json
create mode 100644 supabase/migrations/20250731120000-create-blog-posts-table.sql
diff --git a/.gitignore b/.gitignore
index d362a7a..c9ffff9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,7 @@ dist-ssr
*.sw?
supabase/.temp
+
+.env
+cms/.env
+
diff --git a/cms/.env.example b/cms/.env.example
new file mode 100644
index 0000000..97f8877
--- /dev/null
+++ b/cms/.env.example
@@ -0,0 +1,3 @@
+PAYLOAD_SECRET=replace_with_long_random_secret
+DATABASE_URL=postgresql://postgres:password@localhost:5432/postgres
+PAYLOAD_PUBLIC_SERVER_URL=https://cms.monynha.com
diff --git a/cms/package-lock.json b/cms/package-lock.json
new file mode 100644
index 0000000..8af692a
--- /dev/null
+++ b/cms/package-lock.json
@@ -0,0 +1,6908 @@
+{
+ "name": "monynha-cms",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "monynha-cms",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "@payloadcms/db-postgres": "^3.51.0",
+ "@payloadcms/richtext-lexical": "^3.51.0",
+ "cross-env": "^10.0.0",
+ "dotenv": "^17.2.1",
+ "payload": "^3.51.0",
+ "pg": "^8.16.3",
+ "ts-node": "^10.9.2",
+ "typescript": "^5.9.2"
+ },
+ "devDependencies": {
+ "@types/node": "^24.3.0"
+ }
+ },
+ "node_modules/@apidevtools/json-schema-ref-parser": {
+ "version": "11.9.3",
+ "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz",
+ "integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jsdevtools/ono": "^7.1.3",
+ "@types/json-schema": "^7.0.15",
+ "js-yaml": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/philsturgeon"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
+ "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.3",
+ "@babel/types": "^7.28.2",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.30",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
+ "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
+ "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz",
+ "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
+ "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.3",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.2",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.2",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
+ "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@borewit/text-codec": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz",
+ "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@date-fns/tz": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.2.0.tgz",
+ "integrity": "sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==",
+ "license": "MIT"
+ },
+ "node_modules/@dnd-kit/accessibility": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
+ "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/core": {
+ "version": "6.0.8",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.0.8.tgz",
+ "integrity": "sha512-lYaoP8yHTQSLlZe6Rr9qogouGUz9oRUj4AHhDQGQzq/hqaJRpFo65X+JKsdHf8oUFBzx5A+SJPUvxAwTF2OabA==",
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/accessibility": "^3.0.0",
+ "@dnd-kit/utilities": "^3.2.1",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/sortable": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-7.0.2.tgz",
+ "integrity": "sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==",
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.0",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.0.7",
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/utilities": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
+ "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@drizzle-team/brocli": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz",
+ "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
+ "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
+ "license": "MIT"
+ },
+ "node_modules/@epic-web/invariant": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
+ "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
+ "license": "MIT"
+ },
+ "node_modules/@esbuild-kit/core-utils": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz",
+ "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==",
+ "deprecated": "Merged into tsx: https://tsx.is",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.18.20",
+ "source-map-support": "^0.5.21"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+ "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+ "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+ "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+ "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+ "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+ "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+ "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+ "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+ "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+ "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+ "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+ "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+ "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+ "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+ "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+ "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+ "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+ "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+ "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+ "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
+ "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.18.20",
+ "@esbuild/android-arm64": "0.18.20",
+ "@esbuild/android-x64": "0.18.20",
+ "@esbuild/darwin-arm64": "0.18.20",
+ "@esbuild/darwin-x64": "0.18.20",
+ "@esbuild/freebsd-arm64": "0.18.20",
+ "@esbuild/freebsd-x64": "0.18.20",
+ "@esbuild/linux-arm": "0.18.20",
+ "@esbuild/linux-arm64": "0.18.20",
+ "@esbuild/linux-ia32": "0.18.20",
+ "@esbuild/linux-loong64": "0.18.20",
+ "@esbuild/linux-mips64el": "0.18.20",
+ "@esbuild/linux-ppc64": "0.18.20",
+ "@esbuild/linux-riscv64": "0.18.20",
+ "@esbuild/linux-s390x": "0.18.20",
+ "@esbuild/linux-x64": "0.18.20",
+ "@esbuild/netbsd-x64": "0.18.20",
+ "@esbuild/openbsd-x64": "0.18.20",
+ "@esbuild/sunos-x64": "0.18.20",
+ "@esbuild/win32-arm64": "0.18.20",
+ "@esbuild/win32-ia32": "0.18.20",
+ "@esbuild/win32-x64": "0.18.20"
+ }
+ },
+ "node_modules/@esbuild-kit/esm-loader": {
+ "version": "2.6.5",
+ "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz",
+ "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==",
+ "deprecated": "Merged into tsx: https://tsx.is",
+ "license": "MIT",
+ "dependencies": {
+ "@esbuild-kit/core-utils": "^3.3.2",
+ "get-tsconfig": "^4.7.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
+ "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
+ "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
+ "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
+ "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
+ "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
+ "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
+ "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
+ "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
+ "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
+ "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
+ "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
+ "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
+ "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
+ "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
+ "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
+ "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
+ "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
+ "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
+ "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
+ "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
+ "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@faceless-ui/modal": {
+ "version": "3.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/@faceless-ui/modal/-/modal-3.0.0-beta.2.tgz",
+ "integrity": "sha512-UmXvz7Iw3KMO4Pm3llZczU4uc5pPQDb6rdqwoBvYDFgWvkraOAHKx0HxSZgwqQvqOhn8joEFBfFp6/Do2562ow==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/jacobsfletch"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "body-scroll-lock": "4.0.0-beta.0",
+ "focus-trap": "7.5.4",
+ "react-transition-group": "4.4.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc.0"
+ }
+ },
+ "node_modules/@faceless-ui/scroll-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@faceless-ui/scroll-info/-/scroll-info-2.0.0.tgz",
+ "integrity": "sha512-BkyJ9OQ4bzpKjE3UhI8BhcG36ZgfB4run8TmlaR4oMFUbl59dfyarNfjveyimrxIso9RhFEja/AJ5nQmbcR9hw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/jacobsfletch"
+ }
+ ],
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@faceless-ui/window-info": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@faceless-ui/window-info/-/window-info-3.0.1.tgz",
+ "integrity": "sha512-uPjdJYE/j7hqVNelE9CRUNOeXuXDdPxR4DMe+oz3xwyZi2Y4CxsfpfdPTqqwmNAZa1P33O+ZiCyIkBEeNed0kw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/jacobsfletch"
+ }
+ ],
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
+ "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz",
+ "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.3",
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/react": {
+ "version": "0.27.15",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.15.tgz",
+ "integrity": "sha512-0LGxhBi3BB1DwuSNQAmuaSuertFzNAerlMdPbotjTVnvPtdOs7CkrHLaev5NIXemhzDXNC0tFzuseut7cWA5mw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.1.5",
+ "@floating-ui/utils": "^0.2.10",
+ "tabbable": "^6.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=17.0.0",
+ "react-dom": ">=17.0.0"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.5.tgz",
+ "integrity": "sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.7.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
+ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+ "license": "MIT"
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz",
+ "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz",
+ "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz",
+ "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz",
+ "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz",
+ "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz",
+ "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz",
+ "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz",
+ "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz",
+ "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz",
+ "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz",
+ "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz",
+ "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz",
+ "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz",
+ "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz",
+ "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz",
+ "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz",
+ "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz",
+ "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz",
+ "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.4.4"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz",
+ "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz",
+ "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz",
+ "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.30",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
+ "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@jsdevtools/ono": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
+ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
+ "license": "MIT"
+ },
+ "node_modules/@lexical/clipboard": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/clipboard/-/clipboard-0.28.0.tgz",
+ "integrity": "sha512-LYqion+kAwFQJStA37JAEMxTL/m1WlZbotDfM/2WuONmlO0yWxiyRDI18oeCwhBD6LQQd9c3Ccxp9HFwUG1AVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/html": "0.28.0",
+ "@lexical/list": "0.28.0",
+ "@lexical/selection": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/code": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/code/-/code-0.28.0.tgz",
+ "integrity": "sha512-9LOKSWdRhxqAKRq5yveNC21XKtW4h2rmFNTucwMWZ9vLu9xteOHEwZdO1Qv82PFUmgCpAhg6EntmnZu9xD3K7Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0",
+ "prismjs": "^1.30.0"
+ }
+ },
+ "node_modules/@lexical/devtools-core": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/devtools-core/-/devtools-core-0.28.0.tgz",
+ "integrity": "sha512-Fk4itAjZ+MqTYXN84aE5RDf+wQX67N5nyo3JVxQTFZGAghx7Ux1xLWHB25zzD0YfjMtJ0NQROAbE3xdecZzxcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/html": "0.28.0",
+ "@lexical/link": "0.28.0",
+ "@lexical/mark": "0.28.0",
+ "@lexical/table": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ },
+ "peerDependencies": {
+ "react": ">=17.x",
+ "react-dom": ">=17.x"
+ }
+ },
+ "node_modules/@lexical/dragon": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/dragon/-/dragon-0.28.0.tgz",
+ "integrity": "sha512-T6T8YaHnhU863ruuqmRHTLUYa8sfg/ArYcrnNGZGfpvvFTfFjpWb/ELOvOWo8N6Y/4fnSLjQ20aXexVW1KcTBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/hashtag": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/hashtag/-/hashtag-0.28.0.tgz",
+ "integrity": "sha512-zcqX9Qna4lj96bAUfwSQSVEhYQ0O5erSjrIhOVqEgeQ5ubz0EvqnnMbbwNHIb2n6jzSwAvpD/3UZJZtolh+zVg==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/headless": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/headless/-/headless-0.28.0.tgz",
+ "integrity": "sha512-btcaTfw9I/xQ/XYom6iKWgsPecmRawGd/5jOhP7QDtLUp7gxgM7/kiCZFYa8jDJO6j20rXuWTkc81ynVpKvjow==",
+ "license": "MIT",
+ "dependencies": {
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/history": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/history/-/history-0.28.0.tgz",
+ "integrity": "sha512-CHzDxaGDn6qCFFhU0YKP1B8sgEb++0Ksqsj6BfDL/6TMxoLNQwRQhP3BUNNXl1kvUhxTQZgk3b9MjJZRaFKG9Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/html": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/html/-/html-0.28.0.tgz",
+ "integrity": "sha512-ayb0FPxr55Ko99/d9ewbfrApul4L0z+KpU2ZG03im7EvUPVLyIGLx4S0QguMDvQh0Vu+eJ7/EESuonDs5BCe3A==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/selection": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/link": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/link/-/link-0.28.0.tgz",
+ "integrity": "sha512-T5VKxpOnML5DcXv2lW3Le0vjNlcbdohZjS9f6PAvm6eX8EzBKDpLQCopr1/0KGdlLd1QrzQsykQrdU7ieC4LRg==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/list": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/list/-/list-0.28.0.tgz",
+ "integrity": "sha512-3a8QcZ75n2TLxP+xkSPJ2V15jsysMLMe0YoObG+ew/sioVelIU8GciYsWBo5GgQmwSzJNQJeK5cJ9p1b71z2cg==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/selection": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/mark": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/mark/-/mark-0.28.0.tgz",
+ "integrity": "sha512-v5PzmTACsJrw3GvNZy2rgPxrNn9InLvLFoKqrSlNhhyvYNIAcuC4KVy00LKLja43Gw/fuB3QwKohYfAtM3yR3g==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/markdown": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/markdown/-/markdown-0.28.0.tgz",
+ "integrity": "sha512-F3JXClqN4cjmXYLDK0IztxkbZuqkqS/AVbxnhGvnDYHQ9Gp8l7BonczhOiPwmJCDubJrAACP0L9LCqyt0jDRFw==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/code": "0.28.0",
+ "@lexical/link": "0.28.0",
+ "@lexical/list": "0.28.0",
+ "@lexical/rich-text": "0.28.0",
+ "@lexical/text": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/offset": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/offset/-/offset-0.28.0.tgz",
+ "integrity": "sha512-/SMDQgBPeWM936t04mtH6UAn3xAjP/meu9q136bcT3S7p7V8ew9JfNp9aznTPTx+2W3brJORAvUow7Xn1fSHmw==",
+ "license": "MIT",
+ "dependencies": {
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/overflow": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/overflow/-/overflow-0.28.0.tgz",
+ "integrity": "sha512-ppmhHXEZVicBm05w9EVflzwFavTVNAe4q0bkabWUeW0IoCT3Vg2A3JT7PC9ypmp+mboUD195foFEr1BBSv1Y8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/plain-text": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/plain-text/-/plain-text-0.28.0.tgz",
+ "integrity": "sha512-Jj2dCMDEfRuVetfDKcUes8J5jvAfZrLnILFlHxnu7y+lC+7R/NR403DYb3NJ8H7+lNiH1K15+U2K7ewbjxS6KQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/clipboard": "0.28.0",
+ "@lexical/selection": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/react": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/react/-/react-0.28.0.tgz",
+ "integrity": "sha512-dWPnxrKrbQFjNqExqnaAsV0UEUgw/5M1ZYRWd5FGBGjHqVTCaX2jNHlKLMA68Od0VPIoOX2Zy1TYZ8ZKtsj5Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/devtools-core": "0.28.0",
+ "@lexical/dragon": "0.28.0",
+ "@lexical/hashtag": "0.28.0",
+ "@lexical/history": "0.28.0",
+ "@lexical/link": "0.28.0",
+ "@lexical/list": "0.28.0",
+ "@lexical/mark": "0.28.0",
+ "@lexical/markdown": "0.28.0",
+ "@lexical/overflow": "0.28.0",
+ "@lexical/plain-text": "0.28.0",
+ "@lexical/rich-text": "0.28.0",
+ "@lexical/table": "0.28.0",
+ "@lexical/text": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "@lexical/yjs": "0.28.0",
+ "lexical": "0.28.0",
+ "react-error-boundary": "^3.1.4"
+ },
+ "peerDependencies": {
+ "react": ">=17.x",
+ "react-dom": ">=17.x"
+ }
+ },
+ "node_modules/@lexical/react/node_modules/react-error-boundary": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
+ "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ },
+ "peerDependencies": {
+ "react": ">=16.13.1"
+ }
+ },
+ "node_modules/@lexical/rich-text": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/rich-text/-/rich-text-0.28.0.tgz",
+ "integrity": "sha512-y+vUWI+9uFupIb9UvssKU/DKcT9dFUZuQBu7utFkLadxCNyXQHeRjxzjzmvFiM3DBV0guPUDGu5VS5TPnIA+OA==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/clipboard": "0.28.0",
+ "@lexical/selection": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/selection": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/selection/-/selection-0.28.0.tgz",
+ "integrity": "sha512-AJDi67Nsexyejzp4dEQSVoPov4P+FJ0t1v6DxUU+YmcvV56QyJQi6ue0i/xd8unr75ZufzLsAC0cDJJCEI7QDA==",
+ "license": "MIT",
+ "dependencies": {
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/table": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/table/-/table-0.28.0.tgz",
+ "integrity": "sha512-HMPCwXdj0sRWdlDzsHcNWRgbeKbEhn3L8LPhFnTq7q61gZ4YW2umdmuvQFKnIBcKq49drTH8cUwZoIwI8+AEEw==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/clipboard": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/text": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/text/-/text-0.28.0.tgz",
+ "integrity": "sha512-PT/A2RZv+ktn7SG/tJkOpGlYE6zjOND59VtRHnV/xciZ+jEJVaqAHtWjhbWibAIZQAkv/O7UouuDqzDaNTSGAA==",
+ "license": "MIT",
+ "dependencies": {
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/utils": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/utils/-/utils-0.28.0.tgz",
+ "integrity": "sha512-Qw00DjkS1nRK7DLSgqJpJ77Ti2AuiOQ6m5eM38YojoWXkVmoxqKAUMaIbVNVKqjFgrQvKFF46sXxIJPbUQkB0w==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/list": "0.28.0",
+ "@lexical/selection": "0.28.0",
+ "@lexical/table": "0.28.0",
+ "lexical": "0.28.0"
+ }
+ },
+ "node_modules/@lexical/yjs": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@lexical/yjs/-/yjs-0.28.0.tgz",
+ "integrity": "sha512-rKHpUEd3nrvMY7ghmOC0AeGSYT7YIviba+JViaOzrCX4/Wtv5C/3Sl7Io12Z9k+s1BKmy7C28bOdQHvRWaD7vQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/offset": "0.28.0",
+ "@lexical/selection": "0.28.0",
+ "lexical": "0.28.0"
+ },
+ "peerDependencies": {
+ "yjs": ">=13.5.22"
+ }
+ },
+ "node_modules/@monaco-editor/loader": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.5.0.tgz",
+ "integrity": "sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==",
+ "license": "MIT",
+ "dependencies": {
+ "state-local": "^1.0.6"
+ }
+ },
+ "node_modules/@monaco-editor/react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
+ "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@monaco-editor/loader": "^1.5.0"
+ },
+ "peerDependencies": {
+ "monaco-editor": ">= 0.25.0 < 1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.4.6.tgz",
+ "integrity": "sha512-yHDKVTcHrZy/8TWhj0B23ylKv5ypocuCwey9ZqPyv4rPdUdRzpGCkSi03t04KBPyU96kxVtUqx6O3nE1kpxASQ==",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.6.tgz",
+ "integrity": "sha512-667R0RTP4DwxzmrqTs4Lr5dcEda9OxuZsVFsjVtxVMVhzSpo6nLclXejJVfQo2/g7/Z9qF3ETDmN3h65mTjpTQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.4.6.tgz",
+ "integrity": "sha512-KMSFoistFkaiQYVQQnaU9MPWtp/3m0kn2Xed1Ces5ll+ag1+rlac20sxG+MqhH2qYWX1O2GFOATQXEyxKiIscg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.4.6.tgz",
+ "integrity": "sha512-PnOx1YdO0W7m/HWFeYd2A6JtBO8O8Eb9h6nfJia2Dw1sRHoHpNf6lN1U4GKFRzRDBi9Nq2GrHk9PF3Vmwf7XVw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.4.6.tgz",
+ "integrity": "sha512-XBbuQddtY1p5FGPc2naMO0kqs4YYtLYK/8aPausI5lyOjr4J77KTG9mtlU4P3NwkLI1+OjsPzKVvSJdMs3cFaw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.4.6.tgz",
+ "integrity": "sha512-+WTeK7Qdw82ez3U9JgD+igBAP75gqZ1vbK6R8PlEEuY0OIe5FuYXA4aTjL811kWPf7hNeslD4hHK2WoM9W0IgA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.4.6.tgz",
+ "integrity": "sha512-XP824mCbgQsK20jlXKrUpZoh/iO3vUWhMpxCz8oYeagoiZ4V0TQiKy0ASji1KK6IAe3DYGfj5RfKP6+L2020OQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.4.6.tgz",
+ "integrity": "sha512-FxrsenhUz0LbgRkNWx6FRRJIPe/MI1JRA4W4EPd5leXO00AZ6YU8v5vfx4MDXTvN77lM/EqsE3+6d2CIeF5NYg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.4.6.tgz",
+ "integrity": "sha512-T4ufqnZ4u88ZheczkBTtOF+eKaM14V8kbjud/XrAakoM5DKQWjW09vD6B9fsdsWS2T7D5EY31hRHdta7QKWOng==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@payloadcms/db-postgres": {
+ "version": "3.51.0",
+ "resolved": "https://registry.npmjs.org/@payloadcms/db-postgres/-/db-postgres-3.51.0.tgz",
+ "integrity": "sha512-EuRKQqDQpFiYAGq4fkqWZMOMf/de9HPA62iA4CAjxHQj4Y9LWSMGi7yXWwEOWMXrtabHEZv0DZarR+gvkzNdZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@payloadcms/drizzle": "3.51.0",
+ "@types/pg": "8.10.2",
+ "console-table-printer": "2.12.1",
+ "drizzle-kit": "0.31.4",
+ "drizzle-orm": "0.44.2",
+ "pg": "8.16.3",
+ "prompts": "2.4.2",
+ "to-snake-case": "1.0.0",
+ "uuid": "10.0.0"
+ },
+ "peerDependencies": {
+ "payload": "3.51.0"
+ }
+ },
+ "node_modules/@payloadcms/drizzle": {
+ "version": "3.51.0",
+ "resolved": "https://registry.npmjs.org/@payloadcms/drizzle/-/drizzle-3.51.0.tgz",
+ "integrity": "sha512-D56zRG23B/PKIH0HRNQRsoljIjMhDqNB3vZq4mQdS3Kt0jqsjkGweh+N1jHJjR591wUjrM1Y627ar0H0kBsFNA==",
+ "license": "MIT",
+ "dependencies": {
+ "console-table-printer": "2.12.1",
+ "dequal": "2.0.3",
+ "drizzle-orm": "0.44.2",
+ "prompts": "2.4.2",
+ "to-snake-case": "1.0.0",
+ "uuid": "9.0.0"
+ },
+ "peerDependencies": {
+ "payload": "3.51.0"
+ }
+ },
+ "node_modules/@payloadcms/drizzle/node_modules/uuid": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
+ "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/@payloadcms/graphql": {
+ "version": "3.51.0",
+ "resolved": "https://registry.npmjs.org/@payloadcms/graphql/-/graphql-3.51.0.tgz",
+ "integrity": "sha512-sFhQl9d6oi/9cJDvdvQVF0Nk1wgvjdFjouS9hOwAY6IZ+8TR5I3z9ynmccCgCkeiALgM/JyQTqSe34eY7x2iGA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "graphql-scalars": "1.22.2",
+ "pluralize": "8.0.0",
+ "ts-essentials": "10.0.3",
+ "tsx": "4.19.2"
+ },
+ "bin": {
+ "payload-graphql": "bin.js"
+ },
+ "peerDependencies": {
+ "graphql": "^16.8.1",
+ "payload": "3.51.0"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
+ "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/android-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
+ "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/android-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
+ "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/android-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
+ "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
+ "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/darwin-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
+ "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
+ "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/linux-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
+ "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/linux-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
+ "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/linux-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
+ "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/linux-loong64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
+ "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
+ "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
+ "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
+ "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/linux-s390x": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
+ "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/linux-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
+ "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/sunos-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
+ "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/win32-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
+ "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/win32-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
+ "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/@esbuild/win32-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
+ "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/esbuild": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
+ "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.23.1",
+ "@esbuild/android-arm": "0.23.1",
+ "@esbuild/android-arm64": "0.23.1",
+ "@esbuild/android-x64": "0.23.1",
+ "@esbuild/darwin-arm64": "0.23.1",
+ "@esbuild/darwin-x64": "0.23.1",
+ "@esbuild/freebsd-arm64": "0.23.1",
+ "@esbuild/freebsd-x64": "0.23.1",
+ "@esbuild/linux-arm": "0.23.1",
+ "@esbuild/linux-arm64": "0.23.1",
+ "@esbuild/linux-ia32": "0.23.1",
+ "@esbuild/linux-loong64": "0.23.1",
+ "@esbuild/linux-mips64el": "0.23.1",
+ "@esbuild/linux-ppc64": "0.23.1",
+ "@esbuild/linux-riscv64": "0.23.1",
+ "@esbuild/linux-s390x": "0.23.1",
+ "@esbuild/linux-x64": "0.23.1",
+ "@esbuild/netbsd-x64": "0.23.1",
+ "@esbuild/openbsd-arm64": "0.23.1",
+ "@esbuild/openbsd-x64": "0.23.1",
+ "@esbuild/sunos-x64": "0.23.1",
+ "@esbuild/win32-arm64": "0.23.1",
+ "@esbuild/win32-ia32": "0.23.1",
+ "@esbuild/win32-x64": "0.23.1"
+ }
+ },
+ "node_modules/@payloadcms/graphql/node_modules/tsx": {
+ "version": "4.19.2",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz",
+ "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "esbuild": "~0.23.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/@payloadcms/next": {
+ "version": "3.51.0",
+ "resolved": "https://registry.npmjs.org/@payloadcms/next/-/next-3.51.0.tgz",
+ "integrity": "sha512-kLxozeWTp2ON2cWC4y6PbuU2WdG0REAilDRO/i7IvleWpKx5J8OJc80dka0VMsQLYrb0DgzLt3gOhQw1A+TvHw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@dnd-kit/core": "6.0.8",
+ "@payloadcms/graphql": "3.51.0",
+ "@payloadcms/translations": "3.51.0",
+ "@payloadcms/ui": "3.51.0",
+ "busboy": "^1.6.0",
+ "dequal": "2.0.3",
+ "file-type": "19.3.0",
+ "graphql-http": "^1.22.0",
+ "graphql-playground-html": "1.6.30",
+ "http-status": "2.1.0",
+ "path-to-regexp": "6.3.0",
+ "qs-esm": "7.0.2",
+ "sass": "1.77.4",
+ "uuid": "10.0.0"
+ },
+ "engines": {
+ "node": "^18.20.2 || >=20.9.0"
+ },
+ "peerDependencies": {
+ "graphql": "^16.8.1",
+ "next": "^15.2.3",
+ "payload": "3.51.0"
+ }
+ },
+ "node_modules/@payloadcms/richtext-lexical": {
+ "version": "3.51.0",
+ "resolved": "https://registry.npmjs.org/@payloadcms/richtext-lexical/-/richtext-lexical-3.51.0.tgz",
+ "integrity": "sha512-EJxlJqRBW3RumBtIyGtinBjh/EfuXMKrAEWfTcmyvq47fBZ0NWPsEUsKkmRrCfmJhc/yA+jSrnHHMWF+Wde4GA==",
+ "license": "MIT",
+ "dependencies": {
+ "@lexical/headless": "0.28.0",
+ "@lexical/html": "0.28.0",
+ "@lexical/link": "0.28.0",
+ "@lexical/list": "0.28.0",
+ "@lexical/mark": "0.28.0",
+ "@lexical/react": "0.28.0",
+ "@lexical/rich-text": "0.28.0",
+ "@lexical/selection": "0.28.0",
+ "@lexical/table": "0.28.0",
+ "@lexical/utils": "0.28.0",
+ "@payloadcms/translations": "3.51.0",
+ "@payloadcms/ui": "3.51.0",
+ "@types/uuid": "10.0.0",
+ "acorn": "8.12.1",
+ "bson-objectid": "2.0.4",
+ "csstype": "3.1.3",
+ "dequal": "2.0.3",
+ "escape-html": "1.0.3",
+ "jsox": "1.2.121",
+ "lexical": "0.28.0",
+ "mdast-util-from-markdown": "2.0.2",
+ "mdast-util-mdx-jsx": "3.1.3",
+ "micromark-extension-mdx-jsx": "3.0.1",
+ "qs-esm": "7.0.2",
+ "react-error-boundary": "4.1.2",
+ "ts-essentials": "10.0.3",
+ "uuid": "10.0.0"
+ },
+ "engines": {
+ "node": "^18.20.2 || >=20.9.0"
+ },
+ "peerDependencies": {
+ "@faceless-ui/modal": "3.0.0-beta.2",
+ "@faceless-ui/scroll-info": "2.0.0",
+ "@payloadcms/next": "3.51.0",
+ "payload": "3.51.0",
+ "react": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020",
+ "react-dom": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020"
+ }
+ },
+ "node_modules/@payloadcms/richtext-lexical/node_modules/acorn": {
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/@payloadcms/translations": {
+ "version": "3.51.0",
+ "resolved": "https://registry.npmjs.org/@payloadcms/translations/-/translations-3.51.0.tgz",
+ "integrity": "sha512-2spku7152o4xLOlDJLVauclmvUT9wrzdTnYTKN2tZTkAChdMJPNVczeuXBazBwVR95mx3p0HFtN37GiEEcfhlQ==",
+ "license": "MIT",
+ "dependencies": {
+ "date-fns": "4.1.0"
+ }
+ },
+ "node_modules/@payloadcms/ui": {
+ "version": "3.51.0",
+ "resolved": "https://registry.npmjs.org/@payloadcms/ui/-/ui-3.51.0.tgz",
+ "integrity": "sha512-DF+0VnFagPVQenufoC871zkMuoqpDDUi1PcVrIchaQIr5EnlHIw9tCzJw3Fyi8stjZiAFEdj1RKqVI3iAr/hNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@date-fns/tz": "1.2.0",
+ "@dnd-kit/core": "6.0.8",
+ "@dnd-kit/sortable": "7.0.2",
+ "@dnd-kit/utilities": "3.2.2",
+ "@faceless-ui/modal": "3.0.0-beta.2",
+ "@faceless-ui/scroll-info": "2.0.0",
+ "@faceless-ui/window-info": "3.0.1",
+ "@monaco-editor/react": "4.7.0",
+ "@payloadcms/translations": "3.51.0",
+ "bson-objectid": "2.0.4",
+ "date-fns": "4.1.0",
+ "dequal": "2.0.3",
+ "md5": "2.3.0",
+ "object-to-formdata": "4.5.1",
+ "qs-esm": "7.0.2",
+ "react-datepicker": "7.6.0",
+ "react-image-crop": "10.1.8",
+ "react-select": "5.9.0",
+ "scheduler": "0.25.0",
+ "sonner": "^1.7.2",
+ "ts-essentials": "10.0.3",
+ "use-context-selector": "2.0.0",
+ "uuid": "10.0.0"
+ },
+ "engines": {
+ "node": "^18.20.2 || >=20.9.0"
+ },
+ "peerDependencies": {
+ "next": "^15.2.3",
+ "payload": "3.51.0",
+ "react": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020",
+ "react-dom": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tokenizer/token": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
+ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/acorn": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
+ "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/busboy": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.4.tgz",
+ "integrity": "sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.3.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
+ "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.10.0"
+ }
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/pg": {
+ "version": "8.10.2",
+ "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.2.tgz",
+ "integrity": "sha512-MKFs9P6nJ+LAeHLU3V0cODEOgyThJ3OAnmOlsZsxux6sfQs3HRXR5bBn7xG5DjckEFhTAxsXi7k7cd0pCMxpJw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "pg-protocol": "*",
+ "pg-types": "^4.0.1"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.1.10",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz",
+ "integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+ "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@types/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
+ "license": "MIT"
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
+ "node_modules/atomic-sleep": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/body-scroll-lock": {
+ "version": "4.0.0-beta.0",
+ "resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-4.0.0-beta.0.tgz",
+ "integrity": "sha512-a7tP5+0Mw3YlUJcGAKUqIBkYYGlYxk2fnCasq/FUph1hadxlTRjF+gAcZksxANnaMnALjxEddmSi/H3OR8ugcQ==",
+ "license": "MIT"
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bson-objectid": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/bson-objectid/-/bson-objectid-2.0.4.tgz",
+ "integrity": "sha512-vgnKAUzcDoa+AeyYwXCoHyF2q6u/8H46dxu5JN+4/TZeq/Dlinn0K6GvxsCLb3LHUJl0m/TLiEK31kUwtgocMQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "license": "MIT"
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001735",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz",
+ "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0",
+ "peer": true
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz",
+ "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/console-table-printer": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.12.1.tgz",
+ "integrity": "sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ==",
+ "license": "MIT",
+ "dependencies": {
+ "simple-wcswidth": "^1.0.1"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "license": "MIT"
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+ "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "license": "MIT"
+ },
+ "node_modules/croner": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/croner/-/croner-9.1.0.tgz",
+ "integrity": "sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0"
+ }
+ },
+ "node_modules/cross-env": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.0.0.tgz",
+ "integrity": "sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@epic-web/invariant": "^1.0.0",
+ "cross-spawn": "^7.0.6"
+ },
+ "bin": {
+ "cross-env": "dist/bin/cross-env.js",
+ "cross-env-shell": "dist/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/cssfilter": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
+ "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "license": "MIT"
+ },
+ "node_modules/dataloader": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz",
+ "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==",
+ "license": "MIT"
+ },
+ "node_modules/date-fns": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
+ "node_modules/dateformat": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
+ "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
+ "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "17.2.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
+ "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/drizzle-kit": {
+ "version": "0.31.4",
+ "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.4.tgz",
+ "integrity": "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==",
+ "license": "MIT",
+ "dependencies": {
+ "@drizzle-team/brocli": "^0.10.2",
+ "@esbuild-kit/esm-loader": "^2.5.5",
+ "esbuild": "^0.25.4",
+ "esbuild-register": "^3.5.0"
+ },
+ "bin": {
+ "drizzle-kit": "bin.cjs"
+ }
+ },
+ "node_modules/drizzle-orm": {
+ "version": "0.44.2",
+ "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.2.tgz",
+ "integrity": "sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@aws-sdk/client-rds-data": ">=3",
+ "@cloudflare/workers-types": ">=4",
+ "@electric-sql/pglite": ">=0.2.0",
+ "@libsql/client": ">=0.10.0",
+ "@libsql/client-wasm": ">=0.10.0",
+ "@neondatabase/serverless": ">=0.10.0",
+ "@op-engineering/op-sqlite": ">=2",
+ "@opentelemetry/api": "^1.4.1",
+ "@planetscale/database": ">=1.13",
+ "@prisma/client": "*",
+ "@tidbcloud/serverless": "*",
+ "@types/better-sqlite3": "*",
+ "@types/pg": "*",
+ "@types/sql.js": "*",
+ "@upstash/redis": ">=1.34.7",
+ "@vercel/postgres": ">=0.8.0",
+ "@xata.io/client": "*",
+ "better-sqlite3": ">=7",
+ "bun-types": "*",
+ "expo-sqlite": ">=14.0.0",
+ "gel": ">=2",
+ "knex": "*",
+ "kysely": "*",
+ "mysql2": ">=2",
+ "pg": ">=8",
+ "postgres": ">=3",
+ "sql.js": ">=1",
+ "sqlite3": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/client-rds-data": {
+ "optional": true
+ },
+ "@cloudflare/workers-types": {
+ "optional": true
+ },
+ "@electric-sql/pglite": {
+ "optional": true
+ },
+ "@libsql/client": {
+ "optional": true
+ },
+ "@libsql/client-wasm": {
+ "optional": true
+ },
+ "@neondatabase/serverless": {
+ "optional": true
+ },
+ "@op-engineering/op-sqlite": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@planetscale/database": {
+ "optional": true
+ },
+ "@prisma/client": {
+ "optional": true
+ },
+ "@tidbcloud/serverless": {
+ "optional": true
+ },
+ "@types/better-sqlite3": {
+ "optional": true
+ },
+ "@types/pg": {
+ "optional": true
+ },
+ "@types/sql.js": {
+ "optional": true
+ },
+ "@upstash/redis": {
+ "optional": true
+ },
+ "@vercel/postgres": {
+ "optional": true
+ },
+ "@xata.io/client": {
+ "optional": true
+ },
+ "better-sqlite3": {
+ "optional": true
+ },
+ "bun-types": {
+ "optional": true
+ },
+ "expo-sqlite": {
+ "optional": true
+ },
+ "gel": {
+ "optional": true
+ },
+ "knex": {
+ "optional": true
+ },
+ "kysely": {
+ "optional": true
+ },
+ "mysql2": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ },
+ "postgres": {
+ "optional": true
+ },
+ "prisma": {
+ "optional": true
+ },
+ "sql.js": {
+ "optional": true
+ },
+ "sqlite3": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
+ "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.9",
+ "@esbuild/android-arm": "0.25.9",
+ "@esbuild/android-arm64": "0.25.9",
+ "@esbuild/android-x64": "0.25.9",
+ "@esbuild/darwin-arm64": "0.25.9",
+ "@esbuild/darwin-x64": "0.25.9",
+ "@esbuild/freebsd-arm64": "0.25.9",
+ "@esbuild/freebsd-x64": "0.25.9",
+ "@esbuild/linux-arm": "0.25.9",
+ "@esbuild/linux-arm64": "0.25.9",
+ "@esbuild/linux-ia32": "0.25.9",
+ "@esbuild/linux-loong64": "0.25.9",
+ "@esbuild/linux-mips64el": "0.25.9",
+ "@esbuild/linux-ppc64": "0.25.9",
+ "@esbuild/linux-riscv64": "0.25.9",
+ "@esbuild/linux-s390x": "0.25.9",
+ "@esbuild/linux-x64": "0.25.9",
+ "@esbuild/netbsd-arm64": "0.25.9",
+ "@esbuild/netbsd-x64": "0.25.9",
+ "@esbuild/openbsd-arm64": "0.25.9",
+ "@esbuild/openbsd-x64": "0.25.9",
+ "@esbuild/openharmony-arm64": "0.25.9",
+ "@esbuild/sunos-x64": "0.25.9",
+ "@esbuild/win32-arm64": "0.25.9",
+ "@esbuild/win32-ia32": "0.25.9",
+ "@esbuild/win32-x64": "0.25.9"
+ }
+ },
+ "node_modules/esbuild-register": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz",
+ "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
+ "peerDependencies": {
+ "esbuild": ">=0.12 <1"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-util-visit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz",
+ "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/fast-copy": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
+ "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==",
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/fast-redact": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
+ "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
+ "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-type": {
+ "version": "19.3.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-19.3.0.tgz",
+ "integrity": "sha512-mROwiKLZf/Kwa/2Rol+OOZQn1eyTkPB3ZTwC0ExY6OLFCbgxHYZvBm7xI77NvfZFMKBsmuXfmLJnD4eEftEhrA==",
+ "license": "MIT",
+ "dependencies": {
+ "strtok3": "^8.0.0",
+ "token-types": "^6.0.0",
+ "uint8array-extras": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/file-type?sponsor=1"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+ "license": "MIT"
+ },
+ "node_modules/focus-trap": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz",
+ "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==",
+ "license": "MIT",
+ "dependencies": {
+ "tabbable": "^6.2.0"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
+ "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/graphql": {
+ "version": "16.11.0",
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz",
+ "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
+ }
+ },
+ "node_modules/graphql-http": {
+ "version": "1.22.4",
+ "resolved": "https://registry.npmjs.org/graphql-http/-/graphql-http-1.22.4.tgz",
+ "integrity": "sha512-OC3ucK988teMf+Ak/O+ZJ0N2ukcgrEurypp8ePyJFWq83VzwRAmHxxr+XxrMpxO/FIwI4a7m/Fzv3tWGJv0wPA==",
+ "license": "MIT",
+ "peer": true,
+ "workspaces": [
+ "implementations/**/*"
+ ],
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "graphql": ">=0.11 <=16"
+ }
+ },
+ "node_modules/graphql-playground-html": {
+ "version": "1.6.30",
+ "resolved": "https://registry.npmjs.org/graphql-playground-html/-/graphql-playground-html-1.6.30.tgz",
+ "integrity": "sha512-tpCujhsJMva4aqE8ULnF7/l3xw4sNRZcSHu+R00VV+W0mfp+Q20Plvcrp+5UXD+2yS6oyCXncA+zoQJQqhGCEw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "xss": "^1.0.6"
+ }
+ },
+ "node_modules/graphql-scalars": {
+ "version": "1.22.2",
+ "resolved": "https://registry.npmjs.org/graphql-scalars/-/graphql-scalars-1.22.2.tgz",
+ "integrity": "sha512-my9FB4GtghqXqi/lWSVAOPiTzTnnEzdOXCsAC2bb5V7EFNQjVjwy3cSSbUvgYOtDuDibd+ZsCDhz+4eykYOlhQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.5.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/help-me": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
+ "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
+ "license": "MIT"
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/http-status": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/http-status/-/http-status-2.1.0.tgz",
+ "integrity": "sha512-O5kPr7AW7wYd/BBiOezTwnVAnmSNFY+J7hlZD2X5IOxVBetjcHAiTXhzj0gMrnojQlwy+UT1/Y3H3vJ3UlmvLA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/image-size": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz",
+ "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==",
+ "license": "MIT",
+ "bin": {
+ "image-size": "bin/image-size.js"
+ },
+ "engines": {
+ "node": ">=16.x"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
+ "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
+ "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "license": "MIT"
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "license": "MIT"
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/isomorphic.js": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz",
+ "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "type": "GitHub Sponsors ❤",
+ "url": "https://github.com/sponsors/dmonad"
+ }
+ },
+ "node_modules/jose": {
+ "version": "5.9.6",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz",
+ "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/joycon": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
+ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "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==",
+ "license": "MIT"
+ },
+ "node_modules/json-schema-to-typescript": {
+ "version": "15.0.3",
+ "resolved": "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-15.0.3.tgz",
+ "integrity": "sha512-iOKdzTUWEVM4nlxpFudFsWyUiu/Jakkga4OZPEt7CGoSEsAsUgdOZqR6pcgx2STBek9Gm4hcarJpXSzIvZ/hKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@apidevtools/json-schema-ref-parser": "^11.5.5",
+ "@types/json-schema": "^7.0.15",
+ "@types/lodash": "^4.17.7",
+ "is-glob": "^4.0.3",
+ "js-yaml": "^4.1.0",
+ "lodash": "^4.17.21",
+ "minimist": "^1.2.8",
+ "prettier": "^3.2.5",
+ "tinyglobby": "^0.2.9"
+ },
+ "bin": {
+ "json2ts": "dist/src/cli.js"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/jsox": {
+ "version": "1.2.121",
+ "resolved": "https://registry.npmjs.org/jsox/-/jsox-1.2.121.tgz",
+ "integrity": "sha512-9Ag50tKhpTwS6r5wh3MJSAvpSof0UBr39Pto8OnzFT32Z/pAbxAsKHzyvsyMEHVslELvHyO/4/jaQELHk8wDcw==",
+ "license": "MIT",
+ "bin": {
+ "jsox": "lib/cli.js"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lexical": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/lexical/-/lexical-0.28.0.tgz",
+ "integrity": "sha512-dLE3O1PZg0TlZxRQo9YDpjCjDUj8zluGyBO9MHdjo21qZmMUNrxQPeCRt8fn2s5l4HKYFQ1YNgl7k1pOJB/vZQ==",
+ "license": "MIT"
+ },
+ "node_modules/lib0": {
+ "version": "0.2.114",
+ "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz",
+ "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "isomorphic.js": "^0.2.4"
+ },
+ "bin": {
+ "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js",
+ "0gentesthtml": "bin/gentesthtml.js",
+ "0serve": "bin/0serve.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "type": "GitHub Sponsors ❤",
+ "url": "https://github.com/sponsors/dmonad"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "license": "MIT"
+ },
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "license": "ISC"
+ },
+ "node_modules/md5": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
+ "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "charenc": "0.0.2",
+ "crypt": "0.0.2",
+ "is-buffer": "~1.1.6"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
+ "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz",
+ "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/memoize-one": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
+ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
+ "license": "MIT"
+ },
+ "node_modules/micromark": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-mdx-jsx": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.1.tgz",
+ "integrity": "sha512-vNuFb9czP8QCtAQcEJn0UJQJZA8Dk6DXKBqx+bg/w0WGuSxDxNr7hErW89tHUY31dUW4NqEOWwmEUNhjTFmHkg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/acorn": "^4.0.0",
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "micromark-factory-mdx-expression": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-mdx-expression": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz",
+ "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-events-to-acorn": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-position-from-estree": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-events-to-acorn": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz",
+ "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-visit": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "vfile-message": "^4.0.0"
+ }
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/monaco-editor": {
+ "version": "0.52.2",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
+ "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "15.4.6",
+ "resolved": "https://registry.npmjs.org/next/-/next-15.4.6.tgz",
+ "integrity": "sha512-us++E/Q80/8+UekzB3SAGs71AlLDsadpFMXVNM/uQ0BMwsh9m3mr0UNQIfjKed8vpWXsASe+Qifrnu1oLIcKEQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@next/env": "15.4.6",
+ "@swc/helpers": "0.5.15",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "15.4.6",
+ "@next/swc-darwin-x64": "15.4.6",
+ "@next/swc-linux-arm64-gnu": "15.4.6",
+ "@next/swc-linux-arm64-musl": "15.4.6",
+ "@next/swc-linux-x64-gnu": "15.4.6",
+ "@next/swc-linux-x64-musl": "15.4.6",
+ "@next/swc-win32-arm64-msvc": "15.4.6",
+ "@next/swc-win32-x64-msvc": "15.4.6",
+ "sharp": "^0.34.3"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-to-formdata": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/object-to-formdata/-/object-to-formdata-4.5.1.tgz",
+ "integrity": "sha512-QiM9D0NiU5jV6J6tjE1g7b4Z2tcUnKs1OPUi4iMb2zH+7jwlcUrASghgkFk9GtzqNNq8rTQJtT8AzjBAvLoNMw==",
+ "license": "MIT"
+ },
+ "node_modules/obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+ "license": "MIT"
+ },
+ "node_modules/on-exit-leak-free": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
+ "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "license": "MIT"
+ },
+ "node_modules/path-to-regexp": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
+ "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
+ "license": "MIT"
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/payload": {
+ "version": "3.51.0",
+ "resolved": "https://registry.npmjs.org/payload/-/payload-3.51.0.tgz",
+ "integrity": "sha512-PJzAxzH2tbvacstb7+FX188EFesAQMD1/bTxhD74Ci8/eSRbTUFryuUBf9SBNO1qMWp14qg5slnlVUU1ztZy/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "^15.1.5",
+ "@payloadcms/translations": "3.51.0",
+ "@types/busboy": "1.5.4",
+ "ajv": "8.17.1",
+ "bson-objectid": "2.0.4",
+ "busboy": "^1.6.0",
+ "ci-info": "^4.1.0",
+ "console-table-printer": "2.12.1",
+ "croner": "9.1.0",
+ "dataloader": "2.2.3",
+ "deepmerge": "4.3.1",
+ "file-type": "19.3.0",
+ "get-tsconfig": "4.8.1",
+ "http-status": "2.1.0",
+ "image-size": "2.0.2",
+ "ipaddr.js": "2.2.0",
+ "jose": "5.9.6",
+ "json-schema-to-typescript": "15.0.3",
+ "minimist": "1.2.8",
+ "path-to-regexp": "6.3.0",
+ "pino": "9.5.0",
+ "pino-pretty": "13.0.0",
+ "pluralize": "8.0.0",
+ "qs-esm": "7.0.2",
+ "sanitize-filename": "1.6.3",
+ "scmp": "2.1.0",
+ "ts-essentials": "10.0.3",
+ "tsx": "4.20.3",
+ "undici": "7.10.0",
+ "uuid": "10.0.0",
+ "ws": "^8.16.0"
+ },
+ "bin": {
+ "payload": "bin.js"
+ },
+ "engines": {
+ "node": "^18.20.2 || >=20.9.0"
+ },
+ "peerDependencies": {
+ "graphql": "^16.8.1"
+ }
+ },
+ "node_modules/payload/node_modules/get-tsconfig": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz",
+ "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==",
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/peek-readable": {
+ "version": "5.4.2",
+ "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz",
+ "integrity": "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/pg": {
+ "version": "8.16.3",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
+ "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-connection-string": "^2.9.1",
+ "pg-pool": "^3.10.1",
+ "pg-protocol": "^1.10.3",
+ "pg-types": "2.2.0",
+ "pgpass": "1.0.5"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "optionalDependencies": {
+ "pg-cloudflare": "^1.2.7"
+ },
+ "peerDependencies": {
+ "pg-native": ">=3.0.1"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pg-cloudflare": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
+ "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
+ "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
+ "license": "MIT"
+ },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/pg-numeric": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz",
+ "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pg-pool": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
+ "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "pg": ">=8.0"
+ }
+ },
+ "node_modules/pg-protocol": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
+ "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==",
+ "license": "MIT"
+ },
+ "node_modules/pg-types": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz",
+ "integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "pg-numeric": "1.0.2",
+ "postgres-array": "~3.0.1",
+ "postgres-bytea": "~3.0.0",
+ "postgres-date": "~2.1.0",
+ "postgres-interval": "^3.0.0",
+ "postgres-range": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/pg/node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pg/node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pg/node_modules/postgres-bytea": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+ "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pg/node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pg/node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.1.0"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pino": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-9.5.0.tgz",
+ "integrity": "sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw==",
+ "license": "MIT",
+ "dependencies": {
+ "atomic-sleep": "^1.0.0",
+ "fast-redact": "^3.1.1",
+ "on-exit-leak-free": "^2.1.0",
+ "pino-abstract-transport": "^2.0.0",
+ "pino-std-serializers": "^7.0.0",
+ "process-warning": "^4.0.0",
+ "quick-format-unescaped": "^4.0.3",
+ "real-require": "^0.2.0",
+ "safe-stable-stringify": "^2.3.1",
+ "sonic-boom": "^4.0.1",
+ "thread-stream": "^3.0.0"
+ },
+ "bin": {
+ "pino": "bin.js"
+ }
+ },
+ "node_modules/pino-abstract-transport": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
+ "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.0.0"
+ }
+ },
+ "node_modules/pino-pretty": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.0.0.tgz",
+ "integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==",
+ "license": "MIT",
+ "dependencies": {
+ "colorette": "^2.0.7",
+ "dateformat": "^4.6.3",
+ "fast-copy": "^3.0.2",
+ "fast-safe-stringify": "^2.1.1",
+ "help-me": "^5.0.0",
+ "joycon": "^3.1.1",
+ "minimist": "^1.2.6",
+ "on-exit-leak-free": "^2.1.0",
+ "pino-abstract-transport": "^2.0.0",
+ "pump": "^3.0.0",
+ "secure-json-parse": "^2.4.0",
+ "sonic-boom": "^4.0.1",
+ "strip-json-comments": "^3.1.1"
+ },
+ "bin": {
+ "pino-pretty": "bin.js"
+ }
+ },
+ "node_modules/pino-std-serializers": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
+ "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
+ "license": "MIT"
+ },
+ "node_modules/pluralize": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postgres-array": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz",
+ "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/postgres-bytea": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
+ "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
+ "license": "MIT",
+ "dependencies": {
+ "obuf": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postgres-date": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz",
+ "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/postgres-interval": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
+ "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/postgres-range": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz",
+ "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==",
+ "license": "MIT"
+ },
+ "node_modules/prettier": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
+ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prismjs": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
+ "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/process-warning": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz",
+ "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/pump": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/qs-esm": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/qs-esm/-/qs-esm-7.0.2.tgz",
+ "integrity": "sha512-D8NAthKSD7SGn748v+GLaaO6k08Mvpoqroa35PqIQC4gtUa8/Pb/k+r0m0NnGBVbHDP1gKZ2nVywqfMisRhV5A==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/quick-format-unescaped": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
+ "license": "MIT"
+ },
+ "node_modules/react": {
+ "version": "19.1.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
+ "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-datepicker": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-7.6.0.tgz",
+ "integrity": "sha512-9cQH6Z/qa4LrGhzdc3XoHbhrxNcMi9MKjZmYgF/1MNNaJwvdSjv3Xd+jjvrEEbKEf71ZgCA3n7fQbdwd70qCRw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/react": "^0.27.0",
+ "clsx": "^2.1.1",
+ "date-fns": "^3.6.0"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc",
+ "react-dom": "^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/react-datepicker/node_modules/date-fns": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
+ "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
+ "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.1"
+ }
+ },
+ "node_modules/react-dom/node_modules/scheduler": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
+ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/react-error-boundary": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.1.2.tgz",
+ "integrity": "sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5"
+ },
+ "peerDependencies": {
+ "react": ">=16.13.1"
+ }
+ },
+ "node_modules/react-image-crop": {
+ "version": "10.1.8",
+ "resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-10.1.8.tgz",
+ "integrity": "sha512-4rb8XtXNx7ZaOZarKKnckgz4xLMvds/YrU6mpJfGhGAsy2Mg4mIw1x+DCCGngVGq2soTBVVOxx2s/C6mTX9+pA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": ">=16.13.1"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
+ "node_modules/react-select": {
+ "version": "5.9.0",
+ "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.9.0.tgz",
+ "integrity": "sha512-nwRKGanVHGjdccsnzhFte/PULziueZxGD8LL2WojON78Mvnq7LdAMEtu2frrwld1fr3geixg3iiMBIc/LLAZpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.0",
+ "@emotion/cache": "^11.4.0",
+ "@emotion/react": "^11.8.1",
+ "@floating-ui/dom": "^1.0.1",
+ "@types/react-transition-group": "^4.4.0",
+ "memoize-one": "^6.0.0",
+ "prop-types": "^15.6.0",
+ "react-transition-group": "^4.3.0",
+ "use-isomorphic-layout-effect": "^1.2.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/readdirp/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/real-require": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sanitize-filename": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
+ "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
+ "license": "WTFPL OR ISC",
+ "dependencies": {
+ "truncate-utf8-bytes": "^1.0.0"
+ }
+ },
+ "node_modules/sass": {
+ "version": "1.77.4",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.4.tgz",
+ "integrity": "sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
+ "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
+ "license": "MIT"
+ },
+ "node_modules/scmp": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz",
+ "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/secure-json-parse": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
+ "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "license": "ISC",
+ "optional": true,
+ "peer": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.3",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
+ "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.4",
+ "semver": "^7.7.2"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.3",
+ "@img/sharp-darwin-x64": "0.34.3",
+ "@img/sharp-libvips-darwin-arm64": "1.2.0",
+ "@img/sharp-libvips-darwin-x64": "1.2.0",
+ "@img/sharp-libvips-linux-arm": "1.2.0",
+ "@img/sharp-libvips-linux-arm64": "1.2.0",
+ "@img/sharp-libvips-linux-ppc64": "1.2.0",
+ "@img/sharp-libvips-linux-s390x": "1.2.0",
+ "@img/sharp-libvips-linux-x64": "1.2.0",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.0",
+ "@img/sharp-linux-arm": "0.34.3",
+ "@img/sharp-linux-arm64": "0.34.3",
+ "@img/sharp-linux-ppc64": "0.34.3",
+ "@img/sharp-linux-s390x": "0.34.3",
+ "@img/sharp-linux-x64": "0.34.3",
+ "@img/sharp-linuxmusl-arm64": "0.34.3",
+ "@img/sharp-linuxmusl-x64": "0.34.3",
+ "@img/sharp-wasm32": "0.34.3",
+ "@img/sharp-win32-arm64": "0.34.3",
+ "@img/sharp-win32-ia32": "0.34.3",
+ "@img/sharp-win32-x64": "0.34.3"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/simple-swizzle/node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/simple-wcswidth": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz",
+ "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==",
+ "license": "MIT"
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "license": "MIT"
+ },
+ "node_modules/sonic-boom": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
+ "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
+ "license": "MIT",
+ "dependencies": {
+ "atomic-sleep": "^1.0.0"
+ }
+ },
+ "node_modules/sonner": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz",
+ "integrity": "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
+ "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/state-local": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
+ "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
+ "license": "MIT"
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "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==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strtok3": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-8.1.0.tgz",
+ "integrity": "sha512-ExzDvHYPj6F6QkSNe/JxSlBxTh3OrI6wrAIz53ulxo1c4hBJ1bT9C/JrAthEKHWG9riVH3Xzg7B03Oxty6S2Lw==",
+ "license": "MIT",
+ "dependencies": {
+ "@tokenizer/token": "^0.3.0",
+ "peek-readable": "^5.1.4"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+ "license": "MIT"
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==",
+ "license": "MIT"
+ },
+ "node_modules/thread-stream": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
+ "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
+ "license": "MIT",
+ "dependencies": {
+ "real-require": "^0.2.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/to-no-case": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz",
+ "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==",
+ "license": "MIT"
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/to-snake-case": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz",
+ "integrity": "sha512-joRpzBAk1Bhi2eGEYBjukEWHOe/IvclOkiJl3DtA91jV6NwQ3MwXA4FHYeqk8BNp/D8bmi9tcNbRu/SozP0jbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "to-space-case": "^1.0.0"
+ }
+ },
+ "node_modules/to-space-case": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz",
+ "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==",
+ "license": "MIT",
+ "dependencies": {
+ "to-no-case": "^1.0.0"
+ }
+ },
+ "node_modules/token-types": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz",
+ "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@borewit/text-codec": "^0.1.0",
+ "@tokenizer/token": "^0.3.0",
+ "ieee754": "^1.2.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/truncate-utf8-bytes": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
+ "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
+ "license": "WTFPL",
+ "dependencies": {
+ "utf8-byte-length": "^1.0.1"
+ }
+ },
+ "node_modules/ts-essentials": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-10.0.3.tgz",
+ "integrity": "sha512-/FrVAZ76JLTWxJOERk04fm8hYENDo0PWSP3YLQKxevLwWtxemGcl5JJEzN4iqfDlRve0ckyfFaOBu4xbNH/wZw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "typescript": ">=4.5.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tsx": {
+ "version": "4.20.3",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz",
+ "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.25.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
+ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/uint8array-extras": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.1.tgz",
+ "integrity": "sha512-+NWHrac9dvilNgme+gP4YrBSumsaMZP0fNBtXXFIf33RLLKEcBUKaQZ7ULUbS0sBfcjxIZ4V96OTRkCbM7hxpw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/undici": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz",
+ "integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.18.1"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
+ "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
+ "license": "MIT"
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
+ "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position-from-estree": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz",
+ "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
+ "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
+ "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/use-context-selector": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/use-context-selector/-/use-context-selector-2.0.0.tgz",
+ "integrity": "sha512-owfuSmUNd3eNp3J9CdDl0kMgfidV+MkDvHPpvthN5ThqM+ibMccNE0k+Iq7TWC6JPFvGZqanqiGCuQx6DyV24g==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "scheduler": ">=0.19.0"
+ }
+ },
+ "node_modules/use-isomorphic-layout-effect": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz",
+ "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/utf8-byte-length": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
+ "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==",
+ "license": "(WTFPL OR MIT)"
+ },
+ "node_modules/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "license": "MIT"
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/ws": {
+ "version": "8.18.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xss": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.15.tgz",
+ "integrity": "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "commander": "^2.20.3",
+ "cssfilter": "0.0.10"
+ },
+ "bin": {
+ "xss": "bin/xss"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/yjs": {
+ "version": "13.6.27",
+ "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz",
+ "integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "lib0": "^0.2.99"
+ },
+ "engines": {
+ "node": ">=16.0.0",
+ "npm": ">=8.0.0"
+ },
+ "funding": {
+ "type": "GitHub Sponsors ❤",
+ "url": "https://github.com/sponsors/dmonad"
+ }
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ }
+ }
+}
diff --git a/cms/package.json b/cms/package.json
new file mode 100644
index 0000000..f04084a
--- /dev/null
+++ b/cms/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "monynha-cms",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "dev": "cross-env NODE_ENV=development ts-node ./node_modules/payload/bin/payload serve",
+ "build": "tsc",
+ "start": "node ./node_modules/payload/bin/payload serve"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "type": "module",
+ "dependencies": {
+ "@payloadcms/db-postgres": "^3.51.0",
+ "@payloadcms/richtext-lexical": "^3.51.0",
+ "cross-env": "^10.0.0",
+ "dotenv": "^17.2.1",
+ "payload": "^3.51.0",
+ "pg": "^8.16.3",
+ "ts-node": "^10.9.2",
+ "typescript": "^5.9.2"
+ },
+ "devDependencies": {
+ "@types/node": "^24.3.0"
+ },
+ "private": true
+}
diff --git a/cms/payload.config.ts b/cms/payload.config.ts
new file mode 100644
index 0000000..14c2d39
--- /dev/null
+++ b/cms/payload.config.ts
@@ -0,0 +1,28 @@
+import path from 'path';
+import { fileURLToPath } from 'url';
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+import { buildConfig } from 'payload';
+import { postgresAdapter } from '@payloadcms/db-postgres';
+import Posts from './src/collections/Posts';
+import Users from './src/collections/Users';
+import Media from './src/collections/Media';
+
+export default buildConfig({
+ secret: process.env.PAYLOAD_SECRET!,
+ serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL,
+ admin: { user: 'users', meta: { titleSuffix: ' · Monynha CMS' } },
+ localization: {
+ locales: ['pt-BR', 'en'],
+ defaultLocale: 'pt-BR',
+ fallback: true,
+ },
+ collections: [Posts, Users, Media],
+ db: postgresAdapter({
+ pool: {
+ connectionString: process.env.DATABASE_URL!,
+ },
+ schemaName: 'public',
+ }),
+ typescript: { outputFile: path.resolve(__dirname, 'payload-types.ts') },
+});
diff --git a/cms/src/collections/Media.ts b/cms/src/collections/Media.ts
new file mode 100644
index 0000000..6488c2e
--- /dev/null
+++ b/cms/src/collections/Media.ts
@@ -0,0 +1,12 @@
+import type { CollectionConfig } from 'payload';
+
+const Media: CollectionConfig = {
+ slug: 'media',
+ upload: true,
+ access: {
+ read: () => true,
+ },
+ fields: [],
+};
+
+export default Media;
diff --git a/cms/src/collections/Posts.ts b/cms/src/collections/Posts.ts
new file mode 100644
index 0000000..e2f817c
--- /dev/null
+++ b/cms/src/collections/Posts.ts
@@ -0,0 +1,71 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+import { lexicalEditor } from '@payloadcms/richtext-lexical';
+import { Pool } from 'pg';
+
+const pool = new Pool({ connectionString: process.env.DATABASE_URL });
+
+const upsertIntoPublic = async ({ doc }: { doc: any }) => {
+ const id = String(doc.id);
+ const slug = doc.slug;
+ const published = !!doc.published;
+ const title =
+ typeof doc.title === 'object'
+ ? (doc.title['pt-BR'] ?? doc.title['en'] ?? '')
+ : doc.title;
+ const excerpt =
+ typeof doc.excerpt === 'object'
+ ? (doc.excerpt['pt-BR'] ?? doc.excerpt['en'] ?? null)
+ : (doc.excerpt ?? null);
+ const image_url = doc?.coverImage?.url ?? null;
+ const content =
+ typeof doc.content === 'string'
+ ? doc.content
+ : JSON.stringify(doc.content ?? {});
+
+ await pool.query(
+ `insert into public.blog_posts (id, slug, title, excerpt, image_url, content, published, updated_at)
+ values ($1,$2,$3,$4,$5,$6,$7, now())
+ on conflict (id) do update set
+ slug=excluded.slug,
+ title=excluded.title,
+ excerpt=excluded.excerpt,
+ image_url=excluded.image_url,
+ content=excluded.content,
+ published=excluded.published,
+ updated_at=now()`,
+ [id, slug, title, excerpt, image_url, content, published]
+ );
+};
+
+const Posts: CollectionConfig = {
+ slug: 'posts',
+ admin: { useAsTitle: 'slug' },
+ access: {
+ read: () => true,
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ { name: 'slug', type: 'text', required: true, unique: true },
+ { name: 'title', type: 'text', required: true, localized: true },
+ { name: 'excerpt', type: 'textarea', localized: true },
+ {
+ name: 'content',
+ type: 'richText',
+ localized: true,
+ editor: lexicalEditor(),
+ },
+ { name: 'published', type: 'checkbox', defaultValue: false },
+ {
+ name: 'coverImage',
+ type: 'upload',
+ relationTo: 'media',
+ required: false,
+ },
+ ],
+ hooks: { afterChange: [upsertIntoPublic] },
+};
+
+export default Posts;
diff --git a/cms/src/collections/Users.ts b/cms/src/collections/Users.ts
new file mode 100644
index 0000000..2069644
--- /dev/null
+++ b/cms/src/collections/Users.ts
@@ -0,0 +1,13 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+
+const Users: CollectionConfig = {
+ slug: 'users',
+ auth: true,
+ access: {
+ read: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [{ name: 'name', type: 'text' }],
+};
+
+export default Users;
diff --git a/cms/tsconfig.json b/cms/tsconfig.json
new file mode 100644
index 0000000..8c2dfe6
--- /dev/null
+++ b/cms/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "outDir": "dist"
+ },
+ "include": ["payload.config.ts", "src/**/*"]
+}
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index df29f59..e90f8a4 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -175,7 +175,7 @@ const Footer = () => {
}
- {t('footer.location')}: São Paulo, Brasil
+ {t('footer.location')}: Faro, Portugal
diff --git a/src/locales/en.json b/src/locales/en.json
index 5e1788a..8c6bb01 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -35,7 +35,7 @@
},
"index": {
"hero": {
- "headline": "Building the <0/><1>Future of Technology1>",
+ "headline": "Building the <0/><1>Future with Technology1>",
"description": "Democratizing innovation through inclusive, accessible solutions that empower diverse communities and promote social equity.",
"cta": "Start Your Project",
"viewSolutions": "View Solutions"
diff --git a/src/locales/es.json b/src/locales/es.json
index 07a4bb2..7f12aee 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -35,7 +35,7 @@
},
"index": {
"hero": {
- "headline": "Construyendo el <0/><1>Futuro de la Tecnología1>",
+ "headline": "Construyendo el <0/><1>futuro con tecnología1>",
"description": "Democratizando la innovación con soluciones inclusivas y accesibles que empoderan a comunidades diversas y promueven la equidad social.",
"cta": "Comenzar Proyecto",
"viewSolutions": "Ver Soluciones"
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 85b9fae..0d9c331 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -35,7 +35,7 @@
},
"index": {
"hero": {
- "headline": "Construire l'<0/><1>Avenir de la Technologie1>",
+ "headline": "Construire l'<0/><1>Avenir avec la Technologie1>",
"description": "Démocratiser l'innovation grâce à des solutions inclusives et accessibles qui renforcent les communautés diverses et favorisent l'équité sociale.",
"cta": "Démarrer le projet",
"viewSolutions": "Voir les solutions"
diff --git a/src/locales/pt.json b/src/locales/pt.json
index 10e11a0..5f8ec36 100644
--- a/src/locales/pt.json
+++ b/src/locales/pt.json
@@ -35,7 +35,7 @@
},
"index": {
"hero": {
- "headline": "Construindo o <0/><1>Futuro da Tecnologia1>",
+ "headline": "Construindo o <0/><1>Futuro com Tecnologia1>",
"description": "Democratizando a inovação com soluções inclusivas e acessíveis que capacitam comunidades diversas e promovem a equidade social.",
"cta": "Iniciar Projeto",
"viewSolutions": "Ver Soluções"
diff --git a/src/pages/Blog.tsx b/src/pages/Blog.tsx
index 8d93149..31f0406 100644
--- a/src/pages/Blog.tsx
+++ b/src/pages/Blog.tsx
@@ -18,76 +18,6 @@ import {
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
-const fallbackPosts = [
- {
- title: 'The Future of AI in Restaurant Management',
- excerpt:
- 'Discover how artificial intelligence is revolutionizing the food service industry with predictive analytics, inventory optimization, and customer experience enhancement.',
- image:
- 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
- author: 'Monynha Softwares Team',
- date: 'March 15, 2024',
- readTime: '5 min read',
- category: 'AI Insights',
- featured: true,
- },
- {
- title: 'Building Custom AI Assistants: Best Practices',
- excerpt:
- 'Learn the essential considerations and strategies for developing AI assistants that truly understand and serve your business needs.',
- image:
- 'https://images.unsplash.com/photo-1485827404703-89b55fcc595e?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
- author: 'Development Team',
- date: 'March 8, 2024',
- readTime: '7 min read',
- category: 'Development',
- },
- {
- title: 'How Boteco Pro Increased Restaurant Efficiency by 40%',
- excerpt:
- 'A detailed case study of how our restaurant management system transformed operations for a growing chain of establishments.',
- image:
- 'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
- author: 'Case Study Team',
- date: 'February 28, 2024',
- readTime: '6 min read',
- category: 'Case Study',
- },
- {
- title: 'The ROI of Intelligent Automation in Small Businesses',
- excerpt:
- 'Understanding the financial impact and long-term benefits of implementing AI-powered automation in your business operations.',
- image:
- 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
- author: 'Business Analytics',
- date: 'February 20, 2024',
- readTime: '4 min read',
- category: 'Business',
- },
- {
- title: 'Security First: Protecting Your AI Systems',
- excerpt:
- 'Essential security measures and best practices for maintaining robust protection in AI-powered business applications.',
- image:
- 'https://images.unsplash.com/photo-1563986768609-322da13575f3?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
- author: 'Security Team',
- date: 'February 12, 2024',
- readTime: '8 min read',
- category: 'Security',
- },
- {
- title: 'Integration Strategies for Legacy Systems',
- excerpt:
- 'How to successfully integrate modern AI solutions with existing business systems without disrupting operations.',
- image:
- 'https://images.unsplash.com/photo-1518770660439-4636190af475?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
- author: 'Integration Team',
- date: 'February 5, 2024',
- readTime: '6 min read',
- category: 'Integration',
- },
-];
-
const Blog = () => {
const { t } = useTranslation();
@@ -113,9 +43,9 @@ const Blog = () => {
queryFn: async () => {
const { data, error } = await supabase
.from('blog_posts')
- .select('*')
+ .select('id, slug, title, excerpt, image_url, updated_at')
.eq('published', true)
- .order('created_at', { ascending: false });
+ .order('updated_at', { ascending: false });
if (error) {
throw new Error(error.message);
@@ -129,7 +59,7 @@ const Blog = () => {
post.image_url ||
'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
author: 'Monynha Softwares Team',
- date: new Date(post.created_at).toLocaleDateString('pt-BR', {
+ date: new Date(post.updated_at).toLocaleDateString('pt-BR', {
year: 'numeric',
month: 'long',
day: 'numeric',
@@ -138,7 +68,6 @@ const Blog = () => {
category: 'AI Insights',
featured: index === 0,
slug: post.slug,
- content: post.content,
})) || []
);
},
@@ -240,7 +169,7 @@ const Blog = () => {
{/* Featured Post */}
- {(posts.length > 0 ? posts : fallbackPosts)
+ {posts
.filter((post) => post.featured)
.map((post, index) => (
@@ -297,7 +226,7 @@ const Blog = () => {
- {(posts.length > 0 ? posts : fallbackPosts)
+ {posts
.filter((post) => !post.featured)
.map((post, index) => (
Date: Tue, 16 Sep 2025 19:42:45 +0100
Subject: [PATCH 08/15] Sync with dev (#29)
* feat: add payload cms
* Update Footer.tsx
* Update pt.json
* Update en.json
* Update fr.json
* Update es.json
* Update 002_update_homepage_features_custom_emojis.sql
* Delete cms directory
* chore: clinning up supabase old migrations
* Add schemaa
* chore: types justs
* Revert "Login pages corrections, and othes things"
* feat: add Liquid Ether background effect
* Revert "feat: add Liquid Ether background effect"
* Add Supabase auth flow with dashboard protection
* Revert "Add Supabase auth flow with dashboard protection"
* chore: polish dashboard fallbacks
---------
Co-authored-by: Marcelo Santos
---
src/App.tsx | 5 +
src/components/NewsletterSection.tsx | 2 +-
src/components/ProtectedRoute.tsx | 24 ++
src/components/TeamSection.tsx | 2 +-
src/hooks/useAuth.tsx | 18 +-
src/integrations/supabase.ts | 120 ++++++++
src/integrations/supabase/client.ts | 22 --
src/pages/Auth.tsx | 223 +++++++++-----
src/pages/Blog.tsx | 2 +-
src/pages/Contact.tsx | 2 +-
src/pages/Dashboard.tsx | 417 +++++++++++++++++++++++++++
src/pages/Index.tsx | 2 +-
src/pages/Projects.tsx | 2 +-
src/pages/Solutions.tsx | 2 +-
14 files changed, 732 insertions(+), 111 deletions(-)
create mode 100644 src/components/ProtectedRoute.tsx
create mode 100644 src/integrations/supabase.ts
delete mode 100644 src/integrations/supabase/client.ts
create mode 100644 src/pages/Dashboard.tsx
diff --git a/src/App.tsx b/src/App.tsx
index 39d1af5..b69a7a0 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -8,6 +8,7 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Suspense, lazy } from 'react';
import Loading from './components/Loading';
import ScrollToTop from './components/ScrollToTop';
+import ProtectedRoute from './components/ProtectedRoute';
const Index = lazy(() => import('./pages/Index'));
const Solutions = lazy(() => import('./pages/Solutions'));
@@ -16,6 +17,7 @@ const About = lazy(() => import('./pages/About'));
const Blog = lazy(() => import('./pages/Blog'));
const Contact = lazy(() => import('./pages/Contact'));
const Auth = lazy(() => import('./pages/Auth'));
+const Dashboard = lazy(() => import('./pages/Dashboard'));
const NotFound = lazy(() => import('./pages/NotFound'));
const queryClient = new QueryClient();
@@ -38,6 +40,9 @@ const App = () => (
} />
} />
} />
+ }>
+ } />
+
} />
diff --git a/src/components/NewsletterSection.tsx b/src/components/NewsletterSection.tsx
index 31d1dea..3f7444c 100644
--- a/src/components/NewsletterSection.tsx
+++ b/src/components/NewsletterSection.tsx
@@ -2,7 +2,7 @@ import { useState } from 'react';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
-import { supabase } from '@/integrations/supabase/client';
+import { supabase } from '@/integrations/supabase';
import { useToast } from '@/hooks/use-toast';
import { Mail, CheckCircle } from 'lucide-react';
import { useTranslation } from 'react-i18next';
diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx
new file mode 100644
index 0000000..fc9cbdd
--- /dev/null
+++ b/src/components/ProtectedRoute.tsx
@@ -0,0 +1,24 @@
+import { Navigate, Outlet, useLocation } from 'react-router-dom';
+import Loading from './Loading';
+import { useAuth } from '@/hooks/useAuth';
+
+const ProtectedRoute = () => {
+ const { user, isLoading } = useAuth();
+ const location = useLocation();
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ if (!user) {
+ return ;
+ }
+
+ return ;
+};
+
+export default ProtectedRoute;
diff --git a/src/components/TeamSection.tsx b/src/components/TeamSection.tsx
index 2a4801a..14986a0 100644
--- a/src/components/TeamSection.tsx
+++ b/src/components/TeamSection.tsx
@@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
-import { supabase } from '@/integrations/supabase/client';
+import { supabase } from '@/integrations/supabase';
import { Linkedin, Mail } from 'lucide-react';
import { useTranslation } from 'react-i18next';
diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx
index 54e2e8f..0557c6e 100644
--- a/src/hooks/useAuth.tsx
+++ b/src/hooks/useAuth.tsx
@@ -1,6 +1,6 @@
import { useState, useEffect, createContext, useContext } from 'react';
import { User, Session } from '@supabase/supabase-js';
-import { supabase } from '@/integrations/supabase/client';
+import { supabase, clearSupabaseSession } from '@/integrations/supabase';
interface AuthContextType {
user: User | null;
@@ -28,7 +28,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
// Set up auth state listener
const {
data: { subscription },
- } = supabase.auth.onAuthStateChange((event, session) => {
+ } = supabase.auth.onAuthStateChange((_event, session) => {
setSession(session);
setUser(session?.user ?? null);
setIsLoading(false);
@@ -48,7 +48,19 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
* Ends the current user session.
*/
const signOut = async () => {
- await supabase.auth.signOut();
+ try {
+ const { error } = await supabase.auth.signOut();
+ if (error) {
+ throw error;
+ }
+ } catch (error) {
+ console.error('Erro ao encerrar sessão', error);
+ throw error;
+ } finally {
+ clearSupabaseSession();
+ setSession(null);
+ setUser(null);
+ }
};
return (
diff --git a/src/integrations/supabase.ts b/src/integrations/supabase.ts
new file mode 100644
index 0000000..3d93074
--- /dev/null
+++ b/src/integrations/supabase.ts
@@ -0,0 +1,120 @@
+import { createClient } from '@supabase/supabase-js';
+import type { Database } from '@/integrations/supabase/types';
+
+const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL as string;
+const SUPABASE_ANON_KEY =
+ (import.meta.env.VITE_SUPABASE_ANON_KEY ||
+ import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY) as string;
+
+const AUTH_COOKIE_PREFIX = 'monynha_supabase_';
+const AUTH_STORAGE_KEY = 'auth_token';
+const DEFAULT_MAX_AGE = 60 * 60 * 24 * 30; // 30 days
+
+class CookieStorage implements Storage {
+ private readonly path: string;
+ private readonly maxAge: number;
+
+ constructor(
+ private readonly prefix = AUTH_COOKIE_PREFIX,
+ options?: { path?: string; maxAge?: number }
+ ) {
+ this.path = options?.path ?? '/';
+ this.maxAge = options?.maxAge ?? DEFAULT_MAX_AGE;
+ }
+
+ get length(): number {
+ return this.getPrefixedKeys().length;
+ }
+
+ clear(): void {
+ this.getPrefixedKeys().forEach((key) => this.removeItem(key));
+ }
+
+ getItem(key: string): string | null {
+ if (typeof document === 'undefined') return null;
+ const cookieName = this.getCookieName(key);
+ const entry = this.getCookies().find(({ name }) => name === cookieName);
+ if (!entry) return null;
+ return decodeURIComponent(entry.value);
+ }
+
+ key(index: number): string | null {
+ const keys = this.getPrefixedKeys();
+ return index >= 0 && index < keys.length ? keys[index] : null;
+ }
+
+ removeItem(key: string): void {
+ if (typeof document === 'undefined') return;
+ const cookieName = this.getCookieName(key);
+ const secureSuffix =
+ typeof window !== 'undefined' && window.location.protocol === 'https:'
+ ? '; Secure'
+ : '';
+ document.cookie =
+ `${cookieName}=; Path=${this.path}; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax${secureSuffix}`;
+ }
+
+ setItem(key: string, value: string): void {
+ if (typeof document === 'undefined') return;
+ const cookieName = this.getCookieName(key);
+ const secureSuffix =
+ typeof window !== 'undefined' && window.location.protocol === 'https:'
+ ? '; Secure'
+ : '';
+ document.cookie =
+ `${cookieName}=${encodeURIComponent(value)}; Path=${this.path}; Max-Age=${this.maxAge}; SameSite=Lax${secureSuffix}`;
+ }
+
+ private getCookieName(key: string): string {
+ return `${this.prefix}${key}`;
+ }
+
+ private getCookies(): Array<{ name: string; value: string }> {
+ if (typeof document === 'undefined' || !document.cookie) {
+ return [];
+ }
+
+ return document.cookie.split('; ').map((cookie) => {
+ const [rawName, ...rest] = cookie.split('=');
+ return {
+ name: decodeURIComponent(rawName),
+ value: rest.join('='),
+ };
+ });
+ }
+
+ private getPrefixedKeys(): string[] {
+ return this.getCookies()
+ .filter(({ name }) => name.startsWith(this.prefix))
+ .map(({ name }) => name.slice(this.prefix.length));
+ }
+}
+
+const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
+
+const cookieStorage = isBrowser
+ ? new CookieStorage(AUTH_COOKIE_PREFIX, { path: '/', maxAge: DEFAULT_MAX_AGE })
+ : undefined;
+
+export const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
+ auth: {
+ storage: cookieStorage,
+ storageKey: AUTH_STORAGE_KEY,
+ persistSession: true,
+ autoRefreshToken: true,
+ },
+});
+
+export const clearSupabaseSession = () => {
+ if (cookieStorage) {
+ cookieStorage.clear();
+ } else if (typeof window !== 'undefined') {
+ try {
+ window.localStorage.removeItem(AUTH_STORAGE_KEY);
+ } catch (error) {
+ console.warn('Unable to clear Supabase session storage', error);
+ }
+ }
+};
+
+export { AUTH_STORAGE_KEY };
diff --git a/src/integrations/supabase/client.ts b/src/integrations/supabase/client.ts
deleted file mode 100644
index 550753b..0000000
--- a/src/integrations/supabase/client.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { createClient } from '@supabase/supabase-js';
-import type { Database } from './types';
-
-const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
-const SUPABASE_ANON_KEY =
- import.meta.env.VITE_SUPABASE_ANON_KEY ||
- import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY;
-
-// Import the supabase client like this:
-// import { supabase } from '@/integrations/supabase/client'
-
-export const supabase = createClient(
- SUPABASE_URL,
- SUPABASE_ANON_KEY,
- {
- auth: {
- storage: localStorage,
- persistSession: true,
- autoRefreshToken: true,
- },
- }
-);
diff --git a/src/pages/Auth.tsx b/src/pages/Auth.tsx
index e882dcd..591629f 100644
--- a/src/pages/Auth.tsx
+++ b/src/pages/Auth.tsx
@@ -1,6 +1,6 @@
-import React, { useState, useEffect } from 'react';
-import { Link, useNavigate } from 'react-router-dom';
-import { supabase } from '@/integrations/supabase/client';
+import { FormEvent, useEffect, useMemo, useState } from 'react';
+import { Link, useLocation, useNavigate } from 'react-router-dom';
+import { supabase } from '@/integrations/supabase';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
@@ -13,93 +13,117 @@ import {
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Label } from '@/components/ui/label';
import { useToast } from '@/hooks/use-toast';
-import { Eye, EyeOff, Loader2, ArrowLeft } from 'lucide-react';
+import { ArrowLeft, Eye, EyeOff, Loader2 } from 'lucide-react';
+
+type AuthView = 'signin' | 'signup';
const Auth = () => {
- const [isLoading, setIsLoading] = useState(false);
+ const [authView, setAuthView] = useState('signin');
+ const [loadingView, setLoadingView] = useState(null);
const [showPassword, setShowPassword] = useState(false);
- const [email, setEmail] = useState('');
- const [password, setPassword] = useState('');
- const [name, setName] = useState('');
+ const [signInForm, setSignInForm] = useState({ email: '', password: '' });
+ const [signUpForm, setSignUpForm] = useState({ name: '', email: '', password: '' });
+
const navigate = useNavigate();
+ const location = useLocation();
const { toast } = useToast();
+ const redirectPath = useMemo(() => {
+ const state = location.state as { from?: { pathname?: string } } | null;
+ return state?.from?.pathname ?? '/dashboard';
+ }, [location.state]);
+
useEffect(() => {
- // Check if user is already logged in
const checkUser = async () => {
const {
data: { session },
} = await supabase.auth.getSession();
+
if (session) {
- navigate('/');
+ navigate(redirectPath, { replace: true });
}
};
+
checkUser();
- }, [navigate]);
+ }, [navigate, redirectPath]);
- const handleSignUp = async (e: React.FormEvent) => {
- e.preventDefault();
- setIsLoading(true);
+ const handleSignIn = async (event: FormEvent) => {
+ event.preventDefault();
+ setLoadingView('signin');
try {
- const redirectUrl = `${window.location.origin}/`;
-
- const { error } = await supabase.auth.signUp({
- email,
- password,
- options: {
- emailRedirectTo: redirectUrl,
- data: {
- name: name,
- },
- },
+ const { error } = await supabase.auth.signInWithPassword({
+ email: signInForm.email.trim(),
+ password: signInForm.password,
});
- if (error) throw error;
+ if (error) {
+ throw error;
+ }
toast({
- title: 'Conta criada com sucesso!',
- description: 'Verifique seu email para confirmar a conta.',
+ title: 'Login realizado com sucesso!',
+ description: 'Bem-vindo de volta!',
});
- } catch (error: unknown) {
- const message = error instanceof Error ? error.message : String(error);
+
+ navigate(redirectPath, { replace: true });
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : 'Não foi possível fazer login.';
toast({
- title: 'Erro ao criar conta',
+ title: 'Erro ao fazer login',
description: message,
variant: 'destructive',
});
} finally {
- setIsLoading(false);
+ setLoadingView(null);
}
};
- const handleSignIn = async (e: React.FormEvent) => {
- e.preventDefault();
- setIsLoading(true);
+ const handleSignUp = async (event: FormEvent) => {
+ event.preventDefault();
+ setLoadingView('signup');
try {
- const { error } = await supabase.auth.signInWithPassword({
- email,
- password,
+ const redirectTo =
+ typeof window !== 'undefined'
+ ? `${window.location.origin}/dashboard`
+ : undefined;
+
+ const { error } = await supabase.auth.signUp({
+ email: signUpForm.email.trim(),
+ password: signUpForm.password,
+ options: {
+ emailRedirectTo: redirectTo,
+ data: {
+ name: signUpForm.name.trim(),
+ },
+ },
});
- if (error) throw error;
+ if (error) {
+ throw error;
+ }
toast({
- title: 'Login realizado com sucesso!',
- description: 'Bem-vindo de volta!',
+ title: 'Conta criada com sucesso!',
+ description: 'Verifique seu email para confirmar a conta.',
});
- navigate('/');
- } catch (error: unknown) {
- const message = error instanceof Error ? error.message : String(error);
+ setSignInForm({ email: signUpForm.email.trim(), password: '' });
+ setSignUpForm({ name: '', email: '', password: '' });
+ setAuthView('signin');
+ setShowPassword(false);
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : 'Não foi possível criar a conta.';
toast({
- title: 'Erro ao fazer login',
+ title: 'Erro ao criar conta',
description: message,
variant: 'destructive',
});
} finally {
- setIsLoading(false);
+ setLoadingView(null);
}
};
@@ -115,15 +139,20 @@ const Auth = () => {
Voltar para home
-
- Monynha Softwares
-
+ Monynha Softwares
Faça login ou crie sua conta
-
+ {
+ setAuthView(value as AuthView);
+ setShowPassword(false);
+ }}
+ className="w-full"
+ >
Login
Cadastro
@@ -132,26 +161,38 @@ const Auth = () => {
- {/* Mission Section */}
-
-
-
- {t('about.missionTitle')}
-
-
- {t('about.mission')}
-
+ {/* Mission & Values Section */}
+
+
+
+
+
+
+ {t('about.missionTitle')}
+
+
+
+ {t('about.description')}
+
+
+ {t('about.mission')}
+
+
+
+
+
+ {t('about.valuesTitle')}
+
+
+ {t('about.valuesDescription')}
+
+
+
+ {values.map((value, index) => (
+
+
+
+
+
+
+
+ {value.title}
+
+
+ {value.description}
+
+
+
+
+ ))}
+
+
+
@@ -82,84 +217,175 @@ const About = () => {
-
-
- {t('about.storyTitle')}
-
-
-
{t('about.story.p1')}
-
{t('about.story.p2')}
-
{t('about.story.p3')}
+
+
+
+ {t('about.storyTitle')}
+
+
+ {t('about.title')}
+
+
+ {storyTimeline.map((story, index) => (
+ -
+
+ {(index + 1).toString().padStart(2, '0')}
+
+
+ {story}
+
+
+ ))}
+
-
-
-
-
+
+
+
+

+
+
- {/* Values Section */}
+ {/* Stats Section */}
- {t('about.valuesTitle')}
+ {t('about.impactTitle')}
- {t('about.valuesDescription')}
+ {t('about.impactDescription')}
-
- {values.map((value, index) => (
-
-
-
-
+ {isStatsLoading ? (
+
+
{t('about.statsLoading')}
+ {Array.from({ length: 4 }).map((_, index) => (
+
+ ))}
+
+ ) : statsError ? (
+
{t('about.statsError')}
+ ) : aboutStats && aboutStats.length > 0 ? (
+
+ {aboutStats.map((stat, index) => (
+
+
+ {stat.number}
-
- {value.title}
-
- {value.description}
-
-
- ))}
-
+
{stat.label}
+
+ ))}
+
+ ) : (
+ {t('about.statsEmpty')}
+ )}
- {/* Stats Section */}
+ {/* Team Section */}
- {t('about.impactTitle')}
+ {t('team.title')}
-
- {t('about.impactDescription')}
+
+ {t('team.description')}
-
- {stats.map((stat, index) => (
-
-
- {stat.number}
-
-
{stat.label}
-
- ))}
-
+ {isTeamLoading ? (
+
+
{t('team.loading')}
+ {Array.from({ length: 4 }).map((_, index) => (
+
+
+
+ ))}
+
+ ) : teamError ? (
+
{t('team.error')}
+ ) : teamMembers && teamMembers.length > 0 ? (
+
+ {teamMembers.map((member) => (
+
+
+
+
+
+ {member.name
+ .split(' ')
+ .map((n) => n[0])
+ .join('')}
+
+
+
+
+ {member.name}
+
+
{member.role}
+
+
+
+ ))}
+
+ ) : (
+
{t('team.empty')}
+ )}
+
+
+
+ {/* CTA Section */}
+
+
+
+ {t('about.ctaTitle')}
+
+
+ {t('about.ctaDescription')}
+
+
diff --git a/src/pages/Blog.tsx b/src/pages/Blog.tsx
index 8901bc8..287c7e5 100644
--- a/src/pages/Blog.tsx
+++ b/src/pages/Blog.tsx
@@ -1,13 +1,19 @@
+import { useState, useMemo, useCallback } from 'react';
import { useQuery } from '@tanstack/react-query';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import Layout from '@/components/Layout';
import Meta from '@/components/Meta';
import NewsletterSection from '@/components/NewsletterSection';
-import { ArrowRight, Clock, User } from 'lucide-react';
+import {
+ ArrowRight,
+ Clock,
+ User,
+ ChevronLeft,
+ ChevronRight,
+} from 'lucide-react';
import { supabase } from '@/integrations/supabase';
-import { useTranslation, Trans } from 'react-i18next';
-import { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import {
Breadcrumb,
@@ -17,9 +23,85 @@ import {
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
+import {
+ Pagination,
+ PaginationContent,
+ PaginationItem,
+ PaginationLink,
+ PaginationEllipsis,
+} from '@/components/ui/pagination';
+import { cn } from '@/lib/utils';
+import { getNormalizedLocale } from '@/lib/i18n';
+import type { Database } from '@/integrations/supabase/types';
+
+const FALLBACK_IMAGE =
+ 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80';
+
+const POSTS_PER_PAGE = 7;
+
+type BlogPostRow = Pick<
+ Database['public']['Tables']['blog_posts']['Row'],
+ 'id' | 'slug' | 'title' | 'excerpt' | 'image_url' | 'updated_at'
+>;
+
+type PaginationEntry = number | 'ellipsis';
+
+const getPaginationRange = (
+ currentPage: number,
+ totalPages: number
+): PaginationEntry[] => {
+ if (totalPages <= 1) {
+ return [1];
+ }
+
+ const range: PaginationEntry[] = [];
+ const delta = 1;
+ const left = Math.max(2, currentPage - delta);
+ const right = Math.min(totalPages - 1, currentPage + delta);
+
+ range.push(1);
+
+ if (left > 2) {
+ range.push('ellipsis');
+ }
+
+ for (let page = left; page <= right; page += 1) {
+ range.push(page);
+ }
+
+ if (right < totalPages - 1) {
+ range.push('ellipsis');
+ }
+
+ if (totalPages > 1) {
+ range.push(totalPages);
+ }
+
+ return range;
+};
+
const Blog = () => {
- const { t } = useTranslation();
+ const { t, i18n } = useTranslation();
+ const [page, setPage] = useState(1);
+
+ const normalizedLocale = useMemo(
+ () => getNormalizedLocale(i18n.language),
+ [i18n.language]
+ );
+
+ const dateFormatter = useMemo(() => {
+ try {
+ return new Intl.DateTimeFormat(normalizedLocale, {
+ dateStyle: 'medium',
+ });
+ } catch (error) {
+ console.error('Unsupported locale for blog date formatting', error);
+ return new Intl.DateTimeFormat('en-US', {
+ dateStyle: 'medium',
+ });
+ }
+ }, [normalizedLocale]);
const categories = useMemo(
() => [
@@ -34,48 +116,86 @@ const Blog = () => {
[t]
);
- const {
- data: postsData,
- isLoading,
- isError,
- } = useQuery({
- queryKey: ['blog_posts'],
+ const { data, isLoading, isError } = useQuery<{
+ posts: BlogPostRow[];
+ total: number;
+ }>({
+ queryKey: ['blog_posts', page],
+ keepPreviousData: true,
queryFn: async () => {
- const { data, error } = await supabase
+ const from = (page - 1) * POSTS_PER_PAGE;
+ const to = from + POSTS_PER_PAGE - 1;
+
+ const { data, error, count } = await supabase
.from('blog_posts')
- .select('id, slug, title, excerpt, image_url, updated_at')
+ .select('id, slug, title, excerpt, image_url, updated_at', {
+ count: 'exact',
+ })
.eq('published', true)
- .order('updated_at', { ascending: false });
+ .order('updated_at', { ascending: false })
+ .range(from, to);
if (error) {
throw new Error(error.message);
}
- return (
- data?.map((post, index) => ({
- title: post.title,
- excerpt: post.excerpt || 'Read more about this topic...',
- image:
- post.image_url ||
- 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80',
- author: 'Monynha Softwares Team',
- date: new Date(post.updated_at).toLocaleDateString('pt-BR', {
- year: 'numeric',
- month: 'long',
- day: 'numeric',
- }),
- readTime: '5 min read',
- category: 'AI Insights',
- featured: index === 0,
- slug: post.slug,
- })) || []
- );
+ return {
+ posts: data ?? [],
+ total: count ?? 0,
+ };
},
});
- const posts = postsData || [];
+ const totalPosts = data?.total ?? 0;
+ const totalPages = totalPosts > 0 ? Math.ceil(totalPosts / POSTS_PER_PAGE) : 1;
+
+ const formattedPosts = useMemo(
+ () =>
+ (data?.posts ?? []).map((post, index) => ({
+ id: post.id,
+ slug: post.slug,
+ title: post.title,
+ excerpt: post.excerpt ?? t('blog.fallbackExcerpt'),
+ image: post.image_url ?? FALLBACK_IMAGE,
+ author: 'Monynha Softwares Team',
+ date: dateFormatter.format(new Date(post.updated_at)),
+ readTime: t('blog.readTimeDefault'),
+ category: 'AI Insights',
+ featured: page === 1 && index === 0,
+ })),
+ [data?.posts, dateFormatter, page, t]
+ );
+
+ const featuredPost = page === 1 ? formattedPosts[0] : undefined;
+ const regularPosts = page === 1 ? formattedPosts.slice(1) : formattedPosts;
+
+ const paginationRange = useMemo(
+ () => getPaginationRange(page, totalPages),
+ [page, totalPages]
+ );
+
+ const showingRangeStart =
+ totalPosts === 0 ? 0 : (page - 1) * POSTS_PER_PAGE + 1;
+ const showingRangeEnd =
+ totalPosts === 0
+ ? 0
+ : Math.min(showingRangeStart + formattedPosts.length - 1, totalPosts);
+
+ const handlePageChange = useCallback(
+ (newPage: number) => {
+ if (newPage < 1 || newPage > totalPages || newPage === page) {
+ return;
+ }
+
+ setPage(newPage);
+ if (typeof window !== 'undefined') {
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }
+ },
+ [page, totalPages]
+ );
- if (isLoading) {
+ if (isLoading && !data) {
return (
{
ogImage="/placeholder.svg"
/>
- Loading...
+ {t('blog.loading')}
);
@@ -103,7 +223,7 @@ const Blog = () => {
ogImage="/placeholder.svg"
/>
- Error loading posts
+ {t('blog.error')}
);
@@ -133,7 +253,6 @@ const Blog = () => {
- {/* Hero Section */}
@@ -147,19 +266,19 @@ const Blog = () => {
- {/* Categories Filter */}
{categories.map((category, index) => (
@@ -168,72 +287,79 @@ const Blog = () => {
- {/* Featured Post */}
- {posts
- .filter((post) => post.featured)
- .map((post, index) => (
-
-
-
-
-
-

-
-
- {t('blog.featured')}
-
-
+ {featuredPost && (
+
+
+
+
+
+

+
+
+ {t('blog.featured')}
+
-
-
-
- {post.category}
-
-
-
-
- {post.author}
-
-
-
- {post.readTime}
-
+
+
+
+
+ {featuredPost.category}
+
+
+
+
+ {featuredPost.author}
+
+
+
+ {featuredPost.readTime}
-
- {post.title}
-
-
- {post.excerpt}
-
-
+
+ {featuredPost.title}
+
+
+ {featuredPost.excerpt}
+
+
-
+
+
-
-
-
- ))}
+
+
+
+
+ )}
- {/* Blog Grid */}
-
- {posts
- .filter((post) => !post.featured)
- .map((post, index) => (
+ {totalPosts === 0 ? (
+
+ {t('blog.empty')}
+
+ ) : (
+
+ {regularPosts.map((post) => (
-
+

{
{post.category}
-
+
@@ -257,27 +383,110 @@ const Blog = () => {
{post.readTime}
-
+
{post.title}
-
+
{post.excerpt}
))}
-
+
+ )}
- {/* Newsletter CTA */}
+ {totalPosts > POSTS_PER_PAGE && (
+
+
+
+
+ {t('blog.pagination.showing', {
+ start: showingRangeStart,
+ end: showingRangeEnd,
+ total: totalPosts,
+ })}
+
+
+
+
+ {
+ event.preventDefault();
+ handlePageChange(page - 1);
+ }}
+ >
+
+ {t('blog.pagination.previous')}
+
+
+ {paginationRange.map((item, index) => (
+
+ {item === 'ellipsis' ? (
+
+ ) : (
+ {
+ event.preventDefault();
+ handlePageChange(item);
+ }}
+ >
+ {item}
+
+ )}
+
+ ))}
+
+ {
+ event.preventDefault();
+ handlePageChange(page + 1);
+ }}
+ >
+ {t('blog.pagination.next')}
+
+
+
+
+
+
+
+
+ )}
+
diff --git a/src/pages/Projects.tsx b/src/pages/Projects.tsx
index dae50b1..3d210d2 100644
--- a/src/pages/Projects.tsx
+++ b/src/pages/Projects.tsx
@@ -1,5 +1,12 @@
+import { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
-import { Github, ExternalLink, Calendar } from 'lucide-react';
+import {
+ Github,
+ ExternalLink,
+ Calendar,
+ ArrowRight,
+ CheckCircle,
+} from 'lucide-react';
import Layout from '../components/Layout';
import Meta from '@/components/Meta';
import { Button } from '@/components/ui/button';
@@ -16,6 +23,14 @@ import {
import { supabase } from '@/integrations/supabase';
import { useTranslation, Trans } from 'react-i18next';
import { Link } from 'react-router-dom';
+import useRepositorySync from '@/hooks/useRepositorySync';
+import {
+ fetchSupabaseSolutions,
+ getFallbackSolutions,
+ mapGitHubRepoToContent,
+} from '@/lib/solutions';
+import type { GitHubRepository } from '@/lib/solutions';
+import type { SolutionContent } from '@/types/solutions';
interface Repository {
id: string;
@@ -23,18 +38,24 @@ interface Repository {
description: string;
github_url: string;
demo_url: string | null;
- tags: string[];
+ tags: string[] | null;
created_at: string;
}
+const GITHUB_REPOS_URL =
+ 'https://api.github.com/orgs/Monynha-Softwares/repos?per_page=100';
+
const Projects = () => {
const { t } = useTranslation();
+ useRepositorySync();
+
+ const memoizedFallbackSolutions = useMemo(() => getFallbackSolutions(), []);
const {
data: repositories = [],
- isLoading,
- isError,
- } = useQuery({
+ isLoading: repositoriesLoading,
+ isError: repositoriesError,
+ } = useQuery({
queryKey: ['repositories'],
queryFn: async () => {
const { data, error } = await supabase
@@ -47,19 +68,115 @@ const Projects = () => {
throw new Error(error.message);
}
- return data || [];
+ return (data ?? []) as Repository[];
},
});
- const formatDate = (dateString: string) => {
- return new Date(dateString).toLocaleDateString('en-US', {
- year: 'numeric',
- month: 'short',
- day: 'numeric',
- });
- };
+ const {
+ data: supabaseSolutions = [],
+ isLoading: supabaseSolutionsLoading,
+ isError: supabaseSolutionsError,
+ } = useQuery({
+ queryKey: ['solutions'],
+ queryFn: fetchSupabaseSolutions,
+ staleTime: 1000 * 60 * 10,
+ retry: 1,
+ refetchOnWindowFocus: false,
+ });
+
+ // Removed duplicate githubSolutions useQuery block
+
+ // Removed duplicate memoizedFallbackSolutions declaration
+
+ const {
+ data: githubSolutions = [],
+ isLoading: isGitHubLoading,
+ isError: isGitHubError,
+ } = useQuery({
+ queryKey: ['projects-github-solutions'],
+ queryFn: async () => {
+ const response = await fetch(GITHUB_REPOS_URL, {
+ headers: {
+ Accept: 'application/vnd.github+json',
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`GitHub API responded with ${response.status}`);
+ }
+
+ const repositories: GitHubRepository[] = await response.json();
+
+ const toTimestamp = (repository: GitHubRepository) => {
+ const reference =
+ repository.pushed_at ?? repository.updated_at ?? repository.created_at;
+ const timestamp = new Date(reference).getTime();
+ return Number.isNaN(timestamp) ? 0 : timestamp;
+ };
+
+ const activeRepositories = repositories.filter(
+ (repository) =>
+ !repository.private && !repository.archived && !repository.disabled
+ );
+
+ const sortedRepositories = activeRepositories
+ .slice()
+ .sort((a, b) => toTimestamp(b) - toTimestamp(a));
+
+ return sortedRepositories.map((repository, index) =>
+ mapGitHubRepoToContent(repository, index)
+ );
+ },
+ staleTime: 1000 * 60 * 10,
+ retry: 1,
+ refetchOnWindowFocus: false,
+ });
+
+ const combinedSolutions = useMemo(() => {
+ const seen = new Set();
+ const merged: SolutionContent[] = [];
+
+ const addSolution = (solution: SolutionContent) => {
+ if (!solution) {
+ return;
+ }
+
+ const key = solution.slug
+ ? solution.slug.toLowerCase()
+ : solution.title.toLowerCase();
+
+ if (seen.has(key)) {
+ return;
+ }
+ seen.add(key);
+ merged.push(solution);
+ };
+
+ // Add solutions from supabase
+ if (Array.isArray(supabaseSolutions)) {
+ supabaseSolutions.forEach(addSolution);
+ }
+
+ // Add solutions from GitHub
+ if (Array.isArray(githubSolutions)) {
+ githubSolutions.forEach(addSolution);
+ }
+
+ // Add fallback solutions if nothing else
+ if (merged.length === 0 && Array.isArray(memoizedFallbackSolutions)) {
+ memoizedFallbackSolutions.forEach(addSolution);
+ }
+
+ return merged;
+ }, [supabaseSolutions, githubSolutions, memoizedFallbackSolutions]);
+
+ const displayGitHubSolutions =
+ Array.isArray(githubSolutions) && githubSolutions.length > 0
+ ? githubSolutions
+ : memoizedFallbackSolutions;
+
+ if (repositoriesLoading) {
- if (isLoading) {
return (
{
);
}
- if (isError) {
+ const formatDate = (dateString: string) =>
+ new Date(dateString).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ });
+
+ if (repositoriesError && supabaseSolutionsError) {
return (
{
- {/* Projects Grid */}
-
- {repositories.map((repo) => (
-
-
-
-
- {repo.name}
-
-
-
- {formatDate(repo.created_at)}
-
-
-
- {repo.description}
-
-
-
-
- {/* Tags */}
- {repo.tags && repo.tags.length > 0 && (
-
- {repo.tags.map((tag, index) => (
-
+
+
+
+ {t('solutionsPage.title')}
+
+
+ {t('solutionsPage.description')}
+
+
+
+ {supabaseSolutionsLoading ? (
+
+ Loading solutions...
+
+ ) : (
+
+ {Array.isArray(combinedSolutions) && combinedSolutions.length > 0
+ ? combinedSolutions.map((solution) => (
+
- {tag}
-
- ))}
-
- )}
-
- {/* Action Buttons */}
-
+ )}
+
+ {supabaseSolutionsError && (
+
+ Error loading solutions
+
+ )}
+
+
+
+ {/* Projects Grid */}
+
+ {repositoriesError && repositories.length === 0 ? (
+
+ Error loading projects
+
+ ) : (
+
+ {repositories.map((repo) => (
+
+
+
+
+ {repo.name}
+
+
+
+ {formatDate(repo.created_at)}
+
+
+
+ {repo.description}
+
+
+
+
+ {/* Tags */}
+ {repo.tags && repo.tags.length > 0 && (
+
+ {repo.tags.map((tag, index) => (
+
+ {tag}
+
+ ))}
+
+ )}
+
+ {/* Action Buttons */}
+
+
- )}
-
-
-
- ))}
-
+
+
+ {t('projects.viewGithub')}
+
+
+
+ {repo.demo_url && (
+
+ )}
+
+
+
+ ))}
+
+ )}
+
+ {repositoriesError && repositories.length > 0 && (
+
+ Error loading additional projects
+
+ )}
+
{/* Call to Action */}
@@ -235,8 +412,113 @@ const Projects = () => {
+
+
+
+
+
+ {t('solutionsPage.title')}
+
+
+ {t('solutionsPage.description')}
+
+
+
+ {isGitHubLoading ? (
+
Loading...
+ ) : isGitHubError ? (
+
+ Error loading GitHub projects
+
+ ) : (
+
+ {displayGitHubSolutions.map((solution) => (
+
+ {solution.imageUrl && (
+
+

+
+
+ )}
+
+
+
+
+ {solution.title}
+
+
+
+ {solution.description}
+
+
+ {solution.features.length > 0 && (
+
+ {solution.features.map((feature, featureIndex) => (
+ -
+
+
+
+
+ {feature}
+
+
+ ))}
+
+ )}
+
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+
);
-};
+}
export default Projects;
diff --git a/src/pages/Solutions.tsx b/src/pages/Solutions.tsx
index e881e01..a1c8094 100644
--- a/src/pages/Solutions.tsx
+++ b/src/pages/Solutions.tsx
@@ -1,3 +1,4 @@
+import { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
@@ -12,186 +13,54 @@ import {
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
-import {
- ArrowRight,
- BarChart3,
- Users,
- Zap,
- Shield,
- Brain,
- MessageSquare,
- Calendar,
- Settings,
-} from 'lucide-react';
-import { supabase } from '@/integrations/supabase';
+import { ArrowRight, CheckCircle } from 'lucide-react';
import { useTranslation } from 'react-i18next';
+import { supabase } from '@/integrations/supabase';
+import {
+ getFallbackSolutions,
+ mapSupabaseSolutionToContent,
+} from '@/lib/solutions';
-const fallbackSolutions = [
- {
- name: 'Boteco Pro',
- tagline: 'Restaurant & Bar Management System',
- description:
- 'Complete management solution for restaurants and bars with AI-powered analytics, inventory management, and customer insights.',
- image:
- 'https://images.unsplash.com/photo-1514933651103-005eec06c04b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
- features: [
- {
- icon: BarChart3,
- title: 'Real-time Analytics',
- description:
- 'Track sales, inventory, and customer behavior in real-time',
- },
- {
- icon: Users,
- title: 'Staff Management',
- description: 'Efficient scheduling and performance tracking',
- },
- {
- icon: Zap,
- title: 'Quick Order Processing',
- description: 'Streamlined POS system with mobile integration',
- },
- {
- icon: Shield,
- title: 'Secure Payments',
- description: 'Multiple payment methods with fraud protection',
- },
- ],
- gradient: 'from-brand-purple to-brand-blue',
- color: 'brand-purple',
- },
- {
- name: 'AssisTina AI',
- tagline: 'Personalized AI Assistant',
- description:
- 'Intelligent AI assistant that learns your business processes and automates routine tasks, increasing efficiency and productivity.',
- image:
- 'https://images.unsplash.com/photo-1677442136019-21780ecad995?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
- features: [
- {
- icon: Brain,
- title: 'Machine Learning',
- description: 'Continuously learns and adapts to your workflow',
- },
- {
- icon: MessageSquare,
- title: 'Natural Language',
- description: 'Communicate naturally with voice and text',
- },
- {
- icon: Calendar,
- title: 'Task Automation',
- description: 'Automate scheduling, reminders, and follow-ups',
- },
- {
- icon: Settings,
- title: 'Custom Integration',
- description: 'Seamlessly integrates with your existing tools',
- },
- ],
- gradient: 'from-brand-pink to-brand-orange',
- color: 'brand-pink',
- },
-];
+import type { SolutionContent } from '@/types/solutions';
const Solutions = () => {
const { t } = useTranslation();
+ const memoizedFallbackSolutions = useMemo(
+ () => getFallbackSolutions(),
+ []
+ );
+
const {
data: solutions = [],
isLoading,
isError,
- } = useQuery({
+ } = useQuery
({
queryKey: ['solutions'],
queryFn: async () => {
const { data, error } = await supabase
.from('solutions')
.select('*')
.eq('active', true)
- .order('created_at', { ascending: true });
+ .order('created_at', { ascending: false });
+
if (error) {
throw new Error(error.message);
}
- return (
- data?.map((solution, index) => {
- const getFeatures = (slug: string) => {
- if (slug === 'boteco-pro') {
- return [
- {
- icon: BarChart3,
- title: 'Real-time Analytics',
- description:
- 'Track sales, inventory, and customer behavior in real-time',
- },
- {
- icon: Users,
- title: 'Staff Management',
- description: 'Efficient scheduling and performance tracking',
- },
- {
- icon: Zap,
- title: 'Quick Order Processing',
- description: 'Streamlined POS system with mobile integration',
- },
- {
- icon: Shield,
- title: 'Secure Payments',
- description: 'Multiple payment methods with fraud protection',
- },
- ];
- } else {
- return [
- {
- icon: Brain,
- title: 'Machine Learning',
- description:
- 'Continuously learns and adapts to your workflow',
- },
- {
- icon: MessageSquare,
- title: 'Natural Language',
- description: 'Communicate naturally with voice and text',
- },
- {
- icon: Calendar,
- title: 'Task Automation',
- description: 'Automate scheduling, reminders, and follow-ups',
- },
- {
- icon: Settings,
- title: 'Custom Integration',
- description: 'Seamlessly integrates with your existing tools',
- },
- ];
- }
- };
- return {
- name: solution.title,
- tagline:
- solution.slug === 'boteco-pro'
- ? 'Restaurant & Bar Management System'
- : 'Personalized AI Assistant',
- description: solution.description,
- slug: solution.slug,
- image:
- solution.image_url ||
- (solution.slug === 'boteco-pro'
- ? 'https://images.unsplash.com/photo-1514933651103-005eec06c04b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80'
- : 'https://images.unsplash.com/photo-1677442136019-21780ecad995?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80'),
- features: getFeatures(solution.slug),
- gradient:
- solution.slug === 'boteco-pro'
- ? 'from-brand-purple to-brand-blue'
- : 'from-brand-pink to-brand-orange',
- color:
- solution.slug === 'boteco-pro' ? 'brand-purple' : 'brand-pink',
- };
- }) || []
+ return (data ?? []).map((solution, index) =>
+ mapSupabaseSolutionToContent(solution, { index })
);
},
+
+ staleTime: 1000 * 60 * 10,
+ retry: 1,
+ refetchOnWindowFocus: false,
});
+ const displaySolutions =
+ solutions.length > 0 ? solutions : memoizedFallbackSolutions;
+
if (isLoading) {
return (
@@ -265,79 +134,92 @@ const Solutions = () => {
{/* Solutions Detail */}
- {(solutions.length > 0 ? solutions : fallbackSolutions).map(
- (solution, index) => (
-
-
-
+
+
+ {displaySolutions.map((solution) => (
+
-
+ {solution.imageUrl && (
+
+

+
+
+ )}
+
-
- {solution.name}
-
-
- {solution.tagline}
-
-
+ className={`h-1 w-16 bg-gradient-to-r ${solution.gradient} rounded-full mb-6`}
+ />
+
+
+ {solution.title}
+
+
+
{solution.description}
-
- {solution.features.map((feature, featureIndex) => (
-
-
0 && (
+
+ {solution.features.map((feature, featureIndex) => (
+ -
-
-
-
-
- {feature.title}
-
-
- {feature.description}
-
-
-
- ))}
-
+
+
+
+
+ {feature}
+
+
+ ))}
+
+ )}
-
-
-
-
- )
- )}
+
+
+ {t('solutionsPage.requestDemo')}
+
+
+
+
+
+
+ ))}
+
+
+
{/* Custom Solutions CTA */}
diff --git a/src/pages/blog/[slug].tsx b/src/pages/blog/[slug].tsx
new file mode 100644
index 0000000..a8c04ca
--- /dev/null
+++ b/src/pages/blog/[slug].tsx
@@ -0,0 +1,435 @@
+import React, { ReactNode, useMemo } from 'react';
+import { useParams, Link } from 'react-router-dom';
+import { useQuery } from '@tanstack/react-query';
+import { useTranslation } from 'react-i18next';
+import { ArrowLeft, Clock, User } from 'lucide-react';
+import Layout from '@/components/Layout';
+import Meta from '@/components/Meta';
+import NewsletterSection from '@/components/NewsletterSection';
+import CommentsSection from '@/components/blog/CommentsSection';
+import { supabase } from '@/integrations/supabase';
+import { getNormalizedLocale } from '@/lib/i18n';
+import type { Database } from '@/integrations/supabase/types';
+import {
+ Breadcrumb,
+ BreadcrumbList,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from '@/components/ui/breadcrumb';
+
+type BlogPost = Database['public']['Tables']['blog_posts']['Row'];
+
+type LexicalNode = {
+ type: string;
+ text?: string;
+ tag?: string;
+ url?: string;
+ format?: number | string;
+ children?: LexicalNode[];
+};
+
+const headingStyles: Record = {
+ h1: 'text-4xl font-bold text-neutral-900',
+ h2: 'text-3xl font-bold text-neutral-900',
+ h3: 'text-2xl font-semibold text-neutral-900',
+ h4: 'text-xl font-semibold text-neutral-900',
+ h5: 'text-lg font-semibold text-neutral-900',
+ h6: 'text-lg font-semibold text-neutral-900',
+ default: 'text-3xl font-bold text-neutral-900',
+};
+
+const renderNodes = (
+ nodes: LexicalNode[] | undefined,
+ keyPrefix: string
+): ReactNode[] =>
+ (nodes ?? []).map((child, index) =>
+ renderLexicalNode(child, `${keyPrefix}-${index}`)
+ );
+
+const renderTextNode = (node: LexicalNode, key: string): ReactNode => {
+ let content: ReactNode = node.text ?? '';
+ let wrapIndex = 0;
+
+ const wrapWith = (tag: keyof JSX.IntrinsicElements, className?: string) => {
+ wrapIndex += 1;
+ content = React.createElement(
+ tag,
+ { key: `${key}-${tag}-${wrapIndex}`, className },
+ content
+ );
+ };
+
+ const { format } = node;
+
+ if (typeof format === 'number') {
+ if ((format & 16) === 16) {
+ wrapWith(
+ 'code',
+ 'rounded bg-neutral-900 px-1.5 py-0.5 font-mono text-sm text-white'
+ );
+ } else {
+ if ((format & 1) === 1) wrapWith('strong');
+ if ((format & 2) === 2) wrapWith('em');
+ if ((format & 4) === 4) wrapWith('span', 'underline');
+ if ((format & 8) === 8) wrapWith('span', 'line-through');
+ }
+ } else if (typeof format === 'string') {
+ const tokens = format.split(' ');
+ if (tokens.includes('code')) {
+ wrapWith(
+ 'code',
+ 'rounded bg-neutral-900 px-1.5 py-0.5 font-mono text-sm text-white'
+ );
+ }
+ if (tokens.includes('bold')) wrapWith('strong');
+ if (tokens.includes('italic')) wrapWith('em');
+ if (tokens.includes('underline')) wrapWith('span', 'underline');
+ if (tokens.includes('strikethrough'))
+ wrapWith('span', 'line-through');
+ }
+
+ if (typeof content === 'string') {
+ content = (
+
+ {content}
+
+ );
+ }
+
+ return {content};
+};
+
+const renderLexicalNode = (node: LexicalNode, key: string): ReactNode => {
+ switch (node.type) {
+ case 'root':
+ return {renderNodes(node.children, key)};
+ case 'paragraph':
+ return (
+
+ {renderNodes(node.children, key)}
+
+ );
+ case 'heading': {
+ const Tag = (node.tag ?? 'h2') as keyof JSX.IntrinsicElements;
+ const baseClass = headingStyles[node.tag ?? 'default'] ?? headingStyles.default;
+ return (
+
+ {renderNodes(node.children, key)}
+
+ );
+ }
+ case 'quote':
+ return (
+
+ {renderNodes(node.children, key)}
+
+ );
+ case 'list': {
+ const ListTag = node.tag === 'ol' ? 'ol' : 'ul';
+ const listClasses =
+ ListTag === 'ol'
+ ? 'ml-6 list-decimal space-y-2 text-neutral-700'
+ : 'ml-6 list-disc space-y-2 text-neutral-700';
+ return (
+
+ {renderNodes(node.children, key)}
+
+ );
+ }
+ case 'listitem':
+ return (
+
+ {renderNodes(node.children, key)}
+
+ );
+ case 'code': {
+ const codeChildren = node.text
+ ? node.text
+ : renderNodes(node.children, `${key}-code`);
+ return (
+
+ {codeChildren}
+
+ );
+ }
+ case 'linebreak':
+ return
;
+ case 'link':
+ return (
+
+ {renderNodes(node.children, key)}
+
+ );
+ case 'text':
+ return renderTextNode(node, key);
+ default:
+ if (node.children) {
+ return {renderNodes(node.children, key)};
+ }
+ return null;
+ }
+};
+
+const renderBlogContent = (content: string): ReactNode[] => {
+ if (!content) {
+ return [];
+ }
+
+ try {
+ const parsed = JSON.parse(content);
+ if (parsed && typeof parsed === 'object' && 'root' in parsed) {
+ const root = parsed as { root?: { children?: LexicalNode[] } };
+ return renderNodes(root.root?.children, 'node');
+ }
+ } catch (error) {
+ console.warn('Unable to parse rich text content, falling back to plain text.', error);
+ }
+
+ return content
+ .split(/\n{2,}/)
+ .filter((paragraph) => paragraph.trim().length > 0)
+ .map((paragraph, index) => (
+
+ {paragraph.trim()}
+
+ ));
+};
+
+const BlogPostPage = () => {
+ const { slug } = useParams<{ slug: string }>();
+ const { t, i18n } = useTranslation();
+
+ const normalizedLocale = useMemo(
+ () => getNormalizedLocale(i18n.language),
+ [i18n.language]
+ );
+
+ const dateFormatter = useMemo(() => {
+ try {
+ return new Intl.DateTimeFormat(normalizedLocale, {
+ dateStyle: 'long',
+ });
+ } catch (error) {
+ console.error('Unsupported locale for blog post date formatting', error);
+ return new Intl.DateTimeFormat('en-US', { dateStyle: 'long' });
+ }
+ }, [normalizedLocale]);
+
+ const {
+ data: post,
+ isLoading,
+ isError,
+ } = useQuery({
+ queryKey: ['blog_post', slug],
+ enabled: Boolean(slug),
+ queryFn: async () => {
+ if (!slug) {
+ return null;
+ }
+
+ const { data, error } = await supabase
+ .from('blog_posts')
+ .select('id, slug, title, content, excerpt, image_url, updated_at, created_at, published')
+ .eq('slug', slug)
+ .eq('published', true)
+ .maybeSingle();
+
+ if (error) {
+ throw new Error(error.message);
+ }
+
+ return data;
+ },
+ });
+
+ const renderedContent = useMemo(
+ () => renderBlogContent(post?.content ?? ''),
+ [post?.content]
+ );
+
+ const updatedDate = post
+ ? dateFormatter.format(new Date(post.updated_at))
+ : '';
+
+ const metaTitle = post
+ ? `${post.title} - Monynha Softwares Agency`
+ : 'Insights & Updates - Monynha Softwares Agency';
+
+ const metaDescription = post?.excerpt ?? t('blog.description');
+ const metaImage = post?.image_url ?? '/placeholder.svg';
+ const defaultAuthor = 'Monynha Softwares Team';
+ const readTimeLabel = t('blog.readTimeDefault');
+
+ if (isLoading) {
+ return (
+
+
+
+ {t('blog.post.loading')}
+
+
+ );
+ }
+
+ if (isError) {
+ return (
+
+
+
+ {t('blog.post.error')}
+
+
+ );
+ }
+
+ if (!post) {
+ return (
+
+
+
+
{t('blog.post.notFound')}
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+ {t('navigation.home')}
+
+
+
+
+
+ {t('navigation.blog')}
+
+
+
+
+ {post.title}
+
+
+
+
+
+
+
+
+ {t('blog.post.back')}
+
+
+
+
+
+ {post.image_url && (
+
+

+
+ )}
+
+
+ {renderedContent.length > 0 ? (
+ renderedContent
+ ) : (
+
{post.content}
+ )}
+
+
+
+
+
+
+
+
+ );
+};
+
+const ButtonLink = () => {
+ const { t } = useTranslation();
+ return (
+
+ {t('blog.post.back')}
+
+ );
+};
+
+export default BlogPostPage;
diff --git a/src/pages/solutions/[slug].tsx b/src/pages/solutions/[slug].tsx
new file mode 100644
index 0000000..01859ca
--- /dev/null
+++ b/src/pages/solutions/[slug].tsx
@@ -0,0 +1,291 @@
+import { useMemo } from 'react';
+import { useParams, Link } from 'react-router-dom';
+import { useQuery } from '@tanstack/react-query';
+import Layout from '@/components/Layout';
+import Meta from '@/components/Meta';
+import { Button } from '@/components/ui/button';
+import { Card } from '@/components/ui/card';
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from '@/components/ui/breadcrumb';
+import { ArrowLeft, ArrowRight, CheckCircle } from 'lucide-react';
+import { useTranslation } from 'react-i18next';
+import { supabase } from '@/integrations/supabase';
+import type { GitHubRepository } from '@/lib/solutions';
+import type { SolutionContent } from '@/types/solutions';
+import {
+ getFallbackSolution,
+ mapGitHubRepoToContent,
+ mapSupabaseSolutionToContent,
+} from '@/lib/solutions';
+
+const getRepositoryUrl = (repositorySlug: string) =>
+ `https://api.github.com/repos/Monynha-Softwares/${encodeURIComponent(
+ repositorySlug
+ )}`;
+
+const SolutionDetail = () => {
+ const { slug } = useParams<{ slug: string }>();
+ const { t } = useTranslation();
+
+ const fallbackSolution = useMemo(
+ () => (slug ? getFallbackSolution(slug) : undefined),
+ [slug]
+ );
+
+ const {
+ data: solution,
+ isLoading,
+ isError,
+ } = useQuery({
+ queryKey: ['solution', slug],
+ enabled: Boolean(slug),
+ queryFn: async () => {
+ if (!slug) {
+ return null;
+ }
+
+ const { data, error } = await supabase
+ .from('solutions')
+ .select('*')
+ .eq('slug', slug)
+ .eq('active', true)
+ .maybeSingle();
+
+ if (error) {
+ throw new Error(error.message);
+ }
+
+ if (data) {
+ return mapSupabaseSolutionToContent(data);
+ }
+
+ const response = await fetch(getRepositoryUrl(slug), {
+ headers: {
+ Accept: 'application/vnd.github+json',
+ },
+ });
+
+ if (response.status === 404) {
+ return null;
+ }
+
+ if (!response.ok) {
+ throw new Error(`GitHub API responded with ${response.status}`);
+ }
+
+ const repository: GitHubRepository = await response.json();
+
+ if (repository.private || repository.archived || repository.disabled) {
+ return null;
+ }
+
+ return mapGitHubRepoToContent(repository, 0);
+ },
+ staleTime: 1000 * 60 * 10,
+ retry: 1,
+ keepPreviousData: true,
+ refetchOnWindowFocus: false,
+ });
+
+ const displaySolution = solution ?? fallbackSolution ?? null;
+
+ if (isLoading) {
+ return (
+
+
+ Loading...
+
+ );
+ }
+
+ if (isError) {
+ return (
+
+
+
+ Error loading solution
+
+
+ );
+ }
+
+ if (!displaySolution) {
+ return (
+
+
+
+
+
+ {t('solutionsPage.notFound')}
+
+
+ {t('solutionsPage.description')}
+
+
+ {t('solutionsPage.backToSolutions')}
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+ {t('navigation.home')}
+
+
+
+
+
+ {t('navigation.solutions')}
+
+
+
+
+ {displaySolution.title}
+
+
+
+
+
+
+
+
+
+
+
+ {displaySolution.title}
+
+
+ {displaySolution.description}
+
+
+
+
+
+
+ {t('solutionsPage.backToSolutions')}
+
+
+
+
+ {t('solutionsPage.requestDemo')}
+
+
+
+
+
+
+ {displaySolution.imageUrl && (
+
+
+
+
+ )}
+
+
+
+
+ {displaySolution.features.length > 0 && (
+
+
+
+ {t('solutionsPage.featuresTitle')}
+
+
+ {displaySolution.features.map((feature, index) => (
+
+ ))}
+
+
+
+ )}
+
+
+
+
+ {t('solutionsPage.customTitle')}
+
+
+ {t('solutionsPage.customDescription')}
+
+
+
+ {t('solutionsPage.discuss')}
+
+
+
+
+
+
+ );
+};
+
+export default SolutionDetail;
diff --git a/src/types/solutions.ts b/src/types/solutions.ts
new file mode 100644
index 0000000..152eb69
--- /dev/null
+++ b/src/types/solutions.ts
@@ -0,0 +1,9 @@
+export interface SolutionContent {
+ id?: string;
+ title: string;
+ description: string;
+ slug: string;
+ imageUrl?: string | null;
+ features: string[];
+ gradient: string;
+}
diff --git a/supabase/migrations/20250801120000-create-comments-table.sql b/supabase/migrations/20250801120000-create-comments-table.sql
new file mode 100644
index 0000000..ed1a048
--- /dev/null
+++ b/supabase/migrations/20250801120000-create-comments-table.sql
@@ -0,0 +1,33 @@
+create table public.comments (
+ id uuid primary key default gen_random_uuid(),
+ post_id uuid not null references public.blog_posts(id) on delete cascade,
+ user_id uuid not null references auth.users(id) on delete cascade,
+ content text not null check (char_length(trim(content)) > 0),
+ created_at timestamptz not null default now()
+);
+
+alter table public.comments
+ add constraint comments_user_profile_fk
+ foreign key (user_id)
+ references public.profiles(user_id)
+ on delete cascade;
+
+create index if not exists comments_post_id_idx
+ on public.comments (post_id, created_at desc);
+
+create index if not exists comments_user_id_idx
+ on public.comments (user_id);
+
+alter table public.comments enable row level security;
+
+drop policy if exists "comments_select_public" on public.comments;
+create policy "comments_select_public" on public.comments
+for select
+to anon, authenticated
+using (true);
+
+drop policy if exists "comments_insert_authenticated" on public.comments;
+create policy "comments_insert_authenticated" on public.comments
+for insert
+to authenticated
+with check (auth.uid() = user_id);
From 2f99c9d0b94335c6b1f493ec69bc5408229498e5 Mon Sep 17 00:00:00 2001
From: Marcelo Santos <117441129+marcelo-m7@users.noreply.github.com>
Date: Fri, 17 Oct 2025 12:59:53 +0100
Subject: [PATCH 10/15] Refactor blog post IDs to Supabase UUIDs (#100)
---
AGENTS.md | 25 ++++++++
cms/src/collections/Posts.ts | 61 ++++++++++++++-----
docs/blog-posts-uuid-migration.md | 26 ++++++++
src/integrations/supabase/types.ts | 26 ++++----
...20250731120000-create-blog-posts-table.sql | 53 +++++++++++++++-
5 files changed, 163 insertions(+), 28 deletions(-)
create mode 100644 AGENTS.md
create mode 100644 docs/blog-posts-uuid-migration.md
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..89378f9
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,25 @@
+# Agent Notes
+
+## Supabase blog post UUID migration
+- Migration `20250731120000-create-blog-posts-table.sql` now enforces
+ `public.blog_posts.id` as a `uuid` with `gen_random_uuid()` and backfills any
+ legacy `text` identifiers (including cascading updates for `public.comments`).
+- Payload CMS posts now store their Supabase UUID in the hidden `supabaseId`
+ field; the after-change hook syncs both directions using Supabase-generated
+ IDs.
+- TypeScript definitions under `src/integrations/supabase/types.ts` expose a
+ shared `UUID` alias so frontend code treats `blog_posts` and `comments`
+ identifiers as UUID values.
+
+## Data cleanup summary
+- Legacy `text` IDs are renamed to `legacy_id` during the migration, random UUIDs
+ are generated per row, and comment foreign keys are rewritten before the
+ `legacy_id` column is dropped.
+
+## Next steps
+- Run the updated migration in every Supabase environment and verify that the
+ comment foreign-key backfill succeeds (no rows should retain `legacy_id`).
+- After deploying, execute an end-to-end blog comment publish flow (CMS ➜
+ Supabase ➜ `/blog/[slug]`) and confirm UUID identifiers propagate correctly.
+- Update any downstream services that previously referenced the numeric/text blog
+ IDs so they read the new UUID format instead.
diff --git a/cms/src/collections/Posts.ts b/cms/src/collections/Posts.ts
index e2f817c..069e0dc 100644
--- a/cms/src/collections/Posts.ts
+++ b/cms/src/collections/Posts.ts
@@ -5,8 +5,8 @@ import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
-const upsertIntoPublic = async ({ doc }: { doc: any }) => {
- const id = String(doc.id);
+const upsertIntoPublic = async ({ doc, req }: { doc: any; req: any }) => {
+ const supabaseId = doc.supabaseId ?? null;
const slug = doc.slug;
const published = !!doc.published;
const title =
@@ -23,19 +23,45 @@ const upsertIntoPublic = async ({ doc }: { doc: any }) => {
? doc.content
: JSON.stringify(doc.content ?? {});
- await pool.query(
- `insert into public.blog_posts (id, slug, title, excerpt, image_url, content, published, updated_at)
- values ($1,$2,$3,$4,$5,$6,$7, now())
- on conflict (id) do update set
- slug=excluded.slug,
- title=excluded.title,
- excerpt=excluded.excerpt,
- image_url=excluded.image_url,
- content=excluded.content,
- published=excluded.published,
- updated_at=now()`,
- [id, slug, title, excerpt, image_url, content, published]
- );
+ const queryWithId = `
+ insert into public.blog_posts (id, slug, title, excerpt, image_url, content, published, updated_at)
+ values ($1::uuid,$2,$3,$4,$5,$6,$7, now())
+ on conflict (id) do update set
+ slug=excluded.slug,
+ title=excluded.title,
+ excerpt=excluded.excerpt,
+ image_url=excluded.image_url,
+ content=excluded.content,
+ published=excluded.published,
+ updated_at=now()
+ returning id`;
+
+ const queryWithoutId = `
+ insert into public.blog_posts (slug, title, excerpt, image_url, content, published, updated_at)
+ values ($1,$2,$3,$4,$5,$6, now())
+ on conflict (slug) do update set
+ title=excluded.title,
+ excerpt=excluded.excerpt,
+ image_url=excluded.image_url,
+ content=excluded.content,
+ published=excluded.published,
+ updated_at=now()
+ returning id`;
+
+ const result = supabaseId
+ ? await pool.query(queryWithId, [supabaseId, slug, title, excerpt, image_url, content, published])
+ : await pool.query(queryWithoutId, [slug, title, excerpt, image_url, content, published]);
+
+ const generatedId = result?.rows?.[0]?.id;
+
+ if (!supabaseId && generatedId && req?.payload) {
+ await req.payload.update({
+ collection: 'posts',
+ id: doc.id,
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+ }
};
const Posts: CollectionConfig = {
@@ -48,6 +74,11 @@ const Posts: CollectionConfig = {
delete: ({ req }: { req: any }) => Boolean(req.user),
},
fields: [
+ {
+ name: 'supabaseId',
+ type: 'text',
+ admin: { hidden: true },
+ },
{ name: 'slug', type: 'text', required: true, unique: true },
{ name: 'title', type: 'text', required: true, localized: true },
{ name: 'excerpt', type: 'textarea', localized: true },
diff --git a/docs/blog-posts-uuid-migration.md b/docs/blog-posts-uuid-migration.md
new file mode 100644
index 0000000..42152de
--- /dev/null
+++ b/docs/blog-posts-uuid-migration.md
@@ -0,0 +1,26 @@
+# Blog Post UUID Migration
+
+This document tracks the changes applied to move `public.blog_posts.id` to the
+PostgreSQL `uuid` type generated with `gen_random_uuid()`.
+
+## Summary
+
+- Ensured the primary migration at
+ `supabase/migrations/20250731120000-create-blog-posts-table.sql` provisions the
+ table with a UUID primary key and backfills any legacy `text` identifiers.
+- Added resiliency to update existing comments so that foreign keys are
+ realigned after UUIDs are generated.
+- Updated the Payload CMS upsert hook to rely on Supabase-generated UUIDs and to
+ persist the assigned identifier back into the CMS entry.
+- Regenerated the local Supabase TypeScript definitions so that all blog post
+ and comment identifiers are typed as UUIDs in the frontend codebase.
+
+## Follow-up actions
+
+- Apply the migration to every Supabase environment and verify the data
+ realignment logs before and after deployment.
+- Run end-to-end testing for blog comment creation (CMS ➜ Supabase ➜ frontend)
+ once the migration is live.
+- Remove any stale `text` identifiers from downstream integrations or tooling
+ (for example, Zapier zaps or analytics exports) that might still reference the
+ legacy ID format.
diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts
index e33b4eb..f5a98a7 100644
--- a/src/integrations/supabase/types.ts
+++ b/src/integrations/supabase/types.ts
@@ -6,6 +6,8 @@ export type Json =
| { [key: string]: Json | undefined }
| Json[];
+export type UUID = string;
+
export type Database = {
// Allows to automatically instanciate createClient with right options
// instead of createClient(URL, KEY)
@@ -19,7 +21,7 @@ export type Database = {
content: string;
created_at: string;
excerpt: string | null;
- id: string;
+ id: UUID;
image_url: string | null;
published: boolean;
slug: string;
@@ -30,7 +32,7 @@ export type Database = {
content: string;
created_at?: string;
excerpt?: string | null;
- id?: string;
+ id?: UUID;
image_url?: string | null;
published?: boolean;
slug: string;
@@ -41,7 +43,7 @@ export type Database = {
content?: string;
created_at?: string;
excerpt?: string | null;
- id?: string;
+ id?: UUID;
image_url?: string | null;
published?: boolean;
slug?: string;
@@ -54,23 +56,23 @@ export type Database = {
Row: {
content: string;
created_at: string;
- id: string;
- post_id: string;
- user_id: string;
+ id: UUID;
+ post_id: UUID;
+ user_id: UUID;
};
Insert: {
content: string;
created_at?: string;
- id?: string;
- post_id: string;
- user_id: string;
+ id?: UUID;
+ post_id: UUID;
+ user_id: UUID;
};
Update: {
content?: string;
created_at?: string;
- id?: string;
- post_id?: string;
- user_id?: string;
+ id?: UUID;
+ post_id?: UUID;
+ user_id?: UUID;
};
Relationships: [];
};
diff --git a/supabase/migrations/20250731120000-create-blog-posts-table.sql b/supabase/migrations/20250731120000-create-blog-posts-table.sql
index 4f2c866..69488c8 100644
--- a/supabase/migrations/20250731120000-create-blog-posts-table.sql
+++ b/supabase/migrations/20250731120000-create-blog-posts-table.sql
@@ -1,5 +1,5 @@
create table if not exists public.blog_posts (
- id text primary key,
+ id uuid primary key default gen_random_uuid(),
slug text not null unique,
title text not null,
excerpt text,
@@ -17,3 +17,54 @@ create policy "blog_posts_select_public" on public.blog_posts
for select
to anon, public
using (true);
+
+do $$
+begin
+ if exists (
+ select 1
+ from information_schema.columns
+ where table_schema = 'public'
+ and table_name = 'blog_posts'
+ and column_name = 'id'
+ and data_type <> 'uuid'
+ ) then
+ alter table public.blog_posts drop constraint if exists blog_posts_pkey;
+ alter table public.blog_posts rename column id to legacy_id;
+
+ alter table public.blog_posts
+ add column id uuid default gen_random_uuid();
+
+ update public.blog_posts
+ set id = gen_random_uuid()
+ where id is null;
+
+ alter table public.blog_posts
+ alter column id set not null;
+
+ alter table public.blog_posts
+ add constraint blog_posts_pkey primary key (id);
+
+ if exists (
+ select 1
+ from information_schema.columns
+ where table_schema = 'public'
+ and table_name = 'comments'
+ and column_name = 'post_id'
+ ) then
+ alter table public.comments drop constraint if exists comments_post_id_fkey;
+
+ update public.comments c
+ set post_id = bp.id
+ from public.blog_posts bp
+ where c.post_id::text = bp.legacy_id;
+
+ alter table public.comments
+ add constraint comments_post_id_fkey
+ foreign key (post_id)
+ references public.blog_posts(id)
+ on delete cascade;
+ end if;
+
+ alter table public.blog_posts drop column legacy_id;
+ end if;
+end $$;
From 7c23360b008fce039468961573995c587876b0f1 Mon Sep 17 00:00:00 2001
From: Marcelo Santos <117441129+marcelo-m7@users.noreply.github.com>
Date: Sat, 18 Oct 2025 12:46:08 +0100
Subject: [PATCH 11/15] chore: align environment docs and verify migrations
(#102)
---
.env.example | 5 +-
.github/workflows/deploy.yml | 5 ++
AGENTS.md | 9 ++++
README.md | 49 ++++++++++++-----
cms/.env.example | 7 +--
cms/README.md | 38 +++++++++++++
cms/package.json | 1 +
docs/supabase-migration-verification.md | 54 +++++++++++++++++++
package.json | 5 +-
...3084803_create_leads_and_users_tables.sql} | 0
...50713085804_create_repositories_table.sql} | 0
...50715100836_create_team_members_table.sql} | 0
... 20250719195733_create_profiles_table.sql} | 0
...ql => 20250730183503_fix_profiles_rls.sql} | 0
...0250731120000_create_blog_posts_table.sql} | 0
... 20250801120000_create_comments_table.sql} | 0
16 files changed, 155 insertions(+), 18 deletions(-)
create mode 100644 cms/README.md
create mode 100644 docs/supabase-migration-verification.md
rename supabase/migrations/{20250713084803-a0140220-fc16-4656-8ab6-fd8d86625284.sql => 20250713084803_create_leads_and_users_tables.sql} (100%)
rename supabase/migrations/{20250713085804-664b9f08-352b-47f0-a417-b374eb4ba2a3.sql => 20250713085804_create_repositories_table.sql} (100%)
rename supabase/migrations/{20250715100836-9ec60325-03d3-444c-8a4a-c646f9b47b2f.sql => 20250715100836_create_team_members_table.sql} (100%)
rename supabase/migrations/{20250719195733-32e01339-2144-4e8d-a778-9c7a36324e1e.sql => 20250719195733_create_profiles_table.sql} (100%)
rename supabase/migrations/{20250730183503-fix-profiles-rls.sql => 20250730183503_fix_profiles_rls.sql} (100%)
rename supabase/migrations/{20250731120000-create-blog-posts-table.sql => 20250731120000_create_blog_posts_table.sql} (100%)
rename supabase/migrations/{20250801120000-create-comments-table.sql => 20250801120000_create_comments_table.sql} (100%)
diff --git a/.env.example b/.env.example
index 8835182..02e890f 100644
--- a/.env.example
+++ b/.env.example
@@ -1,5 +1,8 @@
+# Frontend application
VITE_SUPABASE_URL=
VITE_SUPABASE_ANON_KEY=
+SITE_URL=
+
+# Shared Supabase access
SUPABASE_PROJECT_ID=
SUPABASE_SERVICE_ROLE_KEY=
-SITE_URL=
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index e5316ea..392441f 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -20,9 +20,11 @@ jobs:
node-version: 20
cache: 'npm'
- run: npm ci
+ - run: npm --prefix cms ci
- run: npm run cleanup-lovable
- run: npm run format
- run: npm run lint
+ - run: npm run cms:lint
- run: npm test
build:
@@ -36,11 +38,14 @@ jobs:
node-version: 20
cache: 'npm'
- run: npm ci
+ - run: npm --prefix cms ci
- run: npm run cleanup-lovable
- run: npm run format
- run: npm run lint
+ - run: npm run cms:lint
- run: npm run sitemap
- run: npm run build
+ - run: npm run cms:build
- name: Upload build artifact
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
diff --git a/AGENTS.md b/AGENTS.md
index 89378f9..c68462e 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -23,3 +23,12 @@
Supabase ➜ `/blog/[slug]`) and confirm UUID identifiers propagate correctly.
- Update any downstream services that previously referenced the numeric/text blog
IDs so they read the new UUID format instead.
+
+## Status log (2025-02-14)
+- Environment state: Local PostgreSQL 16 instance installed for disposable
+ verification; no background services left running after dropping the
+ `monynha_tmp` database.
+- Schema status: All SQL migrations apply cleanly in timestamp order when run via
+ `npx supabase migration up --db-url postgresql://postgres:postgres@127.0.0.1:5432/monynha_tmp`.
+- Next stage owner: Web platform team to replay the migrations in managed
+ Supabase projects and run the CMS ➜ Supabase ➜ frontend publishing smoke test.
diff --git a/README.md b/README.md
index 641a51a..5877e04 100644
--- a/README.md
+++ b/README.md
@@ -10,18 +10,29 @@ Monynha Softwares Web Spark is a demo web application built with **React**, **Vi
```sh
npm install
```
-2. Create a `.env` file based on `.env.example` and provide your Supabase credentials.
+2. Create matching environment files for the frontend and CMS workspaces.
```sh
cp .env.example .env
- # edit .env and set VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY
+ cp cms/.env.example cms/.env
+ # edit both files and populate the required values listed below
```
-3. Start the development server
+3. Install the CMS dependencies (they live in a separate workspace).
+ ```sh
+ npm --prefix cms install
+ ```
+4. Start the development server
```sh
npm run dev
```
The application will be available on [http://localhost:5173](http://localhost:5173) by default.
+To boot Payload locally run the shared script from the repository root:
+
+```sh
+npm run cms:dev
+```
+
## Folder structure
```
@@ -35,23 +46,35 @@ src/
## Scripts
-- `npm run dev` – start the development server
-- `npm run lint` – run ESLint
+- `npm run dev` – start the frontend development server
+- `npm run lint` – run ESLint across the frontend workspace
+- `npm run cms:dev` – run the Payload CMS development server
+- `npm run cms:lint` – type-check the CMS workspace with TypeScript
+- `npm run cms:build` – compile the CMS TypeScript sources
- `npm run test` – run unit tests (none at the moment)
-- `npm run build` – create a production build
+- `npm run build` – create a production build of the frontend
- `npm run sitemap` – generate `public/sitemap.xml`
### Environment variables
-Create a `.env` file in the project root with the following entries so the
-Supabase client can connect to your instance:
+Populate the following variables in `.env` to configure the frontend app and
+automation scripts:
-```bash
-VITE_SUPABASE_URL=
-VITE_SUPABASE_ANON_KEY=
-```
+| Variable | Purpose |
+| --- | --- |
+| `VITE_SUPABASE_URL` | Supabase instance URL used by the frontend client. |
+| `VITE_SUPABASE_ANON_KEY` | Supabase anon key exposed to the browser. |
+| `SITE_URL` | Canonical site URL used when generating the sitemap. |
+| `SUPABASE_PROJECT_ID` | Supabase project identifier for external tooling. |
+| `SUPABASE_SERVICE_ROLE_KEY` | Service role key for server-to-server jobs. |
+
+The CMS workspace (`cms/.env`) requires its own secrets:
-These variables match the placeholders in `.env.example`.
+| Variable | Purpose |
+| --- | --- |
+| `PAYLOAD_SECRET` | Secret string for signing Payload authentication tokens. |
+| `PAYLOAD_PUBLIC_SERVER_URL` | Public URL used by Payload when generating links. |
+| `DATABASE_URL` | Postgres connection string shared with Supabase. |
## Technologies
diff --git a/cms/.env.example b/cms/.env.example
index 97f8877..918ef1f 100644
--- a/cms/.env.example
+++ b/cms/.env.example
@@ -1,3 +1,4 @@
-PAYLOAD_SECRET=replace_with_long_random_secret
-DATABASE_URL=postgresql://postgres:password@localhost:5432/postgres
-PAYLOAD_PUBLIC_SERVER_URL=https://cms.monynha.com
+# Payload CMS
+PAYLOAD_SECRET=
+PAYLOAD_PUBLIC_SERVER_URL=
+DATABASE_URL=
diff --git a/cms/README.md b/cms/README.md
new file mode 100644
index 0000000..f8e1818
--- /dev/null
+++ b/cms/README.md
@@ -0,0 +1,38 @@
+# Monynha CMS workspace
+
+This directory contains the [Payload CMS](https://payloadcms.com/) instance used
+for managing marketing content and syncing blog posts with Supabase.
+
+## Getting started
+
+1. Install dependencies from the repository root (recommended) or inside the
+ workspace:
+ ```sh
+ npm --prefix cms install
+ # or
+ cd cms && npm install
+ ```
+2. Copy the sample environment file and fill in the required secrets:
+ ```sh
+ cp .env.example .env
+ ```
+
+## Required environment variables
+
+| Variable | Purpose |
+| --- | --- |
+| `PAYLOAD_SECRET` | Secret string for signing Payload authentication tokens. |
+| `PAYLOAD_PUBLIC_SERVER_URL` | Public URL Payload uses when generating links. |
+| `DATABASE_URL` | Postgres connection string that points at your Supabase database. |
+
+## Scripts
+
+All scripts can be executed from the repository root via the shared npm
+commands:
+
+- `npm run cms:dev` – start the Payload development server.
+- `npm run cms:lint` – run `tsc --noEmit` to type-check the CMS sources.
+- `npm run cms:build` – compile the CMS TypeScript output used in production.
+
+You can also run the equivalent scripts directly inside this directory with
+`npm run dev`, `npm run lint`, and `npm run build`.
diff --git a/cms/package.json b/cms/package.json
index f04084a..6fbc113 100644
--- a/cms/package.json
+++ b/cms/package.json
@@ -6,6 +6,7 @@
"scripts": {
"dev": "cross-env NODE_ENV=development ts-node ./node_modules/payload/bin/payload serve",
"build": "tsc",
+ "lint": "tsc --noEmit",
"start": "node ./node_modules/payload/bin/payload serve"
},
"keywords": [],
diff --git a/docs/supabase-migration-verification.md b/docs/supabase-migration-verification.md
new file mode 100644
index 0000000..0e9c28b
--- /dev/null
+++ b/docs/supabase-migration-verification.md
@@ -0,0 +1,54 @@
+# Supabase migration verification
+
+Use this checklist to replay the SQL migrations against a disposable PostgreSQL
+instance and confirm that the scripts run in order.
+
+## Prerequisites
+
+- Local PostgreSQL 16+ with `createdb`, `dropdb`, and `psql` commands available.
+- Node.js 20+ (the repository already ships with `npx supabase`).
+
+## Procedure
+
+1. Start from the repository root and create a throwaway database:
+ ```sh
+ createdb -h localhost -U postgres monynha_tmp
+ ```
+2. Prime the database with the minimal Supabase objects the migrations expect:
+ ```sh
+ psql -h localhost -U postgres -d monynha_tmp <<'SQL'
+ CREATE ROLE anon NOLOGIN;
+ CREATE ROLE authenticated NOLOGIN;
+ CREATE SCHEMA auth;
+ CREATE EXTENSION IF NOT EXISTS pgcrypto;
+ CREATE TABLE auth.users (id uuid PRIMARY KEY);
+ CREATE OR REPLACE FUNCTION auth.uid()
+ RETURNS uuid
+ LANGUAGE sql
+ STABLE
+ AS $$ SELECT '00000000-0000-0000-0000-000000000000'::uuid $$;
+ SQL
+ ```
+3. Run the migrations against the disposable database:
+ ```sh
+ npx supabase migration up --db-url "postgresql://postgres:postgres@127.0.0.1:5432/monynha_tmp"
+ ```
+ A successful run applies every script in timestamp order and ends with a
+ confirmation similar to the following:
+ > Applying migration 20250730183503_fix_profiles_rls.sql...
+ > Applying migration 20250731120000_create_blog_posts_table.sql...
+ > Applying migration 20250801120000_create_comments_table.sql...
+ > Local database is up to date.
+4. Drop the disposable database when you are finished:
+ ```sh
+ dropdb -h localhost -U postgres monynha_tmp
+ ```
+
+## Notes
+
+- The verification flow relies on stub roles (`anon`, `authenticated`) and the
+ `auth.uid()` helper to emulate Supabase's platform functions. These stubs are
+ only required for local testing and should not be added to production.
+- PostgreSQL will emit harmless warnings when a migration tries to grant access
+ to roles without login privileges; this mirrors Supabase's behaviour and can
+ be safely ignored during verification. 【2e324a†L1-L15】
diff --git a/package.json b/package.json
index a89ec63..eddf969 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,10 @@
"preview": "vite preview",
"test": "jest --passWithNoTests",
"format": "prettier --write .",
- "prepare": "husky install"
+ "prepare": "husky install",
+ "cms:dev": "npm --prefix cms run dev",
+ "cms:build": "npm --prefix cms run build",
+ "cms:lint": "npm --prefix cms run lint"
},
"dependencies": {
"@radix-ui/react-accordion": "^1.2.0",
diff --git a/supabase/migrations/20250713084803-a0140220-fc16-4656-8ab6-fd8d86625284.sql b/supabase/migrations/20250713084803_create_leads_and_users_tables.sql
similarity index 100%
rename from supabase/migrations/20250713084803-a0140220-fc16-4656-8ab6-fd8d86625284.sql
rename to supabase/migrations/20250713084803_create_leads_and_users_tables.sql
diff --git a/supabase/migrations/20250713085804-664b9f08-352b-47f0-a417-b374eb4ba2a3.sql b/supabase/migrations/20250713085804_create_repositories_table.sql
similarity index 100%
rename from supabase/migrations/20250713085804-664b9f08-352b-47f0-a417-b374eb4ba2a3.sql
rename to supabase/migrations/20250713085804_create_repositories_table.sql
diff --git a/supabase/migrations/20250715100836-9ec60325-03d3-444c-8a4a-c646f9b47b2f.sql b/supabase/migrations/20250715100836_create_team_members_table.sql
similarity index 100%
rename from supabase/migrations/20250715100836-9ec60325-03d3-444c-8a4a-c646f9b47b2f.sql
rename to supabase/migrations/20250715100836_create_team_members_table.sql
diff --git a/supabase/migrations/20250719195733-32e01339-2144-4e8d-a778-9c7a36324e1e.sql b/supabase/migrations/20250719195733_create_profiles_table.sql
similarity index 100%
rename from supabase/migrations/20250719195733-32e01339-2144-4e8d-a778-9c7a36324e1e.sql
rename to supabase/migrations/20250719195733_create_profiles_table.sql
diff --git a/supabase/migrations/20250730183503-fix-profiles-rls.sql b/supabase/migrations/20250730183503_fix_profiles_rls.sql
similarity index 100%
rename from supabase/migrations/20250730183503-fix-profiles-rls.sql
rename to supabase/migrations/20250730183503_fix_profiles_rls.sql
diff --git a/supabase/migrations/20250731120000-create-blog-posts-table.sql b/supabase/migrations/20250731120000_create_blog_posts_table.sql
similarity index 100%
rename from supabase/migrations/20250731120000-create-blog-posts-table.sql
rename to supabase/migrations/20250731120000_create_blog_posts_table.sql
diff --git a/supabase/migrations/20250801120000-create-comments-table.sql b/supabase/migrations/20250801120000_create_comments_table.sql
similarity index 100%
rename from supabase/migrations/20250801120000-create-comments-table.sql
rename to supabase/migrations/20250801120000_create_comments_table.sql
From 872ea4cf47009f3303276c44cb4a34f5c47937cb Mon Sep 17 00:00:00 2001
From: Marcelo Santos <117441129+marcelo-m7@users.noreply.github.com>
Date: Sat, 18 Oct 2025 17:31:53 +0100
Subject: [PATCH 12/15] Add Payload collections for Supabase-backed content
(#105)
* feat(cms): add supabase-synced collections
* Add admin provisioning webhook and secure auth flows
* feat: centralize supabase content sync and localization
* Refactor content_sync migration and remove gradient
Removed gradient column and related logic from solutions table. Updated content_sync_webhook_config table and its policies.
* Add automated QA coverage, observability hooks, and CI gating
---
.github/workflows/deploy.yml | 51 ++
AGENTS.md | 60 ++
README.md | 4 +
cms/__tests__/solutions.hook.unit.test.ts | 106 +++
cms/payload-types.ts | 696 ++++++++++++++++
cms/payload.config.ts | 32 +-
cms/src/collections/Authors.ts | 59 ++
cms/src/collections/Categories.ts | 46 ++
cms/src/collections/HomepageFeatures.ts | 180 +++++
cms/src/collections/Leads.ts | 150 ++++
cms/src/collections/NewsletterSubscribers.ts | 124 +++
cms/src/collections/Posts.ts | 71 +-
cms/src/collections/Repositories.ts | 197 +++++
cms/src/collections/SiteSettings.ts | 144 ++++
cms/src/collections/Solutions.ts | 219 +++++
cms/src/collections/TeamMembers.ts | 140 ++++
cms/src/collections/index.ts | 12 +
cms/src/utilities/localization.ts | 32 +
cms/src/utilities/monitoring.ts | 55 ++
cms/src/utilities/pool.ts | 5 +
cms/src/utilities/preview.ts | 23 +
docs/auth-flows.md | 63 ++
jest.config.cjs | 23 +
package-lock.json | 89 +++
package.json | 9 +-
src/App.tsx | 49 +-
src/components/NewsletterSection.tsx | 45 +-
src/components/TeamSection.tsx | 27 +-
src/components/blog/CommentsSection.tsx | 65 +-
src/hooks/useDynamicCopy.ts | 30 +
src/integrations/supabase.ts | 2 +-
src/lib/data/supabase.ts | 411 ++++++++++
src/lib/solutions.ts | 191 +----
.../types.ts => lib/supabase/types.gen.ts} | 47 +-
src/locales/en.json | 24 +-
src/locales/es.json | 24 +-
src/locales/fr.json | 24 +-
src/locales/pt.json | 24 +-
src/pages/About.tsx | 31 +-
src/pages/Auth.tsx | 506 ++++++++----
src/pages/Blog.tsx | 40 +-
src/pages/Contact.tsx | 122 +--
src/pages/Dashboard.tsx | 749 +++++++++++++++---
src/pages/Index.tsx | 198 +++--
src/pages/Projects.tsx | 39 +-
src/pages/Solutions.tsx | 53 +-
src/pages/blog/[slug].tsx | 21 +-
src/pages/solutions/[slug].tsx | 30 +-
.../functions/payload-admin-sync/index.ts | 308 +++++++
.../20250815150000_payload_admin_sync.sql | 151 ++++
...250820120000_content_sync_and_gradient.sql | 19 +
tests/__mocks__/fileMock.ts | 2 +
tests/__mocks__/styleMock.ts | 1 +
tests/accessibility/solutions.a11y.test.tsx | 85 ++
tests/e2e/spa.user-journeys.e2e.test.tsx | 207 +++++
tests/setupEnv.ts | 1 +
tests/setupTests.ts | 44 +
tests/supabase/policies.integration.test.ts | 44 +
tsconfig.app.json | 3 +-
tsconfig.json | 3 +-
60 files changed, 5283 insertions(+), 927 deletions(-)
create mode 100644 cms/__tests__/solutions.hook.unit.test.ts
create mode 100644 cms/payload-types.ts
create mode 100644 cms/src/collections/Authors.ts
create mode 100644 cms/src/collections/Categories.ts
create mode 100644 cms/src/collections/HomepageFeatures.ts
create mode 100644 cms/src/collections/Leads.ts
create mode 100644 cms/src/collections/NewsletterSubscribers.ts
create mode 100644 cms/src/collections/Repositories.ts
create mode 100644 cms/src/collections/SiteSettings.ts
create mode 100644 cms/src/collections/Solutions.ts
create mode 100644 cms/src/collections/TeamMembers.ts
create mode 100644 cms/src/collections/index.ts
create mode 100644 cms/src/utilities/localization.ts
create mode 100644 cms/src/utilities/monitoring.ts
create mode 100644 cms/src/utilities/pool.ts
create mode 100644 cms/src/utilities/preview.ts
create mode 100644 docs/auth-flows.md
create mode 100644 jest.config.cjs
create mode 100644 src/hooks/useDynamicCopy.ts
create mode 100644 src/lib/data/supabase.ts
rename src/{integrations/supabase/types.ts => lib/supabase/types.gen.ts} (90%)
create mode 100644 supabase/functions/payload-admin-sync/index.ts
create mode 100644 supabase/migrations/20250815150000_payload_admin_sync.sql
create mode 100644 supabase/migrations/20250820120000_content_sync_and_gradient.sql
create mode 100644 tests/__mocks__/fileMock.ts
create mode 100644 tests/__mocks__/styleMock.ts
create mode 100644 tests/accessibility/solutions.a11y.test.tsx
create mode 100644 tests/e2e/spa.user-journeys.e2e.test.tsx
create mode 100644 tests/setupEnv.ts
create mode 100644 tests/setupTests.ts
create mode 100644 tests/supabase/policies.integration.test.ts
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 392441f..95534b4 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -53,6 +53,57 @@ jobs:
name: build
path: dist
+ deploy_spa:
+ needs: [build]
+ if: github.ref == 'refs/heads/prod' && github.event_name == 'push'
+ runs-on: ubuntu-latest
+ environment:
+ name: spa-production
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: 'npm'
+ - run: npm ci
+ - run: npm run build
+ - name: Archive SPA bundle
+ run: tar -czf spa-dist.tar.gz -C dist .
+ - name: Upload SPA release bundle
+ uses: actions/upload-artifact@v4
+ with:
+ name: spa-dist
+ path: spa-dist.tar.gz
+ - name: Placeholder deploy command
+ run: |
+ echo "Deploying SPA to production environment"
+
+ deploy_cms:
+ needs: [build]
+ if: github.ref == 'refs/heads/prod' && github.event_name == 'push'
+ runs-on: ubuntu-latest
+ environment:
+ name: cms-production
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: 'npm'
+ - run: npm ci
+ - run: npm --prefix cms ci
+ - run: npm --prefix cms run build
+ - name: Archive CMS build output
+ run: tar -czf cms-dist.tar.gz -C cms/dist .
+ - name: Upload CMS release bundle
+ uses: actions/upload-artifact@v4
+ with:
+ name: cms-dist
+ path: cms-dist.tar.gz
+ - name: Placeholder deploy command
+ run: |
+ echo "Deploying Payload CMS to production environment"
+
# deploy-preview:
# needs: build
# if: github.event_name == 'pull_request'
diff --git a/AGENTS.md b/AGENTS.md
index c68462e..8d72918 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -24,6 +24,39 @@
- Update any downstream services that previously referenced the numeric/text blog
IDs so they read the new UUID format instead.
+## CMS collection sync (2025-02-15)
+- Payload CMS now exposes collections for: `solutions`, `repositories`,
+ `teamMembers`, `homepageFeatures`, `siteSettings`, `newsletterSubscribers`,
+ `leads`, plus optional taxonomies (`authors`, `categories`). Each collection
+ pushes updates to the matching Supabase table via `afterChange` hooks that
+ reuse the shared PostgreSQL `Pool`.
+- Localization is enabled for user-facing text fields so the SPA can render
+ `pt-BR` and `en` copy without code changes. Slugs, booleans, and select lists
+ (icons, gradients) enforce validation consistent with frontend expectations.
+- Seed recommendations: create at least two `solutions` (with features array),
+ three `homepageFeatures` rows ordered for the landing page carousel, and seed
+ `teamMembers`/`repositories` with the profiles already present in Supabase to
+ avoid duplicate inserts when the hooks run.
+- Follow-ups:
+ - Extend the Supabase `solutions` table with a `gradient` column and update
+ the SPA to consume it instead of falling back to hard-coded styles.
+ - Expose `siteSettings` records through the frontend for dynamic contact
+ metadata and gradient overrides sourced from Payload.
+ - Add automated deletion hooks (or manual SOP) so removing a document from
+ Payload also removes the Supabase row when appropriate.
+
+## Shared frontend data layer (2025-03-01)
+- Supabase read/write helpers now live in `src/lib/data/supabase.ts` with
+ generated types (`src/lib/supabase/types.gen.ts`). Run
+ `npm run supabase:types` after schema changes to regenerate typings.
+- React code should consume data through the helpers (e.g.
+ `fetchSolutions`, `createLead`, `fetchLocalizedCopy`) instead of calling the
+ Supabase client directly. Use `useDynamicCopy` to merge CMS-managed strings
+ into i18n resources at runtime.
+- CMS preview URLs are wired via `cms/src/utilities/preview.ts` so editors can
+ open SPA/Next routes for blog posts, solutions, home content, and site
+ settings.
+
## Status log (2025-02-14)
- Environment state: Local PostgreSQL 16 instance installed for disposable
verification; no background services left running after dropping the
@@ -32,3 +65,30 @@
`npx supabase migration up --db-url postgresql://postgres:postgres@127.0.0.1:5432/monynha_tmp`.
- Next stage owner: Web platform team to replay the migrations in managed
Supabase projects and run the CMS ➜ Supabase ➜ frontend publishing smoke test.
+
+## Security & access QA (2025-02-20)
+- RPC `get_admin_dashboard_data` enforces admin-only reads (non-admin sessions receive `42501`).
+- Admin dashboard requires `aal2` when a TOTP factor exists; after `challengeAndVerify`, leads/newsletter tables load successfully.
+- Payload role transitions now emit webhooks to `payload-admin-sync`, updating `profiles.payload_user_id` and issuing password resets for new admins.
+
+### Outstanding follow-up
+- Populate `public.payload_admin_sync_config` with the deployed edge-function URL and secret in each Supabase environment.
+- Deploy `supabase/functions/payload-admin-sync` with the required environment variables (`PAYLOAD_API_BASE_URL`, `PAYLOAD_ADMIN_TOKEN`, etc.).
+- Perform an end-to-end CMS role promotion/demotion against a real Payload instance once the webhook endpoint is live.
+
+## QA & Observability (2025-10-18)
+- Automated coverage now includes:
+ - Unit tests for Payload ➜ Supabase hooks (`cms/__tests__/solutions.hook.unit.test.ts`).
+ - Integration checks for row-level security definitions (`tests/supabase/policies.integration.test.ts`).
+ - End-to-end SPA navigation and lead capture (`tests/e2e/spa.user-journeys.e2e.test.tsx`).
+ - Accessibility regression guard for the Solutions catalog (`tests/accessibility/solutions.a11y.test.tsx`).
+- Monitoring endpoints:
+ - CMS sync events emit to `CMS_OBSERVABILITY_WEBHOOK_URL` with dashboards discoverable via `CMS_OBSERVABILITY_DASHBOARD_URL`.
+ - Supabase `payload-admin-sync` forwards status updates to `OBSERVABILITY_WEBHOOK_URL` and links back to the same observability dashboard.
+- Release workflow:
+ - GitHub Actions `deploy_spa` (environment `spa-production`) archives the Vite bundle and requires approval before execution.
+ - GitHub Actions `deploy_cms` (environment `cms-production`) builds the Payload TypeScript output and likewise pauses for approval.
+- Runbook snapshot:
+ 1. Execute `npm test` locally to validate CMS hooks, policies, SPA journeys, and a11y gates.
+ 2. Merge to `prod`; obtain approvals for both `spa-production` and `cms-production` environments inside the workflow run.
+ 3. Monitor the observability dashboard for `payload-admin-sync` success or error logs immediately after release.
diff --git a/README.md b/README.md
index 5877e04..6bce8f6 100644
--- a/README.md
+++ b/README.md
@@ -76,6 +76,10 @@ The CMS workspace (`cms/.env`) requires its own secrets:
| `PAYLOAD_PUBLIC_SERVER_URL` | Public URL used by Payload when generating links. |
| `DATABASE_URL` | Postgres connection string shared with Supabase. |
+## Authentication & Security
+
+- [Authentication and security flows](docs/auth-flows.md): password reset lifecycle, dashboard MFA management, the admin RPC, and the Payload provisioning webhook.
+
## Technologies
- React & Vite
diff --git a/cms/__tests__/solutions.hook.unit.test.ts b/cms/__tests__/solutions.hook.unit.test.ts
new file mode 100644
index 0000000..3fb0ad1
--- /dev/null
+++ b/cms/__tests__/solutions.hook.unit.test.ts
@@ -0,0 +1,106 @@
+import { jest } from '@jest/globals';
+
+const mockQuery = jest.fn();
+const mockLogCmsSyncEvent = jest.fn().mockResolvedValue(undefined);
+
+jest.mock('../src/utilities/pool', () => ({
+ pool: { query: mockQuery },
+}));
+
+jest.mock('../src/utilities/monitoring', () => ({
+ logCmsSyncEvent: mockLogCmsSyncEvent,
+}));
+
+describe('Solutions afterChange hook', () => {
+ beforeEach(() => {
+ mockQuery.mockReset();
+ mockLogCmsSyncEvent.mockClear();
+ });
+
+ it('persists solution payloads and backfills the Supabase ID', async () => {
+ const { default: Solutions } = await import('../src/collections/Solutions');
+ const hook = Solutions.hooks?.afterChange?.[0];
+ expect(typeof hook).toBe('function');
+
+ const generatedId = 'c3b6f1d1-8a44-4efb-9f2a-d7faacb3b561';
+ mockQuery.mockResolvedValue({ rows: [{ id: generatedId }] });
+
+ const update = jest.fn().mockResolvedValue(undefined);
+
+ await hook?.({
+ doc: {
+ id: 'payload-doc-id',
+ supabaseId: null,
+ slug: 'demo-solution',
+ title: { en: 'AI Consulting', 'pt-BR': 'Consultoria IA' },
+ description: { en: 'Helping teams deliver AI responsibly.' },
+ image: { url: 'https://cdn.example.com/img.png' },
+ features: [
+ { content: { en: 'Feature one', 'pt-BR': 'Recurso 1' } },
+ { value: { en: 'Second capability' } },
+ ' ',
+ ],
+ active: true,
+ gradient: 'from-brand-blue to-brand-purple',
+ },
+ req: { payload: { update } },
+ });
+
+ expect(mockQuery).toHaveBeenCalledTimes(1);
+ const [queryText, params] = mockQuery.mock.calls[0];
+ expect(queryText).toContain('insert into public.solutions');
+ expect(Array.isArray(params)).toBe(true);
+ expect(params[0]).toBe('demo-solution');
+ expect(JSON.parse(params[4] as string)).toEqual([
+ 'Recurso 1',
+ 'Second capability',
+ ]);
+
+ expect(update).toHaveBeenCalledWith({
+ collection: 'solutions',
+ id: 'payload-doc-id',
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+
+ expect(mockLogCmsSyncEvent).toHaveBeenCalledWith(
+ expect.objectContaining({
+ collection: 'solutions',
+ status: 'success',
+ supabaseId: generatedId,
+ metadata: expect.objectContaining({ featureCount: 2, slug: 'demo-solution' }),
+ })
+ );
+ });
+
+ it('logs failures before surfacing the error', async () => {
+ const { default: Solutions } = await import('../src/collections/Solutions');
+ const hook = Solutions.hooks?.afterChange?.[0];
+ const thrown = new Error('database is offline');
+ mockQuery.mockRejectedValueOnce(thrown);
+
+ await expect(
+ hook?.({
+ doc: {
+ id: 'payload-doc-id',
+ supabaseId: '70dd7c75-028d-412c-82d7-3eeb5c4ce355',
+ slug: 'failing-solution',
+ title: { en: 'Broken sync' },
+ description: { en: 'This will fail.' },
+ features: [],
+ active: false,
+ },
+ req: {},
+ })
+ ).rejects.toThrow('database is offline');
+
+ expect(mockLogCmsSyncEvent).toHaveBeenCalledWith(
+ expect.objectContaining({
+ collection: 'solutions',
+ status: 'error',
+ message: 'database is offline',
+ supabaseId: '70dd7c75-028d-412c-82d7-3eeb5c4ce355',
+ })
+ );
+ });
+});
diff --git a/cms/payload-types.ts b/cms/payload-types.ts
new file mode 100644
index 0000000..d4b9469
--- /dev/null
+++ b/cms/payload-types.ts
@@ -0,0 +1,696 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * This file was automatically generated by Payload.
+ * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
+ * and re-run `payload generate:types` to regenerate this file.
+ */
+
+/**
+ * Supported timezones in IANA format.
+ *
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "supportedTimezones".
+ */
+export type SupportedTimezones =
+ | 'Pacific/Midway'
+ | 'Pacific/Niue'
+ | 'Pacific/Honolulu'
+ | 'Pacific/Rarotonga'
+ | 'America/Anchorage'
+ | 'Pacific/Gambier'
+ | 'America/Los_Angeles'
+ | 'America/Tijuana'
+ | 'America/Denver'
+ | 'America/Phoenix'
+ | 'America/Chicago'
+ | 'America/Guatemala'
+ | 'America/New_York'
+ | 'America/Bogota'
+ | 'America/Caracas'
+ | 'America/Santiago'
+ | 'America/Buenos_Aires'
+ | 'America/Sao_Paulo'
+ | 'Atlantic/South_Georgia'
+ | 'Atlantic/Azores'
+ | 'Atlantic/Cape_Verde'
+ | 'Europe/London'
+ | 'Europe/Berlin'
+ | 'Africa/Lagos'
+ | 'Europe/Athens'
+ | 'Africa/Cairo'
+ | 'Europe/Moscow'
+ | 'Asia/Riyadh'
+ | 'Asia/Dubai'
+ | 'Asia/Baku'
+ | 'Asia/Karachi'
+ | 'Asia/Tashkent'
+ | 'Asia/Calcutta'
+ | 'Asia/Dhaka'
+ | 'Asia/Almaty'
+ | 'Asia/Jakarta'
+ | 'Asia/Bangkok'
+ | 'Asia/Shanghai'
+ | 'Asia/Singapore'
+ | 'Asia/Tokyo'
+ | 'Asia/Seoul'
+ | 'Australia/Brisbane'
+ | 'Australia/Sydney'
+ | 'Pacific/Guam'
+ | 'Pacific/Noumea'
+ | 'Pacific/Auckland'
+ | 'Pacific/Fiji';
+
+export interface Config {
+ auth: {
+ users: UserAuthOperations;
+ };
+ blocks: {};
+ collections: {
+ users: User;
+ media: Media;
+ posts: Post;
+ authors: Author;
+ categories: Category;
+ solutions: Solution;
+ repositories: Repository;
+ teamMembers: TeamMember;
+ homepageFeatures: HomepageFeature;
+ siteSettings: SiteSetting;
+ newsletterSubscribers: NewsletterSubscriber;
+ leads: Lead;
+ 'payload-locked-documents': PayloadLockedDocument;
+ 'payload-preferences': PayloadPreference;
+ 'payload-migrations': PayloadMigration;
+ };
+ collectionsJoins: {};
+ collectionsSelect: {
+ users: UsersSelect | UsersSelect;
+ media: MediaSelect | MediaSelect;
+ posts: PostsSelect | PostsSelect;
+ authors: AuthorsSelect | AuthorsSelect;
+ categories: CategoriesSelect | CategoriesSelect;
+ solutions: SolutionsSelect | SolutionsSelect;
+ repositories: RepositoriesSelect | RepositoriesSelect;
+ teamMembers: TeamMembersSelect | TeamMembersSelect;
+ homepageFeatures: HomepageFeaturesSelect | HomepageFeaturesSelect;
+ siteSettings: SiteSettingsSelect | SiteSettingsSelect;
+ newsletterSubscribers: NewsletterSubscribersSelect | NewsletterSubscribersSelect;
+ leads: LeadsSelect | LeadsSelect;
+ 'payload-locked-documents': PayloadLockedDocumentsSelect | PayloadLockedDocumentsSelect;
+ 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect;
+ 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect;
+ };
+ db: {
+ defaultIDType: number;
+ };
+ globals: {};
+ globalsSelect: {};
+ locale: 'pt-BR' | 'en';
+ user: User & {
+ collection: 'users';
+ };
+ jobs: {
+ tasks: unknown;
+ workflows: unknown;
+ };
+}
+export interface UserAuthOperations {
+ forgotPassword: {
+ email: string;
+ password: string;
+ };
+ login: {
+ email: string;
+ password: string;
+ };
+ registerFirstUser: {
+ email: string;
+ password: string;
+ };
+ unlock: {
+ email: string;
+ password: string;
+ };
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "users".
+ */
+export interface User {
+ id: number;
+ name?: string | null;
+ updatedAt: string;
+ createdAt: string;
+ email: string;
+ resetPasswordToken?: string | null;
+ resetPasswordExpiration?: string | null;
+ salt?: string | null;
+ hash?: string | null;
+ loginAttempts?: number | null;
+ lockUntil?: string | null;
+ sessions?:
+ | {
+ id: string;
+ createdAt?: string | null;
+ expiresAt: string;
+ }[]
+ | null;
+ password?: string | null;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "media".
+ */
+export interface Media {
+ id: number;
+ updatedAt: string;
+ createdAt: string;
+ url?: string | null;
+ thumbnailURL?: string | null;
+ filename?: string | null;
+ mimeType?: string | null;
+ filesize?: number | null;
+ width?: number | null;
+ height?: number | null;
+ focalX?: number | null;
+ focalY?: number | null;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "posts".
+ */
+export interface Post {
+ id: number;
+ supabaseId?: string | null;
+ slug: string;
+ title: string;
+ excerpt?: string | null;
+ content?: {
+ root: {
+ type: string;
+ children: {
+ type: string;
+ version: number;
+ [k: string]: unknown;
+ }[];
+ direction: ('ltr' | 'rtl') | null;
+ format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
+ indent: number;
+ version: number;
+ };
+ [k: string]: unknown;
+ } | null;
+ published?: boolean | null;
+ coverImage?: (number | null) | Media;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "authors".
+ */
+export interface Author {
+ id: number;
+ name: string;
+ slug: string;
+ bio?: string | null;
+ avatar?: (number | null) | Media;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "categories".
+ */
+export interface Category {
+ id: number;
+ name: string;
+ slug: string;
+ description?: string | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "solutions".
+ */
+export interface Solution {
+ id: number;
+ supabaseId?: string | null;
+ slug: string;
+ title: string;
+ description: string;
+ image?: (number | null) | Media;
+ features?:
+ | {
+ content: string;
+ id?: string | null;
+ }[]
+ | null;
+ gradient:
+ | 'from-brand-purple to-brand-blue'
+ | 'from-brand-pink to-brand-orange'
+ | 'from-brand-blue to-brand-purple'
+ | 'from-brand-orange to-brand-pink';
+ active?: boolean | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "repositories".
+ */
+export interface Repository {
+ id: number;
+ supabaseId?: string | null;
+ name: string;
+ description: string;
+ githubUrl: string;
+ demoUrl?: string | null;
+ tags?:
+ | {
+ value: string;
+ id?: string | null;
+ }[]
+ | null;
+ active?: boolean | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "teamMembers".
+ */
+export interface TeamMember {
+ id: number;
+ supabaseId?: string | null;
+ name: string;
+ role: string;
+ bio?: string | null;
+ photo?: (number | null) | Media;
+ linkedinUrl?: string | null;
+ active?: boolean | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "homepageFeatures".
+ */
+export interface HomepageFeature {
+ id: number;
+ supabaseId?: string | null;
+ title: string;
+ description: string;
+ icon: 'Brain' | 'Zap' | 'Shield' | 'Users' | 'Globe' | 'Laptop';
+ url?: string | null;
+ /**
+ * Controls the display order on the homepage.
+ */
+ orderIndex: number;
+ active?: boolean | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "siteSettings".
+ */
+export interface SiteSetting {
+ id: number;
+ supabaseId?: string | null;
+ key: string;
+ description?: string | null;
+ /**
+ * Stores arbitrary configuration consumed by the frontend.
+ */
+ value:
+ | {
+ [k: string]: unknown;
+ }
+ | unknown[]
+ | string
+ | number
+ | boolean
+ | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "newsletterSubscribers".
+ */
+export interface NewsletterSubscriber {
+ id: number;
+ supabaseId?: string | null;
+ email: string;
+ active?: boolean | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "leads".
+ */
+export interface Lead {
+ id: number;
+ supabaseId?: string | null;
+ name: string;
+ email: string;
+ company?: string | null;
+ project?: string | null;
+ /**
+ * Details provided by the lead when contacting the team.
+ */
+ message: string;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "payload-locked-documents".
+ */
+export interface PayloadLockedDocument {
+ id: number;
+ document?:
+ | ({
+ relationTo: 'users';
+ value: number | User;
+ } | null)
+ | ({
+ relationTo: 'media';
+ value: number | Media;
+ } | null)
+ | ({
+ relationTo: 'posts';
+ value: number | Post;
+ } | null)
+ | ({
+ relationTo: 'authors';
+ value: number | Author;
+ } | null)
+ | ({
+ relationTo: 'categories';
+ value: number | Category;
+ } | null)
+ | ({
+ relationTo: 'solutions';
+ value: number | Solution;
+ } | null)
+ | ({
+ relationTo: 'repositories';
+ value: number | Repository;
+ } | null)
+ | ({
+ relationTo: 'teamMembers';
+ value: number | TeamMember;
+ } | null)
+ | ({
+ relationTo: 'homepageFeatures';
+ value: number | HomepageFeature;
+ } | null)
+ | ({
+ relationTo: 'siteSettings';
+ value: number | SiteSetting;
+ } | null)
+ | ({
+ relationTo: 'newsletterSubscribers';
+ value: number | NewsletterSubscriber;
+ } | null)
+ | ({
+ relationTo: 'leads';
+ value: number | Lead;
+ } | null);
+ globalSlug?: string | null;
+ user: {
+ relationTo: 'users';
+ value: number | User;
+ };
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "payload-preferences".
+ */
+export interface PayloadPreference {
+ id: number;
+ user: {
+ relationTo: 'users';
+ value: number | User;
+ };
+ key?: string | null;
+ value?:
+ | {
+ [k: string]: unknown;
+ }
+ | unknown[]
+ | string
+ | number
+ | boolean
+ | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "payload-migrations".
+ */
+export interface PayloadMigration {
+ id: number;
+ name?: string | null;
+ batch?: number | null;
+ updatedAt: string;
+ createdAt: string;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "users_select".
+ */
+export interface UsersSelect {
+ name?: T;
+ updatedAt?: T;
+ createdAt?: T;
+ email?: T;
+ resetPasswordToken?: T;
+ resetPasswordExpiration?: T;
+ salt?: T;
+ hash?: T;
+ loginAttempts?: T;
+ lockUntil?: T;
+ sessions?:
+ | T
+ | {
+ id?: T;
+ createdAt?: T;
+ expiresAt?: T;
+ };
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "media_select".
+ */
+export interface MediaSelect {
+ updatedAt?: T;
+ createdAt?: T;
+ url?: T;
+ thumbnailURL?: T;
+ filename?: T;
+ mimeType?: T;
+ filesize?: T;
+ width?: T;
+ height?: T;
+ focalX?: T;
+ focalY?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "posts_select".
+ */
+export interface PostsSelect {
+ supabaseId?: T;
+ slug?: T;
+ title?: T;
+ excerpt?: T;
+ content?: T;
+ published?: T;
+ coverImage?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "authors_select".
+ */
+export interface AuthorsSelect {
+ name?: T;
+ slug?: T;
+ bio?: T;
+ avatar?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "categories_select".
+ */
+export interface CategoriesSelect {
+ name?: T;
+ slug?: T;
+ description?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "solutions_select".
+ */
+export interface SolutionsSelect {
+ supabaseId?: T;
+ slug?: T;
+ title?: T;
+ description?: T;
+ image?: T;
+ features?:
+ | T
+ | {
+ content?: T;
+ id?: T;
+ };
+ gradient?: T;
+ active?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "repositories_select".
+ */
+export interface RepositoriesSelect {
+ supabaseId?: T;
+ name?: T;
+ description?: T;
+ githubUrl?: T;
+ demoUrl?: T;
+ tags?:
+ | T
+ | {
+ value?: T;
+ id?: T;
+ };
+ active?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "teamMembers_select".
+ */
+export interface TeamMembersSelect {
+ supabaseId?: T;
+ name?: T;
+ role?: T;
+ bio?: T;
+ photo?: T;
+ linkedinUrl?: T;
+ active?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "homepageFeatures_select".
+ */
+export interface HomepageFeaturesSelect {
+ supabaseId?: T;
+ title?: T;
+ description?: T;
+ icon?: T;
+ url?: T;
+ orderIndex?: T;
+ active?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "siteSettings_select".
+ */
+export interface SiteSettingsSelect {
+ supabaseId?: T;
+ key?: T;
+ description?: T;
+ value?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "newsletterSubscribers_select".
+ */
+export interface NewsletterSubscribersSelect {
+ supabaseId?: T;
+ email?: T;
+ active?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "leads_select".
+ */
+export interface LeadsSelect {
+ supabaseId?: T;
+ name?: T;
+ email?: T;
+ company?: T;
+ project?: T;
+ message?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "payload-locked-documents_select".
+ */
+export interface PayloadLockedDocumentsSelect {
+ document?: T;
+ globalSlug?: T;
+ user?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "payload-preferences_select".
+ */
+export interface PayloadPreferencesSelect {
+ user?: T;
+ key?: T;
+ value?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "payload-migrations_select".
+ */
+export interface PayloadMigrationsSelect {
+ name?: T;
+ batch?: T;
+ updatedAt?: T;
+ createdAt?: T;
+}
+/**
+ * This interface was referenced by `Config`'s JSON-Schema
+ * via the `definition` "auth".
+ */
+export interface Auth {
+ [k: string]: unknown;
+}
+
+
+declare module 'payload' {
+ export interface GeneratedTypes extends Config {}
+}
\ No newline at end of file
diff --git a/cms/payload.config.ts b/cms/payload.config.ts
index 14c2d39..fda2530 100644
--- a/cms/payload.config.ts
+++ b/cms/payload.config.ts
@@ -4,9 +4,20 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
import { buildConfig } from 'payload';
import { postgresAdapter } from '@payloadcms/db-postgres';
-import Posts from './src/collections/Posts';
-import Users from './src/collections/Users';
-import Media from './src/collections/Media';
+import {
+ Authors,
+ Categories,
+ HomepageFeatures,
+ Leads,
+ Media,
+ NewsletterSubscribers,
+ Posts,
+ Repositories,
+ SiteSettings,
+ Solutions,
+ TeamMembers,
+ Users,
+} from './src/collections';
export default buildConfig({
secret: process.env.PAYLOAD_SECRET!,
@@ -17,7 +28,20 @@ export default buildConfig({
defaultLocale: 'pt-BR',
fallback: true,
},
- collections: [Posts, Users, Media],
+ collections: [
+ Users,
+ Media,
+ Posts,
+ Authors,
+ Categories,
+ Solutions,
+ Repositories,
+ TeamMembers,
+ HomepageFeatures,
+ SiteSettings,
+ NewsletterSubscribers,
+ Leads,
+ ],
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URL!,
diff --git a/cms/src/collections/Authors.ts b/cms/src/collections/Authors.ts
new file mode 100644
index 0000000..af016c2
--- /dev/null
+++ b/cms/src/collections/Authors.ts
@@ -0,0 +1,59 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+import { resolveLocalizedText } from '../utilities/localization';
+
+const Authors: CollectionConfig = {
+ slug: 'authors',
+ admin: { useAsTitle: 'name' },
+ access: {
+ read: () => true,
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ {
+ name: 'name',
+ type: 'text',
+ required: true,
+ localized: true,
+ },
+ {
+ name: 'slug',
+ type: 'text',
+ required: true,
+ unique: true,
+ validate: (value: unknown, { siblingData }: { siblingData: any }) => {
+ const slug = typeof value === 'string' ? value.trim() : '';
+ if (!slug) {
+ return 'Slug is required.';
+ }
+
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) {
+ return 'Use lowercase letters, numbers, and hyphens only.';
+ }
+
+ if (siblingData?.name) {
+ const resolvedName = resolveLocalizedText(siblingData.name);
+ if (resolvedName && resolvedName.trim().length > 0 && slug === resolvedName) {
+ return 'Slug should differ from the localized name to avoid conflicts.';
+ }
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'bio',
+ type: 'textarea',
+ localized: true,
+ },
+ {
+ name: 'avatar',
+ type: 'upload',
+ relationTo: 'media',
+ },
+ ],
+};
+
+export default Authors;
diff --git a/cms/src/collections/Categories.ts b/cms/src/collections/Categories.ts
new file mode 100644
index 0000000..2990489
--- /dev/null
+++ b/cms/src/collections/Categories.ts
@@ -0,0 +1,46 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+
+const Categories: CollectionConfig = {
+ slug: 'categories',
+ admin: { useAsTitle: 'name' },
+ access: {
+ read: () => true,
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ {
+ name: 'name',
+ type: 'text',
+ required: true,
+ localized: true,
+ },
+ {
+ name: 'slug',
+ type: 'text',
+ required: true,
+ unique: true,
+ validate: (value: unknown) => {
+ const slug = typeof value === 'string' ? value.trim() : '';
+ if (!slug) {
+ return 'Slug is required.';
+ }
+
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) {
+ return 'Use lowercase letters, numbers, and hyphens only.';
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'description',
+ type: 'textarea',
+ localized: true,
+ },
+ ],
+};
+
+export default Categories;
diff --git a/cms/src/collections/HomepageFeatures.ts b/cms/src/collections/HomepageFeatures.ts
new file mode 100644
index 0000000..cf811ae
--- /dev/null
+++ b/cms/src/collections/HomepageFeatures.ts
@@ -0,0 +1,180 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+import { pool } from '../utilities/pool';
+import { resolveLocalizedText } from '../utilities/localization';
+import { buildSpaPreviewUrl } from '../utilities/preview';
+import { logCmsSyncEvent } from '../utilities/monitoring';
+
+const ICON_OPTIONS = ['Brain', 'Zap', 'Shield', 'Users', 'Globe', 'Laptop'];
+
+const normalizeUrl = (value: unknown): string | null => {
+ if (typeof value !== 'string' || value.trim().length === 0) {
+ return null;
+ }
+
+ try {
+ const parsed = new URL(value.trim());
+ return parsed.toString();
+ } catch {
+ return null;
+ }
+};
+
+const upsertIntoSupabase = async ({ doc, req }: { doc: any; req: any }) => {
+ const supabaseId = doc.supabaseId ?? null;
+ const title = resolveLocalizedText(doc.title);
+ const description = resolveLocalizedText(doc.description);
+ const icon = ICON_OPTIONS.includes(doc.icon) ? doc.icon : ICON_OPTIONS[0];
+ const url = normalizeUrl(doc.url);
+ const orderIndex = Number.parseInt(String(doc.orderIndex ?? '0'), 10);
+ const active = !!doc.active;
+
+ const queryWithId = `
+ insert into public.homepage_features (id, title, description, icon, url, order_index, active, updated_at)
+ values ($1::uuid,$2,$3,$4,$5,$6,$7, now())
+ on conflict (id) do update set
+ title=excluded.title,
+ description=excluded.description,
+ icon=excluded.icon,
+ url=excluded.url,
+ order_index=excluded.order_index,
+ active=excluded.active,
+ updated_at=now()
+ returning id`;
+
+ const queryWithoutId = `
+ insert into public.homepage_features (title, description, icon, url, order_index, active, updated_at)
+ values ($1,$2,$3,$4,$5,$6, now())
+ on conflict (title) do update set
+ description=excluded.description,
+ icon=excluded.icon,
+ url=excluded.url,
+ order_index=excluded.order_index,
+ active=excluded.active,
+ updated_at=now()
+ returning id`;
+
+ const paramsWithId = [
+ supabaseId,
+ title,
+ description,
+ icon,
+ url,
+ orderIndex,
+ active,
+ ];
+
+ const paramsWithoutId = [title, description, icon, url, orderIndex, active];
+
+ try {
+ const result = supabaseId
+ ? await pool.query(queryWithId, paramsWithId)
+ : await pool.query(queryWithoutId, paramsWithoutId);
+
+ const generatedId = result?.rows?.[0]?.id ?? null;
+
+ if (!supabaseId && generatedId && req?.payload) {
+ await req.payload.update({
+ collection: 'homepageFeatures',
+ id: doc.id,
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+ }
+
+ await logCmsSyncEvent({
+ collection: 'homepageFeatures',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId: (supabaseId ?? generatedId) ?? null,
+ status: 'success',
+ metadata: { icon, orderIndex, active, urlPresent: Boolean(url) },
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error while syncing homepage feature';
+ await logCmsSyncEvent({
+ collection: 'homepageFeatures',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId,
+ status: 'error',
+ message,
+ metadata: { icon, orderIndex },
+ });
+ throw error;
+ }
+};
+
+const HomepageFeatures: CollectionConfig = {
+ slug: 'homepageFeatures',
+ admin: {
+ useAsTitle: 'title',
+ preview: () => buildSpaPreviewUrl('/'),
+ },
+ access: {
+ read: () => true,
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ {
+ name: 'supabaseId',
+ type: 'text',
+ admin: { hidden: true },
+ },
+ { name: 'title', type: 'text', required: true, localized: true },
+ { name: 'description', type: 'textarea', required: true, localized: true },
+ {
+ name: 'icon',
+ type: 'select',
+ required: true,
+ defaultValue: ICON_OPTIONS[0],
+ options: ICON_OPTIONS.map((option) => ({ label: option, value: option })),
+ },
+ {
+ name: 'url',
+ type: 'text',
+ validate: (value: unknown) => {
+ if (value === null || value === undefined || value === '') {
+ return true;
+ }
+
+ if (!normalizeUrl(value)) {
+ return 'Enter a valid URL or leave blank.';
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'orderIndex',
+ type: 'number',
+ required: true,
+ admin: {
+ step: 1,
+ description: 'Controls the display order on the homepage.',
+ },
+ validate: (value: unknown) => {
+ if (value === null || value === undefined || value === '') {
+ return 'Provide an order number.';
+ }
+
+ const parsed = Number(value);
+ if (!Number.isInteger(parsed) || parsed < 0) {
+ return 'Order index must be a non-negative integer.';
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'active',
+ type: 'checkbox',
+ defaultValue: true,
+ },
+ ],
+ hooks: { afterChange: [upsertIntoSupabase] },
+};
+
+export default HomepageFeatures;
diff --git a/cms/src/collections/Leads.ts b/cms/src/collections/Leads.ts
new file mode 100644
index 0000000..204dd22
--- /dev/null
+++ b/cms/src/collections/Leads.ts
@@ -0,0 +1,150 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+import { pool } from '../utilities/pool';
+import { logCmsSyncEvent } from '../utilities/monitoring';
+
+const normalizeEmail = (value: unknown): string | null => {
+ if (typeof value !== 'string') {
+ return null;
+ }
+
+ const trimmed = value.trim().toLowerCase();
+ if (trimmed.length === 0) {
+ return null;
+ }
+
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return emailPattern.test(trimmed) ? trimmed : null;
+};
+
+const upsertIntoSupabase = async ({ doc, req }: { doc: any; req: any }) => {
+ const supabaseId = doc.supabaseId ?? null;
+ const name = typeof doc.name === 'string' ? doc.name.trim() : '';
+ const email = normalizeEmail(doc.email);
+ const company = typeof doc.company === 'string' && doc.company.trim().length > 0 ? doc.company.trim() : null;
+ const project = typeof doc.project === 'string' && doc.project.trim().length > 0 ? doc.project.trim() : null;
+ const message = typeof doc.message === 'string' ? doc.message.trim() : '';
+
+ if (!name) {
+ throw new Error('Name is required.');
+ }
+
+ if (!email) {
+ throw new Error('A valid email address is required.');
+ }
+
+ if (!message) {
+ throw new Error('Message cannot be empty.');
+ }
+
+ const queryWithId = `
+ insert into public.leads (id, name, email, company, project, message)
+ values ($1::uuid,$2,$3,$4,$5,$6)
+ on conflict (id) do update set
+ name=excluded.name,
+ email=excluded.email,
+ company=excluded.company,
+ project=excluded.project,
+ message=excluded.message
+ returning id`;
+
+ const queryWithoutId = `
+ insert into public.leads (name, email, company, project, message)
+ values ($1,$2,$3,$4,$5)
+ returning id`;
+
+ const paramsWithId = [supabaseId, name, email, company, project, message];
+ const paramsWithoutId = [name, email, company, project, message];
+
+ try {
+ const result = supabaseId
+ ? await pool.query(queryWithId, paramsWithId)
+ : await pool.query(queryWithoutId, paramsWithoutId);
+
+ const generatedId = result?.rows?.[0]?.id ?? null;
+
+ if (!supabaseId && generatedId && req?.payload) {
+ await req.payload.update({
+ collection: 'leads',
+ id: doc.id,
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+ }
+
+ await logCmsSyncEvent({
+ collection: 'leads',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId: (supabaseId ?? generatedId) ?? null,
+ status: 'success',
+ metadata: { email, hasCompany: Boolean(company), hasProject: Boolean(project) },
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error while syncing lead';
+ await logCmsSyncEvent({
+ collection: 'leads',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId,
+ status: 'error',
+ message,
+ metadata: { email },
+ });
+ throw error;
+ }
+};
+
+const Leads: CollectionConfig = {
+ slug: 'leads',
+ admin: { useAsTitle: 'name' },
+ access: {
+ read: ({ req }: { req: any }) => Boolean(req.user),
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ {
+ name: 'supabaseId',
+ type: 'text',
+ admin: { hidden: true },
+ },
+ {
+ name: 'name',
+ type: 'text',
+ required: true,
+ },
+ {
+ name: 'email',
+ type: 'text',
+ required: true,
+ validate: (value: unknown) => {
+ if (!normalizeEmail(value)) {
+ return 'Enter a valid email address.';
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'company',
+ type: 'text',
+ },
+ {
+ name: 'project',
+ type: 'text',
+ },
+ {
+ name: 'message',
+ type: 'textarea',
+ required: true,
+ admin: {
+ description: 'Details provided by the lead when contacting the team.',
+ },
+ },
+ ],
+ hooks: { afterChange: [upsertIntoSupabase] },
+};
+
+export default Leads;
diff --git a/cms/src/collections/NewsletterSubscribers.ts b/cms/src/collections/NewsletterSubscribers.ts
new file mode 100644
index 0000000..21cd285
--- /dev/null
+++ b/cms/src/collections/NewsletterSubscribers.ts
@@ -0,0 +1,124 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+import { pool } from '../utilities/pool';
+import { logCmsSyncEvent } from '../utilities/monitoring';
+
+const normalizeEmail = (value: unknown): string | null => {
+ if (typeof value !== 'string') {
+ return null;
+ }
+
+ const trimmed = value.trim().toLowerCase();
+ if (trimmed.length === 0) {
+ return null;
+ }
+
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return emailPattern.test(trimmed) ? trimmed : null;
+};
+
+const upsertIntoSupabase = async ({ doc, req }: { doc: any; req: any }) => {
+ const supabaseId = doc.supabaseId ?? null;
+ const email = normalizeEmail(doc.email);
+ const active = !!doc.active;
+
+ if (!email) {
+ throw new Error('A valid email address is required.');
+ }
+
+ const queryWithId = `
+ insert into public.newsletter_subscribers (id, email, active)
+ values ($1::uuid,$2,$3)
+ on conflict (id) do update set
+ email=excluded.email,
+ active=excluded.active
+ returning id`;
+
+ const queryWithoutId = `
+ insert into public.newsletter_subscribers (email, active)
+ values ($1,$2)
+ on conflict (email) do update set
+ active=excluded.active,
+ email=excluded.email
+ returning id`;
+
+ const paramsWithId = [supabaseId, email, active];
+ const paramsWithoutId = [email, active];
+
+ try {
+ const result = supabaseId
+ ? await pool.query(queryWithId, paramsWithId)
+ : await pool.query(queryWithoutId, paramsWithoutId);
+
+ const generatedId = result?.rows?.[0]?.id ?? null;
+
+ if (!supabaseId && generatedId && req?.payload) {
+ await req.payload.update({
+ collection: 'newsletterSubscribers',
+ id: doc.id,
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+ }
+
+ await logCmsSyncEvent({
+ collection: 'newsletterSubscribers',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId: (supabaseId ?? generatedId) ?? null,
+ status: 'success',
+ metadata: { email, active },
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error while syncing newsletter subscriber';
+ await logCmsSyncEvent({
+ collection: 'newsletterSubscribers',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId,
+ status: 'error',
+ message,
+ metadata: { email },
+ });
+ throw error;
+ }
+};
+
+const NewsletterSubscribers: CollectionConfig = {
+ slug: 'newsletterSubscribers',
+ admin: { useAsTitle: 'email' },
+ access: {
+ read: ({ req }: { req: any }) => Boolean(req.user),
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ {
+ name: 'supabaseId',
+ type: 'text',
+ admin: { hidden: true },
+ },
+ {
+ name: 'email',
+ type: 'text',
+ required: true,
+ unique: true,
+ validate: (value: unknown) => {
+ if (!normalizeEmail(value)) {
+ return 'Enter a valid email address.';
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'active',
+ type: 'checkbox',
+ defaultValue: true,
+ },
+ ],
+ hooks: { afterChange: [upsertIntoSupabase] },
+};
+
+export default NewsletterSubscribers;
diff --git a/cms/src/collections/Posts.ts b/cms/src/collections/Posts.ts
index 069e0dc..2029f71 100644
--- a/cms/src/collections/Posts.ts
+++ b/cms/src/collections/Posts.ts
@@ -1,22 +1,17 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { CollectionConfig } from 'payload';
import { lexicalEditor } from '@payloadcms/richtext-lexical';
-import { Pool } from 'pg';
-
-const pool = new Pool({ connectionString: process.env.DATABASE_URL });
+import { pool } from '../utilities/pool';
+import { resolveLocalizedText, resolveOptionalLocalizedText } from '../utilities/localization';
+import { buildNextPreviewUrl } from '../utilities/preview';
+import { logCmsSyncEvent } from '../utilities/monitoring';
const upsertIntoPublic = async ({ doc, req }: { doc: any; req: any }) => {
const supabaseId = doc.supabaseId ?? null;
const slug = doc.slug;
const published = !!doc.published;
- const title =
- typeof doc.title === 'object'
- ? (doc.title['pt-BR'] ?? doc.title['en'] ?? '')
- : doc.title;
- const excerpt =
- typeof doc.excerpt === 'object'
- ? (doc.excerpt['pt-BR'] ?? doc.excerpt['en'] ?? null)
- : (doc.excerpt ?? null);
+ const title = resolveLocalizedText(doc.title);
+ const excerpt = resolveOptionalLocalizedText(doc.excerpt);
const image_url = doc?.coverImage?.url ?? null;
const content =
typeof doc.content === 'string'
@@ -48,25 +43,59 @@ const upsertIntoPublic = async ({ doc, req }: { doc: any; req: any }) => {
updated_at=now()
returning id`;
- const result = supabaseId
- ? await pool.query(queryWithId, [supabaseId, slug, title, excerpt, image_url, content, published])
- : await pool.query(queryWithoutId, [slug, title, excerpt, image_url, content, published]);
+ try {
+ const paramsWithId = [supabaseId, slug, title, excerpt, image_url, content, published];
+ const paramsWithoutId = [slug, title, excerpt, image_url, content, published];
+
+ const result = supabaseId
+ ? await pool.query(queryWithId, paramsWithId)
+ : await pool.query(queryWithoutId, paramsWithoutId);
+
+ const generatedId = result?.rows?.[0]?.id ?? null;
- const generatedId = result?.rows?.[0]?.id;
+ if (!supabaseId && generatedId && req?.payload) {
+ await req.payload.update({
+ collection: 'posts',
+ id: doc.id,
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+ }
- if (!supabaseId && generatedId && req?.payload) {
- await req.payload.update({
+ await logCmsSyncEvent({
collection: 'posts',
- id: doc.id,
- data: { supabaseId: generatedId },
- depth: 0,
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId: (supabaseId ?? generatedId) ?? null,
+ status: 'success',
+ metadata: { slug, published, hasImage: Boolean(image_url) },
});
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error while syncing post';
+ await logCmsSyncEvent({
+ collection: 'posts',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId,
+ status: 'error',
+ message,
+ metadata: { slug },
+ });
+ throw error;
}
};
const Posts: CollectionConfig = {
slug: 'posts',
- admin: { useAsTitle: 'slug' },
+ admin: {
+ useAsTitle: 'slug',
+ preview: (doc: any) =>
+ buildNextPreviewUrl(
+ doc?.slug && typeof doc.slug === 'string'
+ ? `/blog/${doc.slug}`
+ : '/blog'
+ ),
+ },
access: {
read: () => true,
create: ({ req }: { req: any }) => Boolean(req.user),
diff --git a/cms/src/collections/Repositories.ts b/cms/src/collections/Repositories.ts
new file mode 100644
index 0000000..e3ce15e
--- /dev/null
+++ b/cms/src/collections/Repositories.ts
@@ -0,0 +1,197 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+import { pool } from '../utilities/pool';
+import { resolveLocalizedText } from '../utilities/localization';
+import { buildSpaPreviewUrl } from '../utilities/preview';
+import { logCmsSyncEvent } from '../utilities/monitoring';
+
+const normalizeUrl = (value: unknown): string | null => {
+ if (typeof value !== 'string' || value.trim().length === 0) {
+ return null;
+ }
+
+ try {
+ const parsed = new URL(value.trim());
+ return parsed.toString();
+ } catch {
+ return null;
+ }
+};
+
+const extractTags = (tags: any): string[] => {
+ if (!Array.isArray(tags)) {
+ return [];
+ }
+
+ const unique = new Set();
+
+ for (const tag of tags) {
+ let value: string | null = null;
+
+ if (tag && typeof tag === 'object') {
+ if ('label' in tag) {
+ value = resolveLocalizedText(tag.label).trim();
+ } else if ('value' in tag) {
+ value = resolveLocalizedText(tag.value).trim();
+ } else if ('tag' in tag) {
+ value = resolveLocalizedText(tag.tag).trim();
+ }
+ } else if (typeof tag === 'string') {
+ value = tag.trim();
+ }
+
+ if (value && value.length > 0) {
+ unique.add(value);
+ }
+ }
+
+ return Array.from(unique);
+};
+
+const upsertIntoSupabase = async ({ doc, req }: { doc: any; req: any }) => {
+ const supabaseId = doc.supabaseId ?? null;
+ const name = resolveLocalizedText(doc.name);
+ const description = resolveLocalizedText(doc.description);
+ const github_url = normalizeUrl(doc.githubUrl);
+ const demo_url = normalizeUrl(doc.demoUrl);
+ const tags = extractTags(doc.tags);
+ const active = !!doc.active;
+
+ if (!github_url) {
+ throw new Error('A valid GitHub URL is required for repositories.');
+ }
+
+ const queryWithId = `
+ insert into public.repositories (id, name, description, github_url, demo_url, tags, active, updated_at)
+ values ($1::uuid,$2,$3,$4,$5,$6,$7, now())
+ on conflict (id) do update set
+ name=excluded.name,
+ description=excluded.description,
+ github_url=excluded.github_url,
+ demo_url=excluded.demo_url,
+ tags=excluded.tags,
+ active=excluded.active,
+ updated_at=now()
+ returning id`;
+
+ const queryWithoutId = `
+ insert into public.repositories (name, description, github_url, demo_url, tags, active, updated_at)
+ values ($1,$2,$3,$4,$5,$6, now())
+ returning id`;
+
+ const paramsWithId = [supabaseId, name, description, github_url, demo_url, tags, active];
+ const paramsWithoutId = [name, description, github_url, demo_url, tags, active];
+
+ try {
+ const result = supabaseId
+ ? await pool.query(queryWithId, paramsWithId)
+ : await pool.query(queryWithoutId, paramsWithoutId);
+
+ const generatedId = result?.rows?.[0]?.id ?? null;
+
+ if (!supabaseId && generatedId && req?.payload) {
+ await req.payload.update({
+ collection: 'repositories',
+ id: doc.id,
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+ }
+
+ await logCmsSyncEvent({
+ collection: 'repositories',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId: (supabaseId ?? generatedId) ?? null,
+ status: 'success',
+ metadata: { github_url, tagsCount: tags.length, active },
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error while syncing repository';
+ await logCmsSyncEvent({
+ collection: 'repositories',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId,
+ status: 'error',
+ message,
+ metadata: { github_url },
+ });
+ throw error;
+ }
+};
+
+const Repositories: CollectionConfig = {
+ slug: 'repositories',
+ admin: {
+ useAsTitle: 'name',
+ preview: () => buildSpaPreviewUrl('/projects'),
+ },
+ access: {
+ read: () => true,
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ {
+ name: 'supabaseId',
+ type: 'text',
+ admin: { hidden: true },
+ },
+ { name: 'name', type: 'text', required: true, localized: true },
+ { name: 'description', type: 'textarea', required: true, localized: true },
+ {
+ name: 'githubUrl',
+ type: 'text',
+ required: true,
+ unique: true,
+ validate: (value: unknown) => {
+ if (!normalizeUrl(value)) {
+ return 'Enter a valid GitHub repository URL.';
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'demoUrl',
+ type: 'text',
+ validate: (value: unknown) => {
+ if (value === null || value === undefined || value === '') {
+ return true;
+ }
+
+ if (!normalizeUrl(value)) {
+ return 'Enter a valid demo URL or leave blank.';
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'tags',
+ type: 'array',
+ labels: {
+ singular: 'Tag',
+ plural: 'Tags',
+ },
+ fields: [
+ {
+ name: 'value',
+ type: 'text',
+ required: true,
+ localized: true,
+ },
+ ],
+ },
+ {
+ name: 'active',
+ type: 'checkbox',
+ defaultValue: true,
+ },
+ ],
+ hooks: { afterChange: [upsertIntoSupabase] },
+};
+
+export default Repositories;
diff --git a/cms/src/collections/SiteSettings.ts b/cms/src/collections/SiteSettings.ts
new file mode 100644
index 0000000..b776b95
--- /dev/null
+++ b/cms/src/collections/SiteSettings.ts
@@ -0,0 +1,144 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+import { pool } from '../utilities/pool';
+import { resolveOptionalLocalizedText } from '../utilities/localization';
+import { buildSpaPreviewUrl } from '../utilities/preview';
+import { logCmsSyncEvent } from '../utilities/monitoring';
+
+const serializeValue = (value: any): string => {
+ if (typeof value === 'string') {
+ try {
+ JSON.parse(value);
+ return value;
+ } catch {
+ return JSON.stringify(value);
+ }
+ }
+
+ if (value === null || value === undefined) {
+ return JSON.stringify({});
+ }
+
+ return JSON.stringify(value);
+};
+
+const upsertIntoSupabase = async ({ doc, req }: { doc: any; req: any }) => {
+ const supabaseId = doc.supabaseId ?? null;
+ const key = doc.key;
+ const description = resolveOptionalLocalizedText(doc.description);
+ const value = serializeValue(doc.value);
+
+ const queryWithId = `
+ insert into public.site_settings (id, key, value, description, updated_at)
+ values ($1::uuid,$2,$3::jsonb,$4, now())
+ on conflict (id) do update set
+ key=excluded.key,
+ value=excluded.value,
+ description=excluded.description,
+ updated_at=now()
+ returning id`;
+
+ const queryWithoutId = `
+ insert into public.site_settings (key, value, description, updated_at)
+ values ($1,$2::jsonb,$3, now())
+ on conflict (key) do update set
+ value=excluded.value,
+ description=excluded.description,
+ updated_at=now()
+ returning id`;
+
+ const paramsWithId = [supabaseId, key, value, description];
+ const paramsWithoutId = [key, value, description];
+
+ try {
+ const result = supabaseId
+ ? await pool.query(queryWithId, paramsWithId)
+ : await pool.query(queryWithoutId, paramsWithoutId);
+
+ const generatedId = result?.rows?.[0]?.id ?? null;
+
+ if (!supabaseId && generatedId && req?.payload) {
+ await req.payload.update({
+ collection: 'siteSettings',
+ id: doc.id,
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+ }
+
+ await logCmsSyncEvent({
+ collection: 'siteSettings',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId: (supabaseId ?? generatedId) ?? null,
+ status: 'success',
+ metadata: { key, hasDescription: Boolean(description) },
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error while syncing site setting';
+ await logCmsSyncEvent({
+ collection: 'siteSettings',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId,
+ status: 'error',
+ message,
+ metadata: { key },
+ });
+ throw error;
+ }
+};
+
+const SiteSettings: CollectionConfig = {
+ slug: 'siteSettings',
+ admin: {
+ useAsTitle: 'key',
+ preview: () => buildSpaPreviewUrl('/contact'),
+ },
+ access: {
+ read: ({ req }: { req: any }) => Boolean(req.user),
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ {
+ name: 'supabaseId',
+ type: 'text',
+ admin: { hidden: true },
+ },
+ {
+ name: 'key',
+ type: 'text',
+ required: true,
+ unique: true,
+ validate: (value: unknown) => {
+ if (typeof value !== 'string' || value.trim().length === 0) {
+ return 'Setting key is required.';
+ }
+
+ if (!/^[a-z0-9_-]+$/.test(value)) {
+ return 'Use lowercase letters, numbers, underscores, or hyphens.';
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'description',
+ type: 'textarea',
+ localized: true,
+ },
+ {
+ name: 'value',
+ type: 'json',
+ required: true,
+ admin: {
+ description: 'Stores arbitrary configuration consumed by the frontend.',
+ },
+ },
+ ],
+ hooks: { afterChange: [upsertIntoSupabase] },
+};
+
+export default SiteSettings;
diff --git a/cms/src/collections/Solutions.ts b/cms/src/collections/Solutions.ts
new file mode 100644
index 0000000..4c59380
--- /dev/null
+++ b/cms/src/collections/Solutions.ts
@@ -0,0 +1,219 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+import { pool } from '../utilities/pool';
+import { resolveLocalizedText, resolveOptionalLocalizedText } from '../utilities/localization';
+import { buildSpaPreviewUrl } from '../utilities/preview';
+import { logCmsSyncEvent } from '../utilities/monitoring';
+
+const gradientOptions = [
+ 'from-brand-purple to-brand-blue',
+ 'from-brand-pink to-brand-orange',
+ 'from-brand-blue to-brand-purple',
+ 'from-brand-orange to-brand-pink',
+];
+
+const extractFeatureList = (features: any): string[] => {
+ if (!Array.isArray(features)) {
+ return [];
+ }
+
+ const values = features
+ .map((feature) => {
+ if (feature && typeof feature === 'object') {
+ if ('content' in feature) {
+ return resolveOptionalLocalizedText(feature.content);
+ }
+
+ if ('value' in feature) {
+ return resolveOptionalLocalizedText(feature.value);
+ }
+
+ if ('label' in feature) {
+ return resolveOptionalLocalizedText(feature.label);
+ }
+ }
+
+ return resolveOptionalLocalizedText(feature);
+ })
+ .filter((item): item is string => Boolean(item && item.trim().length > 0));
+
+ return values;
+};
+
+const upsertIntoSupabase = async ({ doc, req }: { doc: any; req: any }) => {
+ const supabaseId = doc.supabaseId ?? null;
+ const slug = doc.slug;
+ const title = resolveLocalizedText(doc.title);
+ const description = resolveLocalizedText(doc.description);
+ const image_url = doc?.image?.url ?? null;
+ const features = extractFeatureList(doc.features);
+ const active = !!doc.active;
+ const gradient = doc.gradient ?? null;
+
+ const queryWithId = `
+ insert into public.solutions (id, slug, title, description, image_url, features, gradient, active, updated_at)
+ values ($1::uuid,$2,$3,$4,$5,$6::jsonb,$7,$8, now())
+ on conflict (id) do update set
+ slug=excluded.slug,
+ title=excluded.title,
+ description=excluded.description,
+ image_url=excluded.image_url,
+ features=excluded.features,
+ gradient=excluded.gradient,
+ active=excluded.active,
+ updated_at=now()
+ returning id`;
+
+ const queryWithoutId = `
+ insert into public.solutions (slug, title, description, image_url, features, gradient, active, updated_at)
+ values ($1,$2,$3,$4,$5::jsonb,$6,$7, now())
+ on conflict (slug) do update set
+ title=excluded.title,
+ description=excluded.description,
+ image_url=excluded.image_url,
+ features=excluded.features,
+ gradient=excluded.gradient,
+ active=excluded.active,
+ updated_at=now()
+ returning id`;
+
+ const paramsWithId = [
+ supabaseId,
+ slug,
+ title,
+ description,
+ image_url,
+ JSON.stringify(features ?? []),
+ gradient,
+ active,
+ ];
+
+ const paramsWithoutId = [
+ slug,
+ title,
+ description,
+ image_url,
+ JSON.stringify(features ?? []),
+ gradient,
+ active,
+ ];
+
+ try {
+ const result = supabaseId
+ ? await pool.query(queryWithId, paramsWithId)
+ : await pool.query(queryWithoutId, paramsWithoutId);
+
+ const generatedId = result?.rows?.[0]?.id ?? null;
+
+ if (!supabaseId && generatedId && req?.payload) {
+ await req.payload.update({
+ collection: 'solutions',
+ id: doc.id,
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+ }
+
+ await logCmsSyncEvent({
+ collection: 'solutions',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId: (supabaseId ?? generatedId) ?? null,
+ status: 'success',
+ metadata: { slug, hasImage: Boolean(image_url), featureCount: features.length },
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error while syncing solution';
+ await logCmsSyncEvent({
+ collection: 'solutions',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId,
+ status: 'error',
+ message,
+ metadata: { slug },
+ });
+ throw error;
+ }
+};
+
+const Solutions: CollectionConfig = {
+ slug: 'solutions',
+ admin: {
+ useAsTitle: 'title',
+ preview: (doc: any) =>
+ buildSpaPreviewUrl(
+ doc?.slug && typeof doc.slug === 'string'
+ ? `/solutions/${doc.slug}`
+ : '/solutions'
+ ),
+ },
+ access: {
+ read: () => true,
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ {
+ name: 'supabaseId',
+ type: 'text',
+ admin: { hidden: true },
+ },
+ {
+ name: 'slug',
+ type: 'text',
+ required: true,
+ unique: true,
+ validate: (value: unknown) => {
+ if (typeof value !== 'string' || value.trim().length === 0) {
+ return 'Slug is required.';
+ }
+
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value)) {
+ return 'Use lowercase letters, numbers, and hyphens only.';
+ }
+
+ return true;
+ },
+ },
+ { name: 'title', type: 'text', required: true, localized: true },
+ { name: 'description', type: 'textarea', required: true, localized: true },
+ {
+ name: 'image',
+ type: 'upload',
+ relationTo: 'media',
+ },
+ {
+ name: 'features',
+ type: 'array',
+ labels: {
+ singular: 'Feature',
+ plural: 'Features',
+ },
+ fields: [
+ {
+ name: 'content',
+ type: 'text',
+ required: true,
+ localized: true,
+ },
+ ],
+ },
+ {
+ name: 'gradient',
+ type: 'select',
+ required: true,
+ defaultValue: gradientOptions[0],
+ options: gradientOptions.map((value) => ({ label: value, value })),
+ },
+ {
+ name: 'active',
+ type: 'checkbox',
+ defaultValue: true,
+ },
+ ],
+ hooks: { afterChange: [upsertIntoSupabase] },
+};
+
+export default Solutions;
diff --git a/cms/src/collections/TeamMembers.ts b/cms/src/collections/TeamMembers.ts
new file mode 100644
index 0000000..0dce85c
--- /dev/null
+++ b/cms/src/collections/TeamMembers.ts
@@ -0,0 +1,140 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import type { CollectionConfig } from 'payload';
+import { pool } from '../utilities/pool';
+import { resolveLocalizedText, resolveOptionalLocalizedText } from '../utilities/localization';
+import { buildSpaPreviewUrl } from '../utilities/preview';
+import { logCmsSyncEvent } from '../utilities/monitoring';
+
+const normalizeUrl = (value: unknown): string | null => {
+ if (typeof value !== 'string' || value.trim().length === 0) {
+ return null;
+ }
+
+ try {
+ const parsed = new URL(value.trim());
+ return parsed.toString();
+ } catch {
+ return null;
+ }
+};
+
+const upsertIntoSupabase = async ({ doc, req }: { doc: any; req: any }) => {
+ const supabaseId = doc.supabaseId ?? null;
+ const name = resolveLocalizedText(doc.name);
+ const role = resolveLocalizedText(doc.role);
+ const bio = resolveOptionalLocalizedText(doc.bio);
+ const image_url = doc?.photo?.url ?? null;
+ const linkedin_url = normalizeUrl(doc.linkedinUrl);
+ const active = !!doc.active;
+
+ const queryWithId = `
+ insert into public.team_members (id, name, role, bio, image_url, linkedin_url, active, updated_at)
+ values ($1::uuid,$2,$3,$4,$5,$6,$7, now())
+ on conflict (id) do update set
+ name=excluded.name,
+ role=excluded.role,
+ bio=excluded.bio,
+ image_url=excluded.image_url,
+ linkedin_url=excluded.linkedin_url,
+ active=excluded.active,
+ updated_at=now()
+ returning id`;
+
+ const queryWithoutId = `
+ insert into public.team_members (name, role, bio, image_url, linkedin_url, active, updated_at)
+ values ($1,$2,$3,$4,$5,$6, now())
+ returning id`;
+
+ const paramsWithId = [supabaseId, name, role, bio, image_url, linkedin_url, active];
+ const paramsWithoutId = [name, role, bio, image_url, linkedin_url, active];
+
+ try {
+ const result = supabaseId
+ ? await pool.query(queryWithId, paramsWithId)
+ : await pool.query(queryWithoutId, paramsWithoutId);
+
+ const generatedId = result?.rows?.[0]?.id ?? null;
+
+ if (!supabaseId && generatedId && req?.payload) {
+ await req.payload.update({
+ collection: 'teamMembers',
+ id: doc.id,
+ data: { supabaseId: generatedId },
+ depth: 0,
+ });
+ }
+
+ await logCmsSyncEvent({
+ collection: 'teamMembers',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId: (supabaseId ?? generatedId) ?? null,
+ status: 'success',
+ metadata: { hasBio: Boolean(bio), linkedinConnected: Boolean(linkedin_url), active },
+ });
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error while syncing team member';
+ await logCmsSyncEvent({
+ collection: 'teamMembers',
+ action: 'upsert',
+ payloadId: doc.id ?? null,
+ supabaseId,
+ status: 'error',
+ message,
+ metadata: { linkedin_url },
+ });
+ throw error;
+ }
+};
+
+const TeamMembers: CollectionConfig = {
+ slug: 'teamMembers',
+ admin: {
+ useAsTitle: 'name',
+ preview: () => buildSpaPreviewUrl('/about'),
+ },
+ access: {
+ read: () => true,
+ create: ({ req }: { req: any }) => Boolean(req.user),
+ update: ({ req }: { req: any }) => Boolean(req.user),
+ delete: ({ req }: { req: any }) => Boolean(req.user),
+ },
+ fields: [
+ {
+ name: 'supabaseId',
+ type: 'text',
+ admin: { hidden: true },
+ },
+ { name: 'name', type: 'text', required: true, localized: true },
+ { name: 'role', type: 'text', required: true, localized: true },
+ { name: 'bio', type: 'textarea', localized: true },
+ {
+ name: 'photo',
+ type: 'upload',
+ relationTo: 'media',
+ },
+ {
+ name: 'linkedinUrl',
+ type: 'text',
+ validate: (value: unknown) => {
+ if (value === null || value === undefined || value === '') {
+ return true;
+ }
+
+ if (!normalizeUrl(value)) {
+ return 'Enter a valid LinkedIn URL or leave blank.';
+ }
+
+ return true;
+ },
+ },
+ {
+ name: 'active',
+ type: 'checkbox',
+ defaultValue: true,
+ },
+ ],
+ hooks: { afterChange: [upsertIntoSupabase] },
+};
+
+export default TeamMembers;
diff --git a/cms/src/collections/index.ts b/cms/src/collections/index.ts
new file mode 100644
index 0000000..cc94bd7
--- /dev/null
+++ b/cms/src/collections/index.ts
@@ -0,0 +1,12 @@
+export { default as Authors } from './Authors';
+export { default as Categories } from './Categories';
+export { default as HomepageFeatures } from './HomepageFeatures';
+export { default as Leads } from './Leads';
+export { default as Media } from './Media';
+export { default as NewsletterSubscribers } from './NewsletterSubscribers';
+export { default as Posts } from './Posts';
+export { default as Repositories } from './Repositories';
+export { default as SiteSettings } from './SiteSettings';
+export { default as Solutions } from './Solutions';
+export { default as TeamMembers } from './TeamMembers';
+export { default as Users } from './Users';
diff --git a/cms/src/utilities/localization.ts b/cms/src/utilities/localization.ts
new file mode 100644
index 0000000..854bb15
--- /dev/null
+++ b/cms/src/utilities/localization.ts
@@ -0,0 +1,32 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+const LOCALE_PRIORITIES = ['pt-BR', 'en'];
+
+export const resolveLocalizedText = (value: any, fallback = ''): string => {
+ if (typeof value === 'string') {
+ return value;
+ }
+
+ if (value && typeof value === 'object') {
+ for (const locale of LOCALE_PRIORITIES) {
+ const localizedValue = value?.[locale];
+ if (typeof localizedValue === 'string' && localizedValue.trim().length > 0) {
+ return localizedValue;
+ }
+ }
+
+ const firstValue = Object.values(value).find(
+ (entry) => typeof entry === 'string' && entry.trim().length > 0
+ );
+
+ if (typeof firstValue === 'string') {
+ return firstValue;
+ }
+ }
+
+ return fallback;
+};
+
+export const resolveOptionalLocalizedText = (value: any): string | null => {
+ const resolved = resolveLocalizedText(value, '').trim();
+ return resolved.length > 0 ? resolved : null;
+};
diff --git a/cms/src/utilities/monitoring.ts b/cms/src/utilities/monitoring.ts
new file mode 100644
index 0000000..d3c267e
--- /dev/null
+++ b/cms/src/utilities/monitoring.ts
@@ -0,0 +1,55 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+export type CmsSyncStatus = 'success' | 'error';
+
+export interface CmsSyncEvent {
+ collection: string;
+ action: 'upsert' | 'delete';
+ payloadId?: string | number | null;
+ supabaseId?: string | null;
+ status: CmsSyncStatus;
+ message?: string;
+ metadata?: Record;
+}
+
+const OBSERVABILITY_WEBHOOK_URL = process.env.CMS_OBSERVABILITY_WEBHOOK_URL;
+const OBSERVABILITY_DASHBOARD_URL = process.env.CMS_OBSERVABILITY_DASHBOARD_URL;
+
+const safeStringify = (value: unknown) => {
+ try {
+ return JSON.stringify(value);
+ } catch (error) {
+ return `"[unserializable payload: ${error instanceof Error ? error.message : 'unknown'}]"`;
+ }
+};
+
+const toPlainObject = (event: CmsSyncEvent) => ({
+ ...event,
+ dashboardUrl: OBSERVABILITY_DASHBOARD_URL ?? null,
+ timestamp: new Date().toISOString(),
+});
+
+export const logCmsSyncEvent = async (event: CmsSyncEvent) => {
+ const entry = toPlainObject(event);
+ const prefix = `[CMS Sync:${event.collection}]`;
+
+ if (event.status === 'error') {
+ console.error(prefix, event.message ?? 'Sync failed', event.metadata ?? {});
+ } else {
+ console.info(prefix, event.message ?? 'Sync completed', event.metadata ?? {});
+ }
+
+ if (!OBSERVABILITY_WEBHOOK_URL) {
+ return;
+ }
+
+ try {
+ await fetch(OBSERVABILITY_WEBHOOK_URL, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: safeStringify(entry),
+ });
+ } catch (error) {
+ console.warn(`${prefix} Unable to notify observability webhook`, error);
+ }
+};
diff --git a/cms/src/utilities/pool.ts b/cms/src/utilities/pool.ts
new file mode 100644
index 0000000..6d56508
--- /dev/null
+++ b/cms/src/utilities/pool.ts
@@ -0,0 +1,5 @@
+import { Pool } from 'pg';
+
+export const pool = new Pool({
+ connectionString: process.env.DATABASE_URL,
+});
diff --git a/cms/src/utilities/preview.ts b/cms/src/utilities/preview.ts
new file mode 100644
index 0000000..0c67ed4
--- /dev/null
+++ b/cms/src/utilities/preview.ts
@@ -0,0 +1,23 @@
+export const SPA_PREVIEW_BASE =
+ process.env.PAYLOAD_PUBLIC_SPA_PREVIEW_URL ?? 'http://localhost:5173';
+
+const FALLBACK_NEXT_BASE =
+ process.env.PAYLOAD_PUBLIC_PREVIEW_URL ?? SPA_PREVIEW_BASE;
+
+export const NEXT_PREVIEW_BASE =
+ process.env.PAYLOAD_PUBLIC_NEXT_PREVIEW_URL ?? FALLBACK_NEXT_BASE;
+
+const buildPreviewURL = (base: string, path: string): string => {
+ try {
+ const normalizedPath = path.startsWith('/') ? path : `/${path}`;
+ return new URL(normalizedPath, base).toString();
+ } catch {
+ return `${base.replace(/\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;
+ }
+};
+
+export const buildSpaPreviewUrl = (path: string): string =>
+ buildPreviewURL(SPA_PREVIEW_BASE, path);
+
+export const buildNextPreviewUrl = (path: string): string =>
+ buildPreviewURL(NEXT_PREVIEW_BASE, path);
diff --git a/docs/auth-flows.md b/docs/auth-flows.md
new file mode 100644
index 0000000..8513c1d
--- /dev/null
+++ b/docs/auth-flows.md
@@ -0,0 +1,63 @@
+# Authentication and Security Flows
+
+This document describes the end-user and administrator-facing authentication flows now available in the Monynha stack.
+
+## Password reset lifecycle
+
+1. On the `/auth` screen, choose **"Esqueci minha senha"** under the login form.
+2. Supply the email address associated with the account. Supabase will send a password reset email that points back to `/auth?type=recovery`.
+3. Follow the emailed link. After Supabase validates the token, the application shows the **"Salvar nova senha"** form.
+4. Enter and confirm the new password (minimum of 6 characters). After saving:
+ - The password is updated via `supabase.auth.updateUser`.
+ - The user is redirected to the dashboard automatically with a fresh session.
+
+> **Note:** The reset entry point is also available from the dashboard security card via the same email flow.
+
+## Two-factor authentication (TOTP)
+
+The dashboard now exposes a **Segurança da conta** card that allows any authenticated user to manage TOTP-based MFA:
+
+- **Enable:** Click **"Ativar autenticação em duas etapas"** to enroll. A QR code and recovery secret are displayed. Scan the code with an authenticator app and confirm the 6-digit code to finish enrollment.
+- **Disable:** Use **"Desativar autenticação em duas etapas"** to remove the verified factor. This action requires the current session to meet `aal2` (verified) level.
+- **Cancel enrollment:** If enrollment is started but not confirmed, the setup can be cancelled to discard the pending factor.
+
+The Supabase client APIs used:
+
+- `supabase.auth.mfa.enroll({ factorType: 'totp' })`
+- `supabase.auth.mfa.challenge` + `supabase.auth.mfa.verify`
+- `supabase.auth.mfa.unenroll`
+- `supabase.auth.mfa.challengeAndVerify` (for on-demand step-up verification)
+
+## Admin data step-up requirement
+
+Administrators who have a verified TOTP factor must enter a fresh authenticator code before the dashboard loads sensitive tables (`leads`, `newsletter_subscribers`). The UI presents a dedicated **Confirmação de duas etapas** card whenever the current authenticator assurance level is below `aal2`. Once validated, the data loads through the `get_admin_dashboard_data` RPC and the session is promoted to `aal2`.
+
+## Payload CMS admin provisioning
+
+A new Supabase trigger calls the edge function `payload-admin-sync` whenever `public.profiles.role` transitions to or from `admin`. The function provisions (or removes) the corresponding Payload CMS user and keeps the `payload_user_id` column synced.
+
+To configure the integration:
+
+1. Create a row in `public.payload_admin_sync_config` with the deployed function URL and a shared secret:
+ ```sql
+ insert into public.payload_admin_sync_config (id, webhook_url, webhook_secret)
+ values (
+ 1,
+ 'https://.functions.supabase.co/payload-admin-sync',
+ ''
+ )
+ on conflict (id) do update set webhook_url = excluded.webhook_url, webhook_secret = excluded.webhook_secret;
+ ```
+2. Configure the edge function environment variables:
+ - `SUPABASE_URL`
+ - `SUPABASE_SERVICE_ROLE_KEY`
+ - `PAYLOAD_API_BASE_URL`
+ - `PAYLOAD_ADMIN_TOKEN`
+ - `PAYLOAD_SYNC_SHARED_SECRET`
+3. Deploy the function with `supabase functions deploy payload-admin-sync`.
+
+The function automatically triggers a Payload password-reset email whenever a new admin is provisioned.
+
+## Admin dashboard RPC
+
+The dashboard now retrieves restricted data via the `get_admin_dashboard_data` RPC. The function enforces the `role = 'admin'` check server-side and returns the `leads` and `newsletter_subscribers` collections in a single round-trip. Access is granted to authenticated and service roles only.
diff --git a/jest.config.cjs b/jest.config.cjs
new file mode 100644
index 0000000..d0ef43e
--- /dev/null
+++ b/jest.config.cjs
@@ -0,0 +1,23 @@
+const config = {
+ preset: 'ts-jest/presets/default-esm',
+ testEnvironment: 'jsdom',
+ extensionsToTreatAsEsm: ['.ts', '.tsx'],
+ moduleNameMapper: {
+ '^@/(.*)$': '/src/$1',
+ '\\.(css|less|scss|sass)$': '/tests/__mocks__/styleMock.ts',
+ '\\.(svg|png|jpg|jpeg|gif|webp|avif)$': '/tests/__mocks__/fileMock.ts',
+ },
+ setupFiles: ['/tests/setupEnv.ts'],
+ setupFilesAfterEnv: ['/tests/setupTests.ts'],
+ testMatch: ['**/?(*.)+(unit|integration|e2e).test.ts?(x)'],
+ collectCoverageFrom: ['src/**/*.{ts,tsx}', 'cms/src/**/*.ts'],
+ globals: {
+ 'ts-jest': {
+ tsconfig: '/tsconfig.app.json',
+ useESM: true,
+ isolatedModules: true,
+ },
+ },
+};
+
+module.exports = config;
diff --git a/package-lock.json b/package-lock.json
index c330eb9..fd23a8e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -73,12 +73,14 @@
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
+ "axe-core": "^4.10.0",
"eslint": "^9.9.0",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.9",
"globals": "^15.9.0",
"husky": "^9.1.7",
"jest": "^29.7.0",
+ "jest-axe": "^8.0.0",
"jest-environment-jsdom": "^29.7.0",
"prettier": "^3.6.2",
"tailwindcss": "^3.4.11",
@@ -4832,6 +4834,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/axe-core": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz",
+ "integrity": "sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/babel-jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
@@ -7705,6 +7717,83 @@
}
}
},
+ "node_modules/jest-axe": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/jest-axe/-/jest-axe-8.0.0.tgz",
+ "integrity": "sha512-4kNcNn7J0jPO4jANEYZOHeQ/tSBvkXS+MxTbX1CKbXGd0+ZbRGDn/v/8IYWI/MmYX15iLVyYRnRev9X3ksePWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "axe-core": "4.7.2",
+ "chalk": "4.1.2",
+ "jest-matcher-utils": "29.2.2",
+ "lodash.merge": "4.6.2"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/jest-axe/node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jest-axe/node_modules/axe-core": {
+ "version": "4.7.2",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz",
+ "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/jest-axe/node_modules/jest-matcher-utils": {
+ "version": "29.2.2",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.2.2.tgz",
+ "integrity": "sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.0.0",
+ "jest-diff": "^29.2.1",
+ "jest-get-type": "^29.2.0",
+ "pretty-format": "^29.2.1"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-axe/node_modules/pretty-format": {
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/jest-axe/node_modules/react-is": {
+ "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,
+ "license": "MIT"
+ },
"node_modules/jest-changed-files": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
diff --git a/package.json b/package.json
index eddf969..1fa27f1 100644
--- a/package.json
+++ b/package.json
@@ -11,12 +11,13 @@
"sitemap": "tsx scripts/generate-sitemap.ts",
"cleanup-lovable": "tsx scripts/cleanup-lovable.ts",
"preview": "vite preview",
- "test": "jest --passWithNoTests",
+ "test": "jest --runInBand",
"format": "prettier --write .",
- "prepare": "husky install",
+ "prepare": "husky install || echo 'husky skipped'",
"cms:dev": "npm --prefix cms run dev",
"cms:build": "npm --prefix cms run build",
- "cms:lint": "npm --prefix cms run lint"
+ "cms:lint": "npm --prefix cms run lint",
+ "supabase:types": "supabase gen types typescript --linked --schema public > src/lib/supabase/types.gen.ts"
},
"dependencies": {
"@radix-ui/react-accordion": "^1.2.0",
@@ -84,12 +85,14 @@
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
+ "axe-core": "^4.10.0",
"eslint": "^9.9.0",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.9",
"globals": "^15.9.0",
"husky": "^9.1.7",
"jest": "^29.7.0",
+ "jest-axe": "^8.0.0",
"jest-environment-jsdom": "^29.7.0",
"prettier": "^3.6.2",
"tailwindcss": "^3.4.11",
diff --git a/src/App.tsx b/src/App.tsx
index 0cf9308..3234bff 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -8,6 +8,7 @@ import { Suspense, lazy } from 'react';
import Loading from './components/Loading';
import ScrollToTop from './components/ScrollToTop';
import ProtectedRoute from './components/ProtectedRoute';
+import useDynamicCopy from '@/hooks/useDynamicCopy';
const Index = lazy(() => import('./pages/Index'));
const Solutions = lazy(() => import('./pages/Solutions'));
@@ -23,32 +24,40 @@ const NotFound = lazy(() => import('./pages/NotFound'));
const queryClient = new QueryClient();
+const AppRoutes = () => {
+ useDynamicCopy();
+
+ return (
+
+
+ }>
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ }>
+ } />
+
+ } />
+
+
+
+ );
+};
+
const App = () => (
-
-
- }>
-
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- }>
- } />
-
- } />
-
-
-
+
diff --git a/src/components/NewsletterSection.tsx b/src/components/NewsletterSection.tsx
index 3f7444c..278e907 100644
--- a/src/components/NewsletterSection.tsx
+++ b/src/components/NewsletterSection.tsx
@@ -2,10 +2,10 @@ import { useState } from 'react';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
-import { supabase } from '@/integrations/supabase';
import { useToast } from '@/hooks/use-toast';
import { Mail, CheckCircle } from 'lucide-react';
import { useTranslation } from 'react-i18next';
+import { subscribeToNewsletter } from '@/lib/data/supabase';
const NewsletterSection = () => {
const { t } = useTranslation();
@@ -30,28 +30,7 @@ const NewsletterSection = () => {
}
try {
- const { error } = await supabase
- .from('newsletter_subscribers')
- .insert([{ email: email.trim() }]);
-
- if (error) {
- if (error.code === '23505') {
- // Unique constraint violation
- toast({
- title: t('newsletterSection.alreadySubscribedTitle'),
- description: t('newsletterSection.alreadySubscribedDescription'),
- variant: 'destructive',
- });
- } else {
- toast({
- title: t('newsletterSection.errorTitle'),
- description: t('newsletterSection.errorDescription'),
- variant: 'destructive',
- });
- }
- setIsSubmitting(false);
- return;
- }
+ await subscribeToNewsletter({ email: email.trim() });
setIsSubscribed(true);
toast({
@@ -60,11 +39,21 @@ const NewsletterSection = () => {
});
} catch (error) {
console.error('Newsletter subscription error:', error);
- toast({
- title: t('newsletterSection.errorTitle'),
- description: t('newsletterSection.errorDescription'),
- variant: 'destructive',
- });
+ const code = (error as { code?: string }).code;
+
+ if (code === '23505') {
+ toast({
+ title: t('newsletterSection.alreadySubscribedTitle'),
+ description: t('newsletterSection.alreadySubscribedDescription'),
+ variant: 'destructive',
+ });
+ } else {
+ toast({
+ title: t('newsletterSection.errorTitle'),
+ description: t('newsletterSection.errorDescription'),
+ variant: 'destructive',
+ });
+ }
} finally {
setIsSubmitting(false);
}
diff --git a/src/components/TeamSection.tsx b/src/components/TeamSection.tsx
index 14986a0..c7eb804 100644
--- a/src/components/TeamSection.tsx
+++ b/src/components/TeamSection.tsx
@@ -2,18 +2,10 @@ import { useQuery } from '@tanstack/react-query';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
-import { supabase } from '@/integrations/supabase';
-import { Linkedin, Mail } from 'lucide-react';
+import { Linkedin } from 'lucide-react';
import { useTranslation } from 'react-i18next';
-
-interface TeamMember {
- id: string;
- name: string;
- role: string;
- bio: string | null;
- image_url: string | null;
- linkedin_url: string | null;
-}
+import { fetchTeamMembers } from '@/lib/data/supabase';
+import type { TeamMemberRow } from '@/lib/data/supabase';
const TeamSection = () => {
const { t } = useTranslation();
@@ -22,18 +14,9 @@ const TeamSection = () => {
data: members,
isLoading,
error,
- } = useQuery({
+ } = useQuery({
queryKey: ['team-members'],
- queryFn: async () => {
- const { data, error } = await supabase
- .from('team_members')
- .select('*')
- .eq('active', true)
- .order('created_at', { ascending: true });
-
- if (error) throw error;
- return data as TeamMember[];
- },
+ queryFn: fetchTeamMembers,
});
if (isLoading) {
diff --git a/src/components/blog/CommentsSection.tsx b/src/components/blog/CommentsSection.tsx
index bd3aafe..31b1ffd 100644
--- a/src/components/blog/CommentsSection.tsx
+++ b/src/components/blog/CommentsSection.tsx
@@ -1,8 +1,6 @@
import { useMemo, useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
-import { supabase } from '@/integrations/supabase';
-import type { Database } from '@/integrations/supabase/types';
import { useAuth } from '@/hooks/useAuth';
import { useToast } from '@/hooks/use-toast';
import { Card, CardContent } from '@/components/ui/card';
@@ -12,21 +10,16 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Loader2 } from 'lucide-react';
import { Link } from 'react-router-dom';
import { getNormalizedLocale } from '@/lib/i18n';
+import {
+ fetchCommentsWithAuthors,
+ insertComment,
+ type CommentWithAuthor,
+} from '@/lib/data/supabase';
interface CommentsSectionProps {
postId: string;
}
-type CommentRow = Database['public']['Tables']['comments']['Row'];
-type ProfileRow = Pick<
- Database['public']['Tables']['profiles']['Row'],
- 'name' | 'avatar_url'
->;
-
-type CommentWithAuthor = CommentRow & {
- author: ProfileRow | null;
-};
-
const getInitials = (name: string) => {
const parts = name
.split(' ')
@@ -74,47 +67,7 @@ const CommentsSection = ({ postId }: CommentsSectionProps) => {
} = useQuery({
queryKey: ['comments', postId],
enabled: Boolean(postId),
- queryFn: async () => {
- const { data, error } = await supabase
- .from('comments')
- .select('id, post_id, user_id, content, created_at')
- .eq('post_id', postId)
- .order('created_at', { ascending: true });
-
- if (error) {
- throw new Error(error.message);
- }
-
- if (!data?.length) {
- return [];
- }
-
- const userIds = Array.from(
- new Set(data.map((comment) => comment.user_id).filter(Boolean))
- );
-
- let profilesMap = new Map();
-
- if (userIds.length > 0) {
- const { data: profilesData, error: profilesError } = await supabase
- .from('profiles')
- .select('user_id, name, avatar_url')
- .in('user_id', userIds);
-
- if (profilesError) {
- console.error('Error fetching comment author profiles', profilesError);
- } else if (profilesData) {
- profilesMap = new Map(
- profilesData.map((profile) => [profile.user_id, profile])
- );
- }
- }
-
- return data.map((comment) => ({
- ...comment,
- author: profilesMap.get(comment.user_id) ?? null,
- }));
- },
+ queryFn: () => fetchCommentsWithAuthors(postId),
});
const addComment = useMutation({
@@ -123,15 +76,11 @@ const CommentsSection = ({ postId }: CommentsSectionProps) => {
throw new Error(t('blog.comments.signInRequiredDescription'));
}
- const { error } = await supabase.from('comments').insert({
+ await insertComment({
post_id: postId,
user_id: user.id,
content,
});
-
- if (error) {
- throw new Error(error.message);
- }
},
onSuccess: () => {
setNewComment('');
diff --git a/src/hooks/useDynamicCopy.ts b/src/hooks/useDynamicCopy.ts
new file mode 100644
index 0000000..bdb7861
--- /dev/null
+++ b/src/hooks/useDynamicCopy.ts
@@ -0,0 +1,30 @@
+import { useEffect } from 'react';
+import { useQuery } from '@tanstack/react-query';
+import { useTranslation } from 'react-i18next';
+import { fetchLocalizedCopy, type LocalizedCopy } from '@/lib/data/supabase';
+
+export const useDynamicCopy = () => {
+ const { i18n } = useTranslation();
+
+ const query = useQuery({
+ queryKey: ['site-settings', 'localized-copy'],
+ queryFn: fetchLocalizedCopy,
+ staleTime: 1000 * 60 * 5,
+ });
+
+ useEffect(() => {
+ if (!query.data) {
+ return;
+ }
+
+ for (const [locale, bundle] of Object.entries(query.data)) {
+ if (bundle && typeof bundle === 'object') {
+ i18n.addResourceBundle(locale, 'translation', bundle, true, true);
+ }
+ }
+ }, [i18n, query.data]);
+
+ return query;
+};
+
+export default useDynamicCopy;
diff --git a/src/integrations/supabase.ts b/src/integrations/supabase.ts
index 3d93074..c1f1fd6 100644
--- a/src/integrations/supabase.ts
+++ b/src/integrations/supabase.ts
@@ -1,5 +1,5 @@
import { createClient } from '@supabase/supabase-js';
-import type { Database } from '@/integrations/supabase/types';
+import type { Database } from '@/lib/supabase/types.gen';
const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL as string;
const SUPABASE_ANON_KEY =
diff --git a/src/lib/data/supabase.ts b/src/lib/data/supabase.ts
new file mode 100644
index 0000000..3e64062
--- /dev/null
+++ b/src/lib/data/supabase.ts
@@ -0,0 +1,411 @@
+import { supabase } from '@/integrations/supabase';
+import type { Tables } from '@/lib/supabase/types.gen';
+import type { SolutionContent } from '@/types/solutions';
+
+export type SupabaseSolutionRow = Tables<'solutions'>;
+export type HomepageFeatureRow = Tables<'homepage_features'>;
+export type TeamMemberRow = Tables<'team_members'>;
+export type RepositoryRow = Tables<'repositories'>;
+export type BlogPostRow = Tables<'blog_posts'>;
+export type CommentRow = Tables<'comments'>;
+export type ProfileRow = Tables<'profiles'>;
+
+const DEFAULT_SOLUTION_GRADIENT = 'from-brand-purple to-brand-blue';
+
+type SupabaseClientError = { message: string; code?: string };
+
+const wrapSupabaseError = (error: SupabaseClientError): never => {
+ const wrappedError = new Error(error.message) as Error & {
+ code?: string;
+ };
+
+ if (error.code) {
+ wrappedError.code = error.code;
+ }
+
+ throw wrappedError;
+};
+
+const toStringArray = (value: unknown): string[] => {
+ if (!value) {
+ return [];
+ }
+
+ if (Array.isArray(value)) {
+ return value
+ .map((entry) => {
+ if (typeof entry === 'string') {
+ return entry;
+ }
+
+ if (typeof entry === 'number' || typeof entry === 'boolean') {
+ return String(entry);
+ }
+
+ return null;
+ })
+ .filter((entry): entry is string => Boolean(entry && entry.trim().length > 0));
+ }
+
+ if (typeof value === 'string') {
+ try {
+ const parsed = JSON.parse(value);
+ return Array.isArray(parsed) ? toStringArray(parsed) : [];
+ } catch {
+ return value.trim().length > 0 ? [value] : [];
+ }
+ }
+
+ return [];
+};
+
+const ensureJsonObject = >(value: unknown): T | null => {
+ if (!value) {
+ return null;
+ }
+
+ if (typeof value === 'string') {
+ try {
+ const parsed = JSON.parse(value) as unknown;
+ return ensureJsonObject(parsed);
+ } catch {
+ return null;
+ }
+ }
+
+ if (typeof value === 'object' && !Array.isArray(value)) {
+ return value as T;
+ }
+
+ return null;
+};
+
+const parseSiteSettingValue = (value: unknown): T | null => {
+ if (value === null || value === undefined) {
+ return null;
+ }
+
+ if (typeof value === 'string') {
+ try {
+ return JSON.parse(value) as T;
+ } catch {
+ return null;
+ }
+ }
+
+ return value as T;
+};
+
+export interface SolutionsQueryOptions {
+ limit?: number;
+ slug?: string;
+ activeOnly?: boolean;
+ orderAscending?: boolean;
+}
+
+export const fetchSolutions = async (
+ options: SolutionsQueryOptions = {}
+): Promise => {
+ const { limit, slug, activeOnly = true, orderAscending = true } = options;
+
+ let query = supabase
+ .from('solutions')
+ .select('id, slug, title, description, image_url, features, gradient, active, created_at')
+ .order('created_at', { ascending: orderAscending });
+
+ if (activeOnly) {
+ query = query.eq('active', true);
+ }
+
+ if (slug) {
+ query = query.eq('slug', slug);
+ }
+
+ if (typeof limit === 'number') {
+ query = query.limit(limit);
+ }
+
+ const { data, error } = await query;
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+
+ const rows: SupabaseSolutionRow[] = data ?? [];
+
+ return rows.map((solution) => ({
+ id: solution.id,
+ title: solution.title,
+ description: solution.description,
+ slug: solution.slug,
+ imageUrl: solution.image_url,
+ features: toStringArray(solution.features),
+ gradient:
+ solution.gradient && solution.gradient.length > 0
+ ? solution.gradient
+ : DEFAULT_SOLUTION_GRADIENT,
+ }));
+};
+
+export const fetchSolutionBySlug = async (
+ slug: string
+): Promise => {
+ const { data, error } = await supabase
+ .from('solutions')
+ .select('id, slug, title, description, image_url, features, gradient, active')
+ .eq('slug', slug)
+ .eq('active', true)
+ .maybeSingle();
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+
+ if (!data) {
+ return null;
+ }
+
+ return {
+ id: data.id,
+ title: data.title,
+ description: data.description,
+ slug: data.slug,
+ imageUrl: data.image_url,
+ features: toStringArray(data.features),
+ gradient:
+ data.gradient && data.gradient.length > 0
+ ? data.gradient
+ : DEFAULT_SOLUTION_GRADIENT,
+ } satisfies SolutionContent;
+};
+
+export const fetchHomepageFeatures = async (): Promise => {
+ const { data, error } = await supabase
+ .from('homepage_features')
+ .select('*')
+ .eq('active', true)
+ .order('order_index', { ascending: true });
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+
+ return data ?? [];
+};
+
+export const fetchTeamMembers = async (): Promise => {
+ const { data, error } = await supabase
+ .from('team_members')
+ .select('*')
+ .eq('active', true)
+ .order('created_at', { ascending: true });
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+
+ return data ?? [];
+};
+
+export const fetchRepositories = async (): Promise => {
+ const { data, error } = await supabase
+ .from('repositories')
+ .select('*')
+ .eq('active', true)
+ .order('created_at', { ascending: false });
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+
+ return data ?? [];
+};
+
+export interface BlogPostsResult {
+ posts: Array<
+ Pick<
+ BlogPostRow,
+ 'id' | 'slug' | 'title' | 'excerpt' | 'image_url' | 'updated_at'
+ >
+ >;
+ total: number;
+}
+
+export const fetchPublishedBlogPosts = async (
+ page: number,
+ pageSize: number
+): Promise => {
+ const from = (page - 1) * pageSize;
+ const to = from + pageSize - 1;
+
+ const { data, error, count } = await supabase
+ .from('blog_posts')
+ .select('id, slug, title, excerpt, image_url, updated_at', {
+ count: 'exact',
+ })
+ .eq('published', true)
+ .order('updated_at', { ascending: false })
+ .range(from, to);
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+
+ return {
+ posts: data ?? [],
+ total: count ?? 0,
+ };
+};
+
+export const fetchPublishedBlogPostBySlug = async (
+ slug: string
+): Promise => {
+ const { data, error } = await supabase
+ .from('blog_posts')
+ .select('*')
+ .eq('slug', slug)
+ .eq('published', true)
+ .maybeSingle();
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+
+ return data ?? null;
+};
+
+export interface CommentWithAuthor extends CommentRow {
+ author: Pick | null;
+}
+
+export const fetchCommentsWithAuthors = async (
+ postId: string
+): Promise => {
+ const { data, error } = await supabase
+ .from('comments')
+ .select('id, post_id, user_id, content, created_at')
+ .eq('post_id', postId)
+ .order('created_at', { ascending: true });
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+
+ if (!data?.length) {
+ return [];
+ }
+
+ const userIds = Array.from(
+ new Set(data.map((comment) => comment.user_id).filter(Boolean))
+ );
+
+ let profilesMap = new Map>();
+
+ if (userIds.length > 0) {
+ const { data: profilesData, error: profilesError } = await supabase
+ .from('profiles')
+ .select('user_id, name, avatar_url')
+ .in('user_id', userIds);
+
+ if (!profilesError && profilesData) {
+ profilesMap = new Map(
+ profilesData.map((profile) => [profile.user_id, profile])
+ );
+ } else if (profilesError) {
+ console.error('Unable to load comment authors', profilesError);
+ }
+ }
+
+ return data.map((comment) => ({
+ ...comment,
+ author: profilesMap.get(comment.user_id) ?? null,
+ }));
+};
+
+export const insertComment = async (
+ input: Pick
+): Promise => {
+ const { error } = await supabase.from('comments').insert(input);
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+};
+
+export interface NewsletterSubscriptionInput {
+ email: string;
+}
+
+export const subscribeToNewsletter = async (
+ input: NewsletterSubscriptionInput
+): Promise => {
+ const { error } = await supabase
+ .from('newsletter_subscribers')
+ .insert({ email: input.email });
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+};
+
+export interface LeadInput {
+ name: string;
+ email: string;
+ message: string;
+ company?: string | null;
+ project?: string | null;
+}
+
+export const createLead = async (input: LeadInput): Promise => {
+ const { error } = await supabase.from('leads').insert({
+ name: input.name,
+ email: input.email,
+ company: input.company ?? null,
+ project: input.project ?? null,
+ message: input.message,
+ });
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+};
+
+export const fetchSiteSettingValue = async (
+ key: string
+): Promise => {
+ const { data, error } = await supabase
+ .from('site_settings')
+ .select('value')
+ .eq('key', key)
+ .maybeSingle();
+
+ if (error) {
+ wrapSupabaseError(error);
+ }
+
+ return parseSiteSettingValue(data?.value ?? null);
+};
+
+export interface ContactInfoSetting {
+ email?: string;
+ phone?: string;
+ address?: string;
+ linkedin?: string;
+}
+
+export const fetchContactInfo = async (): Promise => {
+ const value = await fetchSiteSettingValue('contact_info');
+ return ensureJsonObject(value) ?? null;
+};
+
+export const fetchProjectTypes = async (): Promise => {
+ const value = await fetchSiteSettingValue('project_types');
+ return toStringArray(value);
+};
+
+export type LocalizedCopy = Record>;
+
+export const fetchLocalizedCopy = async (): Promise => {
+ const value = await fetchSiteSettingValue('localized_copy');
+ return ensureJsonObject(value);
+};
diff --git a/src/lib/solutions.ts b/src/lib/solutions.ts
index c035f4e..091c208 100644
--- a/src/lib/solutions.ts
+++ b/src/lib/solutions.ts
@@ -1,14 +1,4 @@
-
-import {
- fallbackSolutions,
- fallbackSolutionsMap,
- gradientOptions,
- normalizeSolutionSlug,
-} from '@/data/solutions';
-import type { Tables } from '@/integrations/supabase/types';
import type { SolutionContent } from '@/types/solutions';
-import { supabase } from '@/integrations/supabase';
-import type { Database } from '@/integrations/supabase/types';
export interface GitHubRepository {
id: number;
@@ -31,7 +21,12 @@ export interface GitHubRepository {
html_url: string;
}
-type SupabaseSolutionRow = Database['public']['Tables']['solutions']['Row'];
+const DEFAULT_GRADIENTS = [
+ 'from-brand-purple to-brand-blue',
+ 'from-brand-pink to-brand-orange',
+ 'from-brand-blue to-brand-purple',
+ 'from-brand-orange to-brand-pink',
+];
const uniqueNonEmpty = (values: Array): string[] => {
const seen = new Set();
@@ -54,41 +49,6 @@ const uniqueNonEmpty = (values: Array): string[] => {
return result;
};
-const coerceToStringArray = (value: unknown): string[] => {
- if (!value) {
- return [];
- }
-
- if (Array.isArray(value)) {
- return uniqueNonEmpty(
- value.map((item) => {
- if (typeof item === 'string') {
- return item;
- }
-
- if (typeof item === 'number' || typeof item === 'boolean') {
- return String(item);
- }
-
- return null;
- })
- );
- }
-
- if (typeof value === 'string') {
- try {
- const parsed = JSON.parse(value);
- if (Array.isArray(parsed)) {
- return coerceToStringArray(parsed);
- }
- } catch {
- return uniqueNonEmpty([value]);
- }
- }
-
- return [];
-};
-
const formatDate = (value: string | null | undefined): string | null => {
if (!value) {
return null;
@@ -106,10 +66,7 @@ const formatDate = (value: string | null | undefined): string | null => {
});
};
-const buildFeatureList = (
- repository: GitHubRepository,
- fallback?: SolutionContent
-): string[] => {
+const buildFeatureList = (repository: GitHubRepository): string[] => {
const normalizedTopics = Array.isArray(repository.topics)
? uniqueNonEmpty(repository.topics)
: [];
@@ -134,137 +91,35 @@ const buildFeatureList = (
const combined = uniqueNonEmpty([...normalizedTopics, ...metadata]);
- if (combined.length > 0) {
- return combined;
- }
-
- return fallback?.features ? [...fallback.features] : [];
+ return combined.length > 0 ? combined : [];
};
+export const normalizeSolutionSlug = (value: string): string =>
+ value
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
+ .replace(/[\s_]+/g, '-')
+ .replace(/[^\w-]+/g, '-')
+ .replace(/--+/g, '-')
+ .toLowerCase()
+ .replace(/^-+|-+$/g, '');
+
export const mapGitHubRepoToContent = (
repository: GitHubRepository,
index: number
): SolutionContent => {
- const normalizedSlug = normalizeSolutionSlug(repository.name);
- const fallback =
- fallbackSolutionsMap[normalizedSlug] ?? fallbackSolutionsMap[repository.name];
- const gradient =
- fallback?.gradient ?? gradientOptions[index % gradientOptions.length];
-
+ const gradient = DEFAULT_GRADIENTS[index % DEFAULT_GRADIENTS.length];
const description = repository.description?.trim();
return {
id: String(repository.id),
- title: fallback?.title ?? repository.name,
+ title: repository.name,
description:
description && description.length > 0
? description
- : fallback?.description ??
- 'Open-source solution maintained by Monynha Softwares.',
- slug: repository.name,
- imageUrl: fallback?.imageUrl ?? null,
- features: buildFeatureList(repository, fallback),
+ : 'Open-source solution maintained by Monynha Softwares.',
+ slug: normalizeSolutionSlug(repository.name),
+ imageUrl: null,
+ features: buildFeatureList(repository),
gradient,
};
};
-
-export const mapSupabaseSolutionToContent = (
- solution: SupabaseSolutionRow,
- index: number
-): SolutionContent => {
- const normalizedSlug = normalizeSolutionSlug(solution.slug);
- const fallback =
- fallbackSolutionsMap[normalizedSlug] ?? fallbackSolutionsMap[solution.slug];
-
- const gradient =
- fallback?.gradient ?? gradientOptions[index % gradientOptions.length];
-
- const parsedFeatures = coerceToStringArray(solution.features);
-
- const features =
- parsedFeatures.length > 0
- ? parsedFeatures
- : fallback?.features
- ? [...fallback.features]
- : [];
-
- return {
- id: solution.id,
- title: solution.title,
- description: solution.description,
- slug: solution.slug,
- imageUrl: solution.image_url ?? fallback?.imageUrl ?? null,
- features,
- gradient,
- };
-};
-
-export const getFallbackSolution = (
- slug: string
-): SolutionContent | undefined => {
- if (!slug) {
- return undefined;
- }
-
- const normalizedSlug = normalizeSolutionSlug(slug);
- const fallback =
- fallbackSolutionsMap[normalizedSlug] ?? fallbackSolutionsMap[slug];
-
- if (!fallback) {
- return undefined;
- }
-
- return {
- ...fallback,
- features: [...fallback.features],
- };
-};
-
-export const getFallbackSolutions = (): SolutionContent[] =>
- fallbackSolutions.map((solution) => ({
- ...solution,
- features: [...solution.features],
- }));
-
-// Removed duplicate type declaration
-
-const mapSupabaseFeatures = (
- features: SupabaseSolutionRow['features'],
- fallback?: SolutionContent
-): string[] => {
- if (Array.isArray(features)) {
- const entries = (features as unknown[]).filter(
- (feature): feature is string => typeof feature === 'string'
- );
-
- if (entries.length > 0) {
- return entries;
- }
- }
-
- if (fallback?.features?.length) {
- return [...fallback.features];
- }
-
- return [];
-};
-
-// Removed duplicate function declaration
-export const fetchSupabaseSolutions = async (): Promise => {
- const { data, error } = await supabase
- .from('solutions')
- .select('*')
- .eq('active', true)
- .order('created_at', { ascending: true });
-
- if (error) {
- throw new Error(error.message);
- }
-
- const rows = data ?? [];
-
- return rows.map((solution, index) =>
- mapSupabaseSolutionToContent(solution, index)
- );
-
- }
diff --git a/src/integrations/supabase/types.ts b/src/lib/supabase/types.gen.ts
similarity index 90%
rename from src/integrations/supabase/types.ts
rename to src/lib/supabase/types.gen.ts
index f5a98a7..513ab86 100644
--- a/src/integrations/supabase/types.ts
+++ b/src/lib/supabase/types.gen.ts
@@ -170,6 +170,7 @@ export type Database = {
email: string;
id: string;
name: string;
+ payload_user_id: number | null;
role: string;
updated_at: string;
user_id: string;
@@ -180,6 +181,7 @@ export type Database = {
email: string;
id?: string;
name: string;
+ payload_user_id?: number | null;
role?: string;
updated_at?: string;
user_id: string;
@@ -190,6 +192,7 @@ export type Database = {
email?: string;
id?: string;
name?: string;
+ payload_user_id?: number | null;
role?: string;
updated_at?: string;
user_id?: string;
@@ -232,6 +235,39 @@ export type Database = {
};
Relationships: [];
};
+ content_sync_webhook_config: {
+ Row: {
+ created_at: string;
+ enabled: boolean;
+ id: string;
+ label: string;
+ secret: string | null;
+ target: string;
+ updated_at: string;
+ url: string;
+ };
+ Insert: {
+ created_at?: string;
+ enabled?: boolean;
+ id?: string;
+ label: string;
+ secret?: string | null;
+ target: string;
+ updated_at?: string;
+ url: string;
+ };
+ Update: {
+ created_at?: string;
+ enabled?: boolean;
+ id?: string;
+ label?: string;
+ secret?: string | null;
+ target?: string;
+ updated_at?: string;
+ url?: string;
+ };
+ Relationships: [];
+ };
site_settings: {
Row: {
created_at: string;
@@ -267,6 +303,7 @@ export type Database = {
features: Json | null;
id: string;
image_url: string | null;
+ gradient: string;
slug: string;
title: string;
updated_at: string;
@@ -278,6 +315,7 @@ export type Database = {
features?: Json | null;
id?: string;
image_url?: string | null;
+ gradient?: string;
slug: string;
title: string;
updated_at?: string;
@@ -289,6 +327,7 @@ export type Database = {
features?: Json | null;
id?: string;
image_url?: string | null;
+ gradient?: string;
slug?: string;
title?: string;
updated_at?: string;
@@ -336,7 +375,13 @@ export type Database = {
[_ in never]: never;
};
Functions: {
- [_ in never]: never;
+ get_admin_dashboard_data: {
+ Args: Record;
+ Returns: {
+ leads: Database['public']['Tables']['leads']['Row'][];
+ newsletter_subscribers: Database['public']['Tables']['newsletter_subscribers']['Row'][];
+ };
+ };
};
Enums: {
[_ in never]: never;
diff --git a/src/locales/en.json b/src/locales/en.json
index 9e49bef..d40a170 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -64,7 +64,10 @@
},
"solutions": {
"title": "Our Flagship Solutions",
- "description": "Proven software solutions that have transformed businesses across industries."
+ "description": "Proven software solutions that have transformed businesses across industries.",
+ "emptyTitle": "We're setting up new solutions",
+ "emptyDescription": "Check back soon for our latest launches or reach out to craft something custom.",
+ "contactCta": "Talk to our team"
},
"cta": {
"title": "Ready to Transform Your Business?",
@@ -73,14 +76,17 @@
},
"learnMore": "Learn More"
},
- "solutionsPage": {
- "title": "Our Software Solutions",
- "description": "Discover our flagship products designed to transform how businesses operate. Each solution is crafted with AI at its core to deliver maximum efficiency and growth.",
- "requestDemo": "Request Demo",
- "featuresTitle": "Key Capabilities",
- "backToSolutions": "Back to Solutions",
- "notFound": "Solution not found",
- "customTitle": "Need a Custom Solution?",
+ "solutionsPage": {
+ "title": "Our Software Solutions",
+ "description": "Discover our flagship products designed to transform how businesses operate. Each solution is crafted with AI at its core to deliver maximum efficiency and growth.",
+ "requestDemo": "Request Demo",
+ "featuresTitle": "Key Capabilities",
+ "backToSolutions": "Back to Solutions",
+ "notFound": "Solution not found",
+ "emptyTitle": "No solutions published yet",
+ "emptyDescription": "Our team is curating the next generation of offerings. Reach out and we’ll build a solution tailored to you.",
+ "contactCta": "Start a project",
+ "customTitle": "Need a Custom Solution?",
"customDescription": "We also create bespoke software solutions tailored to your specific business needs. Let's discuss how we can build something unique for you.",
"discuss": "Discuss Custom Project"
},
diff --git a/src/locales/es.json b/src/locales/es.json
index 600ccca..41738ca 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -64,7 +64,10 @@
},
"solutions": {
"title": "Nuestras Soluciones",
- "description": "Soluciones comprobadas que han transformado negocios en diversas industrias."
+ "description": "Soluciones comprobadas que han transformado negocios en diversas industrias.",
+ "emptyTitle": "Estamos preparando nuevas soluciones",
+ "emptyDescription": "Vuelve pronto para conocer nuestros próximos lanzamientos o contáctanos para crear algo a medida.",
+ "contactCta": "Habla con nuestro equipo"
},
"cta": {
"title": "¿Listo para Transformar tu Negocio?",
@@ -73,14 +76,17 @@
},
"learnMore": "Saber Más"
},
- "solutionsPage": {
- "title": "Nuestras Soluciones de Software",
- "description": "Conoce nuestros productos estrella diseñados para transformar la operación de las empresas. Cada solución se crea con IA en el núcleo para máxima eficiencia.",
- "requestDemo": "Solicitar Demo",
- "featuresTitle": "Capacidades Clave",
- "backToSolutions": "Volver a Soluciones",
- "notFound": "Solución no encontrada",
- "customTitle": "¿Necesitas una Solución Personalizada?",
+ "solutionsPage": {
+ "title": "Nuestras Soluciones de Software",
+ "description": "Conoce nuestros productos estrella diseñados para transformar la operación de las empresas. Cada solución se crea con IA en el núcleo para máxima eficiencia.",
+ "requestDemo": "Solicitar Demo",
+ "featuresTitle": "Capacidades Clave",
+ "backToSolutions": "Volver a Soluciones",
+ "notFound": "Solución no encontrada",
+ "emptyTitle": "Aún no hay soluciones publicadas",
+ "emptyDescription": "Nuestro equipo está preparando la próxima generación de ofertas. Ponte en contacto y construiremos una solución a tu medida.",
+ "contactCta": "Iniciar un proyecto",
+ "customTitle": "¿Necesitas una Solución Personalizada?",
"customDescription": "También creamos soluciones a medida para tus necesidades específicas. Conversemos sobre cómo construir algo único para ti.",
"discuss": "Discutir Proyecto"
},
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 55b548d..6b5120f 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -64,7 +64,10 @@
},
"solutions": {
"title": "Nos Solutions",
- "description": "Des solutions éprouvées qui ont transformé des entreprises dans divers secteurs."
+ "description": "Des solutions éprouvées qui ont transformé des entreprises dans divers secteurs.",
+ "emptyTitle": "Nous préparons de nouvelles solutions",
+ "emptyDescription": "Revenez bientôt pour découvrir nos prochains lancements ou contactez-nous pour créer une solution sur mesure.",
+ "contactCta": "Parler avec notre équipe"
},
"cta": {
"title": "Prêt à transformer votre entreprise ?",
@@ -73,14 +76,17 @@
},
"learnMore": "En savoir plus"
},
- "solutionsPage": {
- "title": "Nos Solutions logicielles",
- "description": "Découvrez nos produits phares conçus pour transformer la façon dont les entreprises fonctionnent. Chaque solution est développée avec l'IA au cœur pour une efficacité maximale.",
- "requestDemo": "Demander une démo",
- "featuresTitle": "Fonctionnalités clés",
- "backToSolutions": "Retour aux solutions",
- "notFound": "Solution introuvable",
- "customTitle": "Besoin d'une solution personnalisée ?",
+ "solutionsPage": {
+ "title": "Nos Solutions logicielles",
+ "description": "Découvrez nos produits phares conçus pour transformer la façon dont les entreprises fonctionnent. Chaque solution est développée avec l'IA au cœur pour une efficacité maximale.",
+ "requestDemo": "Demander une démo",
+ "featuresTitle": "Fonctionnalités clés",
+ "backToSolutions": "Retour aux solutions",
+ "notFound": "Solution introuvable",
+ "emptyTitle": "Aucune solution publiée pour le moment",
+ "emptyDescription": "Notre équipe prépare la prochaine génération d'offres. Contactez-nous et nous construirons une solution adaptée à vos besoins.",
+ "contactCta": "Lancer un projet",
+ "customTitle": "Besoin d'une solution personnalisée ?",
"customDescription": "Nous créons également des solutions sur mesure adaptées à vos besoins spécifiques. Discutons de la manière de construire quelque chose d'unique pour vous.",
"discuss": "Discuter du projet"
},
diff --git a/src/locales/pt.json b/src/locales/pt.json
index 02ec382..bcbd2e1 100644
--- a/src/locales/pt.json
+++ b/src/locales/pt.json
@@ -64,7 +64,10 @@
},
"solutions": {
"title": "Nossas Soluções",
- "description": "Soluções comprovadas que transformaram empresas em diversos setores."
+ "description": "Soluções comprovadas que transformaram empresas em diversos setores.",
+ "emptyTitle": "Estamos preparando novas soluções",
+ "emptyDescription": "Volte em breve para conhecer nossos lançamentos ou fale conosco para criar algo sob medida.",
+ "contactCta": "Fale com nossa equipe"
},
"cta": {
"title": "Pronto para Transformar seu Negócio?",
@@ -73,14 +76,17 @@
},
"learnMore": "Saiba Mais"
},
- "solutionsPage": {
- "title": "Nossas Soluções de Software",
- "description": "Conheça nossos produtos desenvolvidos para transformar a operação de empresas. Cada solução é criada com IA no núcleo para máxima eficiência.",
- "requestDemo": "Solicitar Demonstração",
- "featuresTitle": "Principais Recursos",
- "backToSolutions": "Voltar para Soluções",
- "notFound": "Solução não encontrada",
- "customTitle": "Precisa de uma Solução Personalizada?",
+ "solutionsPage": {
+ "title": "Nossas Soluções de Software",
+ "description": "Conheça nossos produtos desenvolvidos para transformar a operação de empresas. Cada solução é criada com IA no núcleo para máxima eficiência.",
+ "requestDemo": "Solicitar Demonstração",
+ "featuresTitle": "Principais Recursos",
+ "backToSolutions": "Voltar para Soluções",
+ "notFound": "Solução não encontrada",
+ "emptyTitle": "Nenhuma solução publicada ainda",
+ "emptyDescription": "Nossa equipe está preparando a próxima geração de ofertas. Entre em contato e construiremos uma solução sob medida para você.",
+ "contactCta": "Iniciar um projeto",
+ "customTitle": "Precisa de uma Solução Personalizada?",
"customDescription": "Também criamos soluções sob medida para suas necessidades específicas de negócio. Vamos conversar sobre como construir algo único para você.",
"discuss": "Discutir Projeto"
},
diff --git a/src/pages/About.tsx b/src/pages/About.tsx
index cf2725e..3d82f3b 100644
--- a/src/pages/About.tsx
+++ b/src/pages/About.tsx
@@ -7,8 +7,11 @@ import Meta from '@/components/Meta';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
-import { supabase } from '@/integrations/supabase';
import { useTranslation } from 'react-i18next';
+import {
+ fetchTeamMembers,
+ fetchSiteSettingValue,
+} from '@/lib/data/supabase';
interface TeamMemberCard {
id: string;
@@ -107,14 +110,13 @@ const About = () => {
} = useQuery({
queryKey: ['team-members', 'about'],
queryFn: async () => {
- const { data, error } = await supabase
- .from('team_members')
- .select('id, name, role, image_url')
- .eq('active', true)
- .order('created_at', { ascending: true });
-
- if (error) throw error;
- return (data ?? []) as TeamMemberCard[];
+ const data = await fetchTeamMembers();
+ return data.map((member) => ({
+ id: member.id,
+ name: member.name,
+ role: member.role,
+ image_url: member.image_url,
+ })) as TeamMemberCard[];
},
});
@@ -125,15 +127,8 @@ const About = () => {
} = useQuery({
queryKey: ['site-settings', 'about-stats'],
queryFn: async () => {
- const { data, error } = await supabase
- .from('site_settings')
- .select('value')
- .eq('key', 'about_stats')
- .maybeSingle();
-
- if (error) throw error;
-
- return normalizeAboutStats(data?.value);
+ const value = await fetchSiteSettingValue('about_stats');
+ return normalizeAboutStats(value);
},
});
diff --git a/src/pages/Auth.tsx b/src/pages/Auth.tsx
index 591629f..ca4ea9c 100644
--- a/src/pages/Auth.tsx
+++ b/src/pages/Auth.tsx
@@ -15,7 +15,7 @@ import { Label } from '@/components/ui/label';
import { useToast } from '@/hooks/use-toast';
import { ArrowLeft, Eye, EyeOff, Loader2 } from 'lucide-react';
-type AuthView = 'signin' | 'signup';
+type AuthView = 'signin' | 'signup' | 'reset' | 'update-password';
const Auth = () => {
const [authView, setAuthView] = useState('signin');
@@ -23,6 +23,11 @@ const Auth = () => {
const [showPassword, setShowPassword] = useState(false);
const [signInForm, setSignInForm] = useState({ email: '', password: '' });
const [signUpForm, setSignUpForm] = useState({ name: '', email: '', password: '' });
+ const [resetEmail, setResetEmail] = useState('');
+ const [newPasswordForm, setNewPasswordForm] = useState({
+ password: '',
+ confirmPassword: '',
+ });
const navigate = useNavigate();
const location = useLocation();
@@ -33,6 +38,16 @@ const Auth = () => {
return state?.from?.pathname ?? '/dashboard';
}, [location.state]);
+ useEffect(() => {
+ const searchParams = new URLSearchParams(location.search);
+ const type = searchParams.get('type');
+
+ if (type === 'recovery') {
+ setAuthView('update-password');
+ setShowPassword(false);
+ }
+ }, [location.search]);
+
useEffect(() => {
const checkUser = async () => {
const {
@@ -127,6 +142,104 @@ const Auth = () => {
}
};
+ const handleResetPassword = async (event: FormEvent) => {
+ event.preventDefault();
+ setLoadingView('reset');
+
+ try {
+ const redirectTo =
+ typeof window !== 'undefined'
+ ? `${window.location.origin}/auth?type=recovery`
+ : undefined;
+
+ const { error } = await supabase.auth.resetPasswordForEmail(
+ resetEmail.trim(),
+ { redirectTo }
+ );
+
+ if (error) {
+ throw error;
+ }
+
+ toast({
+ title: 'Verifique seu email',
+ description: 'Enviamos um link seguro para redefinição de senha.',
+ });
+
+ setSignInForm((current) => ({ ...current, email: resetEmail.trim() }));
+ setResetEmail('');
+ setAuthView('signin');
+ setShowPassword(false);
+ } catch (error) {
+ const message =
+ error instanceof Error
+ ? error.message
+ : 'Não foi possível enviar o email de redefinição.';
+ toast({
+ title: 'Erro ao solicitar redefinição',
+ description: message,
+ variant: 'destructive',
+ });
+ } finally {
+ setLoadingView(null);
+ }
+ };
+
+ const handleUpdatePassword = async (event: FormEvent) => {
+ event.preventDefault();
+ setLoadingView('update-password');
+
+ if (newPasswordForm.password.length < 6) {
+ toast({
+ title: 'Senha muito curta',
+ description: 'Utilize ao menos 6 caracteres para a nova senha.',
+ variant: 'destructive',
+ });
+ setLoadingView(null);
+ return;
+ }
+
+ if (newPasswordForm.password !== newPasswordForm.confirmPassword) {
+ toast({
+ title: 'Senhas diferentes',
+ description: 'As senhas informadas precisam ser iguais.',
+ variant: 'destructive',
+ });
+ setLoadingView(null);
+ return;
+ }
+
+ try {
+ const { error } = await supabase.auth.updateUser({
+ password: newPasswordForm.password,
+ });
+
+ if (error) {
+ throw error;
+ }
+
+ toast({
+ title: 'Senha atualizada',
+ description: 'Sua senha foi redefinida com sucesso.',
+ });
+
+ setNewPasswordForm({ password: '', confirmPassword: '' });
+ navigate('/dashboard', { replace: true });
+ } catch (error) {
+ const message =
+ error instanceof Error
+ ? error.message
+ : 'Não foi possível atualizar a senha.';
+ toast({
+ title: 'Erro ao atualizar senha',
+ description: message,
+ variant: 'destructive',
+ });
+ } finally {
+ setLoadingView(null);
+ }
+ };
+
return (
@@ -145,134 +258,146 @@ const Auth = () => {
- {
- setAuthView(value as AuthView);
- setShowPassword(false);
- }}
- className="w-full"
- >
-
- Login
- Cadastro
-
-
-
-