-
Sign Up
+ {#if success}
+
Check Your Email
- {#if magicLinkSent}
-
-
Magic link sent! Check your email to sign up.
-
Back to sign up
+
+
+
+ We've sent a verification link to {email}
+
+
+ Please check your email and click the verification link to activate your account.
+
+
+
+
+ Back to Sign In
+
{:else}
+
Sign Up
+
+ {#if magicLinkSent}
+
+
Magic link sent! Check your email to sign up.
+
Back to sign up
+
+ {:else}
{#snippet passwordTabContent()}
+ {/if}
{/if}
diff --git a/examples/demos/vue-demo/src/assets/main.css b/examples/demos/vue-demo/src/assets/main.css
index 0bad6f6a8..ad7dac517 100644
--- a/examples/demos/vue-demo/src/assets/main.css
+++ b/examples/demos/vue-demo/src/assets/main.css
@@ -666,3 +666,376 @@ pre {
.opacity-75 {
opacity: 0.75;
}
+
+/* Todos specific styles */
+.container {
+ max-width: 42rem;
+ margin: 0 auto;
+ padding: 1.5rem;
+}
+
+.auth-message {
+ text-align: center;
+ padding: 2rem;
+ background-color: rgba(31, 41, 55, 0.5);
+ border-radius: 0.5rem;
+ border: 1px solid var(--border-color);
+}
+
+.page-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 2rem;
+}
+
+.page-title {
+ font-size: 1.875rem;
+ font-weight: bold;
+ color: var(--text-primary);
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.add-todo-btn {
+ background-color: var(--primary);
+ color: white;
+ border: none;
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: 50%;
+ font-size: 1.25rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.add-todo-btn:hover {
+ background-color: var(--primary-hover);
+ transform: scale(1.05);
+}
+
+.error-message {
+ background-color: rgba(239, 68, 68, 0.1);
+ border: 1px solid rgba(239, 68, 68, 0.5);
+ color: var(--error);
+ padding: 0.75rem 1rem;
+ border-radius: 0.5rem;
+ margin-bottom: 1.5rem;
+}
+
+.todo-form-card {
+ background-color: rgba(31, 41, 55, 0.8);
+ border: 1px solid var(--border-color);
+ border-radius: 0.5rem;
+ padding: 1.5rem;
+ margin-bottom: 2rem;
+}
+
+.todo-form {
+ width: 100%;
+}
+
+.form-title {
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: var(--text-primary);
+ margin-bottom: 1.5rem;
+}
+
+.form-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.field-group {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.field-group label {
+ font-size: 0.875rem;
+ font-weight: 500;
+ color: var(--text-secondary);
+}
+
+.field-group input,
+.field-group textarea {
+ width: 100%;
+ padding: 0.75rem;
+ background-color: rgba(17, 24, 39, 0.8);
+ border: 1px solid var(--border-color);
+ border-radius: 0.375rem;
+ color: var(--text-primary);
+ font-size: 0.875rem;
+ transition: all 0.2s;
+}
+
+.field-group input:focus,
+.field-group textarea:focus {
+ outline: none;
+ border-color: var(--primary);
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
+}
+
+.form-actions {
+ display: flex;
+ gap: 0.75rem;
+ margin-top: 0.5rem;
+}
+
+.btn {
+ padding: 0.625rem 1rem;
+ border: none;
+ border-radius: 0.375rem;
+ font-weight: 500;
+ font-size: 0.875rem;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+}
+
+.btn-cancel {
+ background-color: rgba(107, 114, 128, 0.2);
+ color: var(--text-secondary);
+ border: 1px solid var(--border-color);
+}
+
+.btn-cancel:hover {
+ background-color: rgba(107, 114, 128, 0.3);
+ color: var(--text-primary);
+}
+
+.loading-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 4rem 0;
+}
+
+.loading-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1rem;
+}
+
+.spinner {
+ width: 2rem;
+ height: 2rem;
+ border: 2px solid var(--border-color);
+ border-top: 2px solid var(--primary);
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+.loading-text {
+ color: var(--text-secondary);
+ font-size: 0.875rem;
+}
+
+.todos-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.empty-state {
+ text-align: center;
+ padding: 4rem 2rem;
+ color: var(--text-muted);
+}
+
+.empty-icon {
+ width: 4rem;
+ height: 4rem;
+ margin: 0 auto 1.5rem;
+ color: var(--text-muted);
+}
+
+.empty-title {
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: var(--text-secondary);
+ margin-bottom: 0.5rem;
+}
+
+.empty-description {
+ color: var(--text-muted);
+ font-size: 0.875rem;
+}
+
+.todo-card {
+ background-color: rgba(31, 41, 55, 0.8);
+ border: 1px solid var(--border-color);
+ border-radius: 0.5rem;
+ padding: 1.5rem;
+ transition: all 0.2s;
+}
+
+.todo-card:hover {
+ border-color: rgba(99, 102, 241, 0.3);
+ box-shadow: 0 0 0 1px rgba(99, 102, 241, 0.1);
+}
+
+.todo-card.completed {
+ opacity: 0.7;
+ background-color: rgba(34, 197, 94, 0.05);
+ border-color: rgba(34, 197, 94, 0.2);
+}
+
+.todo-edit {
+ width: 100%;
+}
+
+.edit-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ margin-bottom: 1rem;
+}
+
+.edit-actions {
+ display: flex;
+ gap: 0.75rem;
+}
+
+.todo-content {
+ width: 100%;
+}
+
+.todo-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+}
+
+.todo-title-btn {
+ background: none;
+ border: none;
+ color: var(--text-primary);
+ font-size: 1rem;
+ font-weight: 500;
+ text-align: left;
+ cursor: pointer;
+ padding: 0;
+ flex: 1;
+ transition: color 0.2s;
+}
+
+.todo-title-btn:hover {
+ color: var(--primary);
+}
+
+.todo-title-btn.completed {
+ text-decoration: line-through;
+ color: var(--text-muted);
+}
+
+.todo-actions {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.action-btn {
+ background: none;
+ border: none;
+ color: var(--text-secondary);
+ cursor: pointer;
+ padding: 0.25rem;
+ border-radius: 0.25rem;
+ font-size: 0.875rem;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 1.75rem;
+ height: 1.75rem;
+}
+
+.action-btn:hover {
+ background-color: rgba(31, 41, 55, 0.8);
+}
+
+.action-btn-complete {
+ color: var(--secondary);
+}
+
+.action-btn-complete:hover {
+ color: var(--secondary-hover);
+ background-color: rgba(16, 185, 129, 0.1);
+}
+
+.action-btn-edit:hover {
+ color: var(--primary);
+ background-color: rgba(99, 102, 241, 0.1);
+}
+
+.action-btn-delete:hover {
+ color: var(--error);
+ background-color: rgba(239, 68, 68, 0.1);
+}
+
+.todo-details {
+ margin-top: 1rem;
+ padding-top: 1rem;
+ border-top: 1px solid var(--border-color);
+}
+
+.todo-description {
+ margin-bottom: 1rem;
+}
+
+.todo-description.completed {
+ opacity: 0.7;
+}
+
+.todo-description p {
+ color: var(--text-secondary);
+ line-height: 1.5;
+ margin: 0;
+}
+
+.todo-meta {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.meta-dates {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.meta-item {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+}
+
+.completion-badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.25rem 0.75rem;
+ background-color: rgba(34, 197, 94, 0.1);
+ color: var(--secondary);
+ border-radius: 9999px;
+ font-size: 0.75rem;
+ font-weight: 500;
+ width: fit-content;
+}
+
+.completion-icon {
+ width: 0.875rem;
+ height: 0.875rem;
+}
diff --git a/examples/demos/vue-demo/src/components/Navigation.vue b/examples/demos/vue-demo/src/components/Navigation.vue
index ed73300bf..c81321bde 100644
--- a/examples/demos/vue-demo/src/components/Navigation.vue
+++ b/examples/demos/vue-demo/src/components/Navigation.vue
@@ -5,6 +5,13 @@
+
+ Todos
+
Nhost SDK Demo
-
+
+
Check Your Email
+
+
+
+
+ We've sent a verification link to {{ email }}
+
+
+ Please check your email and click the verification link to activate your account.
+
+
+
+
+ Back to Sign In
+
+
+
+
+
Sign Up
@@ -109,6 +132,7 @@ const password = ref("");
const displayName = ref("");
const isLoading = ref(false);
const error = ref(null);
+const success = ref(false);
// If already authenticated, redirect to profile
onMounted(() => {
@@ -127,15 +151,16 @@ const handleSubmit = async (): Promise => {
password: password.value,
options: {
displayName: displayName.value,
+ redirectTo: `${window.location.origin}/verify`,
},
});
- if (response.body) {
+ if (response.body?.session) {
// Successfully signed up and automatically signed in
router.push("/profile");
} else {
// Verification email sent
- router.push("/verify");
+ success.value = true;
}
} catch (err) {
const errorObj = err as FetchError;
diff --git a/examples/demos/vue-demo/src/views/Todos.vue b/examples/demos/vue-demo/src/views/Todos.vue
new file mode 100644
index 000000000..434d44134
--- /dev/null
+++ b/examples/demos/vue-demo/src/views/Todos.vue
@@ -0,0 +1,466 @@
+
+
+
Please sign in to view your todos.
+
+
+
+
+
+
+ Error: {{ error }}
+
+
+
+
+
+
+
+
+
+
+
+
+
No todos yet
+
+ Create your first todo to get started!
+
+
+
+
+
+
+
+ Title
+
+
+
+ Details
+
+
+
+
+ ✓ Save Changes
+
+
+ ✕ Cancel
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/guides/Makefile b/examples/guides/Makefile
index 997a8ef69..fce2633ff 100644
--- a/examples/guides/Makefile
+++ b/examples/guides/Makefile
@@ -4,12 +4,12 @@ include $(ROOT_DIR)/build/makefiles/general.makefile
.PHONY: _dev-env-up
_dev-env-up:
- cd ../demos/backend/ && $(ROOT_DIR)/examples/demos/backend/env-up.sh
+ cd ./backend/ && $(ROOT_DIR)/examples/demos/backend/env-up.sh
.PHONY: _dev-env-down
_dev-env-down:
- cd ../demos/backend/ && nhost down --volumes
+ cd ./backend/ && nhost down --volumes
.PHONY: _dev-env-build
diff --git a/examples/guides/backend/.gitignore b/examples/guides/backend/.gitignore
new file mode 100644
index 000000000..5d1bff8da
--- /dev/null
+++ b/examples/guides/backend/.gitignore
@@ -0,0 +1,2 @@
+.nhost
+.secrets
diff --git a/examples/guides/backend/.secrets.example b/examples/guides/backend/.secrets.example
new file mode 100644
index 000000000..9b220abe2
--- /dev/null
+++ b/examples/guides/backend/.secrets.example
@@ -0,0 +1,16 @@
+GRAFANA_ADMIN_PASSWORD = 'grafana-admin-password'
+HASURA_GRAPHQL_ADMIN_SECRET = 'nhost-admin-secret'
+HASURA_GRAPHQL_JWT_SECRET = '55b1d038dff8d4f9a440e848250668527fa5b563700be0dc39e356f1c91f867e'
+NHOST_WEBHOOK_SECRET = 'nhost-webhook-secret'
+GITHUB_CLIENT_ID='fixme'
+GITHUB_CLIENT_SECRET='fixme'
+APPLE_TEAM_ID='fakeTeamId'
+APPLE_CLIENT_ID='host.exp.Exponent'
+APPLE_AUDIENCE='host.exp.Exponent'
+APPLE_KEY_ID='fakeKeyId'
+APPLE_PRIVATE_KEY='''-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQglHTWHjauHnKCxjEP
+BpMYsTDI2cihQi4tAYHTthj+FF+gCgYIKoZIzj0DAQehRANCAAR30Hs8vTbED10z
+Qx2m4sJu+lE/ZJsRvDkqLqYF8uh1Tb1g7/KKr8Y7qkK3DmCg72bCyirEq4NVUi2r
+M/6TYMpw
+-----END PRIVATE KEY-----'''
diff --git a/examples/guides/backend/Makefile b/examples/guides/backend/Makefile
new file mode 100644
index 000000000..85bfedcda
--- /dev/null
+++ b/examples/guides/backend/Makefile
@@ -0,0 +1,7 @@
+.PHONY: dev-env-up
+dev-env-up:
+ @./env-up.sh
+
+.PHONY: dev-env-down
+dev-env-down:
+ @nhost down --volumes
diff --git a/examples/guides/backend/README.md b/examples/guides/backend/README.md
new file mode 100644
index 000000000..04af0e395
--- /dev/null
+++ b/examples/guides/backend/README.md
@@ -0,0 +1,29 @@
+# backend
+
+This is a very simple Nhost backend that we will use to demonstrate how to use the various SDKs we are experimenting with. The backend will consist of the following:
+
+## Database schema
+
+- A `tasks` table with the following columns:
+
+ - `id` (UUID)
+ - `created_at` (Timestamp)
+ - `updated_at` (Timestamp)
+ - `user_id` (foreigh key to `auth.users.id`)
+ - `title` (Text)
+ - `description` (Text)
+ - `completed` (Boolean)
+
+- An `attachments` table with the following columns:
+ - `task_id` (foreign key to `tasks.id`)
+ - `file_id` (foreign key to `storage.files.id`)
+
+Permissions:
+
+- `tasks`: the `user` role can insert/select/update tasks that they own. Ownership is tracked by the `user_id` column which is set automatically on insert from the session.
+- `attachments`: the `user` role can insert/select/delete attachments for tasks and files that they own
+- `storage.files`: the `user` role can insert/select/delete files that they own
+
+## Functions
+
+- A `simple` function called `echo` that will just return back some request information
diff --git a/examples/guides/backend/env-up.sh b/examples/guides/backend/env-up.sh
new file mode 100755
index 000000000..6ff54ef84
--- /dev/null
+++ b/examples/guides/backend/env-up.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# if .secrets file doesn't exist, cp .secrets.example .secrets
+if [ ! -f .secrets ]; then
+ cp .secrets.example .secrets
+fi
+
+nhost up
diff --git a/examples/guides/backend/functions/package-lock.json b/examples/guides/backend/functions/package-lock.json
new file mode 100644
index 000000000..b3a60ffe0
--- /dev/null
+++ b/examples/guides/backend/functions/package-lock.json
@@ -0,0 +1,14 @@
+{
+ "name": "functions",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "functions",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {}
+ }
+ }
+}
diff --git a/examples/guides/backend/functions/package.json b/examples/guides/backend/functions/package.json
new file mode 100644
index 000000000..98ef9bba4
--- /dev/null
+++ b/examples/guides/backend/functions/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "functions",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "devDependencies": {},
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/examples/guides/backend/functions/tsconfig.json b/examples/guides/backend/functions/tsconfig.json
new file mode 100644
index 000000000..0f4467244
--- /dev/null
+++ b/examples/guides/backend/functions/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "skipLibCheck": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "strictNullChecks": false
+ }
+}
diff --git a/examples/guides/backend/nhost/config.yaml b/examples/guides/backend/nhost/config.yaml
new file mode 100644
index 000000000..0a70affa4
--- /dev/null
+++ b/examples/guides/backend/nhost/config.yaml
@@ -0,0 +1 @@
+version: 3
diff --git a/examples/guides/backend/nhost/emails/bg/email-confirm-change/body.html b/examples/guides/backend/nhost/emails/bg/email-confirm-change/body.html
new file mode 100644
index 000000000..437face7d
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Потвърдете смяната на вашия имейл
+ Използвайте посочения линк, за да повърдите смяната на имейл:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/bg/email-confirm-change/subject.txt b/examples/guides/backend/nhost/emails/bg/email-confirm-change/subject.txt
new file mode 100644
index 000000000..790147e6c
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Потвърждение за смяна на имейл
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/bg/email-verify/body.html b/examples/guides/backend/nhost/emails/bg/email-verify/body.html
new file mode 100644
index 000000000..9a9604f40
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Потвърдете вашия имейл
+ Използвайте посочения линк, за да потвърдите вашия имейл:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/bg/email-verify/subject.txt b/examples/guides/backend/nhost/emails/bg/email-verify/subject.txt
new file mode 100644
index 000000000..f0109ef96
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/email-verify/subject.txt
@@ -0,0 +1 @@
+Потвърждаване на имейл
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/bg/password-reset/body.html b/examples/guides/backend/nhost/emails/bg/password-reset/body.html
new file mode 100644
index 000000000..d69b734de
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Смяна на парола
+ Използвайте посочения линк, за да смените вашата парола:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/bg/password-reset/subject.txt b/examples/guides/backend/nhost/emails/bg/password-reset/subject.txt
new file mode 100644
index 000000000..11b95a290
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/password-reset/subject.txt
@@ -0,0 +1 @@
+Смяна на парола
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/bg/signin-otp/body.html b/examples/guides/backend/nhost/emails/bg/signin-otp/body.html
new file mode 100644
index 000000000..2fab88cba
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ За да влезете в ${redirectTo}, моля, използвайте следната еднократна парола:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/bg/signin-otp/subject.txt b/examples/guides/backend/nhost/emails/bg/signin-otp/subject.txt
new file mode 100644
index 000000000..ec9e5f7e3
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/signin-otp/subject.txt
@@ -0,0 +1 @@
+Еднократна парола за ${redirectTo}
diff --git a/examples/guides/backend/nhost/emails/bg/signin-passwordless-sms/body.txt b/examples/guides/backend/nhost/emails/bg/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..ccb34253e
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Вашият код е ${code}.
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/bg/signin-passwordless/body.html b/examples/guides/backend/nhost/emails/bg/signin-passwordless/body.html
new file mode 100644
index 000000000..8d4b40cd2
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Магически линк за вход
+ Използвайте посочения линк за защитен и бърз вход:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/bg/signin-passwordless/subject.txt b/examples/guides/backend/nhost/emails/bg/signin-passwordless/subject.txt
new file mode 100644
index 000000000..945efccb7
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/bg/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Магически линк за вход
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/cs/email-confirm-change/body.html b/examples/guides/backend/nhost/emails/cs/email-confirm-change/body.html
new file mode 100644
index 000000000..b2b4da445
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Potvrzení změny emailové adresy
+ Použijte tento odkaz k potvrzení změny emailové adresy:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/cs/email-confirm-change/subject.txt b/examples/guides/backend/nhost/emails/cs/email-confirm-change/subject.txt
new file mode 100644
index 000000000..52b99137e
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Změna vaší emailové adresy
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/cs/email-verify/body.html b/examples/guides/backend/nhost/emails/cs/email-verify/body.html
new file mode 100644
index 000000000..22671ba22
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Ověření emailové adresy
+ Použijte tento odkaz k ověření vaší emailové adresy:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/cs/email-verify/subject.txt b/examples/guides/backend/nhost/emails/cs/email-verify/subject.txt
new file mode 100644
index 000000000..8e3eabacf
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/email-verify/subject.txt
@@ -0,0 +1 @@
+Ověření vaší emailové adresy
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/cs/password-reset/body.html b/examples/guides/backend/nhost/emails/cs/password-reset/body.html
new file mode 100644
index 000000000..3a3fa1d97
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Obnova hesla
+ Použijte tento odkaz k obnovení vašeho hesla:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/cs/password-reset/subject.txt b/examples/guides/backend/nhost/emails/cs/password-reset/subject.txt
new file mode 100644
index 000000000..7b8fba50b
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/password-reset/subject.txt
@@ -0,0 +1 @@
+Obnova hesla
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/cs/signin-otp/body.html b/examples/guides/backend/nhost/emails/cs/signin-otp/body.html
new file mode 100644
index 000000000..20115716a
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ Pro přihlášení do ${redirectTo}, prosím, použijte následující jednorázové heslo:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/cs/signin-otp/subject.txt b/examples/guides/backend/nhost/emails/cs/signin-otp/subject.txt
new file mode 100644
index 000000000..e2cd8c1da
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/signin-otp/subject.txt
@@ -0,0 +1 @@
+Jednorázové heslo pro ${redirectTo}
diff --git a/examples/guides/backend/nhost/emails/cs/signin-passwordless-sms/body.txt b/examples/guides/backend/nhost/emails/cs/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..df330b769
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Váš kód je ${code}.
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/cs/signin-passwordless/body.html b/examples/guides/backend/nhost/emails/cs/signin-passwordless/body.html
new file mode 100644
index 000000000..f6f019747
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Magický odkaz
+ Použijte tento odkaz k bezpečnému přihlášení:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/cs/signin-passwordless/subject.txt b/examples/guides/backend/nhost/emails/cs/signin-passwordless/subject.txt
new file mode 100644
index 000000000..8d9a66a25
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/cs/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Bezpečný odkaz k přihlášení
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/en/email-confirm-change/body.html b/examples/guides/backend/nhost/emails/en/email-confirm-change/body.html
new file mode 100644
index 000000000..68034c566
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Confirm Email Change
+ Use this link to confirm changing email:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/en/email-confirm-change/subject.txt b/examples/guides/backend/nhost/emails/en/email-confirm-change/subject.txt
new file mode 100644
index 000000000..1711dad88
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Change your email address
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/en/email-verify/body.html b/examples/guides/backend/nhost/emails/en/email-verify/body.html
new file mode 100644
index 000000000..e98e013ab
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Verify Email
+ Use this link to verify your email:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/en/email-verify/subject.txt b/examples/guides/backend/nhost/emails/en/email-verify/subject.txt
new file mode 100644
index 000000000..ef490a8bc
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/email-verify/subject.txt
@@ -0,0 +1 @@
+Verify your email
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/en/password-reset/body.html b/examples/guides/backend/nhost/emails/en/password-reset/body.html
new file mode 100644
index 000000000..bd3b5b483
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Reset Password
+ Use this link to reset your password:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/en/password-reset/subject.txt b/examples/guides/backend/nhost/emails/en/password-reset/subject.txt
new file mode 100644
index 000000000..e21496754
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/password-reset/subject.txt
@@ -0,0 +1 @@
+Reset your password
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/en/signin-otp/body.html b/examples/guides/backend/nhost/emails/en/signin-otp/body.html
new file mode 100644
index 000000000..95acc397e
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ To sign in to ${redirectTo}, please, use the following one-time password:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/en/signin-otp/subject.txt b/examples/guides/backend/nhost/emails/en/signin-otp/subject.txt
new file mode 100644
index 000000000..3da3fca7f
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/signin-otp/subject.txt
@@ -0,0 +1 @@
+One-time password for ${redirectTo}
diff --git a/examples/guides/backend/nhost/emails/en/signin-passwordless-sms/body.txt b/examples/guides/backend/nhost/emails/en/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..f03ec5b88
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Your code is ${code}.
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/en/signin-passwordless/body.html b/examples/guides/backend/nhost/emails/en/signin-passwordless/body.html
new file mode 100644
index 000000000..e8301b5ad
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Magic Link
+ Use this link to securely sign in:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/en/signin-passwordless/subject.txt b/examples/guides/backend/nhost/emails/en/signin-passwordless/subject.txt
new file mode 100644
index 000000000..8d0ee0119
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/en/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Secure sign-in link
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/es/email-confirm-change/body.html b/examples/guides/backend/nhost/emails/es/email-confirm-change/body.html
new file mode 100644
index 000000000..5cb7388be
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Confirmar cambio de correo electrónico
+ Utiliza el siguiente enlace para confirmar el cambio de correo:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/es/email-confirm-change/subject.txt b/examples/guides/backend/nhost/emails/es/email-confirm-change/subject.txt
new file mode 100644
index 000000000..6a7cf4069
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Cambiar dirección de correo electrónico
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/es/email-verify/body.html b/examples/guides/backend/nhost/emails/es/email-verify/body.html
new file mode 100644
index 000000000..b5b93b813
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Verificar correo electrónico
+ Utilza el siguiente enlace para verificar tu correo:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/es/email-verify/subject.txt b/examples/guides/backend/nhost/emails/es/email-verify/subject.txt
new file mode 100644
index 000000000..cf15cb278
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/email-verify/subject.txt
@@ -0,0 +1 @@
+Verifica tu correo electrónico
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/es/password-reset/body.html b/examples/guides/backend/nhost/emails/es/password-reset/body.html
new file mode 100644
index 000000000..c02bbc800
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Recuperar contraseña
+ Utiliza el siguiente enlace para recuperar tu contraseña:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/es/password-reset/subject.txt b/examples/guides/backend/nhost/emails/es/password-reset/subject.txt
new file mode 100644
index 000000000..417d1c730
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/password-reset/subject.txt
@@ -0,0 +1 @@
+Recuperar contraseña
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/es/signin-otp/body.html b/examples/guides/backend/nhost/emails/es/signin-otp/body.html
new file mode 100644
index 000000000..3effba1cd
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ Para iniciar sesión en ${redirectTo}, por favor, utilice la siguiente contraseña de un solo uso:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/es/signin-otp/subject.txt b/examples/guides/backend/nhost/emails/es/signin-otp/subject.txt
new file mode 100644
index 000000000..f041fe0c5
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/signin-otp/subject.txt
@@ -0,0 +1 @@
+Contraseña de un solo uso para ${redirectTo}
diff --git a/examples/guides/backend/nhost/emails/es/signin-passwordless-sms/body.txt b/examples/guides/backend/nhost/emails/es/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..c74950dd2
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Tu código es ${code}.
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/es/signin-passwordless/body.html b/examples/guides/backend/nhost/emails/es/signin-passwordless/body.html
new file mode 100644
index 000000000..c1b67a841
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Enlace mágico
+ Utiliza este enlace para iniciar sesión de forma segura:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/es/signin-passwordless/subject.txt b/examples/guides/backend/nhost/emails/es/signin-passwordless/subject.txt
new file mode 100644
index 000000000..cbd5ab19f
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/es/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Enlace de acceso seguro
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/fr/email-confirm-change/body.html b/examples/guides/backend/nhost/emails/fr/email-confirm-change/body.html
new file mode 100644
index 000000000..c4ba00737
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Confirmer changement de courriel
+ Utilisez ce lien pour confirmer le changement de courriel:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/fr/email-confirm-change/subject.txt b/examples/guides/backend/nhost/emails/fr/email-confirm-change/subject.txt
new file mode 100644
index 000000000..1e12b4b4d
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Changez votre adresse courriel
diff --git a/examples/guides/backend/nhost/emails/fr/email-verify/body.html b/examples/guides/backend/nhost/emails/fr/email-verify/body.html
new file mode 100644
index 000000000..951c7c7fe
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Vérifiez votre courriel
+ Utilisez ce lien pour vérifier votre courriel:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/fr/email-verify/subject.txt b/examples/guides/backend/nhost/emails/fr/email-verify/subject.txt
new file mode 100644
index 000000000..540cc1800
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/email-verify/subject.txt
@@ -0,0 +1 @@
+Vérifier votre courriel
diff --git a/examples/guides/backend/nhost/emails/fr/password-reset/body.html b/examples/guides/backend/nhost/emails/fr/password-reset/body.html
new file mode 100644
index 000000000..8dc4da721
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Réinitialiser votre mot de passe
+ Utilisez ce lien pour réinitialiser votre mot de passe:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/fr/password-reset/subject.txt b/examples/guides/backend/nhost/emails/fr/password-reset/subject.txt
new file mode 100644
index 000000000..5c2caa35b
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/password-reset/subject.txt
@@ -0,0 +1 @@
+Réinitialiser votre mot de passe
diff --git a/examples/guides/backend/nhost/emails/fr/signin-otp/body.html b/examples/guides/backend/nhost/emails/fr/signin-otp/body.html
new file mode 100644
index 000000000..773e33b83
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ Pour vous connecter à ${redirectTo}, veuillez utiliser le mot de passe à usage unique suivant :
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/fr/signin-otp/subject.txt b/examples/guides/backend/nhost/emails/fr/signin-otp/subject.txt
new file mode 100644
index 000000000..f4156ed0e
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/signin-otp/subject.txt
@@ -0,0 +1 @@
+Mot de passe à usage unique pour ${redirectTo}
diff --git a/examples/guides/backend/nhost/emails/fr/signin-passwordless-sms/body.txt b/examples/guides/backend/nhost/emails/fr/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..72d6ab249
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Votre code est ${code}.
\ No newline at end of file
diff --git a/examples/guides/backend/nhost/emails/fr/signin-passwordless/body.html b/examples/guides/backend/nhost/emails/fr/signin-passwordless/body.html
new file mode 100644
index 000000000..a45f217bc
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Lien magique
+ Utilisez ce lien pour vous connecter de façon sécurisée:
+
+
+
+
+
+
+
+
+
diff --git a/examples/guides/backend/nhost/emails/fr/signin-passwordless/subject.txt b/examples/guides/backend/nhost/emails/fr/signin-passwordless/subject.txt
new file mode 100644
index 000000000..dc2d81026
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/fr/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Lien de connexion sécurisé
diff --git a/examples/guides/backend/nhost/emails/test/email-verify/body.html b/examples/guides/backend/nhost/emails/test/email-verify/body.html
new file mode 100644
index 000000000..5c2dbc158
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/test/email-verify/body.html
@@ -0,0 +1,8 @@
+${link},
+${displayName},
+${email},
+${ticket},
+${redirectTo},
+${serverUrl},
+${clientUrl},
+${locale},
diff --git a/examples/guides/backend/nhost/emails/test/email-verify/subject.txt b/examples/guides/backend/nhost/emails/test/email-verify/subject.txt
new file mode 100644
index 000000000..1fe895365
--- /dev/null
+++ b/examples/guides/backend/nhost/emails/test/email-verify/subject.txt
@@ -0,0 +1 @@
+${link}, ${displayName}, ${email}, ${ticket}, ${redirectTo}, ${serverUrl}, ${clientUrl}, ${locale}
diff --git a/examples/guides/backend/nhost/metadata/actions.graphql b/examples/guides/backend/nhost/metadata/actions.graphql
new file mode 100644
index 000000000..e69de29bb
diff --git a/examples/guides/backend/nhost/metadata/actions.yaml b/examples/guides/backend/nhost/metadata/actions.yaml
new file mode 100644
index 000000000..1edb4c2ff
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/actions.yaml
@@ -0,0 +1,6 @@
+actions: []
+custom_types:
+ enums: []
+ input_objects: []
+ objects: []
+ scalars: []
diff --git a/examples/guides/backend/nhost/metadata/allow_list.yaml b/examples/guides/backend/nhost/metadata/allow_list.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/allow_list.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/guides/backend/nhost/metadata/api_limits.yaml b/examples/guides/backend/nhost/metadata/api_limits.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/api_limits.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/guides/backend/nhost/metadata/backend_configs.yaml b/examples/guides/backend/nhost/metadata/backend_configs.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/backend_configs.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/guides/backend/nhost/metadata/cron_triggers.yaml b/examples/guides/backend/nhost/metadata/cron_triggers.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/cron_triggers.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/guides/backend/nhost/metadata/databases/databases.yaml b/examples/guides/backend/nhost/metadata/databases/databases.yaml
new file mode 100644
index 000000000..65a11b202
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/databases.yaml
@@ -0,0 +1,14 @@
+- name: default
+ kind: postgres
+ configuration:
+ connection_info:
+ database_url:
+ from_env: HASURA_GRAPHQL_DATABASE_URL
+ isolation_level: read-committed
+ pool_settings:
+ connection_lifetime: 600
+ idle_timeout: 180
+ max_connections: 50
+ retries: 1
+ use_prepared_statements: true
+ tables: "!include default/tables/tables.yaml"
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/auth_provider_requests.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_provider_requests.yaml
new file mode 100644
index 000000000..91ba1967d
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_provider_requests.yaml
@@ -0,0 +1,23 @@
+table:
+ name: provider_requests
+ schema: auth
+configuration:
+ column_config:
+ id:
+ custom_name: id
+ options:
+ custom_name: options
+ custom_column_names:
+ id: id
+ options: options
+ custom_name: authProviderRequests
+ custom_root_fields:
+ delete: deleteAuthProviderRequests
+ delete_by_pk: deleteAuthProviderRequest
+ insert: insertAuthProviderRequests
+ insert_one: insertAuthProviderRequest
+ select: authProviderRequests
+ select_aggregate: authProviderRequestsAggregate
+ select_by_pk: authProviderRequest
+ update: updateAuthProviderRequests
+ update_by_pk: updateAuthProviderRequest
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/auth_providers.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_providers.yaml
new file mode 100644
index 000000000..3de528dc9
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_providers.yaml
@@ -0,0 +1,28 @@
+table:
+ name: providers
+ schema: auth
+configuration:
+ column_config:
+ id:
+ custom_name: id
+ custom_column_names:
+ id: id
+ custom_name: authProviders
+ custom_root_fields:
+ delete: deleteAuthProviders
+ delete_by_pk: deleteAuthProvider
+ insert: insertAuthProviders
+ insert_one: insertAuthProvider
+ select: authProviders
+ select_aggregate: authProvidersAggregate
+ select_by_pk: authProvider
+ update: updateAuthProviders
+ update_by_pk: updateAuthProvider
+array_relationships:
+ - name: userProviders
+ using:
+ foreign_key_constraint_on:
+ column: provider_id
+ table:
+ name: user_providers
+ schema: auth
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/auth_refresh_token_types.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_refresh_token_types.yaml
new file mode 100644
index 000000000..8abaa8604
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_refresh_token_types.yaml
@@ -0,0 +1,26 @@
+table:
+ name: refresh_token_types
+ schema: auth
+is_enum: true
+configuration:
+ column_config: {}
+ custom_column_names: {}
+ custom_name: authRefreshTokenTypes
+ custom_root_fields:
+ delete: deleteAuthRefreshTokenTypes
+ delete_by_pk: deleteAuthRefreshTokenType
+ insert: insertAuthRefreshTokenTypes
+ insert_one: insertAuthRefreshTokenType
+ select: authRefreshTokenTypes
+ select_aggregate: authRefreshTokenTypesAggregate
+ select_by_pk: authRefreshTokenType
+ update: updateAuthRefreshTokenTypes
+ update_by_pk: updateAuthRefreshTokenType
+array_relationships:
+ - name: refreshTokens
+ using:
+ foreign_key_constraint_on:
+ column: type
+ table:
+ name: refresh_tokens
+ schema: auth
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/auth_refresh_tokens.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_refresh_tokens.yaml
new file mode 100644
index 000000000..2a332c4f2
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_refresh_tokens.yaml
@@ -0,0 +1,55 @@
+table:
+ name: refresh_tokens
+ schema: auth
+configuration:
+ column_config:
+ created_at:
+ custom_name: createdAt
+ expires_at:
+ custom_name: expiresAt
+ refresh_token_hash:
+ custom_name: refreshTokenHash
+ user_id:
+ custom_name: userId
+ custom_column_names:
+ created_at: createdAt
+ expires_at: expiresAt
+ refresh_token_hash: refreshTokenHash
+ user_id: userId
+ custom_name: authRefreshTokens
+ custom_root_fields:
+ delete: deleteAuthRefreshTokens
+ delete_by_pk: deleteAuthRefreshToken
+ insert: insertAuthRefreshTokens
+ insert_one: insertAuthRefreshToken
+ select: authRefreshTokens
+ select_aggregate: authRefreshTokensAggregate
+ select_by_pk: authRefreshToken
+ update: updateAuthRefreshTokens
+ update_by_pk: updateAuthRefreshToken
+object_relationships:
+ - name: user
+ using:
+ foreign_key_constraint_on: user_id
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - id
+ - created_at
+ - expires_at
+ - metadata
+ - type
+ - user_id
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+delete_permissions:
+ - role: user
+ permission:
+ filter:
+ _and:
+ - user_id:
+ _eq: X-Hasura-User-Id
+ - type:
+ _eq: pat
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/auth_roles.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_roles.yaml
new file mode 100644
index 000000000..bc6d5a2cf
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_roles.yaml
@@ -0,0 +1,35 @@
+table:
+ name: roles
+ schema: auth
+configuration:
+ column_config:
+ role:
+ custom_name: role
+ custom_column_names:
+ role: role
+ custom_name: authRoles
+ custom_root_fields:
+ delete: deleteAuthRoles
+ delete_by_pk: deleteAuthRole
+ insert: insertAuthRoles
+ insert_one: insertAuthRole
+ select: authRoles
+ select_aggregate: authRolesAggregate
+ select_by_pk: authRole
+ update: updateAuthRoles
+ update_by_pk: updateAuthRole
+array_relationships:
+ - name: userRoles
+ using:
+ foreign_key_constraint_on:
+ column: role
+ table:
+ name: user_roles
+ schema: auth
+ - name: usersByDefaultRole
+ using:
+ foreign_key_constraint_on:
+ column: default_role
+ table:
+ name: users
+ schema: auth
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/auth_user_providers.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_user_providers.yaml
new file mode 100644
index 000000000..02b8eb2da
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_user_providers.yaml
@@ -0,0 +1,48 @@
+table:
+ name: user_providers
+ schema: auth
+configuration:
+ column_config:
+ access_token:
+ custom_name: accessToken
+ created_at:
+ custom_name: createdAt
+ id:
+ custom_name: id
+ provider_id:
+ custom_name: providerId
+ provider_user_id:
+ custom_name: providerUserId
+ refresh_token:
+ custom_name: refreshToken
+ updated_at:
+ custom_name: updatedAt
+ user_id:
+ custom_name: userId
+ custom_column_names:
+ access_token: accessToken
+ created_at: createdAt
+ id: id
+ provider_id: providerId
+ provider_user_id: providerUserId
+ refresh_token: refreshToken
+ updated_at: updatedAt
+ user_id: userId
+ custom_name: authUserProviders
+ custom_root_fields:
+ delete: deleteAuthUserProviders
+ delete_by_pk: deleteAuthUserProvider
+ insert: insertAuthUserProviders
+ insert_one: insertAuthUserProvider
+ select: authUserProviders
+ select_aggregate: authUserProvidersAggregate
+ select_by_pk: authUserProvider
+ update: updateAuthUserProviders
+ update_by_pk: updateAuthUserProvider
+object_relationships:
+ - name: provider
+ using:
+ foreign_key_constraint_on: provider_id
+ - name: user
+ using:
+ foreign_key_constraint_on: user_id
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/auth_user_roles.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_user_roles.yaml
new file mode 100644
index 000000000..f90553941
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_user_roles.yaml
@@ -0,0 +1,36 @@
+table:
+ name: user_roles
+ schema: auth
+configuration:
+ column_config:
+ created_at:
+ custom_name: createdAt
+ id:
+ custom_name: id
+ role:
+ custom_name: role
+ user_id:
+ custom_name: userId
+ custom_column_names:
+ created_at: createdAt
+ id: id
+ role: role
+ user_id: userId
+ custom_name: authUserRoles
+ custom_root_fields:
+ delete: deleteAuthUserRoles
+ delete_by_pk: deleteAuthUserRole
+ insert: insertAuthUserRoles
+ insert_one: insertAuthUserRole
+ select: authUserRoles
+ select_aggregate: authUserRolesAggregate
+ select_by_pk: authUserRole
+ update: updateAuthUserRoles
+ update_by_pk: updateAuthUserRole
+object_relationships:
+ - name: roleByRole
+ using:
+ foreign_key_constraint_on: role
+ - name: user
+ using:
+ foreign_key_constraint_on: user_id
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/auth_user_security_keys.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_user_security_keys.yaml
new file mode 100644
index 000000000..415337410
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_user_security_keys.yaml
@@ -0,0 +1,62 @@
+table:
+ name: user_security_keys
+ schema: auth
+configuration:
+ column_config:
+ credential_id:
+ custom_name: credentialId
+ credential_public_key:
+ custom_name: credentialPublicKey
+ id:
+ custom_name: id
+ user_id:
+ custom_name: userId
+ custom_column_names:
+ credential_id: credentialId
+ credential_public_key: credentialPublicKey
+ id: id
+ user_id: userId
+ custom_name: authUserSecurityKeys
+ custom_root_fields:
+ delete: deleteAuthUserSecurityKeys
+ delete_by_pk: deleteAuthUserSecurityKey
+ insert: insertAuthUserSecurityKeys
+ insert_one: insertAuthUserSecurityKey
+ select: authUserSecurityKeys
+ select_aggregate: authUserSecurityKeysAggregate
+ select_by_pk: authUserSecurityKey
+ update: updateAuthUserSecurityKeys
+ update_by_pk: updateAuthUserSecurityKey
+object_relationships:
+ - name: user
+ using:
+ foreign_key_constraint_on: user_id
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - credential_id
+ - id
+ - nickname
+ - user_id
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+ comment: ""
+update_permissions:
+ - role: user
+ permission:
+ columns:
+ - nickname
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+ check: null
+ comment: ""
+delete_permissions:
+ - role: user
+ permission:
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+ comment: ""
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/auth_users.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_users.yaml
new file mode 100644
index 000000000..b76b18ea5
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/auth_users.yaml
@@ -0,0 +1,146 @@
+table:
+ name: users
+ schema: auth
+configuration:
+ column_config:
+ active_mfa_type:
+ custom_name: activeMfaType
+ avatar_url:
+ custom_name: avatarUrl
+ created_at:
+ custom_name: createdAt
+ default_role:
+ custom_name: defaultRole
+ disabled:
+ custom_name: disabled
+ display_name:
+ custom_name: displayName
+ email:
+ custom_name: email
+ email_verified:
+ custom_name: emailVerified
+ id:
+ custom_name: id
+ is_anonymous:
+ custom_name: isAnonymous
+ last_seen:
+ custom_name: lastSeen
+ locale:
+ custom_name: locale
+ new_email:
+ custom_name: newEmail
+ otp_hash:
+ custom_name: otpHash
+ otp_hash_expires_at:
+ custom_name: otpHashExpiresAt
+ otp_method_last_used:
+ custom_name: otpMethodLastUsed
+ password_hash:
+ custom_name: passwordHash
+ phone_number:
+ custom_name: phoneNumber
+ phone_number_verified:
+ custom_name: phoneNumberVerified
+ ticket:
+ custom_name: ticket
+ ticket_expires_at:
+ custom_name: ticketExpiresAt
+ totp_secret:
+ custom_name: totpSecret
+ updated_at:
+ custom_name: updatedAt
+ webauthn_current_challenge:
+ custom_name: currentChallenge
+ custom_column_names:
+ active_mfa_type: activeMfaType
+ avatar_url: avatarUrl
+ created_at: createdAt
+ default_role: defaultRole
+ disabled: disabled
+ display_name: displayName
+ email: email
+ email_verified: emailVerified
+ id: id
+ is_anonymous: isAnonymous
+ last_seen: lastSeen
+ locale: locale
+ new_email: newEmail
+ otp_hash: otpHash
+ otp_hash_expires_at: otpHashExpiresAt
+ otp_method_last_used: otpMethodLastUsed
+ password_hash: passwordHash
+ phone_number: phoneNumber
+ phone_number_verified: phoneNumberVerified
+ ticket: ticket
+ ticket_expires_at: ticketExpiresAt
+ totp_secret: totpSecret
+ updated_at: updatedAt
+ webauthn_current_challenge: currentChallenge
+ custom_name: users
+ custom_root_fields:
+ delete: deleteUsers
+ delete_by_pk: deleteUser
+ insert: insertUsers
+ insert_one: insertUser
+ select: users
+ select_aggregate: usersAggregate
+ select_by_pk: user
+ update: updateUsers
+ update_by_pk: updateUser
+object_relationships:
+ - name: defaultRoleByRole
+ using:
+ foreign_key_constraint_on: default_role
+array_relationships:
+ - name: refreshTokens
+ using:
+ foreign_key_constraint_on:
+ column: user_id
+ table:
+ name: refresh_tokens
+ schema: auth
+ - name: roles
+ using:
+ foreign_key_constraint_on:
+ column: user_id
+ table:
+ name: user_roles
+ schema: auth
+ - name: securityKeys
+ using:
+ foreign_key_constraint_on:
+ column: user_id
+ table:
+ name: user_security_keys
+ schema: auth
+ - name: userProviders
+ using:
+ foreign_key_constraint_on:
+ column: user_id
+ table:
+ name: user_providers
+ schema: auth
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - active_mfa_type
+ - display_name
+ - email
+ - id
+ - metadata
+ filter:
+ id:
+ _eq: X-Hasura-User-Id
+ comment: ""
+update_permissions:
+ - role: user
+ permission:
+ columns:
+ - display_name
+ - metadata
+ filter:
+ id:
+ _eq: X-Hasura-User-Id
+ check: null
+ comment: ""
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/public_attachments.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/public_attachments.yaml
new file mode 100644
index 000000000..632081963
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/public_attachments.yaml
@@ -0,0 +1,70 @@
+table:
+ name: attachments
+ schema: public
+configuration:
+ column_config:
+ task_id:
+ custom_name: taskID
+ custom_column_names:
+ task_id: taskID
+ custom_root_fields:
+ delete: deleteAttachments
+ delete_by_pk: deleteAttachment
+ insert: insertAttachments
+ insert_one: insertAttachment
+ select: attachments
+ select_aggregate: attachmentsAggregate
+ select_by_pk: attachment
+ select_stream: attachmentsStream
+ update: updateAttachments
+ update_by_pk: updateAttachment
+ update_many: updateAttachmentsMany
+object_relationships:
+ - name: file
+ using:
+ foreign_key_constraint_on: file_id
+ - name: task
+ using:
+ foreign_key_constraint_on: task_id
+insert_permissions:
+ - role: user
+ permission:
+ check:
+ _and:
+ - file:
+ uploaded_by_user_id:
+ _eq: X-Hasura-User-Id
+ - task:
+ user_id:
+ _eq: X-Hasura-User-Id
+ columns:
+ - file_id
+ - task_id
+ comment: ""
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - file_id
+ - task_id
+ filter:
+ _and:
+ - file:
+ uploaded_by_user_id:
+ _eq: X-Hasura-User-Id
+ - task:
+ user_id:
+ _eq: X-Hasura-User-Id
+ comment: ""
+delete_permissions:
+ - role: user
+ permission:
+ filter:
+ _and:
+ - file:
+ uploaded_by_user_id:
+ _eq: X-Hasura-User-Id
+ - task:
+ user_id:
+ _eq: X-Hasura-User-Id
+ comment: ""
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/public_comments.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/public_comments.yaml
new file mode 100644
index 000000000..f243a2a97
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/public_comments.yaml
@@ -0,0 +1,86 @@
+table:
+ name: comments
+ schema: public
+configuration:
+ column_config:
+ comment:
+ custom_name: comment
+ created_at:
+ custom_name: createdAt
+ id:
+ custom_name: id
+ ninja_turtle_id:
+ custom_name: ninjaTurtleId
+ updated_at:
+ custom_name: updatedAt
+ user_id:
+ custom_name: userId
+ custom_column_names:
+ comment: comment
+ created_at: createdAt
+ id: id
+ ninja_turtle_id: ninjaTurtleId
+ updated_at: updatedAt
+ user_id: userId
+ custom_name: comments
+ custom_root_fields:
+ delete: deleteComments
+ delete_by_pk: deleteComment
+ insert: insertComments
+ insert_one: insertComment
+ select: comments
+ select_aggregate: commentsAggregate
+ select_by_pk: comment
+ select_stream: commentsStream
+ update: updateComments
+ update_by_pk: updateComment
+ update_many: updateCommentsMany
+object_relationships:
+ - name: ninjaTurtle
+ using:
+ foreign_key_constraint_on: ninja_turtle_id
+ - name: user
+ using:
+ foreign_key_constraint_on: user_id
+insert_permissions:
+ - role: user
+ permission:
+ check:
+ user_id:
+ _eq: X-Hasura-User-Id
+ set:
+ user_id: x-hasura-User-Id
+ columns:
+ - ninja_turtle_id
+ - comment
+ comment: Allow users to add comments, automatically setting their user_id
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - id
+ - created_at
+ - updated_at
+ - ninja_turtle_id
+ - comment
+ - user_id
+ filter: {}
+ allow_aggregations: true
+ comment: Allow users to view all comments
+update_permissions:
+ - role: user
+ permission:
+ columns:
+ - comment
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+ check: {}
+ comment: Allow users to update only their own comments
+delete_permissions:
+ - role: user
+ permission:
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+ comment: Allow users to delete only their own comments
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/public_movies.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/public_movies.yaml
new file mode 100644
index 000000000..336938a94
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/public_movies.yaml
@@ -0,0 +1,16 @@
+table:
+ name: movies
+ schema: public
+select_permissions:
+ - role: public
+ permission:
+ columns:
+ - id
+ - title
+ - director
+ - release_year
+ - genre
+ - rating
+ - created_at
+ - updated_at
+ filter: {}
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/public_ninja_turtles.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/public_ninja_turtles.yaml
new file mode 100644
index 000000000..4dd0f3e60
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/public_ninja_turtles.yaml
@@ -0,0 +1,50 @@
+table:
+ name: ninja_turtles
+ schema: public
+configuration:
+ column_config:
+ created_at:
+ custom_name: createdAt
+ id:
+ custom_name: id
+ name:
+ custom_name: name
+ updated_at:
+ custom_name: updatedAt
+ custom_column_names:
+ created_at: createdAt
+ id: id
+ name: name
+ updated_at: updatedAt
+ custom_name: ninjaTurtles
+ custom_root_fields:
+ delete: deleteNinjaTurtles
+ delete_by_pk: deleteNinjaTurtle
+ insert: insertNinjaTurtles
+ insert_one: insertNinjaTurtle
+ select: ninjaTurtles
+ select_aggregate: ninjaTurtlesAggregate
+ select_by_pk: ninjaTurtle
+ select_stream: ninjaTurtlesStream
+ update: updateNinjaTurtles
+ update_by_pk: updateNinjaTurtle
+ update_many: updateNinjaTurtlesMany
+array_relationships:
+ - name: comments
+ using:
+ foreign_key_constraint_on:
+ column: ninja_turtle_id
+ table:
+ name: comments
+ schema: public
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - created_at
+ - description
+ - id
+ - name
+ - updated_at
+ filter: {}
+ comment: ""
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/public_tasks.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/public_tasks.yaml
new file mode 100644
index 000000000..eac5142fe
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/public_tasks.yaml
@@ -0,0 +1,74 @@
+table:
+ name: tasks
+ schema: public
+configuration:
+ column_config:
+ created_at:
+ custom_name: createdAt
+ updated_at:
+ custom_name: updatedAt
+ user_id:
+ custom_name: userID
+ custom_column_names:
+ created_at: createdAt
+ updated_at: updatedAt
+ user_id: userID
+ custom_root_fields:
+ delete: deleteTasks
+ delete_by_pk: deleteTask
+ insert: insertTasks
+ insert_one: insertTask
+ select: tasks
+ select_aggregate: tasksAggregate
+ select_by_pk: task
+ select_stream: tasksStream
+ update: updateTasks
+ update_by_pk: updateTask
+ update_many: updateTasksMany
+insert_permissions:
+ - role: user
+ permission:
+ check:
+ user_id:
+ _eq: X-Hasura-User-Id
+ set:
+ user_id: x-hasura-User-Id
+ columns:
+ - completed
+ - description
+ - title
+ comment: ""
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - completed
+ - description
+ - title
+ - created_at
+ - updated_at
+ - id
+ - user_id
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+ comment: ""
+update_permissions:
+ - role: user
+ permission:
+ columns:
+ - completed
+ - description
+ - title
+ filter: {}
+ check:
+ user_id:
+ _eq: X-Hasura-User-Id
+ comment: ""
+delete_permissions:
+ - role: user
+ permission:
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+ comment: ""
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/storage_buckets.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/storage_buckets.yaml
new file mode 100644
index 000000000..12b0e8343
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/storage_buckets.yaml
@@ -0,0 +1,49 @@
+table:
+ name: buckets
+ schema: storage
+configuration:
+ column_config:
+ cache_control:
+ custom_name: cacheControl
+ created_at:
+ custom_name: createdAt
+ download_expiration:
+ custom_name: downloadExpiration
+ id:
+ custom_name: id
+ max_upload_file_size:
+ custom_name: maxUploadFileSize
+ min_upload_file_size:
+ custom_name: minUploadFileSize
+ presigned_urls_enabled:
+ custom_name: presignedUrlsEnabled
+ updated_at:
+ custom_name: updatedAt
+ custom_column_names:
+ cache_control: cacheControl
+ created_at: createdAt
+ download_expiration: downloadExpiration
+ id: id
+ max_upload_file_size: maxUploadFileSize
+ min_upload_file_size: minUploadFileSize
+ presigned_urls_enabled: presignedUrlsEnabled
+ updated_at: updatedAt
+ custom_name: buckets
+ custom_root_fields:
+ delete: deleteBuckets
+ delete_by_pk: deleteBucket
+ insert: insertBuckets
+ insert_one: insertBucket
+ select: buckets
+ select_aggregate: bucketsAggregate
+ select_by_pk: bucket
+ update: updateBuckets
+ update_by_pk: updateBucket
+array_relationships:
+ - name: files
+ using:
+ foreign_key_constraint_on:
+ column: bucket_id
+ table:
+ name: files
+ schema: storage
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/storage_files.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/storage_files.yaml
new file mode 100644
index 000000000..80e5a68b7
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/storage_files.yaml
@@ -0,0 +1,114 @@
+table:
+ name: files
+ schema: storage
+configuration:
+ column_config:
+ bucket_id:
+ custom_name: bucketId
+ created_at:
+ custom_name: createdAt
+ etag:
+ custom_name: etag
+ id:
+ custom_name: id
+ is_uploaded:
+ custom_name: isUploaded
+ metadata:
+ custom_name: metadata
+ mime_type:
+ custom_name: mimeType
+ name:
+ custom_name: name
+ size:
+ custom_name: size
+ updated_at:
+ custom_name: updatedAt
+ uploaded_by_user_id:
+ custom_name: uploadedByUserId
+ custom_column_names:
+ bucket_id: bucketId
+ created_at: createdAt
+ etag: etag
+ id: id
+ is_uploaded: isUploaded
+ metadata: metadata
+ mime_type: mimeType
+ name: name
+ size: size
+ updated_at: updatedAt
+ uploaded_by_user_id: uploadedByUserId
+ custom_name: files
+ custom_root_fields:
+ delete: deleteFiles
+ delete_by_pk: deleteFile
+ insert: insertFiles
+ insert_one: insertFile
+ select: files
+ select_aggregate: filesAggregate
+ select_by_pk: file
+ update: updateFiles
+ update_by_pk: updateFile
+object_relationships:
+ - name: bucket
+ using:
+ foreign_key_constraint_on: bucket_id
+insert_permissions:
+ - role: user
+ permission:
+ check: {}
+ set:
+ uploaded_by_user_id: x-hasura-User-Id
+ columns:
+ - bucket_id
+ - id
+ - mime_type
+ - name
+ - size
+ - uploaded_by_user_id
+ comment: ""
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - is_uploaded
+ - size
+ - metadata
+ - bucket_id
+ - etag
+ - mime_type
+ - name
+ - created_at
+ - updated_at
+ - id
+ - uploaded_by_user_id
+ filter:
+ uploaded_by_user_id:
+ _eq: X-Hasura-User-Id
+ comment: ""
+update_permissions:
+ - role: user
+ permission:
+ columns:
+ - is_uploaded
+ - size
+ - metadata
+ - bucket_id
+ - etag
+ - mime_type
+ - name
+ - created_at
+ - updated_at
+ - id
+ - uploaded_by_user_id
+ filter:
+ uploaded_by_user_id:
+ _eq: X-Hasura-User-Id
+ check: {}
+ comment: ""
+delete_permissions:
+ - role: user
+ permission:
+ filter:
+ uploaded_by_user_id:
+ _eq: X-Hasura-User-Id
+ comment: ""
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/storage_virus.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/storage_virus.yaml
new file mode 100644
index 000000000..5c31429ce
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/storage_virus.yaml
@@ -0,0 +1,42 @@
+table:
+ name: virus
+ schema: storage
+configuration:
+ column_config:
+ created_at:
+ custom_name: createdAt
+ file_id:
+ custom_name: fileId
+ filename:
+ custom_name: filename
+ id:
+ custom_name: id
+ updated_at:
+ custom_name: updatedAt
+ user_session:
+ custom_name: userSession
+ virus:
+ custom_name: virus
+ custom_column_names:
+ created_at: createdAt
+ file_id: fileId
+ filename: filename
+ id: id
+ updated_at: updatedAt
+ user_session: userSession
+ virus: virus
+ custom_name: virus
+ custom_root_fields:
+ delete: deleteViruses
+ delete_by_pk: deleteVirus
+ insert: insertViruses
+ insert_one: insertVirus
+ select: viruses
+ select_aggregate: virusesAggregate
+ select_by_pk: virus
+ update: updateViruses
+ update_by_pk: updateVirus
+object_relationships:
+ - name: file
+ using:
+ foreign_key_constraint_on: file_id
diff --git a/examples/guides/backend/nhost/metadata/databases/default/tables/tables.yaml b/examples/guides/backend/nhost/metadata/databases/default/tables/tables.yaml
new file mode 100644
index 000000000..2bfdf536d
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/databases/default/tables/tables.yaml
@@ -0,0 +1,17 @@
+- "!include auth_provider_requests.yaml"
+- "!include auth_providers.yaml"
+- "!include auth_refresh_token_types.yaml"
+- "!include auth_refresh_tokens.yaml"
+- "!include auth_roles.yaml"
+- "!include auth_user_providers.yaml"
+- "!include auth_user_roles.yaml"
+- "!include auth_user_security_keys.yaml"
+- "!include auth_users.yaml"
+- "!include public_attachments.yaml"
+- "!include public_comments.yaml"
+- "!include public_movies.yaml"
+- "!include public_ninja_turtles.yaml"
+- "!include public_tasks.yaml"
+- "!include storage_buckets.yaml"
+- "!include storage_files.yaml"
+- "!include storage_virus.yaml"
diff --git a/examples/guides/backend/nhost/metadata/graphql_schema_introspection.yaml b/examples/guides/backend/nhost/metadata/graphql_schema_introspection.yaml
new file mode 100644
index 000000000..61a4dcac2
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/graphql_schema_introspection.yaml
@@ -0,0 +1 @@
+disabled_for_roles: []
diff --git a/examples/guides/backend/nhost/metadata/inherited_roles.yaml b/examples/guides/backend/nhost/metadata/inherited_roles.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/inherited_roles.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/guides/backend/nhost/metadata/metrics_config.yaml b/examples/guides/backend/nhost/metadata/metrics_config.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/metrics_config.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/guides/backend/nhost/metadata/network.yaml b/examples/guides/backend/nhost/metadata/network.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/network.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/guides/backend/nhost/metadata/opentelemetry.yaml b/examples/guides/backend/nhost/metadata/opentelemetry.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/opentelemetry.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/guides/backend/nhost/metadata/query_collections.yaml b/examples/guides/backend/nhost/metadata/query_collections.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/query_collections.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/guides/backend/nhost/metadata/remote_schemas.yaml b/examples/guides/backend/nhost/metadata/remote_schemas.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/remote_schemas.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/guides/backend/nhost/metadata/rest_endpoints.yaml b/examples/guides/backend/nhost/metadata/rest_endpoints.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/rest_endpoints.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/guides/backend/nhost/metadata/version.yaml b/examples/guides/backend/nhost/metadata/version.yaml
new file mode 100644
index 000000000..0a70affa4
--- /dev/null
+++ b/examples/guides/backend/nhost/metadata/version.yaml
@@ -0,0 +1 @@
+version: 3
diff --git a/examples/guides/backend/nhost/migrations/default/1738758216166_create_table_public_tasks/down.sql b/examples/guides/backend/nhost/migrations/default/1738758216166_create_table_public_tasks/down.sql
new file mode 100644
index 000000000..ba949320f
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1738758216166_create_table_public_tasks/down.sql
@@ -0,0 +1 @@
+DROP TABLE "public"."tasks";
diff --git a/examples/guides/backend/nhost/migrations/default/1738758216166_create_table_public_tasks/up.sql b/examples/guides/backend/nhost/migrations/default/1738758216166_create_table_public_tasks/up.sql
new file mode 100644
index 000000000..c55450939
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1738758216166_create_table_public_tasks/up.sql
@@ -0,0 +1,18 @@
+CREATE TABLE "public"."tasks" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "title" text NOT NULL, "description" text NOT NULL, "completed" boolean NOT NULL DEFAULT false, "user_id" uuid NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON UPDATE cascade ON DELETE cascade);
+CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
+RETURNS TRIGGER AS $$
+DECLARE
+ _new record;
+BEGIN
+ _new := NEW;
+ _new."updated_at" = NOW();
+ RETURN _new;
+END;
+$$ LANGUAGE plpgsql;
+CREATE TRIGGER "set_public_tasks_updated_at"
+BEFORE UPDATE ON "public"."tasks"
+FOR EACH ROW
+EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
+COMMENT ON TRIGGER "set_public_tasks_updated_at" ON "public"."tasks"
+IS 'trigger to set value of column "updated_at" to current timestamp on row update';
+CREATE EXTENSION IF NOT EXISTS pgcrypto;
diff --git a/examples/guides/backend/nhost/migrations/default/1738758597255_create_table_public_attachments/down.sql b/examples/guides/backend/nhost/migrations/default/1738758597255_create_table_public_attachments/down.sql
new file mode 100644
index 000000000..e23b2d607
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1738758597255_create_table_public_attachments/down.sql
@@ -0,0 +1 @@
+DROP TABLE "public"."attachments";
diff --git a/examples/guides/backend/nhost/migrations/default/1738758597255_create_table_public_attachments/up.sql b/examples/guides/backend/nhost/migrations/default/1738758597255_create_table_public_attachments/up.sql
new file mode 100644
index 000000000..f741fc36f
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1738758597255_create_table_public_attachments/up.sql
@@ -0,0 +1 @@
+CREATE TABLE "public"."attachments" ("task_id" uuid NOT NULL, "file_id" uuid NOT NULL, PRIMARY KEY ("task_id","file_id") , FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("file_id") REFERENCES "storage"."files"("id") ON UPDATE restrict ON DELETE restrict);
diff --git a/examples/guides/backend/nhost/migrations/default/1748613758372_create_ninja_turtles_table/down.sql b/examples/guides/backend/nhost/migrations/default/1748613758372_create_ninja_turtles_table/down.sql
new file mode 100644
index 000000000..aedd61eba
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1748613758372_create_ninja_turtles_table/down.sql
@@ -0,0 +1 @@
+DROP TABLE public.ninja_turtles;
diff --git a/examples/guides/backend/nhost/migrations/default/1748613758372_create_ninja_turtles_table/up.sql b/examples/guides/backend/nhost/migrations/default/1748613758372_create_ninja_turtles_table/up.sql
new file mode 100644
index 000000000..f8413ae87
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1748613758372_create_ninja_turtles_table/up.sql
@@ -0,0 +1,6 @@
+CREATE TABLE public.ninja_turtles (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
+ updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
+ name TEXT NOT NULL
+);
diff --git a/examples/guides/backend/nhost/migrations/default/1748613770768_create_comments_table/down.sql b/examples/guides/backend/nhost/migrations/default/1748613770768_create_comments_table/down.sql
new file mode 100644
index 000000000..f1fc85baf
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1748613770768_create_comments_table/down.sql
@@ -0,0 +1 @@
+DROP TABLE public.comments;
diff --git a/examples/guides/backend/nhost/migrations/default/1748613770768_create_comments_table/up.sql b/examples/guides/backend/nhost/migrations/default/1748613770768_create_comments_table/up.sql
new file mode 100644
index 000000000..ce37431e0
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1748613770768_create_comments_table/up.sql
@@ -0,0 +1,8 @@
+CREATE TABLE public.comments (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
+ updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
+ ninja_turtle_id UUID NOT NULL REFERENCES public.ninja_turtles(id) ON DELETE CASCADE,
+ comment TEXT NOT NULL,
+ user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE
+);
diff --git a/examples/guides/backend/nhost/migrations/default/1748615455986_insert_ninja_turtles/down.sql b/examples/guides/backend/nhost/migrations/default/1748615455986_insert_ninja_turtles/down.sql
new file mode 100644
index 000000000..ea7e42bd8
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1748615455986_insert_ninja_turtles/down.sql
@@ -0,0 +1 @@
+DELETE FROM public.ninja_turtles WHERE name IN ('Leonardo', 'Raphael', 'Donatello', 'Michelangelo');
diff --git a/examples/guides/backend/nhost/migrations/default/1748615455986_insert_ninja_turtles/up.sql b/examples/guides/backend/nhost/migrations/default/1748615455986_insert_ninja_turtles/up.sql
new file mode 100644
index 000000000..93c8229b8
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1748615455986_insert_ninja_turtles/up.sql
@@ -0,0 +1,5 @@
+INSERT INTO public.ninja_turtles (id, name, created_at, updated_at) VALUES
+(gen_random_uuid(), 'Leonardo', NOW(), NOW()),
+(gen_random_uuid(), 'Raphael', NOW(), NOW()),
+(gen_random_uuid(), 'Donatello', NOW(), NOW()),
+(gen_random_uuid(), 'Michelangelo', NOW(), NOW());
diff --git a/examples/guides/backend/nhost/migrations/default/1748615530431_add_description_to_ninja_turtles/down.sql b/examples/guides/backend/nhost/migrations/default/1748615530431_add_description_to_ninja_turtles/down.sql
new file mode 100644
index 000000000..cf8138afe
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1748615530431_add_description_to_ninja_turtles/down.sql
@@ -0,0 +1 @@
+ALTER TABLE public.ninja_turtles DROP COLUMN description;
diff --git a/examples/guides/backend/nhost/migrations/default/1748615530431_add_description_to_ninja_turtles/up.sql b/examples/guides/backend/nhost/migrations/default/1748615530431_add_description_to_ninja_turtles/up.sql
new file mode 100644
index 000000000..45397c9d7
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1748615530431_add_description_to_ninja_turtles/up.sql
@@ -0,0 +1,5 @@
+ALTER TABLE public.ninja_turtles ADD COLUMN description TEXT;
+UPDATE public.ninja_turtles SET description = 'Leader of the team who wears a blue mask and uses twin katana blades. Known for his strategic mind and unwavering discipline.' WHERE name = 'Leonardo';
+UPDATE public.ninja_turtles SET description = 'Hot-headed member who wears a red mask and fights with twin sai. Known for his strength, aggression, and intense loyalty to his family.' WHERE name = 'Raphael';
+UPDATE public.ninja_turtles SET description = 'Technical genius who wears a purple mask and wields a bo staff. Known for his intelligence, inventions, and problem-solving abilities.' WHERE name = 'Donatello';
+UPDATE public.ninja_turtles SET description = 'Fun-loving member who wears an orange mask and uses nunchaku. Known for his optimism, sense of humor, and love for pizza.' WHERE name = 'Michelangelo';
diff --git a/examples/guides/backend/nhost/migrations/default/1751454848182_create_movies_table_with_data/down.sql b/examples/guides/backend/nhost/migrations/default/1751454848182_create_movies_table_with_data/down.sql
new file mode 100644
index 000000000..eac446a95
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1751454848182_create_movies_table_with_data/down.sql
@@ -0,0 +1 @@
+DROP TABLE IF EXISTS movies CASCADE;
diff --git a/examples/guides/backend/nhost/migrations/default/1751454848182_create_movies_table_with_data/up.sql b/examples/guides/backend/nhost/migrations/default/1751454848182_create_movies_table_with_data/up.sql
new file mode 100644
index 000000000..dbac2379a
--- /dev/null
+++ b/examples/guides/backend/nhost/migrations/default/1751454848182_create_movies_table_with_data/up.sql
@@ -0,0 +1,2 @@
+CREATE TABLE movies (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL, director TEXT, release_year INTEGER, genre TEXT, rating DECIMAL(3,1), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW());
+INSERT INTO movies (id, title, director, release_year, genre, rating) VALUES ('3d67a6d0-bfb5-444a-9152-aea543ebd171', 'The Matrix', 'Lana Wachowski, Lilly Wachowski', 1999, 'Sci-Fi', 8.7), ('90f374db-16c1-4db5-ba55-643bf38953d3', 'Inception', 'Christopher Nolan', 2010, 'Sci-Fi', 8.8), ('900fa76c-fc79-470d-817b-4dc4412a79e8', 'The Godfather', 'Francis Ford Coppola', 1972, 'Crime', 9.2), ('2867cadd-2904-482f-b43c-f77ce8412a93', 'Pulp Fiction', 'Quentin Tarantino', 1994, 'Crime', 8.9), ('8c06c70a-872e-49a7-8770-29355dcd05c6', 'The Dark Knight', 'Christopher Nolan', 2008, 'Action', 9.0);
diff --git a/examples/guides/backend/nhost/nhost.toml b/examples/guides/backend/nhost/nhost.toml
new file mode 100644
index 000000000..ff40c4eb1
--- /dev/null
+++ b/examples/guides/backend/nhost/nhost.toml
@@ -0,0 +1,203 @@
+[global]
+
+[hasura]
+version = 'v2.46.0-ce'
+adminSecret = '{{ secrets.HASURA_GRAPHQL_ADMIN_SECRET }}'
+webhookSecret = '{{ secrets.NHOST_WEBHOOK_SECRET }}'
+
+[[hasura.jwtSecrets]]
+type = 'HS256'
+key = '{{ secrets.HASURA_GRAPHQL_JWT_SECRET }}'
+
+[hasura.settings]
+corsDomain = ['*']
+devMode = true
+enableAllowList = false
+enableConsole = true
+enableRemoteSchemaPermissions = false
+enabledAPIs = ['metadata', 'graphql', 'pgdump', 'config']
+inferFunctionPermissions = true
+liveQueriesMultiplexedRefetchInterval = 1000
+stringifyNumericTypes = false
+
+[hasura.logs]
+level = 'warn'
+
+[hasura.events]
+httpPoolSize = 100
+
+[functions]
+[functions.node]
+version = 20
+
+[auth]
+version = '0.41.1'
+
+[auth.elevatedPrivileges]
+mode = 'disabled'
+
+[auth.redirections]
+clientUrl = 'http://localhost:3000'
+allowedUrls = ['http://localhost:5173', 'exp://192.168.1.103:8081']
+
+[auth.signUp]
+enabled = true
+disableNewUsers = false
+
+[auth.user]
+[auth.user.roles]
+default = 'user'
+allowed = ['user', 'me']
+
+[auth.user.locale]
+default = 'en'
+allowed = ['en']
+
+[auth.user.gravatar]
+enabled = true
+default = 'blank'
+rating = 'g'
+
+[auth.user.email]
+
+[auth.user.emailDomains]
+
+[auth.session]
+[auth.session.accessToken]
+expiresIn = 65
+
+[auth.session.refreshToken]
+expiresIn = 2592000
+
+[auth.method]
+[auth.method.anonymous]
+enabled = false
+
+[auth.method.emailPasswordless]
+enabled = true
+
+[auth.method.otp]
+[auth.method.otp.email]
+enabled = false
+
+[auth.method.emailPassword]
+hibpEnabled = false
+emailVerificationRequired = false
+passwordMinLength = 9
+
+[auth.method.smsPasswordless]
+enabled = false
+
+[auth.method.oauth]
+[auth.method.oauth.apple]
+enabled = true
+teamId = '{{ secrets.APPLE_TEAM_ID }}'
+clientId = '{{ secrets.APPLE_CLIENT_ID }}'
+audience = '{{ secrets.APPLE_AUDIENCE }}'
+keyId = '{{ secrets.APPLE_KEY_ID }}'
+privateKey = '{{ secrets.APPLE_PRIVATE_KEY }}'
+
+[auth.method.oauth.azuread]
+tenant = 'common'
+enabled = false
+
+[auth.method.oauth.bitbucket]
+enabled = false
+
+[auth.method.oauth.discord]
+enabled = false
+
+[auth.method.oauth.facebook]
+enabled = false
+
+[auth.method.oauth.github]
+enabled = true
+clientId = '{{ secrets.GITHUB_CLIENT_ID }}'
+clientSecret = '{{ secrets.GITHUB_CLIENT_SECRET }}'
+
+[auth.method.oauth.gitlab]
+enabled = false
+
+[auth.method.oauth.google]
+enabled = false
+
+[auth.method.oauth.linkedin]
+enabled = false
+
+[auth.method.oauth.spotify]
+enabled = false
+
+[auth.method.oauth.strava]
+enabled = false
+
+[auth.method.oauth.twitch]
+enabled = false
+
+[auth.method.oauth.twitter]
+enabled = false
+
+[auth.method.oauth.windowslive]
+enabled = false
+
+[auth.method.oauth.workos]
+enabled = false
+
+[auth.method.webauthn]
+enabled = true
+
+[auth.method.webauthn.relyingParty]
+id = 'localhost'
+name = 'nhost-sdk-experiment'
+origins = ['http://localhost:3000', 'http://localhost:5173']
+
+[auth.method.webauthn.attestation]
+timeout = 60000
+
+[auth.totp]
+enabled = true
+issuer = 'new-sdk'
+
+[auth.misc]
+concealErrors = false
+
+[auth.rateLimit]
+[auth.rateLimit.emails]
+limit = 10
+interval = '1h'
+
+[auth.rateLimit.sms]
+limit = 100
+interval = '1h'
+
+[auth.rateLimit.bruteForce]
+limit = 100
+interval = '5m'
+
+[auth.rateLimit.signups]
+limit = 100
+interval = '5m'
+
+[auth.rateLimit.global]
+limit = 1000
+interval = '1m'
+
+[postgres]
+version = '17.5-20250728-1'
+
+[postgres.resources]
+[postgres.resources.storage]
+capacity = 1
+
+[provider]
+
+[storage]
+version = '0.8.0-beta3'
+
+[observability]
+[observability.grafana]
+adminPassword = '{{ secrets.GRAFANA_ADMIN_PASSWORD }}'
+
+[observability.grafana.alerting]
+enabled = false
+
+[observability.grafana.contacts]
diff --git a/examples/tutorials/Makefile b/examples/tutorials/Makefile
new file mode 100644
index 000000000..78a50a417
--- /dev/null
+++ b/examples/tutorials/Makefile
@@ -0,0 +1,17 @@
+ROOT_DIR?=$(abspath ../..)
+include $(ROOT_DIR)/build/makefiles/general.makefile
+
+
+.PHONY: _dev-env-up
+_dev-env-up:
+ @echo "Nothing to do"
+
+
+.PHONY: _dev-env-down
+_dev-env-down:
+ @echo "Nothing to do"
+
+
+.PHONY: _dev-env-build
+_dev-env-build:
+ @echo "Nothing to do"
diff --git a/examples/tutorials/backend/.gitignore b/examples/tutorials/backend/.gitignore
new file mode 100644
index 000000000..5d1bff8da
--- /dev/null
+++ b/examples/tutorials/backend/.gitignore
@@ -0,0 +1,2 @@
+.nhost
+.secrets
diff --git a/examples/tutorials/backend/.secrets.example b/examples/tutorials/backend/.secrets.example
new file mode 100644
index 000000000..591510cda
--- /dev/null
+++ b/examples/tutorials/backend/.secrets.example
@@ -0,0 +1,10 @@
+GRAFANA_ADMIN_PASSWORD = 'grafana-admin-password'
+HASURA_GRAPHQL_ADMIN_SECRET = 'nhost-admin-secret'
+HASURA_GRAPHQL_JWT_SECRET = '0f987876650b4a085e64594fae9219e7781b17506bec02489ad061fba8cb22db'
+NHOST_WEBHOOK_SECRET = 'nhost-webhook-secret'
+
+APPLE_AUDIENCE = 'host.exp.Exponent'
+APPLE_CLIENT_ID = 'host.exp.Exponent'
+APPLE_KEY_ID = 'fakeKeyId'
+APPLE_TEAM_ID = 'fakeTeamId'
+APPLE_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQglHTWHjauHnKCxjEP\nBpMYsTDI2cihQi4tAYHTthj+FF+gCgYIKoZIzj0DAQehRANCAAR30Hs8vTbED10z\nQx2m4sJu+lE/ZJsRvDkqLqYF8uh1Tb1g7/KKr8Y7qkK3DmCg72bCyirEq4NVUi2r\nM/6TYMpw\n-----END PRIVATE KEY-----"
diff --git a/examples/tutorials/backend/env-up.sh b/examples/tutorials/backend/env-up.sh
new file mode 100755
index 000000000..6ff54ef84
--- /dev/null
+++ b/examples/tutorials/backend/env-up.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# if .secrets file doesn't exist, cp .secrets.example .secrets
+if [ ! -f .secrets ]; then
+ cp .secrets.example .secrets
+fi
+
+nhost up
diff --git a/examples/tutorials/backend/functions/package-lock.json b/examples/tutorials/backend/functions/package-lock.json
new file mode 100644
index 000000000..b3a60ffe0
--- /dev/null
+++ b/examples/tutorials/backend/functions/package-lock.json
@@ -0,0 +1,14 @@
+{
+ "name": "functions",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "functions",
+ "version": "1.0.0",
+ "license": "ISC",
+ "devDependencies": {}
+ }
+ }
+}
diff --git a/examples/tutorials/backend/functions/package.json b/examples/tutorials/backend/functions/package.json
new file mode 100644
index 000000000..98ef9bba4
--- /dev/null
+++ b/examples/tutorials/backend/functions/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "functions",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "devDependencies": {},
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/examples/tutorials/backend/functions/tsconfig.json b/examples/tutorials/backend/functions/tsconfig.json
new file mode 100644
index 000000000..0f4467244
--- /dev/null
+++ b/examples/tutorials/backend/functions/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "skipLibCheck": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "strictNullChecks": false
+ }
+}
diff --git a/examples/tutorials/backend/nhost/config.yaml b/examples/tutorials/backend/nhost/config.yaml
new file mode 100644
index 000000000..0a70affa4
--- /dev/null
+++ b/examples/tutorials/backend/nhost/config.yaml
@@ -0,0 +1 @@
+version: 3
diff --git a/examples/tutorials/backend/nhost/emails/bg/email-confirm-change/body.html b/examples/tutorials/backend/nhost/emails/bg/email-confirm-change/body.html
new file mode 100644
index 000000000..437face7d
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Потвърдете смяната на вашия имейл
+ Използвайте посочения линк, за да повърдите смяната на имейл:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/bg/email-confirm-change/subject.txt b/examples/tutorials/backend/nhost/emails/bg/email-confirm-change/subject.txt
new file mode 100644
index 000000000..790147e6c
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Потвърждение за смяна на имейл
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/bg/email-verify/body.html b/examples/tutorials/backend/nhost/emails/bg/email-verify/body.html
new file mode 100644
index 000000000..9a9604f40
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Потвърдете вашия имейл
+ Използвайте посочения линк, за да потвърдите вашия имейл:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/bg/email-verify/subject.txt b/examples/tutorials/backend/nhost/emails/bg/email-verify/subject.txt
new file mode 100644
index 000000000..f0109ef96
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/email-verify/subject.txt
@@ -0,0 +1 @@
+Потвърждаване на имейл
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/bg/password-reset/body.html b/examples/tutorials/backend/nhost/emails/bg/password-reset/body.html
new file mode 100644
index 000000000..d69b734de
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Смяна на парола
+ Използвайте посочения линк, за да смените вашата парола:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/bg/password-reset/subject.txt b/examples/tutorials/backend/nhost/emails/bg/password-reset/subject.txt
new file mode 100644
index 000000000..11b95a290
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/password-reset/subject.txt
@@ -0,0 +1 @@
+Смяна на парола
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/bg/signin-otp/body.html b/examples/tutorials/backend/nhost/emails/bg/signin-otp/body.html
new file mode 100644
index 000000000..2fab88cba
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ За да влезете в ${redirectTo}, моля, използвайте следната еднократна парола:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/bg/signin-otp/subject.txt b/examples/tutorials/backend/nhost/emails/bg/signin-otp/subject.txt
new file mode 100644
index 000000000..ec9e5f7e3
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/signin-otp/subject.txt
@@ -0,0 +1 @@
+Еднократна парола за ${redirectTo}
diff --git a/examples/tutorials/backend/nhost/emails/bg/signin-passwordless-sms/body.txt b/examples/tutorials/backend/nhost/emails/bg/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..ccb34253e
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Вашият код е ${code}.
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/bg/signin-passwordless/body.html b/examples/tutorials/backend/nhost/emails/bg/signin-passwordless/body.html
new file mode 100644
index 000000000..8d4b40cd2
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Магически линк за вход
+ Използвайте посочения линк за защитен и бърз вход:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/bg/signin-passwordless/subject.txt b/examples/tutorials/backend/nhost/emails/bg/signin-passwordless/subject.txt
new file mode 100644
index 000000000..945efccb7
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/bg/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Магически линк за вход
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/cs/email-confirm-change/body.html b/examples/tutorials/backend/nhost/emails/cs/email-confirm-change/body.html
new file mode 100644
index 000000000..b2b4da445
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Potvrzení změny emailové adresy
+ Použijte tento odkaz k potvrzení změny emailové adresy:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/cs/email-confirm-change/subject.txt b/examples/tutorials/backend/nhost/emails/cs/email-confirm-change/subject.txt
new file mode 100644
index 000000000..52b99137e
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Změna vaší emailové adresy
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/cs/email-verify/body.html b/examples/tutorials/backend/nhost/emails/cs/email-verify/body.html
new file mode 100644
index 000000000..22671ba22
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Ověření emailové adresy
+ Použijte tento odkaz k ověření vaší emailové adresy:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/cs/email-verify/subject.txt b/examples/tutorials/backend/nhost/emails/cs/email-verify/subject.txt
new file mode 100644
index 000000000..8e3eabacf
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/email-verify/subject.txt
@@ -0,0 +1 @@
+Ověření vaší emailové adresy
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/cs/password-reset/body.html b/examples/tutorials/backend/nhost/emails/cs/password-reset/body.html
new file mode 100644
index 000000000..3a3fa1d97
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Obnova hesla
+ Použijte tento odkaz k obnovení vašeho hesla:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/cs/password-reset/subject.txt b/examples/tutorials/backend/nhost/emails/cs/password-reset/subject.txt
new file mode 100644
index 000000000..7b8fba50b
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/password-reset/subject.txt
@@ -0,0 +1 @@
+Obnova hesla
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/cs/signin-otp/body.html b/examples/tutorials/backend/nhost/emails/cs/signin-otp/body.html
new file mode 100644
index 000000000..20115716a
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ Pro přihlášení do ${redirectTo}, prosím, použijte následující jednorázové heslo:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/cs/signin-otp/subject.txt b/examples/tutorials/backend/nhost/emails/cs/signin-otp/subject.txt
new file mode 100644
index 000000000..e2cd8c1da
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/signin-otp/subject.txt
@@ -0,0 +1 @@
+Jednorázové heslo pro ${redirectTo}
diff --git a/examples/tutorials/backend/nhost/emails/cs/signin-passwordless-sms/body.txt b/examples/tutorials/backend/nhost/emails/cs/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..df330b769
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Váš kód je ${code}.
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/cs/signin-passwordless/body.html b/examples/tutorials/backend/nhost/emails/cs/signin-passwordless/body.html
new file mode 100644
index 000000000..f6f019747
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Magický odkaz
+ Použijte tento odkaz k bezpečnému přihlášení:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/cs/signin-passwordless/subject.txt b/examples/tutorials/backend/nhost/emails/cs/signin-passwordless/subject.txt
new file mode 100644
index 000000000..8d9a66a25
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/cs/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Bezpečný odkaz k přihlášení
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/en/email-confirm-change/body.html b/examples/tutorials/backend/nhost/emails/en/email-confirm-change/body.html
new file mode 100644
index 000000000..68034c566
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Confirm Email Change
+ Use this link to confirm changing email:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/en/email-confirm-change/subject.txt b/examples/tutorials/backend/nhost/emails/en/email-confirm-change/subject.txt
new file mode 100644
index 000000000..1711dad88
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Change your email address
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/en/email-verify/body.html b/examples/tutorials/backend/nhost/emails/en/email-verify/body.html
new file mode 100644
index 000000000..e98e013ab
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Verify Email
+ Use this link to verify your email:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/en/email-verify/subject.txt b/examples/tutorials/backend/nhost/emails/en/email-verify/subject.txt
new file mode 100644
index 000000000..ef490a8bc
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/email-verify/subject.txt
@@ -0,0 +1 @@
+Verify your email
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/en/password-reset/body.html b/examples/tutorials/backend/nhost/emails/en/password-reset/body.html
new file mode 100644
index 000000000..bd3b5b483
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Reset Password
+ Use this link to reset your password:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/en/password-reset/subject.txt b/examples/tutorials/backend/nhost/emails/en/password-reset/subject.txt
new file mode 100644
index 000000000..e21496754
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/password-reset/subject.txt
@@ -0,0 +1 @@
+Reset your password
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/en/signin-otp/body.html b/examples/tutorials/backend/nhost/emails/en/signin-otp/body.html
new file mode 100644
index 000000000..95acc397e
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ To sign in to ${redirectTo}, please, use the following one-time password:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/en/signin-otp/subject.txt b/examples/tutorials/backend/nhost/emails/en/signin-otp/subject.txt
new file mode 100644
index 000000000..3da3fca7f
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/signin-otp/subject.txt
@@ -0,0 +1 @@
+One-time password for ${redirectTo}
diff --git a/examples/tutorials/backend/nhost/emails/en/signin-passwordless-sms/body.txt b/examples/tutorials/backend/nhost/emails/en/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..f03ec5b88
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Your code is ${code}.
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/en/signin-passwordless/body.html b/examples/tutorials/backend/nhost/emails/en/signin-passwordless/body.html
new file mode 100644
index 000000000..e8301b5ad
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Magic Link
+ Use this link to securely sign in:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/en/signin-passwordless/subject.txt b/examples/tutorials/backend/nhost/emails/en/signin-passwordless/subject.txt
new file mode 100644
index 000000000..8d0ee0119
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/en/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Secure sign-in link
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/es/email-confirm-change/body.html b/examples/tutorials/backend/nhost/emails/es/email-confirm-change/body.html
new file mode 100644
index 000000000..5cb7388be
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Confirmar cambio de correo electrónico
+ Utiliza el siguiente enlace para confirmar el cambio de correo:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/es/email-confirm-change/subject.txt b/examples/tutorials/backend/nhost/emails/es/email-confirm-change/subject.txt
new file mode 100644
index 000000000..6a7cf4069
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Cambiar dirección de correo electrónico
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/es/email-verify/body.html b/examples/tutorials/backend/nhost/emails/es/email-verify/body.html
new file mode 100644
index 000000000..b5b93b813
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Verificar correo electrónico
+ Utilza el siguiente enlace para verificar tu correo:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/es/email-verify/subject.txt b/examples/tutorials/backend/nhost/emails/es/email-verify/subject.txt
new file mode 100644
index 000000000..cf15cb278
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/email-verify/subject.txt
@@ -0,0 +1 @@
+Verifica tu correo electrónico
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/es/password-reset/body.html b/examples/tutorials/backend/nhost/emails/es/password-reset/body.html
new file mode 100644
index 000000000..c02bbc800
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Recuperar contraseña
+ Utiliza el siguiente enlace para recuperar tu contraseña:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/es/password-reset/subject.txt b/examples/tutorials/backend/nhost/emails/es/password-reset/subject.txt
new file mode 100644
index 000000000..417d1c730
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/password-reset/subject.txt
@@ -0,0 +1 @@
+Recuperar contraseña
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/es/signin-otp/body.html b/examples/tutorials/backend/nhost/emails/es/signin-otp/body.html
new file mode 100644
index 000000000..3effba1cd
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ Para iniciar sesión en ${redirectTo}, por favor, utilice la siguiente contraseña de un solo uso:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/es/signin-otp/subject.txt b/examples/tutorials/backend/nhost/emails/es/signin-otp/subject.txt
new file mode 100644
index 000000000..f041fe0c5
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/signin-otp/subject.txt
@@ -0,0 +1 @@
+Contraseña de un solo uso para ${redirectTo}
diff --git a/examples/tutorials/backend/nhost/emails/es/signin-passwordless-sms/body.txt b/examples/tutorials/backend/nhost/emails/es/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..c74950dd2
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Tu código es ${code}.
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/es/signin-passwordless/body.html b/examples/tutorials/backend/nhost/emails/es/signin-passwordless/body.html
new file mode 100644
index 000000000..c1b67a841
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Enlace mágico
+ Utiliza este enlace para iniciar sesión de forma segura:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/es/signin-passwordless/subject.txt b/examples/tutorials/backend/nhost/emails/es/signin-passwordless/subject.txt
new file mode 100644
index 000000000..cbd5ab19f
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/es/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Enlace de acceso seguro
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/fr/email-confirm-change/body.html b/examples/tutorials/backend/nhost/emails/fr/email-confirm-change/body.html
new file mode 100644
index 000000000..c4ba00737
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/email-confirm-change/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Confirmer changement de courriel
+ Utilisez ce lien pour confirmer le changement de courriel:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/fr/email-confirm-change/subject.txt b/examples/tutorials/backend/nhost/emails/fr/email-confirm-change/subject.txt
new file mode 100644
index 000000000..1e12b4b4d
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/email-confirm-change/subject.txt
@@ -0,0 +1 @@
+Changez votre adresse courriel
diff --git a/examples/tutorials/backend/nhost/emails/fr/email-verify/body.html b/examples/tutorials/backend/nhost/emails/fr/email-verify/body.html
new file mode 100644
index 000000000..951c7c7fe
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/email-verify/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Vérifiez votre courriel
+ Utilisez ce lien pour vérifier votre courriel:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/fr/email-verify/subject.txt b/examples/tutorials/backend/nhost/emails/fr/email-verify/subject.txt
new file mode 100644
index 000000000..540cc1800
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/email-verify/subject.txt
@@ -0,0 +1 @@
+Vérifier votre courriel
diff --git a/examples/tutorials/backend/nhost/emails/fr/password-reset/body.html b/examples/tutorials/backend/nhost/emails/fr/password-reset/body.html
new file mode 100644
index 000000000..8dc4da721
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/password-reset/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Réinitialiser votre mot de passe
+ Utilisez ce lien pour réinitialiser votre mot de passe:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/fr/password-reset/subject.txt b/examples/tutorials/backend/nhost/emails/fr/password-reset/subject.txt
new file mode 100644
index 000000000..5c2caa35b
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/password-reset/subject.txt
@@ -0,0 +1 @@
+Réinitialiser votre mot de passe
diff --git a/examples/tutorials/backend/nhost/emails/fr/signin-otp/body.html b/examples/tutorials/backend/nhost/emails/fr/signin-otp/body.html
new file mode 100644
index 000000000..773e33b83
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/signin-otp/body.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ One-time Password
+ Pour vous connecter à ${redirectTo}, veuillez utiliser le mot de passe à usage unique suivant :
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/fr/signin-otp/subject.txt b/examples/tutorials/backend/nhost/emails/fr/signin-otp/subject.txt
new file mode 100644
index 000000000..f4156ed0e
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/signin-otp/subject.txt
@@ -0,0 +1 @@
+Mot de passe à usage unique pour ${redirectTo}
diff --git a/examples/tutorials/backend/nhost/emails/fr/signin-passwordless-sms/body.txt b/examples/tutorials/backend/nhost/emails/fr/signin-passwordless-sms/body.txt
new file mode 100644
index 000000000..72d6ab249
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/signin-passwordless-sms/body.txt
@@ -0,0 +1 @@
+Votre code est ${code}.
\ No newline at end of file
diff --git a/examples/tutorials/backend/nhost/emails/fr/signin-passwordless/body.html b/examples/tutorials/backend/nhost/emails/fr/signin-passwordless/body.html
new file mode 100644
index 000000000..a45f217bc
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/signin-passwordless/body.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+ Lien magique
+ Utilisez ce lien pour vous connecter de façon sécurisée:
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/backend/nhost/emails/fr/signin-passwordless/subject.txt b/examples/tutorials/backend/nhost/emails/fr/signin-passwordless/subject.txt
new file mode 100644
index 000000000..dc2d81026
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/fr/signin-passwordless/subject.txt
@@ -0,0 +1 @@
+Lien de connexion sécurisé
diff --git a/examples/demos/backend/nhost/emails/generator/README.md b/examples/tutorials/backend/nhost/emails/generator/README.md
similarity index 99%
rename from examples/demos/backend/nhost/emails/generator/README.md
rename to examples/tutorials/backend/nhost/emails/generator/README.md
index 77c41f935..b01010786 100644
--- a/examples/demos/backend/nhost/emails/generator/README.md
+++ b/examples/tutorials/backend/nhost/emails/generator/README.md
@@ -20,4 +20,4 @@ For example to generate emails for the German locale (de) you run:
```sh
pnpm generate:emails de
-```
+```
\ No newline at end of file
diff --git a/examples/demos/backend/nhost/emails/generator/email-confirm-change.tsx b/examples/tutorials/backend/nhost/emails/generator/email-confirm-change.tsx
similarity index 69%
rename from examples/demos/backend/nhost/emails/generator/email-confirm-change.tsx
rename to examples/tutorials/backend/nhost/emails/generator/email-confirm-change.tsx
index 6b89e7d77..3c2092982 100644
--- a/examples/demos/backend/nhost/emails/generator/email-confirm-change.tsx
+++ b/examples/tutorials/backend/nhost/emails/generator/email-confirm-change.tsx
@@ -12,8 +12,8 @@ import {
Row,
Section,
Text,
-} from "@react-email/components";
-import * as React from "react";
+} from '@react-email/components';
+import * as React from 'react';
const logo = {
borderRadius: 0,
@@ -22,64 +22,64 @@ const logo = {
};
const main = {
- backgroundColor: "#f5f5f5",
+ backgroundColor: '#f5f5f5',
fontFamily:
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
};
const container = {
- margin: "20px auto 0 auto",
- padding: "20px",
- maxWidth: "560px",
- backgroundColor: "#ffffff",
+ margin: '20px auto 0 auto',
+ padding: '20px',
+ maxWidth: '560px',
+ backgroundColor: '#ffffff',
borderRadius: 8,
- border: "1px solid #ececec",
+ border: '1px solid #ececec',
};
const heading = {
- fontSize: "24px",
- letterSpacing: "-0.5px",
- lineHeight: "1.3",
- fontWeight: "400",
- color: "#484848",
+ fontSize: '24px',
+ letterSpacing: '-0.5px',
+ lineHeight: '1.3',
+ fontWeight: '400',
+ color: '#484848',
marginTop: 0,
};
const paragraph = {
- margin: "0 0 10px",
- fontSize: "15px",
- lineHeight: "1.4",
- color: "#3c4149",
+ margin: '0 0 10px',
+ fontSize: '15px',
+ lineHeight: '1.4',
+ color: '#3c4149',
};
const buttonContainer = {
- padding: "10px 0 0px",
+ padding: '10px 0 0px',
};
const button = {
- backgroundColor: "#0052CD",
- borderRadius: "3px",
- fontWeight: "600",
- color: "#fff",
- fontSize: "15px",
- textDecoration: "none",
- textAlign: "center" as const,
- display: "block",
- padding: "11px 23px",
+ backgroundColor: '#0052CD',
+ borderRadius: '3px',
+ fontWeight: '600',
+ color: '#fff',
+ fontSize: '15px',
+ textDecoration: 'none',
+ textAlign: 'center' as const,
+ display: 'block',
+ padding: '11px 23px',
};
const reportLink = {
- fontSize: "14px",
- color: "#b4becc",
+ fontSize: '14px',
+ color: '#b4becc',
};
const hr = {
- borderColor: "#dfe1e4",
- margin: "20px 0 20px",
+ borderColor: '#dfe1e4',
+ margin: '20px 0 20px',
};
const logoColumn = {
- width: "30px",
+ width: '30px',
};
const linkColumn = {
diff --git a/examples/demos/backend/nhost/emails/generator/email-verify.tsx b/examples/tutorials/backend/nhost/emails/generator/email-verify.tsx
similarity index 69%
rename from examples/demos/backend/nhost/emails/generator/email-verify.tsx
rename to examples/tutorials/backend/nhost/emails/generator/email-verify.tsx
index 185dff56d..e83724526 100644
--- a/examples/demos/backend/nhost/emails/generator/email-verify.tsx
+++ b/examples/tutorials/backend/nhost/emails/generator/email-verify.tsx
@@ -12,8 +12,8 @@ import {
Row,
Section,
Text,
-} from "@react-email/components";
-import * as React from "react";
+} from '@react-email/components';
+import * as React from 'react';
const logo = {
borderRadius: 0,
@@ -22,64 +22,64 @@ const logo = {
};
const main = {
- backgroundColor: "#f5f5f5",
+ backgroundColor: '#f5f5f5',
fontFamily:
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
};
const container = {
- margin: "20px auto 0 auto",
- padding: "20px",
- maxWidth: "560px",
- backgroundColor: "#ffffff",
+ margin: '20px auto 0 auto',
+ padding: '20px',
+ maxWidth: '560px',
+ backgroundColor: '#ffffff',
borderRadius: 8,
- border: "1px solid #ececec",
+ border: '1px solid #ececec',
};
const heading = {
- fontSize: "24px",
- letterSpacing: "-0.5px",
- lineHeight: "1.3",
- fontWeight: "400",
- color: "#484848",
+ fontSize: '24px',
+ letterSpacing: '-0.5px',
+ lineHeight: '1.3',
+ fontWeight: '400',
+ color: '#484848',
marginTop: 0,
};
const paragraph = {
- margin: "0 0 10px",
- fontSize: "15px",
- lineHeight: "1.4",
- color: "#3c4149",
+ margin: '0 0 10px',
+ fontSize: '15px',
+ lineHeight: '1.4',
+ color: '#3c4149',
};
const buttonContainer = {
- padding: "10px 0 0px",
+ padding: '10px 0 0px',
};
const button = {
- backgroundColor: "#0052CD",
- borderRadius: "3px",
- fontWeight: "600",
- color: "#fff",
- fontSize: "15px",
- textDecoration: "none",
- textAlign: "center" as const,
- display: "block",
- padding: "11px 23px",
+ backgroundColor: '#0052CD',
+ borderRadius: '3px',
+ fontWeight: '600',
+ color: '#fff',
+ fontSize: '15px',
+ textDecoration: 'none',
+ textAlign: 'center' as const,
+ display: 'block',
+ padding: '11px 23px',
};
const reportLink = {
- fontSize: "14px",
- color: "#b4becc",
+ fontSize: '14px',
+ color: '#b4becc',
};
const hr = {
- borderColor: "#dfe1e4",
- margin: "20px 0 20px",
+ borderColor: '#dfe1e4',
+ margin: '20px 0 20px',
};
const logoColumn = {
- width: "30px",
+ width: '30px',
};
const linkColumn = {
diff --git a/examples/demos/backend/nhost/emails/generator/password-reset.tsx b/examples/tutorials/backend/nhost/emails/generator/password-reset.tsx
similarity index 69%
rename from examples/demos/backend/nhost/emails/generator/password-reset.tsx
rename to examples/tutorials/backend/nhost/emails/generator/password-reset.tsx
index 3f4a758c1..370c8a79c 100644
--- a/examples/demos/backend/nhost/emails/generator/password-reset.tsx
+++ b/examples/tutorials/backend/nhost/emails/generator/password-reset.tsx
@@ -12,8 +12,8 @@ import {
Row,
Section,
Text,
-} from "@react-email/components";
-import * as React from "react";
+} from '@react-email/components';
+import * as React from 'react';
const logo = {
borderRadius: 0,
@@ -22,64 +22,64 @@ const logo = {
};
const main = {
- backgroundColor: "#f5f5f5",
+ backgroundColor: '#f5f5f5',
fontFamily:
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
};
const container = {
- margin: "20px auto 0 auto",
- padding: "20px",
- maxWidth: "560px",
- backgroundColor: "#ffffff",
+ margin: '20px auto 0 auto',
+ padding: '20px',
+ maxWidth: '560px',
+ backgroundColor: '#ffffff',
borderRadius: 8,
- border: "1px solid #ececec",
+ border: '1px solid #ececec',
};
const heading = {
- fontSize: "24px",
- letterSpacing: "-0.5px",
- lineHeight: "1.3",
- fontWeight: "400",
- color: "#484848",
+ fontSize: '24px',
+ letterSpacing: '-0.5px',
+ lineHeight: '1.3',
+ fontWeight: '400',
+ color: '#484848',
marginTop: 0,
};
const paragraph = {
- margin: "0 0 10px",
- fontSize: "15px",
- lineHeight: "1.4",
- color: "#3c4149",
+ margin: '0 0 10px',
+ fontSize: '15px',
+ lineHeight: '1.4',
+ color: '#3c4149',
};
const buttonContainer = {
- padding: "10px 0 0px",
+ padding: '10px 0 0px',
};
const button = {
- backgroundColor: "#0052CD",
- borderRadius: "3px",
- fontWeight: "600",
- color: "#fff",
- fontSize: "15px",
- textDecoration: "none",
- textAlign: "center" as const,
- display: "block",
- padding: "11px 23px",
+ backgroundColor: '#0052CD',
+ borderRadius: '3px',
+ fontWeight: '600',
+ color: '#fff',
+ fontSize: '15px',
+ textDecoration: 'none',
+ textAlign: 'center' as const,
+ display: 'block',
+ padding: '11px 23px',
};
const reportLink = {
- fontSize: "14px",
- color: "#b4becc",
+ fontSize: '14px',
+ color: '#b4becc',
};
const hr = {
- borderColor: "#dfe1e4",
- margin: "20px 0 20px",
+ borderColor: '#dfe1e4',
+ margin: '20px 0 20px',
};
const logoColumn = {
- width: "30px",
+ width: '30px',
};
const linkColumn = {
diff --git a/examples/demos/backend/nhost/emails/generator/render-emails.ts b/examples/tutorials/backend/nhost/emails/generator/render-emails.ts
similarity index 57%
rename from examples/demos/backend/nhost/emails/generator/render-emails.ts
rename to examples/tutorials/backend/nhost/emails/generator/render-emails.ts
index 6fe1a8227..907d4ad25 100644
--- a/examples/demos/backend/nhost/emails/generator/render-emails.ts
+++ b/examples/tutorials/backend/nhost/emails/generator/render-emails.ts
@@ -1,54 +1,54 @@
-import * as fs from "fs";
-import * as path from "path";
-import prettier from "prettier";
-import { render } from "@react-email/components";
-import { EmailConfirmChange } from "./email-confirm-change";
-import { EmailVerify } from "./email-verify";
-import { PasswordReset } from "./password-reset";
-import { SignInPasswordless } from "./signin-passwordless";
-import { SignInOTP } from "./signin-otp";
+import * as fs from 'fs';
+import * as path from 'path';
+import prettier from 'prettier';
+import { render } from '@react-email/components';
+import { EmailConfirmChange } from './email-confirm-change';
+import { EmailVerify } from './email-verify';
+import { PasswordReset } from './password-reset';
+import { SignInPasswordless } from './signin-passwordless';
+import { SignInOTP } from './signin-otp';
function renderEmails(targetLocale: string) {
const emails = [
{
- name: "email-confirm-change",
+ name: 'email-confirm-change',
body: prettier.format(render(EmailConfirmChange()), {
- parser: "html",
+ parser: 'html',
printWidth: 500,
}),
- subject: "",
+ subject: '',
},
{
- name: "email-verify",
+ name: 'email-verify',
body: prettier.format(render(EmailVerify()), {
- parser: "html",
+ parser: 'html',
printWidth: 500,
}),
- subject: "",
+ subject: '',
},
{
- name: "password-reset",
+ name: 'password-reset',
body: prettier.format(render(PasswordReset()), {
- parser: "html",
+ parser: 'html',
printWidth: 500,
}),
- subject: "",
+ subject: '',
},
{
- name: "signin-passwordless",
+ name: 'signin-passwordless',
body: prettier.format(render(SignInPasswordless()), {
- parser: "html",
+ parser: 'html',
printWidth: 500,
}),
- subject: "",
+ subject: '',
},
{
- name: "signin-otp",
+ name: 'signin-otp',
body: prettier.format(render(SignInOTP()), {
- parser: "html",
+ parser: 'html',
printWidth: 500,
}),
- subject: "",
+ subject: '',
},
];
@@ -66,7 +66,7 @@ function renderEmails(targetLocale: string) {
fs.writeFileSync(`${targetFolder}/${email.name}/body.html`, email.body);
fs.writeFileSync(
`${targetFolder}/${email.name}/subject.txt`,
- email.subject,
+ email.subject
);
});
}
@@ -75,7 +75,7 @@ const args = process.argv.slice(2);
const locale = args[0];
if (!locale) {
- console.error("Please provide a locale for the emails.");
+ console.error('Please provide a locale for the emails.');
process.exit(1);
}
diff --git a/examples/demos/backend/nhost/emails/generator/signin-otp.tsx b/examples/tutorials/backend/nhost/emails/generator/signin-otp.tsx
similarity index 64%
rename from examples/demos/backend/nhost/emails/generator/signin-otp.tsx
rename to examples/tutorials/backend/nhost/emails/generator/signin-otp.tsx
index 280689360..be209ad61 100644
--- a/examples/demos/backend/nhost/emails/generator/signin-otp.tsx
+++ b/examples/tutorials/backend/nhost/emails/generator/signin-otp.tsx
@@ -11,8 +11,8 @@ import {
Row,
Section,
Text,
-} from "@react-email/components";
-import * as React from "react";
+} from '@react-email/components';
+import * as React from 'react';
const logo = {
borderRadius: 0,
@@ -21,52 +21,52 @@ const logo = {
};
const main = {
- backgroundColor: "#f5f5f5",
+ backgroundColor: '#f5f5f5',
fontFamily:
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
};
const container = {
- margin: "20px auto 0 auto",
- padding: "20px",
- maxWidth: "560px",
- backgroundColor: "#ffffff",
+ margin: '20px auto 0 auto',
+ padding: '20px',
+ maxWidth: '560px',
+ backgroundColor: '#ffffff',
borderRadius: 8,
- border: "1px solid #ececec",
+ border: '1px solid #ececec',
};
const heading = {
- fontSize: "24px",
- letterSpacing: "-0.5px",
- lineHeight: "1.3",
- fontWeight: "400",
- color: "#484848",
+ fontSize: '24px',
+ letterSpacing: '-0.5px',
+ lineHeight: '1.3',
+ fontWeight: '400',
+ color: '#484848',
marginTop: 0,
};
const paragraph = {
- margin: "0 0 10px",
- fontSize: "15px",
- lineHeight: "1.4",
- color: "#3c4149",
+ margin: '0 0 10px',
+ fontSize: '15px',
+ lineHeight: '1.4',
+ color: '#3c4149',
};
const buttonContainer = {
- padding: "10px 0 0px",
+ padding: '10px 0 0px',
};
const reportLink = {
- fontSize: "14px",
- color: "#b4becc",
+ fontSize: '14px',
+ color: '#b4becc',
};
const hr = {
- borderColor: "#dfe1e4",
- margin: "20px 0 20px",
+ borderColor: '#dfe1e4',
+ margin: '20px 0 20px',
};
const logoColumn = {
- width: "30px",
+ width: '30px',
};
const linkColumn = {
@@ -74,12 +74,12 @@ const linkColumn = {
};
const amazingText = {
- fontSize: "24px",
- lineHeight: "32px",
- margin: "16px 0",
- color: "#0052cd",
- fontWeight: "600",
-};
+ fontSize:'24px',
+ lineHeight:'32px',
+ margin:'16px 0',
+ color:'#0052cd',
+ fontWeight:'600',
+}
export function SignInOTP() {
const ticket = "${ticket}";
@@ -90,12 +90,11 @@ export function SignInOTP() {
One-time Password
-
- To signin to {redirectTo}, please, use the following one-time
- password:
-
+ To signin to {redirectTo}, please, use the following one-time password:
- {ticket}
+
+ {ticket}
+
diff --git a/examples/demos/backend/nhost/emails/generator/signin-passwordless.tsx b/examples/tutorials/backend/nhost/emails/generator/signin-passwordless.tsx
similarity index 69%
rename from examples/demos/backend/nhost/emails/generator/signin-passwordless.tsx
rename to examples/tutorials/backend/nhost/emails/generator/signin-passwordless.tsx
index 502a9ca41..c9a2315c4 100644
--- a/examples/demos/backend/nhost/emails/generator/signin-passwordless.tsx
+++ b/examples/tutorials/backend/nhost/emails/generator/signin-passwordless.tsx
@@ -12,8 +12,8 @@ import {
Row,
Section,
Text,
-} from "@react-email/components";
-import * as React from "react";
+} from '@react-email/components';
+import * as React from 'react';
const logo = {
borderRadius: 0,
@@ -22,64 +22,64 @@ const logo = {
};
const main = {
- backgroundColor: "#f5f5f5",
+ backgroundColor: '#f5f5f5',
fontFamily:
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
};
const container = {
- margin: "20px auto 0 auto",
- padding: "20px",
- maxWidth: "560px",
- backgroundColor: "#ffffff",
+ margin: '20px auto 0 auto',
+ padding: '20px',
+ maxWidth: '560px',
+ backgroundColor: '#ffffff',
borderRadius: 8,
- border: "1px solid #ececec",
+ border: '1px solid #ececec',
};
const heading = {
- fontSize: "24px",
- letterSpacing: "-0.5px",
- lineHeight: "1.3",
- fontWeight: "400",
- color: "#484848",
+ fontSize: '24px',
+ letterSpacing: '-0.5px',
+ lineHeight: '1.3',
+ fontWeight: '400',
+ color: '#484848',
marginTop: 0,
};
const paragraph = {
- margin: "0 0 10px",
- fontSize: "15px",
- lineHeight: "1.4",
- color: "#3c4149",
+ margin: '0 0 10px',
+ fontSize: '15px',
+ lineHeight: '1.4',
+ color: '#3c4149',
};
const buttonContainer = {
- padding: "10px 0 0px",
+ padding: '10px 0 0px',
};
const button = {
- backgroundColor: "#0052CD",
- borderRadius: "3px",
- fontWeight: "600",
- color: "#fff",
- fontSize: "15px",
- textDecoration: "none",
- textAlign: "center" as const,
- display: "block",
- padding: "11px 23px",
+ backgroundColor: '#0052CD',
+ borderRadius: '3px',
+ fontWeight: '600',
+ color: '#fff',
+ fontSize: '15px',
+ textDecoration: 'none',
+ textAlign: 'center' as const,
+ display: 'block',
+ padding: '11px 23px',
};
const reportLink = {
- fontSize: "14px",
- color: "#b4becc",
+ fontSize: '14px',
+ color: '#b4becc',
};
const hr = {
- borderColor: "#dfe1e4",
- margin: "20px 0 20px",
+ borderColor: '#dfe1e4',
+ margin: '20px 0 20px',
};
const logoColumn = {
- width: "30px",
+ width: '30px',
};
const linkColumn = {
diff --git a/examples/tutorials/backend/nhost/emails/test/email-verify/body.html b/examples/tutorials/backend/nhost/emails/test/email-verify/body.html
new file mode 100644
index 000000000..5c2dbc158
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/test/email-verify/body.html
@@ -0,0 +1,8 @@
+${link},
+${displayName},
+${email},
+${ticket},
+${redirectTo},
+${serverUrl},
+${clientUrl},
+${locale},
diff --git a/examples/tutorials/backend/nhost/emails/test/email-verify/subject.txt b/examples/tutorials/backend/nhost/emails/test/email-verify/subject.txt
new file mode 100644
index 000000000..1fe895365
--- /dev/null
+++ b/examples/tutorials/backend/nhost/emails/test/email-verify/subject.txt
@@ -0,0 +1 @@
+${link}, ${displayName}, ${email}, ${ticket}, ${redirectTo}, ${serverUrl}, ${clientUrl}, ${locale}
diff --git a/examples/tutorials/backend/nhost/metadata/actions.graphql b/examples/tutorials/backend/nhost/metadata/actions.graphql
new file mode 100644
index 000000000..e69de29bb
diff --git a/examples/tutorials/backend/nhost/metadata/actions.yaml b/examples/tutorials/backend/nhost/metadata/actions.yaml
new file mode 100644
index 000000000..1edb4c2ff
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/actions.yaml
@@ -0,0 +1,6 @@
+actions: []
+custom_types:
+ enums: []
+ input_objects: []
+ objects: []
+ scalars: []
diff --git a/examples/tutorials/backend/nhost/metadata/allow_list.yaml b/examples/tutorials/backend/nhost/metadata/allow_list.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/allow_list.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/tutorials/backend/nhost/metadata/api_limits.yaml b/examples/tutorials/backend/nhost/metadata/api_limits.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/api_limits.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/tutorials/backend/nhost/metadata/backend_configs.yaml b/examples/tutorials/backend/nhost/metadata/backend_configs.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/backend_configs.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/tutorials/backend/nhost/metadata/cron_triggers.yaml b/examples/tutorials/backend/nhost/metadata/cron_triggers.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/cron_triggers.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/tutorials/backend/nhost/metadata/databases/databases.yaml b/examples/tutorials/backend/nhost/metadata/databases/databases.yaml
new file mode 100644
index 000000000..65a11b202
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/databases.yaml
@@ -0,0 +1,14 @@
+- name: default
+ kind: postgres
+ configuration:
+ connection_info:
+ database_url:
+ from_env: HASURA_GRAPHQL_DATABASE_URL
+ isolation_level: read-committed
+ pool_settings:
+ connection_lifetime: 600
+ idle_timeout: 180
+ max_connections: 50
+ retries: 1
+ use_prepared_statements: true
+ tables: "!include default/tables/tables.yaml"
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_provider_requests.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_provider_requests.yaml
new file mode 100644
index 000000000..91ba1967d
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_provider_requests.yaml
@@ -0,0 +1,23 @@
+table:
+ name: provider_requests
+ schema: auth
+configuration:
+ column_config:
+ id:
+ custom_name: id
+ options:
+ custom_name: options
+ custom_column_names:
+ id: id
+ options: options
+ custom_name: authProviderRequests
+ custom_root_fields:
+ delete: deleteAuthProviderRequests
+ delete_by_pk: deleteAuthProviderRequest
+ insert: insertAuthProviderRequests
+ insert_one: insertAuthProviderRequest
+ select: authProviderRequests
+ select_aggregate: authProviderRequestsAggregate
+ select_by_pk: authProviderRequest
+ update: updateAuthProviderRequests
+ update_by_pk: updateAuthProviderRequest
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_providers.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_providers.yaml
new file mode 100644
index 000000000..3de528dc9
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_providers.yaml
@@ -0,0 +1,28 @@
+table:
+ name: providers
+ schema: auth
+configuration:
+ column_config:
+ id:
+ custom_name: id
+ custom_column_names:
+ id: id
+ custom_name: authProviders
+ custom_root_fields:
+ delete: deleteAuthProviders
+ delete_by_pk: deleteAuthProvider
+ insert: insertAuthProviders
+ insert_one: insertAuthProvider
+ select: authProviders
+ select_aggregate: authProvidersAggregate
+ select_by_pk: authProvider
+ update: updateAuthProviders
+ update_by_pk: updateAuthProvider
+array_relationships:
+ - name: userProviders
+ using:
+ foreign_key_constraint_on:
+ column: provider_id
+ table:
+ name: user_providers
+ schema: auth
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_refresh_token_types.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_refresh_token_types.yaml
new file mode 100644
index 000000000..8abaa8604
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_refresh_token_types.yaml
@@ -0,0 +1,26 @@
+table:
+ name: refresh_token_types
+ schema: auth
+is_enum: true
+configuration:
+ column_config: {}
+ custom_column_names: {}
+ custom_name: authRefreshTokenTypes
+ custom_root_fields:
+ delete: deleteAuthRefreshTokenTypes
+ delete_by_pk: deleteAuthRefreshTokenType
+ insert: insertAuthRefreshTokenTypes
+ insert_one: insertAuthRefreshTokenType
+ select: authRefreshTokenTypes
+ select_aggregate: authRefreshTokenTypesAggregate
+ select_by_pk: authRefreshTokenType
+ update: updateAuthRefreshTokenTypes
+ update_by_pk: updateAuthRefreshTokenType
+array_relationships:
+ - name: refreshTokens
+ using:
+ foreign_key_constraint_on:
+ column: type
+ table:
+ name: refresh_tokens
+ schema: auth
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_refresh_tokens.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_refresh_tokens.yaml
new file mode 100644
index 000000000..2a332c4f2
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_refresh_tokens.yaml
@@ -0,0 +1,55 @@
+table:
+ name: refresh_tokens
+ schema: auth
+configuration:
+ column_config:
+ created_at:
+ custom_name: createdAt
+ expires_at:
+ custom_name: expiresAt
+ refresh_token_hash:
+ custom_name: refreshTokenHash
+ user_id:
+ custom_name: userId
+ custom_column_names:
+ created_at: createdAt
+ expires_at: expiresAt
+ refresh_token_hash: refreshTokenHash
+ user_id: userId
+ custom_name: authRefreshTokens
+ custom_root_fields:
+ delete: deleteAuthRefreshTokens
+ delete_by_pk: deleteAuthRefreshToken
+ insert: insertAuthRefreshTokens
+ insert_one: insertAuthRefreshToken
+ select: authRefreshTokens
+ select_aggregate: authRefreshTokensAggregate
+ select_by_pk: authRefreshToken
+ update: updateAuthRefreshTokens
+ update_by_pk: updateAuthRefreshToken
+object_relationships:
+ - name: user
+ using:
+ foreign_key_constraint_on: user_id
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - id
+ - created_at
+ - expires_at
+ - metadata
+ - type
+ - user_id
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+delete_permissions:
+ - role: user
+ permission:
+ filter:
+ _and:
+ - user_id:
+ _eq: X-Hasura-User-Id
+ - type:
+ _eq: pat
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_roles.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_roles.yaml
new file mode 100644
index 000000000..bc6d5a2cf
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_roles.yaml
@@ -0,0 +1,35 @@
+table:
+ name: roles
+ schema: auth
+configuration:
+ column_config:
+ role:
+ custom_name: role
+ custom_column_names:
+ role: role
+ custom_name: authRoles
+ custom_root_fields:
+ delete: deleteAuthRoles
+ delete_by_pk: deleteAuthRole
+ insert: insertAuthRoles
+ insert_one: insertAuthRole
+ select: authRoles
+ select_aggregate: authRolesAggregate
+ select_by_pk: authRole
+ update: updateAuthRoles
+ update_by_pk: updateAuthRole
+array_relationships:
+ - name: userRoles
+ using:
+ foreign_key_constraint_on:
+ column: role
+ table:
+ name: user_roles
+ schema: auth
+ - name: usersByDefaultRole
+ using:
+ foreign_key_constraint_on:
+ column: default_role
+ table:
+ name: users
+ schema: auth
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_user_providers.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_user_providers.yaml
new file mode 100644
index 000000000..02b8eb2da
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_user_providers.yaml
@@ -0,0 +1,48 @@
+table:
+ name: user_providers
+ schema: auth
+configuration:
+ column_config:
+ access_token:
+ custom_name: accessToken
+ created_at:
+ custom_name: createdAt
+ id:
+ custom_name: id
+ provider_id:
+ custom_name: providerId
+ provider_user_id:
+ custom_name: providerUserId
+ refresh_token:
+ custom_name: refreshToken
+ updated_at:
+ custom_name: updatedAt
+ user_id:
+ custom_name: userId
+ custom_column_names:
+ access_token: accessToken
+ created_at: createdAt
+ id: id
+ provider_id: providerId
+ provider_user_id: providerUserId
+ refresh_token: refreshToken
+ updated_at: updatedAt
+ user_id: userId
+ custom_name: authUserProviders
+ custom_root_fields:
+ delete: deleteAuthUserProviders
+ delete_by_pk: deleteAuthUserProvider
+ insert: insertAuthUserProviders
+ insert_one: insertAuthUserProvider
+ select: authUserProviders
+ select_aggregate: authUserProvidersAggregate
+ select_by_pk: authUserProvider
+ update: updateAuthUserProviders
+ update_by_pk: updateAuthUserProvider
+object_relationships:
+ - name: provider
+ using:
+ foreign_key_constraint_on: provider_id
+ - name: user
+ using:
+ foreign_key_constraint_on: user_id
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_user_roles.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_user_roles.yaml
new file mode 100644
index 000000000..f90553941
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_user_roles.yaml
@@ -0,0 +1,36 @@
+table:
+ name: user_roles
+ schema: auth
+configuration:
+ column_config:
+ created_at:
+ custom_name: createdAt
+ id:
+ custom_name: id
+ role:
+ custom_name: role
+ user_id:
+ custom_name: userId
+ custom_column_names:
+ created_at: createdAt
+ id: id
+ role: role
+ user_id: userId
+ custom_name: authUserRoles
+ custom_root_fields:
+ delete: deleteAuthUserRoles
+ delete_by_pk: deleteAuthUserRole
+ insert: insertAuthUserRoles
+ insert_one: insertAuthUserRole
+ select: authUserRoles
+ select_aggregate: authUserRolesAggregate
+ select_by_pk: authUserRole
+ update: updateAuthUserRoles
+ update_by_pk: updateAuthUserRole
+object_relationships:
+ - name: roleByRole
+ using:
+ foreign_key_constraint_on: role
+ - name: user
+ using:
+ foreign_key_constraint_on: user_id
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_user_security_keys.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_user_security_keys.yaml
new file mode 100644
index 000000000..9963b6234
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_user_security_keys.yaml
@@ -0,0 +1,33 @@
+table:
+ name: user_security_keys
+ schema: auth
+configuration:
+ column_config:
+ credential_id:
+ custom_name: credentialId
+ credential_public_key:
+ custom_name: credentialPublicKey
+ id:
+ custom_name: id
+ user_id:
+ custom_name: userId
+ custom_column_names:
+ credential_id: credentialId
+ credential_public_key: credentialPublicKey
+ id: id
+ user_id: userId
+ custom_name: authUserSecurityKeys
+ custom_root_fields:
+ delete: deleteAuthUserSecurityKeys
+ delete_by_pk: deleteAuthUserSecurityKey
+ insert: insertAuthUserSecurityKeys
+ insert_one: insertAuthUserSecurityKey
+ select: authUserSecurityKeys
+ select_aggregate: authUserSecurityKeysAggregate
+ select_by_pk: authUserSecurityKey
+ update: updateAuthUserSecurityKeys
+ update_by_pk: updateAuthUserSecurityKey
+object_relationships:
+ - name: user
+ using:
+ foreign_key_constraint_on: user_id
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_users.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_users.yaml
new file mode 100644
index 000000000..730001730
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/auth_users.yaml
@@ -0,0 +1,122 @@
+table:
+ name: users
+ schema: auth
+configuration:
+ column_config:
+ active_mfa_type:
+ custom_name: activeMfaType
+ avatar_url:
+ custom_name: avatarUrl
+ created_at:
+ custom_name: createdAt
+ default_role:
+ custom_name: defaultRole
+ disabled:
+ custom_name: disabled
+ display_name:
+ custom_name: displayName
+ email:
+ custom_name: email
+ email_verified:
+ custom_name: emailVerified
+ id:
+ custom_name: id
+ is_anonymous:
+ custom_name: isAnonymous
+ last_seen:
+ custom_name: lastSeen
+ locale:
+ custom_name: locale
+ new_email:
+ custom_name: newEmail
+ otp_hash:
+ custom_name: otpHash
+ otp_hash_expires_at:
+ custom_name: otpHashExpiresAt
+ otp_method_last_used:
+ custom_name: otpMethodLastUsed
+ password_hash:
+ custom_name: passwordHash
+ phone_number:
+ custom_name: phoneNumber
+ phone_number_verified:
+ custom_name: phoneNumberVerified
+ ticket:
+ custom_name: ticket
+ ticket_expires_at:
+ custom_name: ticketExpiresAt
+ totp_secret:
+ custom_name: totpSecret
+ updated_at:
+ custom_name: updatedAt
+ webauthn_current_challenge:
+ custom_name: currentChallenge
+ custom_column_names:
+ active_mfa_type: activeMfaType
+ avatar_url: avatarUrl
+ created_at: createdAt
+ default_role: defaultRole
+ disabled: disabled
+ display_name: displayName
+ email: email
+ email_verified: emailVerified
+ id: id
+ is_anonymous: isAnonymous
+ last_seen: lastSeen
+ locale: locale
+ new_email: newEmail
+ otp_hash: otpHash
+ otp_hash_expires_at: otpHashExpiresAt
+ otp_method_last_used: otpMethodLastUsed
+ password_hash: passwordHash
+ phone_number: phoneNumber
+ phone_number_verified: phoneNumberVerified
+ ticket: ticket
+ ticket_expires_at: ticketExpiresAt
+ totp_secret: totpSecret
+ updated_at: updatedAt
+ webauthn_current_challenge: currentChallenge
+ custom_name: users
+ custom_root_fields:
+ delete: deleteUsers
+ delete_by_pk: deleteUser
+ insert: insertUsers
+ insert_one: insertUser
+ select: users
+ select_aggregate: usersAggregate
+ select_by_pk: user
+ update: updateUsers
+ update_by_pk: updateUser
+object_relationships:
+ - name: defaultRoleByRole
+ using:
+ foreign_key_constraint_on: default_role
+array_relationships:
+ - name: refreshTokens
+ using:
+ foreign_key_constraint_on:
+ column: user_id
+ table:
+ name: refresh_tokens
+ schema: auth
+ - name: roles
+ using:
+ foreign_key_constraint_on:
+ column: user_id
+ table:
+ name: user_roles
+ schema: auth
+ - name: securityKeys
+ using:
+ foreign_key_constraint_on:
+ column: user_id
+ table:
+ name: user_security_keys
+ schema: auth
+ - name: userProviders
+ using:
+ foreign_key_constraint_on:
+ column: user_id
+ table:
+ name: user_providers
+ schema: auth
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/public_todos.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/public_todos.yaml
new file mode 100644
index 000000000..b66aaa199
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/public_todos.yaml
@@ -0,0 +1,47 @@
+table:
+ name: todos
+ schema: public
+insert_permissions:
+ - role: user
+ permission:
+ check: {}
+ set:
+ user_id: X-Hasura-User-Id
+ columns:
+ - created_at
+ - updated_at
+ - title
+ - details
+ - completed
+ - user_id
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - id
+ - created_at
+ - updated_at
+ - title
+ - details
+ - completed
+ - user_id
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+update_permissions:
+ - role: user
+ permission:
+ columns:
+ - title
+ - details
+ - completed
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
+ check: null
+delete_permissions:
+ - role: user
+ permission:
+ filter:
+ user_id:
+ _eq: X-Hasura-User-Id
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/storage_buckets.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/storage_buckets.yaml
new file mode 100644
index 000000000..12b0e8343
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/storage_buckets.yaml
@@ -0,0 +1,49 @@
+table:
+ name: buckets
+ schema: storage
+configuration:
+ column_config:
+ cache_control:
+ custom_name: cacheControl
+ created_at:
+ custom_name: createdAt
+ download_expiration:
+ custom_name: downloadExpiration
+ id:
+ custom_name: id
+ max_upload_file_size:
+ custom_name: maxUploadFileSize
+ min_upload_file_size:
+ custom_name: minUploadFileSize
+ presigned_urls_enabled:
+ custom_name: presignedUrlsEnabled
+ updated_at:
+ custom_name: updatedAt
+ custom_column_names:
+ cache_control: cacheControl
+ created_at: createdAt
+ download_expiration: downloadExpiration
+ id: id
+ max_upload_file_size: maxUploadFileSize
+ min_upload_file_size: minUploadFileSize
+ presigned_urls_enabled: presignedUrlsEnabled
+ updated_at: updatedAt
+ custom_name: buckets
+ custom_root_fields:
+ delete: deleteBuckets
+ delete_by_pk: deleteBucket
+ insert: insertBuckets
+ insert_one: insertBucket
+ select: buckets
+ select_aggregate: bucketsAggregate
+ select_by_pk: bucket
+ update: updateBuckets
+ update_by_pk: updateBucket
+array_relationships:
+ - name: files
+ using:
+ foreign_key_constraint_on:
+ column: bucket_id
+ table:
+ name: files
+ schema: storage
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/storage_files.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/storage_files.yaml
new file mode 100644
index 000000000..120490ca0
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/storage_files.yaml
@@ -0,0 +1,95 @@
+table:
+ name: files
+ schema: storage
+configuration:
+ column_config:
+ bucket_id:
+ custom_name: bucketId
+ created_at:
+ custom_name: createdAt
+ etag:
+ custom_name: etag
+ id:
+ custom_name: id
+ is_uploaded:
+ custom_name: isUploaded
+ metadata:
+ custom_name: metadata
+ mime_type:
+ custom_name: mimeType
+ name:
+ custom_name: name
+ size:
+ custom_name: size
+ updated_at:
+ custom_name: updatedAt
+ uploaded_by_user_id:
+ custom_name: uploadedByUserId
+ custom_column_names:
+ bucket_id: bucketId
+ created_at: createdAt
+ etag: etag
+ id: id
+ is_uploaded: isUploaded
+ metadata: metadata
+ mime_type: mimeType
+ name: name
+ size: size
+ updated_at: updatedAt
+ uploaded_by_user_id: uploadedByUserId
+ custom_name: files
+ custom_root_fields:
+ delete: deleteFiles
+ delete_by_pk: deleteFile
+ insert: insertFiles
+ insert_one: insertFile
+ select: files
+ select_aggregate: filesAggregate
+ select_by_pk: file
+ update: updateFiles
+ update_by_pk: updateFile
+object_relationships:
+ - name: bucket
+ using:
+ foreign_key_constraint_on: bucket_id
+insert_permissions:
+ - role: user
+ permission:
+ check:
+ bucket_id:
+ _eq: personal
+ set:
+ uploaded_by_user_id: X-Hasura-User-Id
+ columns:
+ - id
+ - bucket_id
+ - name
+ - size
+ - mime_type
+select_permissions:
+ - role: user
+ permission:
+ columns:
+ - id
+ - created_at
+ - updated_at
+ - bucket_id
+ - name
+ - size
+ - mime_type
+ - etag
+ - is_uploaded
+ - uploaded_by_user_id
+ - metadata
+ filter:
+ _and:
+ - bucket_id:
+ _eq: personal
+ - uploaded_by_user_id:
+ _eq: X-Hasura-User-Id
+delete_permissions:
+ - role: user
+ permission:
+ filter:
+ uploaded_by_user_id:
+ _eq: X-Hasura-User-Id
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/storage_virus.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/storage_virus.yaml
new file mode 100644
index 000000000..5c31429ce
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/storage_virus.yaml
@@ -0,0 +1,42 @@
+table:
+ name: virus
+ schema: storage
+configuration:
+ column_config:
+ created_at:
+ custom_name: createdAt
+ file_id:
+ custom_name: fileId
+ filename:
+ custom_name: filename
+ id:
+ custom_name: id
+ updated_at:
+ custom_name: updatedAt
+ user_session:
+ custom_name: userSession
+ virus:
+ custom_name: virus
+ custom_column_names:
+ created_at: createdAt
+ file_id: fileId
+ filename: filename
+ id: id
+ updated_at: updatedAt
+ user_session: userSession
+ virus: virus
+ custom_name: virus
+ custom_root_fields:
+ delete: deleteViruses
+ delete_by_pk: deleteVirus
+ insert: insertViruses
+ insert_one: insertVirus
+ select: viruses
+ select_aggregate: virusesAggregate
+ select_by_pk: virus
+ update: updateViruses
+ update_by_pk: updateVirus
+object_relationships:
+ - name: file
+ using:
+ foreign_key_constraint_on: file_id
diff --git a/examples/tutorials/backend/nhost/metadata/databases/default/tables/tables.yaml b/examples/tutorials/backend/nhost/metadata/databases/default/tables/tables.yaml
new file mode 100644
index 000000000..8c8535fe8
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/databases/default/tables/tables.yaml
@@ -0,0 +1,13 @@
+- "!include auth_provider_requests.yaml"
+- "!include auth_providers.yaml"
+- "!include auth_refresh_token_types.yaml"
+- "!include auth_refresh_tokens.yaml"
+- "!include auth_roles.yaml"
+- "!include auth_user_providers.yaml"
+- "!include auth_user_roles.yaml"
+- "!include auth_user_security_keys.yaml"
+- "!include auth_users.yaml"
+- "!include public_todos.yaml"
+- "!include storage_buckets.yaml"
+- "!include storage_files.yaml"
+- "!include storage_virus.yaml"
diff --git a/examples/tutorials/backend/nhost/metadata/graphql_schema_introspection.yaml b/examples/tutorials/backend/nhost/metadata/graphql_schema_introspection.yaml
new file mode 100644
index 000000000..61a4dcac2
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/graphql_schema_introspection.yaml
@@ -0,0 +1 @@
+disabled_for_roles: []
diff --git a/examples/tutorials/backend/nhost/metadata/inherited_roles.yaml b/examples/tutorials/backend/nhost/metadata/inherited_roles.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/inherited_roles.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/tutorials/backend/nhost/metadata/metrics_config.yaml b/examples/tutorials/backend/nhost/metadata/metrics_config.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/metrics_config.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/tutorials/backend/nhost/metadata/network.yaml b/examples/tutorials/backend/nhost/metadata/network.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/network.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/tutorials/backend/nhost/metadata/opentelemetry.yaml b/examples/tutorials/backend/nhost/metadata/opentelemetry.yaml
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/opentelemetry.yaml
@@ -0,0 +1 @@
+{}
diff --git a/examples/tutorials/backend/nhost/metadata/query_collections.yaml b/examples/tutorials/backend/nhost/metadata/query_collections.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/query_collections.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/tutorials/backend/nhost/metadata/remote_schemas.yaml b/examples/tutorials/backend/nhost/metadata/remote_schemas.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/remote_schemas.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/tutorials/backend/nhost/metadata/rest_endpoints.yaml b/examples/tutorials/backend/nhost/metadata/rest_endpoints.yaml
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/rest_endpoints.yaml
@@ -0,0 +1 @@
+[]
diff --git a/examples/tutorials/backend/nhost/metadata/version.yaml b/examples/tutorials/backend/nhost/metadata/version.yaml
new file mode 100644
index 000000000..0a70affa4
--- /dev/null
+++ b/examples/tutorials/backend/nhost/metadata/version.yaml
@@ -0,0 +1 @@
+version: 3
diff --git a/examples/tutorials/backend/nhost/migrations/default/1757497684426_todos/down.sql b/examples/tutorials/backend/nhost/migrations/default/1757497684426_todos/down.sql
new file mode 100644
index 000000000..11b190226
--- /dev/null
+++ b/examples/tutorials/backend/nhost/migrations/default/1757497684426_todos/down.sql
@@ -0,0 +1 @@
+-- Could not auto-generate a down migration.
diff --git a/examples/tutorials/backend/nhost/migrations/default/1757497684426_todos/up.sql b/examples/tutorials/backend/nhost/migrations/default/1757497684426_todos/up.sql
new file mode 100644
index 000000000..9a24fefef
--- /dev/null
+++ b/examples/tutorials/backend/nhost/migrations/default/1757497684426_todos/up.sql
@@ -0,0 +1,26 @@
+CREATE TABLE public.todos (
+ id uuid DEFAULT gen_random_uuid() NOT NULL,
+ created_at timestamptz DEFAULT now() NOT NULL,
+ updated_at timestamptz DEFAULT now() NOT NULL,
+ title text NOT NULL,
+ details text,
+ completed bool DEFAULT false NOT NULL,
+ user_id uuid NOT NULL,
+ PRIMARY KEY (id),
+ FOREIGN KEY (user_id) REFERENCES auth.users (id) ON UPDATE CASCADE ON DELETE CASCADE
+);
+
+
+CREATE OR REPLACE FUNCTION update_updated_at_column()
+RETURNS TRIGGER AS $$
+BEGIN
+ NEW.updated_at = now();
+ RETURN NEW;
+END;
+$$ language 'plpgsql';
+
+
+CREATE TRIGGER update_todos_updated_at
+ BEFORE UPDATE ON public.todos
+ FOR EACH ROW
+ EXECUTE FUNCTION update_updated_at_column();
diff --git a/examples/tutorials/backend/nhost/migrations/default/1758010532710_added personal bucket/down.sql b/examples/tutorials/backend/nhost/migrations/default/1758010532710_added personal bucket/down.sql
new file mode 100644
index 000000000..11b190226
--- /dev/null
+++ b/examples/tutorials/backend/nhost/migrations/default/1758010532710_added personal bucket/down.sql
@@ -0,0 +1 @@
+-- Could not auto-generate a down migration.
diff --git a/examples/tutorials/backend/nhost/migrations/default/1758010532710_added personal bucket/up.sql b/examples/tutorials/backend/nhost/migrations/default/1758010532710_added personal bucket/up.sql
new file mode 100644
index 000000000..72a00ac9a
--- /dev/null
+++ b/examples/tutorials/backend/nhost/migrations/default/1758010532710_added personal bucket/up.sql
@@ -0,0 +1 @@
+INSERT into storage.buckets (id) VALUES ('personal');
diff --git a/examples/tutorials/backend/nhost/nhost.toml b/examples/tutorials/backend/nhost/nhost.toml
new file mode 100644
index 000000000..717684743
--- /dev/null
+++ b/examples/tutorials/backend/nhost/nhost.toml
@@ -0,0 +1,195 @@
+[global]
+
+[hasura]
+version = 'v2.48.5-ce'
+adminSecret = '{{ secrets.HASURA_GRAPHQL_ADMIN_SECRET }}'
+webhookSecret = '{{ secrets.NHOST_WEBHOOK_SECRET }}'
+
+[[hasura.jwtSecrets]]
+type = 'HS256'
+key = '{{ secrets.HASURA_GRAPHQL_JWT_SECRET }}'
+
+[hasura.settings]
+corsDomain = ['*']
+devMode = true
+enableAllowList = false
+enableConsole = true
+enableRemoteSchemaPermissions = false
+enabledAPIs = ['metadata', 'graphql', 'pgdump', 'config']
+inferFunctionPermissions = true
+liveQueriesMultiplexedRefetchInterval = 1000
+stringifyNumericTypes = false
+
+[hasura.logs]
+level = 'warn'
+
+[hasura.events]
+httpPoolSize = 100
+
+[functions]
+[functions.node]
+version = 22
+
+[auth]
+version = '0.41.1'
+
+[auth.elevatedPrivileges]
+mode = 'disabled'
+
+[auth.redirections]
+clientUrl = 'http://localhost:3000'
+allowedUrls = ['http://localhost:5173', 'exp://192.168.1.108:8081']
+
+[auth.signUp]
+enabled = true
+disableNewUsers = false
+
+[auth.user]
+[auth.user.roles]
+default = 'user'
+allowed = ['user', 'me']
+
+[auth.user.locale]
+default = 'en'
+allowed = ['en']
+
+[auth.user.gravatar]
+enabled = true
+default = 'blank'
+rating = 'g'
+
+[auth.user.email]
+
+[auth.user.emailDomains]
+
+[auth.session]
+[auth.session.accessToken]
+expiresIn = 900
+
+[auth.session.refreshToken]
+expiresIn = 2592000
+
+[auth.method]
+[auth.method.anonymous]
+enabled = false
+
+[auth.method.emailPasswordless]
+enabled = false
+
+[auth.method.otp]
+[auth.method.otp.email]
+enabled = false
+
+[auth.method.emailPassword]
+hibpEnabled = false
+emailVerificationRequired = true
+passwordMinLength = 9
+
+[auth.method.smsPasswordless]
+enabled = false
+
+[auth.method.oauth]
+[auth.method.oauth.apple]
+enabled = true
+audience = '{{ secrets.APPLE_AUDIENCE }}'
+clientId = '{{ secrets.APPLE_CLIENT_ID }}'
+keyId = '{{ secrets.APPLE_KEY_ID }}'
+teamId = '{{ secrets.APPLE_TEAM_ID }}'
+privateKey = '{{ secrets.APPLE_PRIVATE_KEY }}'
+
+[auth.method.oauth.azuread]
+tenant = 'common'
+enabled = false
+
+[auth.method.oauth.bitbucket]
+enabled = false
+
+[auth.method.oauth.discord]
+enabled = false
+
+[auth.method.oauth.facebook]
+enabled = false
+
+[auth.method.oauth.github]
+enabled = false
+
+[auth.method.oauth.gitlab]
+enabled = false
+
+[auth.method.oauth.google]
+enabled = false
+
+[auth.method.oauth.linkedin]
+enabled = false
+
+[auth.method.oauth.spotify]
+enabled = false
+
+[auth.method.oauth.strava]
+enabled = false
+
+[auth.method.oauth.twitch]
+enabled = false
+
+[auth.method.oauth.twitter]
+enabled = false
+
+[auth.method.oauth.windowslive]
+enabled = false
+
+[auth.method.oauth.workos]
+enabled = false
+
+[auth.method.webauthn]
+enabled = false
+
+[auth.method.webauthn.attestation]
+timeout = 60000
+
+[auth.totp]
+enabled = false
+
+[auth.misc]
+concealErrors = false
+
+[auth.rateLimit]
+[auth.rateLimit.emails]
+limit = 10
+interval = '1h'
+
+[auth.rateLimit.sms]
+limit = 10
+interval = '1h'
+
+[auth.rateLimit.bruteForce]
+limit = 10
+interval = '5m'
+
+[auth.rateLimit.signups]
+limit = 10
+interval = '5m'
+
+[auth.rateLimit.global]
+limit = 100
+interval = '1m'
+
+[postgres]
+version = '14.18-20250728-1'
+
+[postgres.resources]
+[postgres.resources.storage]
+capacity = 1
+
+[provider]
+
+[storage]
+version = '0.8.0-beta5'
+
+[observability]
+[observability.grafana]
+adminPassword = '{{ secrets.GRAFANA_ADMIN_PASSWORD }}'
+
+[observability.grafana.alerting]
+enabled = false
+
+[observability.grafana.contacts]
diff --git a/examples/tutorials/nhost-nextjs-tutorial/.gitignore b/examples/tutorials/nhost-nextjs-tutorial/.gitignore
new file mode 100644
index 000000000..5ef6a5207
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/examples/tutorials/nhost-nextjs-tutorial/README.md b/examples/tutorials/nhost-nextjs-tutorial/README.md
new file mode 100644
index 000000000..b40da8761
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/README.md
@@ -0,0 +1,3 @@
+This is the result of following the tutorial at:
+
+https://docs.nhost.io/getting-started/tutorials/nextjs/1-introduction
diff --git a/examples/tutorials/nhost-nextjs-tutorial/next.config.ts b/examples/tutorials/nhost-nextjs-tutorial/next.config.ts
new file mode 100644
index 000000000..e9ffa3083
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/next.config.ts
@@ -0,0 +1,7 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ /* config options here */
+};
+
+export default nextConfig;
diff --git a/examples/tutorials/nhost-nextjs-tutorial/package.json b/examples/tutorials/nhost-nextjs-tutorial/package.json
new file mode 100644
index 000000000..0faa7092b
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "nhost-nextjs-tutorial",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --turbopack",
+ "build": "next build",
+ "start": "next start",
+ "generate": "echo 'Nothing to do'",
+ "test": "pnpm test:typecheck && pnpm test:lint",
+ "test:typecheck": "tsc --noEmit",
+ "test:lint": "biome check",
+ "format": "biome format --write"
+ },
+ "dependencies": {
+ "@nhost/nhost-js": "workspace:^",
+ "react": "19.1.0",
+ "react-dom": "19.1.0",
+ "next": "15.5.3"
+ },
+ "devDependencies": {
+ "typescript": "^5",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19"
+ }
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/pnpm-lock.yaml b/examples/tutorials/nhost-nextjs-tutorial/pnpm-lock.yaml
new file mode 100644
index 000000000..6ac1a9b40
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/pnpm-lock.yaml
@@ -0,0 +1,602 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@nhost/nhost-js':
+ specifier: workspace:^
+ version: link:../../../packages/nhost-js
+ next:
+ specifier: 15.5.3
+ version: 15.5.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react:
+ specifier: 19.1.0
+ version: 19.1.0
+ react-dom:
+ specifier: 19.1.0
+ version: 19.1.0(react@19.1.0)
+ devDependencies:
+ '@types/node':
+ specifier: ^20
+ version: 20.19.13
+ '@types/react':
+ specifier: ^19
+ version: 19.1.12
+ '@types/react-dom':
+ specifier: ^19
+ version: 19.1.9(@types/react@19.1.12)
+ typescript:
+ specifier: ^5
+ version: 5.9.2
+
+packages:
+
+ '@emnapi/runtime@1.5.0':
+ resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==}
+
+ '@img/sharp-darwin-arm64@0.34.3':
+ resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.34.3':
+ resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-arm64@1.2.0':
+ resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.2.0':
+ resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.2.0':
+ resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-arm@1.2.0':
+ resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-ppc64@1.2.0':
+ resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-s390x@1.2.0':
+ resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-x64@1.2.0':
+ resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.0':
+ resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.2.0':
+ resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linux-arm64@0.34.3':
+ resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linux-arm@0.34.3':
+ resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-linux-ppc64@0.34.3':
+ resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@img/sharp-linux-s390x@0.34.3':
+ resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-linux-x64@0.34.3':
+ resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-arm64@0.34.3':
+ resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-x64@0.34.3':
+ resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-wasm32@0.34.3':
+ resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-arm64@0.34.3':
+ resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@img/sharp-win32-ia32@0.34.3':
+ resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.34.3':
+ resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@next/env@15.5.3':
+ resolution: {integrity: sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==}
+
+ '@next/swc-darwin-arm64@15.5.3':
+ resolution: {integrity: sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@15.5.3':
+ resolution: {integrity: sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@15.5.3':
+ resolution: {integrity: sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-arm64-musl@15.5.3':
+ resolution: {integrity: sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-x64-gnu@15.5.3':
+ resolution: {integrity: sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-linux-x64-musl@15.5.3':
+ resolution: {integrity: sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-win32-arm64-msvc@15.5.3':
+ resolution: {integrity: sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@15.5.3':
+ resolution: {integrity: sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@swc/helpers@0.5.15':
+ resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+
+ '@types/node@20.19.13':
+ resolution: {integrity: sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==}
+
+ '@types/react-dom@19.1.9':
+ resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==}
+ peerDependencies:
+ '@types/react': ^19.0.0
+
+ '@types/react@19.1.12':
+ resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==}
+
+ caniuse-lite@1.0.30001741:
+ resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==}
+
+ client-only@0.0.1:
+ resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
+ color@4.2.3:
+ resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
+ engines: {node: '>=12.5.0'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ detect-libc@2.0.4:
+ resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
+ engines: {node: '>=8'}
+
+ is-arrayish@0.3.2:
+ resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ next@15.5.3:
+ resolution: {integrity: sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==}
+ engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
+ hasBin: true
+ 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
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ react-dom@19.1.0:
+ resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
+ peerDependencies:
+ react: ^19.1.0
+
+ react@19.1.0:
+ resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
+ engines: {node: '>=0.10.0'}
+
+ scheduler@0.26.0:
+ resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
+
+ semver@7.7.2:
+ resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ sharp@0.34.3:
+ resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
+ simple-swizzle@0.2.2:
+ resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ styled-jsx@5.1.6:
+ resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ 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
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ typescript@5.9.2:
+ resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+snapshots:
+
+ '@emnapi/runtime@1.5.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@img/sharp-darwin-arm64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.2.0
+ optional: true
+
+ '@img/sharp-darwin-x64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.2.0
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.2.0':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.2.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.2.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.2.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-ppc64@1.2.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.2.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.2.0':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.0':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.2.0':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.2.0
+ optional: true
+
+ '@img/sharp-linux-arm@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.2.0
+ optional: true
+
+ '@img/sharp-linux-ppc64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-ppc64': 1.2.0
+ optional: true
+
+ '@img/sharp-linux-s390x@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.2.0
+ optional: true
+
+ '@img/sharp-linux-x64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.2.0
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.0
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.0
+ optional: true
+
+ '@img/sharp-wasm32@0.34.3':
+ dependencies:
+ '@emnapi/runtime': 1.5.0
+ optional: true
+
+ '@img/sharp-win32-arm64@0.34.3':
+ optional: true
+
+ '@img/sharp-win32-ia32@0.34.3':
+ optional: true
+
+ '@img/sharp-win32-x64@0.34.3':
+ optional: true
+
+ '@next/env@15.5.3': {}
+
+ '@next/swc-darwin-arm64@15.5.3':
+ optional: true
+
+ '@next/swc-darwin-x64@15.5.3':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@15.5.3':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@15.5.3':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@15.5.3':
+ optional: true
+
+ '@next/swc-linux-x64-musl@15.5.3':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@15.5.3':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@15.5.3':
+ optional: true
+
+ '@swc/helpers@0.5.15':
+ dependencies:
+ tslib: 2.8.1
+
+ '@types/node@20.19.13':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/react-dom@19.1.9(@types/react@19.1.12)':
+ dependencies:
+ '@types/react': 19.1.12
+
+ '@types/react@19.1.12':
+ dependencies:
+ csstype: 3.1.3
+
+ caniuse-lite@1.0.30001741: {}
+
+ client-only@0.0.1: {}
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+ optional: true
+
+ color-name@1.1.4:
+ optional: true
+
+ color-string@1.9.1:
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.2
+ optional: true
+
+ color@4.2.3:
+ dependencies:
+ color-convert: 2.0.1
+ color-string: 1.9.1
+ optional: true
+
+ csstype@3.1.3: {}
+
+ detect-libc@2.0.4:
+ optional: true
+
+ is-arrayish@0.3.2:
+ optional: true
+
+ nanoid@3.3.11: {}
+
+ next@15.5.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@next/env': 15.5.3
+ '@swc/helpers': 0.5.15
+ caniuse-lite: 1.0.30001741
+ postcss: 8.4.31
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ styled-jsx: 5.1.6(react@19.1.0)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 15.5.3
+ '@next/swc-darwin-x64': 15.5.3
+ '@next/swc-linux-arm64-gnu': 15.5.3
+ '@next/swc-linux-arm64-musl': 15.5.3
+ '@next/swc-linux-x64-gnu': 15.5.3
+ '@next/swc-linux-x64-musl': 15.5.3
+ '@next/swc-win32-arm64-msvc': 15.5.3
+ '@next/swc-win32-x64-msvc': 15.5.3
+ sharp: 0.34.3
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
+ picocolors@1.1.1: {}
+
+ postcss@8.4.31:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ react-dom@19.1.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ scheduler: 0.26.0
+
+ react@19.1.0: {}
+
+ scheduler@0.26.0: {}
+
+ semver@7.7.2:
+ optional: true
+
+ sharp@0.34.3:
+ dependencies:
+ color: 4.2.3
+ detect-libc: 2.0.4
+ semver: 7.7.2
+ 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
+ optional: true
+
+ simple-swizzle@0.2.2:
+ dependencies:
+ is-arrayish: 0.3.2
+ optional: true
+
+ source-map-js@1.2.1: {}
+
+ styled-jsx@5.1.6(react@19.1.0):
+ dependencies:
+ client-only: 0.0.1
+ react: 19.1.0
+
+ tslib@2.8.1: {}
+
+ typescript@5.9.2: {}
+
+ undici-types@6.21.0: {}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/public/file.svg b/examples/tutorials/nhost-nextjs-tutorial/public/file.svg
new file mode 100644
index 000000000..004145cdd
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/public/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/tutorials/nhost-nextjs-tutorial/public/globe.svg b/examples/tutorials/nhost-nextjs-tutorial/public/globe.svg
new file mode 100644
index 000000000..567f17b0d
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/public/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/tutorials/nhost-nextjs-tutorial/public/next.svg b/examples/tutorials/nhost-nextjs-tutorial/public/next.svg
new file mode 100644
index 000000000..5174b28c5
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/tutorials/nhost-nextjs-tutorial/public/vercel.svg b/examples/tutorials/nhost-nextjs-tutorial/public/vercel.svg
new file mode 100644
index 000000000..770539603
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/tutorials/nhost-nextjs-tutorial/public/window.svg b/examples/tutorials/nhost-nextjs-tutorial/public/window.svg
new file mode 100644
index 000000000..b2b2a44f6
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/public/window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/favicon.ico b/examples/tutorials/nhost-nextjs-tutorial/src/app/favicon.ico
new file mode 100644
index 000000000..718d6fea4
Binary files /dev/null and b/examples/tutorials/nhost-nextjs-tutorial/src/app/favicon.ico differ
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/files/FilesClient.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/files/FilesClient.tsx
new file mode 100644
index 000000000..05eeadadb
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/files/FilesClient.tsx
@@ -0,0 +1,359 @@
+"use client";
+
+import type { FileMetadata } from "@nhost/nhost-js/storage";
+import { useRef, useState, useTransition } from "react";
+import { deleteFileAction, uploadFileAction } from "./actions";
+
+interface FilesClientProps {
+ initialFiles: FileMetadata[];
+ serverError: string | null;
+}
+
+interface DeleteStatus {
+ message: string;
+ isError: boolean;
+}
+
+function formatFileSize(bytes: number): string {
+ if (bytes === 0) return "0 Bytes";
+
+ const sizes: string[] = ["Bytes", "KB", "MB", "GB", "TB"];
+ const i: number = Math.floor(Math.log(bytes) / Math.log(1024));
+
+ return `${parseFloat((bytes / 1024 ** i).toFixed(2))} ${sizes[i]}`;
+}
+
+export default function FilesClient({
+ initialFiles,
+ serverError,
+}: FilesClientProps) {
+ const fileInputRef = useRef(null);
+ const [_isPending, startTransition] = useTransition();
+
+ const [selectedFile, setSelectedFile] = useState(null);
+ const [uploading, setUploading] = useState(false);
+ const [uploadResult, setUploadResult] = useState(null);
+ const [error, setError] = useState(serverError);
+ const [files, setFiles] = useState(initialFiles);
+ const [viewingFile, setViewingFile] = useState(null);
+ const [deleting, setDeleting] = useState(null);
+ const [deleteStatus, setDeleteStatus] = useState(null);
+
+ const handleFileChange = (e: React.ChangeEvent): void => {
+ if (e.target.files && e.target.files.length > 0) {
+ const file = e.target.files[0];
+ if (file) {
+ setSelectedFile(file);
+ setError(null);
+ setUploadResult(null);
+ }
+ }
+ };
+
+ const handleUpload = async (): Promise => {
+ if (!selectedFile) {
+ setError("Please select a file to upload");
+ return;
+ }
+
+ setUploading(true);
+ setError(null);
+
+ startTransition(async () => {
+ try {
+ const formData = new FormData();
+ formData.append("file", selectedFile);
+
+ const result = await uploadFileAction(formData);
+
+ if (result.success && result.data) {
+ const file = result.data.file;
+ setUploadResult(file);
+
+ // Clear the form
+ setSelectedFile(null);
+ if (fileInputRef.current) {
+ fileInputRef.current.value = "";
+ }
+
+ // Update the files list
+ setFiles((prevFiles) => [file, ...prevFiles]);
+
+ // Clear success message after 3 seconds
+ setTimeout(() => {
+ setUploadResult(null);
+ }, 3000);
+ } else {
+ setError(result.error || "Failed to upload file");
+ }
+ } catch (err: unknown) {
+ const message = (err as Error).message || "An unknown error occurred";
+ setError(`Failed to upload file: ${message}`);
+ } finally {
+ setUploading(false);
+ }
+ });
+ };
+
+ const handleViewFile = async (
+ fileId: string,
+ fileName: string,
+ mimeType: string,
+ ): Promise => {
+ setViewingFile(fileId);
+
+ try {
+ // Handle different file types appropriately
+ if (
+ mimeType.startsWith("image/") ||
+ mimeType === "application/pdf" ||
+ mimeType.startsWith("text/") ||
+ mimeType.startsWith("video/") ||
+ mimeType.startsWith("audio/")
+ ) {
+ // Use download route for viewable files (inline viewing)
+ const viewUrl = `/files/download/${fileId}?fileName=${encodeURIComponent(fileName)}`;
+ window.open(viewUrl, "_blank");
+ } else {
+ // Use download route for downloads (force download)
+ const downloadUrl = `/files/download/${fileId}?fileName=${encodeURIComponent(fileName)}&download=true`;
+ const link = document.createElement("a");
+ link.href = downloadUrl;
+ link.download = fileName;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+
+ // Show download confirmation
+ const newWindow = window.open("", "_blank", "width=400,height=200");
+ if (newWindow) {
+ newWindow.document.documentElement.innerHTML = `
+
+ File Download
+
+
+
+ Downloading: ${fileName}
+ Your download has started. You can close this window.
+
+ `;
+ }
+ }
+ } catch (err) {
+ const message = (err as Error).message || "An unknown error occurred";
+ setError(`Failed to view file: ${message}`);
+ console.error("Error viewing file:", err);
+ } finally {
+ setViewingFile(null);
+ }
+ };
+
+ const handleDeleteFile = async (fileId: string): Promise => {
+ if (!fileId || deleting) return;
+
+ setDeleting(fileId);
+ setError(null);
+ setDeleteStatus(null);
+
+ const fileToDelete = files.find((file) => file.id === fileId);
+ const fileName = fileToDelete?.name || "File";
+
+ startTransition(async () => {
+ try {
+ const result = await deleteFileAction(fileId, fileName);
+
+ if (result.success && result.data) {
+ setDeleteStatus({
+ message: result.data.message,
+ isError: false,
+ });
+
+ // Remove from local state
+ setFiles(files.filter((file) => file.id !== fileId));
+
+ // Clear success message after 3 seconds
+ setTimeout(() => {
+ setDeleteStatus(null);
+ }, 3000);
+ } else {
+ setDeleteStatus({
+ message: result.error || `Failed to delete ${fileName}`,
+ isError: true,
+ });
+ }
+ } catch (err) {
+ const message = (err as Error).message || "An unknown error occurred";
+ setDeleteStatus({
+ message: `Failed to delete ${fileName}: ${message}`,
+ isError: true,
+ });
+ console.error("Error deleting file:", err);
+ } finally {
+ setDeleting(null);
+ }
+ });
+ };
+
+ return (
+ <>
+
+
Upload a File
+
+
+
+
fileInputRef.current?.click()}
+ >
+
+
+
+ Click to select a file
+ {selectedFile && (
+
+ {selectedFile.name} ({formatFileSize(selectedFile.size)})
+
+ )}
+
+
+
+ {error &&
{error}
}
+
+ {uploadResult && (
+
File uploaded successfully!
+ )}
+
+
+ {uploading ? "Uploading..." : "Upload File"}
+
+
+
+
+
Your Files
+
+ {deleteStatus && (
+
+ {deleteStatus.message}
+
+ )}
+
+ {files.length === 0 ? (
+
+
+
+
+
No files yet
+
+ Upload your first file to get started!
+
+
+ ) : (
+
+
+
+
+ Name
+ Type
+ Size
+ Actions
+
+
+
+ {files.map((file) => (
+
+ {file.name}
+ {file.mimeType}
+
+ {formatFileSize(file.size || 0)}
+
+
+
+
+ handleViewFile(
+ file.id || "unknown",
+ file.name || "unknown",
+ file.mimeType || "unknown",
+ )
+ }
+ disabled={viewingFile === file.id}
+ className="action-btn action-btn-edit"
+ title="View File"
+ >
+ {viewingFile === file.id ? "⏳" : "👁️"}
+
+ handleDeleteFile(file.id || "unknown")}
+ disabled={deleting === file.id}
+ className="action-btn action-btn-delete"
+ title="Delete File"
+ >
+ {deleting === file.id ? "⏳" : "🗑️"}
+
+
+
+
+ ))}
+
+
+
+ )}
+
+ >
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/files/actions.ts b/examples/tutorials/nhost-nextjs-tutorial/src/app/files/actions.ts
new file mode 100644
index 000000000..32e8a4091
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/files/actions.ts
@@ -0,0 +1,84 @@
+"use server";
+
+import type { FileMetadata } from "@nhost/nhost-js/storage";
+import { revalidatePath } from "next/cache";
+import { createNhostClient } from "../../lib/nhost/server";
+
+export interface ActionResult {
+ success: boolean;
+ error?: string;
+ data?: T;
+}
+
+export interface UploadFileData {
+ file: FileMetadata;
+ message: string;
+}
+
+export interface DeleteFileData {
+ message: string;
+}
+
+export async function uploadFileAction(
+ formData: FormData,
+): Promise> {
+ try {
+ const nhost = await createNhostClient();
+ const file = formData.get("file") as File;
+
+ if (!file) {
+ return { success: false, error: "Please select a file to upload" };
+ }
+
+ const response = await nhost.storage.uploadFiles({
+ "bucket-id": "personal",
+ "file[]": [file],
+ });
+
+ const uploadedFile = response.body.processedFiles?.[0];
+ if (!uploadedFile) {
+ return { success: false, error: "Failed to upload file" };
+ }
+
+ revalidatePath("/files");
+ return {
+ success: true,
+ data: {
+ file: uploadedFile,
+ message: "File uploaded successfully!",
+ },
+ };
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "An unknown error occurred";
+ return { success: false, error: `Failed to upload file: ${message}` };
+ }
+}
+
+export async function deleteFileAction(
+ fileId: string,
+ fileName: string,
+): Promise> {
+ try {
+ const nhost = await createNhostClient();
+
+ if (!fileId) {
+ return { success: false, error: "File ID is required" };
+ }
+
+ await nhost.storage.deleteFile(fileId);
+
+ revalidatePath("/files");
+ return {
+ success: true,
+ data: { message: `${fileName} deleted successfully` },
+ };
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "An unknown error occurred";
+ return {
+ success: false,
+ error: `Failed to delete ${fileName}: ${message}`,
+ };
+ }
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/files/download/[fileId]/route.ts b/examples/tutorials/nhost-nextjs-tutorial/src/app/files/download/[fileId]/route.ts
new file mode 100644
index 000000000..9a168ab1c
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/files/download/[fileId]/route.ts
@@ -0,0 +1,57 @@
+import { type NextRequest, NextResponse } from "next/server";
+import { createNhostClient } from "../../../../lib/nhost/server";
+
+export async function GET(
+ request: NextRequest,
+ { params }: { params: Promise<{ fileId: string }> },
+) {
+ try {
+ const nhost = await createNhostClient();
+ const { fileId } = await params;
+ const fileName = request.nextUrl.searchParams.get("fileName") || "file";
+ const download = request.nextUrl.searchParams.get("download") === "true";
+
+ if (!fileId) {
+ return NextResponse.json(
+ { error: "File ID is required" },
+ { status: 400 },
+ );
+ }
+
+ const response = await nhost.storage.getFile(fileId);
+
+ if (!response.body) {
+ return NextResponse.json({ error: "File not found" }, { status: 404 });
+ }
+
+ // Get the file content as an array buffer
+ const arrayBuffer = await response.body.arrayBuffer();
+
+ // Determine content disposition based on download parameter
+ const contentDisposition = download
+ ? `attachment; filename="${fileName}"`
+ : `inline; filename="${fileName}"`;
+
+ // Create the response with appropriate headers
+ return new NextResponse(arrayBuffer, {
+ status: 200,
+ headers: {
+ "Content-Type": response.body.type || "application/octet-stream",
+ "Content-Disposition": contentDisposition,
+ "Content-Length": arrayBuffer.byteLength.toString(),
+ "Cache-Control":
+ response.headers.get("Cache-Control") ||
+ "public, max-age=31536000, immutable",
+ Etag: response.headers.get("ETag") || "",
+ "Last-Modified": response.headers.get("Last-Modified") || "",
+ },
+ });
+ } catch (error) {
+ const message =
+ error instanceof Error ? error.message : "An unknown error occurred";
+ return NextResponse.json(
+ { error: `Failed to access file: ${message}` },
+ { status: 500 },
+ );
+ }
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/files/page.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/files/page.tsx
new file mode 100644
index 000000000..76e93b740
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/files/page.tsx
@@ -0,0 +1,54 @@
+import type { FileMetadata } from "@nhost/nhost-js/storage";
+import { createNhostClient } from "../../lib/nhost/server";
+import FilesClient from "./FilesClient";
+
+interface GetFilesResponse {
+ files: FileMetadata[];
+}
+
+export default async function FilesPage() {
+ const nhost = await createNhostClient();
+
+ // Fetch files on the server
+ let files: FileMetadata[] = [];
+ let error: string | null = null;
+
+ try {
+ // Use GraphQL to fetch files from the storage system
+ // Files are automatically filtered by user permissions
+ const response = await nhost.graphql.request({
+ query: `query GetFiles {
+ files {
+ id
+ name
+ size
+ mimeType
+ bucketId
+ uploadedByUserId
+ }
+ }`,
+ });
+
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to fetch files",
+ );
+ }
+
+ files = response.body.data?.files || [];
+ } catch (err) {
+ error = `Failed to load files: ${err instanceof Error ? err.message : "Unknown error"}`;
+ console.error("Error fetching files:", err);
+ }
+
+ return (
+
+
+
+ {/* Pass the server-fetched files to the client component */}
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/globals.css b/examples/tutorials/nhost-nextjs-tutorial/src/app/globals.css
new file mode 100644
index 000000000..4420cbd19
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/globals.css
@@ -0,0 +1,862 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ margin: 0;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+#root {
+ width: 100%;
+ min-height: 100vh;
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+
+a:hover {
+ color: #535bf2;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+
+button:hover {
+ border-color: #646cff;
+}
+
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+input,
+textarea {
+ width: 100%;
+ padding: 0.875rem 1rem;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 8px;
+ font-size: 0.875rem;
+ transition: all 0.2s ease;
+ background: rgba(255, 255, 255, 0.05);
+ color: white;
+ box-sizing: border-box;
+ font-family: inherit;
+}
+
+input:focus,
+textarea:focus {
+ outline: none;
+ border-color: #3b82f6;
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
+ background: rgba(255, 255, 255, 0.08);
+}
+
+input::placeholder,
+textarea::placeholder {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+textarea {
+ resize: vertical;
+ min-height: 4rem;
+}
+
+label {
+ display: block;
+ margin: 0 0 0.5rem 0;
+ font-weight: 500;
+ color: rgba(255, 255, 255, 0.9);
+ font-size: 0.875rem;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+/* Global Layout */
+.app-content {
+ padding: 0 2rem 2rem;
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+.page-center {
+ text-align: center;
+ padding: 2rem;
+}
+
+.page-header {
+ margin-bottom: 2rem;
+}
+
+.page-title {
+ font-weight: 700;
+ margin: 0;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.margin-bottom {
+ margin-bottom: 1rem;
+}
+
+.margin-top {
+ margin-top: 1rem;
+}
+
+.container {
+ width: 800px;
+ max-width: calc(100vw - 4rem);
+ min-width: 320px;
+ margin: 0 auto;
+ padding: 2rem;
+ box-sizing: border-box;
+ position: relative;
+}
+
+/* Status Messages */
+.success-message {
+ padding: 1rem;
+ background-color: #d4edda;
+ color: #155724;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+}
+
+.error-message {
+ background: rgba(239, 68, 68, 0.1);
+ color: #ef4444;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ border-radius: 12px;
+ padding: 1rem 1.5rem;
+ margin: 1rem 0;
+}
+
+.help-text {
+ color: #666;
+}
+
+.verification-status {
+ color: #28a745;
+ font-size: 1.2rem;
+ font-weight: bold;
+ margin-bottom: 1rem;
+}
+
+.verification-status.error {
+ color: #dc3545;
+ font-size: 1.1rem;
+}
+
+/* Email Verification Status */
+.email-verified {
+ color: #10b981;
+ font-weight: bold;
+ margin-left: 0.5rem;
+}
+
+.email-unverified {
+ color: #ef4444;
+ font-weight: bold;
+ margin-left: 0.5rem;
+}
+
+/* Debug Info */
+.debug-panel {
+ margin-bottom: 1rem;
+ padding: 1rem;
+ background-color: #f8f9fa;
+ border-radius: 8px;
+ text-align: left;
+ max-height: 200px;
+ overflow: auto;
+}
+
+.debug-title {
+ font-weight: bold;
+ margin-bottom: 0.5rem;
+}
+
+.debug-item {
+ margin-bottom: 0.25rem;
+}
+
+.debug-key {
+ font-family: monospace;
+ color: #007bff;
+}
+
+.debug-value {
+ font-family: monospace;
+}
+
+/* Session Display */
+.session-display {
+ font-size: 0.75rem;
+ overflow: auto;
+ margin: 0;
+ line-height: 1.4;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+/* Loading Spinner */
+.spinner-verify {
+ width: 32px;
+ height: 32px;
+ border: 3px solid #f3f3f3;
+ border-top: 3px solid #007bff;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin: 0 auto;
+}
+
+/* Navigation */
+.navigation {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ margin-bottom: 2rem;
+}
+
+.nav-container {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 1rem 2rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.nav-logo {
+ font-size: 1.25rem;
+ font-weight: 700;
+ color: white;
+ text-decoration: none;
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.nav-logo:hover {
+ opacity: 0.8;
+}
+
+.nav-links {
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+}
+
+.nav-link {
+ color: rgba(255, 255, 255, 0.8);
+ text-decoration: none;
+ font-weight: 500;
+ font-size: 0.875rem;
+ padding: 0.5rem 0.75rem;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+ border: none;
+ background: none;
+ cursor: pointer;
+ font-family: inherit;
+}
+
+.nav-link:hover {
+ color: white;
+ background: rgba(255, 255, 255, 0.1);
+}
+
+.nav-button {
+ color: #ef4444;
+}
+
+.nav-button:hover {
+ background: rgba(239, 68, 68, 0.2);
+}
+
+/* Buttons */
+.btn {
+ padding: 0.75rem 1.5rem;
+ border: none;
+ border-radius: 8px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ font-size: 0.875rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ min-width: 120px;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ color: white;
+}
+
+.btn-primary:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
+}
+
+.btn-secondary {
+ background: rgba(255, 255, 255, 0.1);
+ color: white;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.btn-secondary:hover {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+.btn-cancel {
+ background: rgba(239, 68, 68, 0.1);
+ color: #ef4444;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+}
+
+.btn-cancel:hover {
+ background: rgba(239, 68, 68, 0.2);
+}
+
+/* Loading State */
+.loading-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 4rem 2rem;
+}
+
+.loading-content {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.spinner {
+ width: 2rem;
+ height: 2rem;
+ border: 3px solid rgba(59, 130, 246, 0.3);
+ border-top: 3px solid #3b82f6;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+.loading-text {
+ color: rgba(255, 255, 255, 0.7);
+ font-size: 0.875rem;
+}
+
+/* Empty State */
+.empty-state {
+ text-align: center;
+ padding: 4rem 2rem;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+}
+
+.empty-icon {
+ width: 4rem;
+ height: 4rem;
+ color: rgba(255, 255, 255, 0.4);
+ margin: 0 auto 1rem;
+}
+
+.empty-title {
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: white;
+ margin: 0 0 0.5rem 0;
+}
+
+.empty-description {
+ color: rgba(255, 255, 255, 0.6);
+ margin: 0;
+}
+
+/* Forms */
+.form-card {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+ padding: 2rem;
+ margin-bottom: 2rem;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.form-title {
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: white;
+ margin: 0 0 1.5rem 0;
+}
+
+.form-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.field-group {
+ display: flex;
+ flex-direction: column;
+}
+
+.form-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1rem;
+}
+
+/* Auth Pages */
+.auth-form {
+ max-width: 400px;
+}
+
+.auth-form-field {
+ margin-bottom: 1rem;
+}
+
+.auth-input {
+ width: 100%;
+ padding: 0.5rem;
+ margin-top: 0.25rem;
+}
+
+.auth-error {
+ color: red;
+ margin-bottom: 1rem;
+ padding: 0.5rem;
+ background-color: #fee;
+ border-radius: 4px;
+}
+
+.auth-button {
+ width: 100%;
+ padding: 0.75rem;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.auth-button:disabled {
+ cursor: not-allowed;
+}
+
+.auth-button.primary {
+ background-color: #28a745;
+}
+
+.auth-button.secondary {
+ background-color: #007bff;
+}
+
+.auth-links {
+ margin-top: 1rem;
+}
+
+/* Todos */
+
+.todo-form {
+ width: 100%;
+}
+
+/* Todo List */
+.todos-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.todo-card {
+ background: rgba(255, 255, 255, 0.03);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ transition: all 0.2s ease;
+ overflow: hidden;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.todo-card:hover {
+ border-color: rgba(255, 255, 255, 0.2);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+}
+
+.todo-card.completed {
+ opacity: 0.7;
+}
+
+/* Todo Content */
+.todo-content {
+ padding: 1rem 1.5rem;
+}
+
+.todo-edit {
+ padding: 1.5rem;
+ min-height: 200px;
+}
+
+.edit-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.edit-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1.5rem;
+}
+
+.todo-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+}
+
+.todo-title-btn {
+ background: none;
+ border: none;
+ padding: 0;
+ text-align: left;
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: white;
+ cursor: pointer;
+ transition: color 0.2s ease;
+ flex: 1;
+ line-height: 1.4;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ max-width: calc(100% - 140px);
+}
+
+.todo-title-btn:hover {
+ color: #3b82f6;
+}
+
+.todo-title-btn.completed {
+ text-decoration: line-through;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.todo-actions {
+ display: flex;
+ gap: 0.5rem;
+ flex-shrink: 0;
+ min-width: 132px;
+ justify-content: flex-end;
+}
+
+/* Action Buttons */
+.action-btn {
+ width: 40px;
+ height: 40px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ background: rgba(255, 255, 255, 0.05);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ transition: all 0.2s ease;
+ -webkit-text-fill-color: currentColor;
+}
+
+.action-btn-complete {
+ color: #10b981;
+ font-size: 20px;
+}
+
+.action-btn-complete:hover {
+ background: rgba(16, 185, 129, 0.2);
+ color: #34d399;
+}
+
+.action-btn-edit {
+ color: #3b82f6;
+}
+
+.action-btn-edit:hover {
+ background: rgba(59, 130, 246, 0.2);
+ color: #60a5fa;
+}
+
+.action-btn-delete {
+ color: #ef4444;
+}
+
+.action-btn-delete:hover {
+ background: rgba(239, 68, 68, 0.2);
+ color: #f87171;
+}
+
+/* Add Todo Button */
+.add-todo-btn {
+ width: 36px;
+ height: 36px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-size: 18px;
+ font-weight: normal;
+ -webkit-text-fill-color: white;
+ transition: all 0.2s ease;
+}
+
+.add-todo-btn:hover {
+ transform: scale(1.1);
+ box-shadow: 0 4px 20px rgba(59, 130, 246, 0.4);
+}
+
+/* Todo Details */
+.todo-details {
+ margin-top: 1rem;
+ padding-top: 1rem;
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.description {
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 8px;
+ padding: 1rem;
+ margin-bottom: 1rem;
+}
+
+.description p {
+ margin: 0;
+ color: rgba(255, 255, 255, 0.8);
+ line-height: 1.6;
+}
+
+.description.completed p {
+ text-decoration: line-through;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.todo-meta {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+}
+
+.meta-dates {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.meta-item {
+ font-size: 0.75rem;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.completion-badge {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ font-size: 0.75rem;
+ color: #10b981;
+ font-weight: 500;
+}
+
+.completion-icon {
+ width: 0.875rem;
+ height: 0.875rem;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .nav-container {
+ padding: 1rem;
+ flex-direction: column;
+ gap: 1rem;
+ }
+
+ .nav-links {
+ gap: 1rem;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+
+ .container {
+ padding: 1rem;
+ }
+
+ .form-actions {
+ flex-direction: column;
+ }
+
+ .edit-actions {
+ flex-direction: column;
+ }
+
+ .todo-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1rem;
+ }
+
+ .todo-actions {
+ align-self: stretch;
+ justify-content: center;
+ }
+
+ .meta-dates {
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+
+ .todo-meta {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ }
+}
+
+/* File Upload */
+.file-upload-btn {
+ min-height: 120px;
+ flex-direction: column;
+ gap: 0.5rem;
+ width: 100%;
+ border: 2px dashed rgba(255, 255, 255, 0.3);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.file-upload-btn:hover {
+ border-color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.file-upload-info {
+ margin-top: 0.5rem;
+ font-size: 0.875rem;
+ color: rgba(255, 255, 255, 0.8);
+}
+
+/* File Table */
+.file-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.file-table th {
+ padding: 0.75rem;
+ text-align: left;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 500;
+ font-size: 0.875rem;
+}
+
+.file-table th:last-child {
+ text-align: center;
+}
+
+.file-table td {
+ padding: 0.75rem;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.file-table tr:hover {
+ background-color: rgba(255, 255, 255, 0.02);
+}
+
+.file-name {
+ color: white;
+ font-weight: 500;
+}
+
+.file-meta {
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 0.875rem;
+}
+
+.file-actions {
+ display: flex;
+ gap: 0.5rem;
+ justify-content: center;
+}
+
+/* Responsive File Table */
+@media (max-width: 768px) {
+ .file-table {
+ font-size: 0.875rem;
+ }
+
+ .file-table th,
+ .file-table td {
+ padding: 0.5rem;
+ }
+
+ .file-actions {
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/layout.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/layout.tsx
new file mode 100644
index 000000000..e93ef5a33
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/layout.tsx
@@ -0,0 +1,23 @@
+import type { Metadata } from "next";
+import "./globals.css";
+import Navigation from "../components/Navigation";
+
+export const metadata: Metadata = {
+ title: "Nhost Next.js Tutorial",
+ description: "Next.js tutorial with Nhost authentication",
+};
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+
+ {children}
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/page.module.css b/examples/tutorials/nhost-nextjs-tutorial/src/app/page.module.css
new file mode 100644
index 000000000..58c71af9e
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/page.module.css
@@ -0,0 +1,167 @@
+.page {
+ --gray-rgb: 0, 0, 0;
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.08);
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.05);
+
+ --button-primary-hover: #383838;
+ --button-secondary-hover: #f2f2f2;
+
+ display: grid;
+ grid-template-rows: 20px 1fr 20px;
+ align-items: center;
+ justify-items: center;
+ min-height: 100svh;
+ padding: 80px;
+ gap: 64px;
+ font-family: var(--font-geist-sans);
+}
+
+@media (prefers-color-scheme: dark) {
+ .page {
+ --gray-rgb: 255, 255, 255;
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.145);
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.06);
+
+ --button-primary-hover: #ccc;
+ --button-secondary-hover: #1a1a1a;
+ }
+}
+
+.main {
+ display: flex;
+ flex-direction: column;
+ gap: 32px;
+ grid-row-start: 2;
+}
+
+.main ol {
+ font-family: var(--font-geist-mono);
+ padding-left: 0;
+ margin: 0;
+ font-size: 14px;
+ line-height: 24px;
+ letter-spacing: -0.01em;
+ list-style-position: inside;
+}
+
+.main li:not(:last-of-type) {
+ margin-bottom: 8px;
+}
+
+.main code {
+ font-family: inherit;
+ background: var(--gray-alpha-100);
+ padding: 2px 4px;
+ border-radius: 4px;
+ font-weight: 600;
+}
+
+.ctas {
+ display: flex;
+ gap: 16px;
+}
+
+.ctas a {
+ appearance: none;
+ border-radius: 128px;
+ height: 48px;
+ padding: 0 20px;
+ border: 1px solid transparent;
+ transition:
+ background 0.2s,
+ color 0.2s,
+ border-color 0.2s;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 16px;
+ line-height: 20px;
+ font-weight: 500;
+}
+
+a.primary {
+ background: var(--foreground);
+ color: var(--background);
+ gap: 8px;
+}
+
+a.secondary {
+ border-color: var(--gray-alpha-200);
+ min-width: 158px;
+}
+
+.footer {
+ grid-row-start: 3;
+ display: flex;
+ gap: 24px;
+}
+
+.footer a {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.footer img {
+ flex-shrink: 0;
+}
+
+/* Enable hover only on non-touch devices */
+@media (hover: hover) and (pointer: fine) {
+ a.primary:hover {
+ background: var(--button-primary-hover);
+ border-color: transparent;
+ }
+
+ a.secondary:hover {
+ background: var(--button-secondary-hover);
+ border-color: transparent;
+ }
+
+ .footer a:hover {
+ text-decoration: underline;
+ text-underline-offset: 4px;
+ }
+}
+
+@media (max-width: 600px) {
+ .page {
+ padding: 32px;
+ padding-bottom: 80px;
+ }
+
+ .main {
+ align-items: center;
+ }
+
+ .main ol {
+ text-align: center;
+ }
+
+ .ctas {
+ flex-direction: column;
+ }
+
+ .ctas a {
+ font-size: 14px;
+ height: 40px;
+ padding: 0 16px;
+ }
+
+ a.secondary {
+ min-width: auto;
+ }
+
+ .footer {
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: center;
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ .logo {
+ filter: invert();
+ }
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/page.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/page.tsx
new file mode 100644
index 000000000..5398ed7c2
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/page.tsx
@@ -0,0 +1,24 @@
+import { createNhostClient } from "../lib/nhost/server";
+
+export default async function Home() {
+ const nhost = await createNhostClient();
+ const session = nhost.getUserSession();
+
+ return (
+
+
+ Welcome to Nhost Next.js Demo
+
+
+ {session ? (
+
+
Hello, {session.user?.displayName || session.user?.email}!
+
+ ) : (
+
+
You are not signed in.
+
+ )}
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/profile/page.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/profile/page.tsx
new file mode 100644
index 000000000..004baf4a8
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/profile/page.tsx
@@ -0,0 +1,55 @@
+import { createNhostClient } from "../../lib/nhost/server";
+
+export default async function Profile() {
+ // Create the client with async cookie access
+ const nhost = await createNhostClient();
+ const session = nhost.getUserSession();
+
+ return (
+
+
+
+
+
User Information
+
+
+ Display Name: {" "}
+ {session?.user?.displayName || "Not set"}
+
+
+ Email: {session?.user?.email || "Not available"}
+
+
+ User ID: {session?.user?.id || "Not available"}
+
+
+ Roles: {session?.user?.roles?.join(", ") || "None"}
+
+
+ Email Verified:
+
+ {session?.user?.emailVerified ? "✓ Yes" : "✗ No"}
+
+
+
+
+
+
+
Session Information
+
+
+ {JSON.stringify(session, null, 2)}
+
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/signin/SignInForm.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/signin/SignInForm.tsx
new file mode 100644
index 000000000..3f4d73f21
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/signin/SignInForm.tsx
@@ -0,0 +1,75 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { useId, useState } from "react";
+import { signIn } from "./actions";
+
+interface SignInFormProps {
+ initialError?: string;
+}
+
+export default function SignInForm({ initialError }: SignInFormProps) {
+ const [error, setError] = useState(initialError);
+ const [isLoading, setIsLoading] = useState(false);
+ const router = useRouter();
+
+ const emailId = useId();
+ const passwordId = useId();
+
+ const handleSubmit = async (formData: FormData) => {
+ setIsLoading(true);
+ setError(undefined);
+
+ try {
+ const result = await signIn(formData);
+
+ if (result.redirect) {
+ router.push(result.redirect);
+ } else if (result.error) {
+ setError(result.error);
+ }
+ } catch (err: unknown) {
+ setError(
+ err instanceof Error ? err.message : "An error occurred during sign in",
+ );
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+
+
+ Email
+
+
+
+
+ Password
+
+
+
+ {error && {error}
}
+
+
+ {isLoading ? "Signing In..." : "Sign In"}
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/signin/actions.ts b/examples/tutorials/nhost-nextjs-tutorial/src/app/signin/actions.ts
new file mode 100644
index 000000000..cc9b1a3f2
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/signin/actions.ts
@@ -0,0 +1,38 @@
+"use server";
+
+import type { ErrorResponse } from "@nhost/nhost-js/auth";
+import type { FetchError } from "@nhost/nhost-js/fetch";
+import { createNhostClient } from "../../lib/nhost/server";
+
+export async function signIn(formData: FormData) {
+ const email = formData.get("email") as string;
+ const password = formData.get("password") as string;
+
+ if (!email || !password) {
+ return {
+ error: "Email and password are required",
+ };
+ }
+
+ try {
+ const nhost = await createNhostClient();
+
+ const response = await nhost.auth.signInEmailPassword({
+ email,
+ password,
+ });
+
+ if (response.body?.session) {
+ return { redirect: "/profile" };
+ } else {
+ return {
+ error: "Failed to sign in. Please check your credentials.",
+ };
+ }
+ } catch (err) {
+ const error = err as FetchError;
+ return {
+ error: `An error occurred during sign in: ${error.message}`,
+ };
+ }
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/signin/page.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/signin/page.tsx
new file mode 100644
index 000000000..01c7b0137
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/signin/page.tsx
@@ -0,0 +1,25 @@
+import Link from "next/link";
+import SignInForm from "./SignInForm";
+
+export default async function SignIn({
+ searchParams,
+}: {
+ searchParams: Promise<{ error?: string }>;
+}) {
+ // Extract error from URL parameters
+ const params = await searchParams;
+ const error = params?.error;
+
+ return (
+
+
Sign In
+
+
+
+
+ Don't have an account? Sign Up
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/signup/SignUpForm.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/signup/SignUpForm.tsx
new file mode 100644
index 000000000..0da8c02a9
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/signup/SignUpForm.tsx
@@ -0,0 +1,89 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { useId, useState } from "react";
+import { signUp } from "./actions";
+
+interface SignUpFormProps {
+ initialError?: string;
+}
+
+export default function SignUpForm({ initialError }: SignUpFormProps) {
+ const [error, setError] = useState(initialError);
+ const [isLoading, setIsLoading] = useState(false);
+ const router = useRouter();
+
+ const displayNameId = useId();
+ const emailId = useId();
+ const passwordId = useId();
+
+ const handleSubmit = async (formData: FormData) => {
+ setIsLoading(true);
+ setError(undefined);
+
+ try {
+ const result = await signUp(formData);
+
+ if (result.redirect) {
+ router.push(result.redirect);
+ } else if (result.error) {
+ setError(result.error);
+ }
+ } catch (err: unknown) {
+ setError(
+ err instanceof Error ? err.message : "An error occurred during sign up",
+ );
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+
+
+ Display Name
+
+
+
+
+ Email
+
+
+
+
+ Password
+
+ Minimum 8 characters
+
+
+ {error && {error}
}
+
+
+ {isLoading ? "Creating Account..." : "Sign Up"}
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/signup/actions.ts b/examples/tutorials/nhost-nextjs-tutorial/src/app/signup/actions.ts
new file mode 100644
index 000000000..cfff81052
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/signup/actions.ts
@@ -0,0 +1,46 @@
+"use server";
+
+import type { ErrorResponse } from "@nhost/nhost-js/auth";
+import type { FetchError } from "@nhost/nhost-js/fetch";
+import { createNhostClient } from "../../lib/nhost/server";
+
+export async function signUp(formData: FormData) {
+ const email = formData.get("email") as string;
+ const password = formData.get("password") as string;
+ const displayName = formData.get("displayName") as string;
+
+ if (!email || !password || !displayName) {
+ return {
+ error: "All fields are required",
+ };
+ }
+
+ try {
+ const nhost = await createNhostClient();
+
+ const response = await nhost.auth.signUpEmailPassword({
+ email,
+ password,
+ options: {
+ displayName,
+ // Set the redirect URL for email verification
+ redirectTo: `${process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"}/verify`,
+ },
+ });
+
+ if (response.body?.session) {
+ // Successfully signed up and automatically signed in
+ return { redirect: "/profile" };
+ } else {
+ // Verification email sent
+ return {
+ redirect: `/signup?verify=success&email=${encodeURIComponent(email)}`,
+ };
+ }
+ } catch (err) {
+ const error = err as FetchError;
+ return {
+ error: `An error occurred during sign up: ${error.message}`,
+ };
+ }
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/signup/page.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/signup/page.tsx
new file mode 100644
index 000000000..831e8b340
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/signup/page.tsx
@@ -0,0 +1,51 @@
+import Link from "next/link";
+import SignUpForm from "./SignUpForm";
+
+export default async function SignUp({
+ searchParams,
+}: {
+ searchParams: Promise<{
+ error?: string;
+ verify?: string;
+ email?: string;
+ }>;
+}) {
+ // Extract parameters from URL
+ const params = await searchParams;
+ const error = params?.error;
+ const verificationSent = params?.verify === "success";
+ const email = params?.email;
+
+ if (verificationSent) {
+ return (
+
+
Check Your Email
+
+
+ We've sent a verification link to {email}
+
+
+ Please check your email and click the verification link to activate
+ your account.
+
+
+
+ Back to Sign In
+
+
+ );
+ }
+
+ return (
+
+
Sign Up
+
+
+
+
+ Already have an account? Sign In
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/todos/TodosClient.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/todos/TodosClient.tsx
new file mode 100644
index 000000000..10601358c
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/todos/TodosClient.tsx
@@ -0,0 +1,368 @@
+"use client";
+
+import { useId, useState } from "react";
+import { addTodo, deleteTodo, updateTodo } from "./actions";
+import type { Todo } from "./page";
+
+interface TodosClientProps {
+ initialTodos: Todo[];
+ initialError: string | null;
+}
+
+export default function TodosClient({
+ initialTodos,
+ initialError,
+}: TodosClientProps) {
+ const [todos, setTodos] = useState(initialTodos);
+ const [error, setError] = useState(initialError);
+ const [newTodoTitle, setNewTodoTitle] = useState("");
+ const [newTodoDetails, setNewTodoDetails] = useState("");
+ const [editingTodo, setEditingTodo] = useState(null);
+ const [showAddForm, setShowAddForm] = useState(false);
+ const [expandedTodos, setExpandedTodos] = useState>(new Set());
+ const [isLoading, setIsLoading] = useState(false);
+
+ const titleId = useId();
+ const detailsId = useId();
+
+ const handleAddTodo = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!newTodoTitle.trim()) return;
+
+ setIsLoading(true);
+ try {
+ // Call server action to add todo
+ const result = await addTodo({
+ title: newTodoTitle.trim(),
+ details: newTodoDetails.trim() || null,
+ });
+
+ if (result.success && result.todo) {
+ setTodos([result.todo, ...todos]);
+ setNewTodoTitle("");
+ setNewTodoDetails("");
+ setShowAddForm(false);
+ setError(null);
+ } else {
+ setError(result.error || "Failed to add todo");
+ }
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to add todo");
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleUpdateTodo = async (
+ id: string,
+ updates: Partial>,
+ ) => {
+ try {
+ // Call server action to update todo
+ const result = await updateTodo(id, updates);
+
+ if (result.success && result.todo) {
+ setTodos(
+ todos.map((todo) => (todo.id === id ? (result.todo ?? todo) : todo)),
+ );
+ setEditingTodo(null);
+ setError(null);
+ } else {
+ setError(result.error || "Failed to update todo");
+ }
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to update todo");
+ }
+ };
+
+ const handleDeleteTodo = async (id: string) => {
+ if (!confirm("Are you sure you want to delete this todo?")) return;
+
+ try {
+ // Call server action to delete todo
+ const result = await deleteTodo(id);
+
+ if (result.success) {
+ setTodos(todos.filter((todo) => todo.id !== id));
+ setError(null);
+ } else {
+ setError(result.error || "Failed to delete todo");
+ }
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to delete todo");
+ }
+ };
+
+ const toggleComplete = async (todo: Todo) => {
+ await handleUpdateTodo(todo.id, { completed: !todo.completed });
+ };
+
+ const saveEdit = async () => {
+ if (!editingTodo) return;
+ await handleUpdateTodo(editingTodo.id, {
+ title: editingTodo.title,
+ details: editingTodo.details,
+ });
+ };
+
+ const toggleTodoExpansion = (todoId: string) => {
+ const newExpanded = new Set(expandedTodos);
+ if (newExpanded.has(todoId)) {
+ newExpanded.delete(todoId);
+ } else {
+ newExpanded.add(todoId);
+ }
+ setExpandedTodos(newExpanded);
+ };
+
+ return (
+
+
+
+ My Todos
+ {!showAddForm && (
+ setShowAddForm(true)}
+ className="add-todo-btn"
+ title="Add a new todo"
+ >
+ +
+
+ )}
+
+
+
+ {error && (
+
+ Error: {error}
+
+ )}
+
+ {showAddForm && (
+
+
+ Add New Todo
+
+
+ Title *
+ setNewTodoTitle(e.target.value)}
+ placeholder="What needs to be done?"
+ required
+ disabled={isLoading}
+ />
+
+
+ Details
+ setNewTodoDetails(e.target.value)}
+ placeholder="Add some details (optional)..."
+ rows={3}
+ disabled={isLoading}
+ />
+
+
+
+ {isLoading ? "Adding..." : "Add Todo"}
+
+ {
+ setShowAddForm(false);
+ setNewTodoTitle("");
+ setNewTodoDetails("");
+ }}
+ className="btn btn-secondary"
+ disabled={isLoading}
+ >
+ Cancel
+
+
+
+
+
+ )}
+
+ {!showAddForm && (
+
+ {todos.length === 0 ? (
+
+
+
+
+
No todos yet
+
+ Create your first todo to get started!
+
+
+ ) : (
+ todos.map((todo) => (
+
+ {editingTodo?.id === todo.id ? (
+
+
+
+ Title
+
+ setEditingTodo({
+ ...editingTodo,
+ title: e.target.value,
+ })
+ }
+ />
+
+
+ Details
+
+ setEditingTodo({
+ ...editingTodo,
+ details: e.target.value,
+ })
+ }
+ rows={3}
+ />
+
+
+
+ ✓ Save Changes
+
+ setEditingTodo(null)}
+ className="btn btn-cancel"
+ >
+ ✕ Cancel
+
+
+
+
+ ) : (
+
+
+
toggleTodoExpansion(todo.id)}
+ >
+ {todo.title}
+
+
+ toggleComplete(todo)}
+ className="action-btn action-btn-complete"
+ title={
+ todo.completed
+ ? "Mark as incomplete"
+ : "Mark as complete"
+ }
+ >
+ {todo.completed ? "↶" : "✓"}
+
+ setEditingTodo(todo)}
+ className="action-btn action-btn-edit"
+ title="Edit todo"
+ >
+ ✏️
+
+ handleDeleteTodo(todo.id)}
+ className="action-btn action-btn-delete"
+ title="Delete todo"
+ >
+ 🗑️
+
+
+
+
+ {expandedTodos.has(todo.id) && (
+
+ {todo.details && (
+
+ )}
+
+
+
+
+ Created:{" "}
+ {new Date(todo.created_at).toLocaleString()}
+
+
+ Updated:{" "}
+ {new Date(todo.updated_at).toLocaleString()}
+
+
+ {todo.completed && (
+
+ )}
+
+
+ )}
+
+ )}
+
+ ))
+ )}
+
+ )}
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/todos/actions.ts b/examples/tutorials/nhost-nextjs-tutorial/src/app/todos/actions.ts
new file mode 100644
index 000000000..29912daf1
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/todos/actions.ts
@@ -0,0 +1,223 @@
+"use server";
+
+import type { ErrorResponse } from "@nhost/nhost-js/auth";
+import type { FetchError } from "@nhost/nhost-js/fetch";
+import { createNhostClient } from "../../lib/nhost/server";
+import type { Todo } from "./page";
+
+// Response types for server actions
+type ActionResult = {
+ success: boolean;
+ error?: string;
+ todo?: T;
+};
+
+// GraphQL response types
+interface InsertTodoResponse {
+ insert_todos_one: Todo | null;
+}
+
+interface UpdateTodoResponse {
+ update_todos_by_pk: Todo | null;
+}
+
+interface DeleteTodoResponse {
+ delete_todos_by_pk: { id: string } | null;
+}
+
+export async function addTodo(data: {
+ title: string;
+ details: string | null;
+}): Promise> {
+ const { title, details } = data;
+
+ if (!title.trim()) {
+ return {
+ success: false,
+ error: "Title is required",
+ };
+ }
+
+ try {
+ const nhost = await createNhostClient();
+ const session = nhost.getUserSession();
+
+ if (!session) {
+ return {
+ success: false,
+ error: "Not authenticated",
+ };
+ }
+
+ // Execute GraphQL mutation to insert a new todo
+ // user_id is automatically set by Hasura based on JWT token
+ const response = await nhost.graphql.request({
+ query: `
+ mutation InsertTodo($title: String!, $details: String) {
+ insert_todos_one(object: { title: $title, details: $details }) {
+ id
+ title
+ details
+ completed
+ created_at
+ updated_at
+ user_id
+ }
+ }
+ `,
+ variables: {
+ title: title.trim(),
+ details: details?.trim() || null,
+ },
+ });
+
+ if (response.body.errors) {
+ return {
+ success: false,
+ error: response.body.errors[0]?.message || "Failed to add todo",
+ };
+ }
+
+ if (!response.body?.data?.insert_todos_one) {
+ return {
+ success: false,
+ error: "Failed to add todo",
+ };
+ }
+
+ return {
+ success: true,
+ todo: response.body.data.insert_todos_one,
+ };
+ } catch (err) {
+ const error = err as FetchError;
+ return {
+ success: false,
+ error: `Failed to add todo: ${error.message}`,
+ };
+ }
+}
+
+export async function updateTodo(
+ id: string,
+ updates: Partial>,
+): Promise> {
+ if (!id) {
+ return {
+ success: false,
+ error: "Todo ID is required",
+ };
+ }
+
+ try {
+ const nhost = await createNhostClient();
+ const session = nhost.getUserSession();
+
+ if (!session) {
+ return {
+ success: false,
+ error: "Not authenticated",
+ };
+ }
+
+ // Execute GraphQL mutation to update an existing todo by primary key
+ // Hasura permissions ensure users can only update their own todos
+ const response = await nhost.graphql.request({
+ query: `
+ mutation UpdateTodo($id: uuid!, $updates: todos_set_input!) {
+ update_todos_by_pk(pk_columns: { id: $id }, _set: $updates) {
+ id
+ title
+ details
+ completed
+ created_at
+ updated_at
+ user_id
+ }
+ }
+ `,
+ variables: {
+ id,
+ updates,
+ },
+ });
+
+ if (response.body.errors) {
+ return {
+ success: false,
+ error: response.body.errors[0]?.message || "Failed to update todo",
+ };
+ }
+
+ if (!response.body?.data?.update_todos_by_pk) {
+ return {
+ success: false,
+ error: "Failed to update todo",
+ };
+ }
+
+ return {
+ success: true,
+ todo: response.body.data.update_todos_by_pk,
+ };
+ } catch (err) {
+ const error = err as FetchError;
+ return {
+ success: false,
+ error: `Failed to update todo: ${error.message}`,
+ };
+ }
+}
+
+export async function deleteTodo(id: string): Promise {
+ if (!id) {
+ return {
+ success: false,
+ error: "Todo ID is required",
+ };
+ }
+
+ try {
+ const nhost = await createNhostClient();
+ const session = nhost.getUserSession();
+
+ if (!session) {
+ return {
+ success: false,
+ error: "Not authenticated",
+ };
+ }
+
+ // Execute GraphQL mutation to delete a todo by primary key
+ // Hasura permissions ensure users can only delete their own todos
+ const response = await nhost.graphql.request({
+ query: `
+ mutation DeleteTodo($id: uuid!) {
+ delete_todos_by_pk(id: $id) {
+ id
+ }
+ }
+ `,
+ variables: {
+ id,
+ },
+ });
+
+ if (response.body.errors) {
+ return {
+ success: false,
+ error: response.body.errors[0]?.message || "Failed to delete todo",
+ };
+ }
+
+ return {
+ success: true,
+ };
+ } catch (err) {
+ const error = err as FetchError;
+ return {
+ success: false,
+ error: `Failed to delete todo: ${error.message}`,
+ };
+ }
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/todos/page.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/todos/page.tsx
new file mode 100644
index 000000000..aaafaa67e
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/todos/page.tsx
@@ -0,0 +1,65 @@
+import { createNhostClient } from "../../lib/nhost/server";
+import TodosClient from "./TodosClient";
+
+// The interfaces below define the structure of our data
+// They are not strictly necessary but help with type safety
+
+// Represents a single todo item
+export interface Todo {
+ id: string;
+ title: string;
+ details: string | null;
+ completed: boolean;
+ created_at: string;
+ updated_at: string;
+ user_id: string;
+}
+
+// This matches the GraphQL response structure for fetching todos
+// Can be used as a generic type on the request method
+interface GetTodos {
+ todos: Todo[];
+}
+
+export default async function TodosPage() {
+ // Fetch initial todos data server-side
+ const nhost = await createNhostClient();
+ const session = nhost.getUserSession();
+
+ let initialTodos: Todo[] = [];
+ let error: string | null = null;
+
+ if (session) {
+ try {
+ // Make GraphQL request to fetch todos using Nhost server client
+ // The query automatically filters by user_id due to Hasura permissions
+ const response = await nhost.graphql.request({
+ query: `
+ query GetTodos {
+ todos(order_by: { created_at: desc }) {
+ id
+ title
+ details
+ completed
+ created_at
+ updated_at
+ user_id
+ }
+ }
+ `,
+ });
+
+ // Check for GraphQL errors in the response body
+ if (response.body.errors) {
+ error = response.body.errors[0]?.message || "Failed to fetch todos";
+ } else {
+ // Extract todos from the GraphQL response data
+ initialTodos = response.body?.data?.todos || [];
+ }
+ } catch (err) {
+ error = err instanceof Error ? err.message : "Failed to fetch todos";
+ }
+ }
+
+ return ;
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/verify/error/page.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/app/verify/error/page.tsx
new file mode 100644
index 000000000..57a46ba47
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/verify/error/page.tsx
@@ -0,0 +1,40 @@
+import Link from "next/link";
+
+export default async function VerifyError({
+ searchParams,
+}: {
+ searchParams: Promise>;
+}) {
+ const params = await searchParams;
+ const message = params?.message || "Unknown verification error";
+
+ // Filter out the message to show other URL parameters
+ const urlParams = Object.entries(params).filter(([key]) => key !== "message");
+
+ return (
+
+
Email Verification
+
+
+
Verification failed
+
{message}
+
+ {urlParams.length > 0 && (
+
+
URL Parameters:
+ {urlParams.map(([key, value]) => (
+
+ {key}: {" "}
+ {value}
+
+ ))}
+
+ )}
+
+
+ Back to Sign In
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/app/verify/route.ts b/examples/tutorials/nhost-nextjs-tutorial/src/app/verify/route.ts
new file mode 100644
index 000000000..d7cf3639b
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/app/verify/route.ts
@@ -0,0 +1,50 @@
+import type { ErrorResponse } from "@nhost/nhost-js/auth";
+import type { FetchError } from "@nhost/nhost-js/fetch";
+import type { NextRequest } from "next/server";
+import { NextResponse } from "next/server";
+import { createNhostClient } from "../../lib/nhost/server";
+
+export async function GET(request: NextRequest) {
+ const refreshToken = request.nextUrl.searchParams.get("refreshToken");
+
+ if (!refreshToken) {
+ // Collect all query parameters for debugging
+ const params = new URLSearchParams(request.nextUrl.searchParams);
+ params.set("message", "No refresh token provided");
+
+ return NextResponse.redirect(
+ new URL(`/verify/error?${params.toString()}`, request.url),
+ );
+ }
+
+ try {
+ const nhost = await createNhostClient();
+
+ if (nhost.getUserSession()) {
+ // Collect all query parameters
+ const params = new URLSearchParams(request.nextUrl.searchParams);
+ params.set("message", "Already signed in");
+
+ return NextResponse.redirect(
+ new URL(`/verify/error?${params.toString()}`, request.url),
+ );
+ }
+
+ // Process the verification token
+ await nhost.auth.refreshToken({ refreshToken });
+
+ // Redirect to profile on successful verification
+ return NextResponse.redirect(new URL("/profile", request.url));
+ } catch (err) {
+ const error = err as FetchError;
+ const errorMessage = `Failed to verify token: ${error.message}`;
+
+ // Collect all query parameters
+ const params = new URLSearchParams(request.nextUrl.searchParams);
+ params.set("message", errorMessage);
+
+ return NextResponse.redirect(
+ new URL(`/verify/error?${params.toString()}`, request.url),
+ );
+ }
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/components/Navigation.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/components/Navigation.tsx
new file mode 100644
index 000000000..a0f7c5ef0
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/components/Navigation.tsx
@@ -0,0 +1,48 @@
+import Link from "next/link";
+import { createNhostClient } from "../lib/nhost/server";
+import SignOutButton from "./SignOutButton";
+
+export default async function Navigation() {
+ const nhost = await createNhostClient();
+ const session = nhost.getUserSession();
+
+ return (
+
+
+
+ Nhost Next.js Demo
+
+
+
+
+ Home
+
+
+ {session ? (
+ <>
+
+ Todos
+
+
+ Files
+
+
+ Profile
+
+
+ >
+ ) : (
+ <>
+
+ Sign In
+
+
+ Sign Up
+
+ >
+ )}
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/components/SignOutButton.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/components/SignOutButton.tsx
new file mode 100644
index 000000000..75578097b
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/components/SignOutButton.tsx
@@ -0,0 +1,28 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { signOut } from "../lib/nhost/actions";
+
+export default function SignOutButton() {
+ const router = useRouter();
+
+ const handleSignOut = async () => {
+ try {
+ await signOut();
+ router.push("/");
+ router.refresh(); // Refresh to update server components
+ } catch (err) {
+ console.error("Error signing out:", err);
+ }
+ };
+
+ return (
+
+ Sign Out
+
+ );
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/lib/nhost/actions.ts b/examples/tutorials/nhost-nextjs-tutorial/src/lib/nhost/actions.ts
new file mode 100644
index 000000000..5d56322ce
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/lib/nhost/actions.ts
@@ -0,0 +1,22 @@
+"use server";
+
+import { redirect } from "next/navigation";
+import { createNhostClient } from "./server";
+
+export async function signOut() {
+ try {
+ const nhost = await createNhostClient();
+ const session = nhost.getUserSession();
+
+ if (session) {
+ await nhost.auth.signOut({
+ refreshToken: session.refreshToken,
+ });
+ }
+ } catch (err) {
+ console.error("Error signing out:", err);
+ throw err;
+ }
+
+ redirect("/");
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/lib/nhost/server.tsx b/examples/tutorials/nhost-nextjs-tutorial/src/lib/nhost/server.tsx
new file mode 100644
index 000000000..968680e09
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/lib/nhost/server.tsx
@@ -0,0 +1,89 @@
+import { createServerClient, type NhostClient } from "@nhost/nhost-js";
+import { DEFAULT_SESSION_KEY, type Session } from "@nhost/nhost-js/session";
+import { cookies } from "next/headers";
+import type { NextRequest, NextResponse } from "next/server";
+
+const key = DEFAULT_SESSION_KEY;
+
+/**
+ * Creates an Nhost client for use in server components.
+ *
+ * We rely on the vanilla createClient method from the Nhost JS SDK and a SessionStorage
+ * customized to be able to retrieve the session from cookies in Next.js server components.
+ */
+export async function createNhostClient(): Promise {
+ const cookieStore = await cookies();
+
+ const nhost = createServerClient({
+ region: process.env["NHOST_REGION"] || "local",
+ subdomain: process.env["NHOST_SUBDOMAIN"] || "local",
+ storage: {
+ // storage compatible with Next.js server components
+ get: (): Session | null => {
+ const s = cookieStore.get(key)?.value || null;
+ if (!s) {
+ return null;
+ }
+ const session = JSON.parse(s) as Session;
+ return session;
+ },
+ set: (value: Session) => {
+ cookieStore.set(key, JSON.stringify(value));
+ },
+ remove: () => {
+ cookieStore.delete(key);
+ },
+ },
+ });
+
+ return nhost;
+}
+
+/**
+ * Middleware function to handle Nhost authentication and session management.
+ *
+ * This function is designed to be used in Next.js middleware to manage user sessions
+ * and refresh tokens. Refreshing the session needs to be done in the middleware
+ * to ensure that the session is always up-to-date an accessible by both server and client components.
+ *
+ * @param {NextRequest} request - The incoming Next.js request object
+ * @param {NextResponse} response - The outgoing Next.js response object
+ */
+export async function handleNhostMiddleware(
+ request: NextRequest,
+ response: NextResponse,
+): Promise {
+ const nhost = createServerClient({
+ region: process.env["NHOST_REGION"] || "local",
+ subdomain: process.env["NHOST_SUBDOMAIN"] || "local",
+ storage: {
+ // storage compatible with Next.js middleware
+ get: (): Session | null => {
+ const raw = request.cookies.get(key)?.value || null;
+ if (!raw) {
+ return null;
+ }
+ const session = JSON.parse(raw) as Session;
+ return session;
+ },
+ set: (value: Session) => {
+ response.cookies.set({
+ name: key,
+ value: JSON.stringify(value),
+ path: "/",
+ httpOnly: false, //if set to true we can't access it in the client
+ secure: process.env.NODE_ENV === "production",
+ sameSite: "lax",
+ maxAge: 60 * 60 * 24 * 30, // 30 days in seconds
+ });
+ },
+ remove: () => {
+ response.cookies.delete(key);
+ },
+ },
+ });
+
+ // we only want to refresh the session if the token will
+ // expire in the next 60 seconds
+ return await nhost.refreshSession(60);
+}
diff --git a/examples/tutorials/nhost-nextjs-tutorial/src/middleware.ts b/examples/tutorials/nhost-nextjs-tutorial/src/middleware.ts
new file mode 100644
index 000000000..0d524686b
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/src/middleware.ts
@@ -0,0 +1,52 @@
+import type { NextRequest } from "next/server";
+import { NextResponse } from "next/server";
+import { handleNhostMiddleware } from "./lib/nhost/server";
+
+// Define public routes that don't require authentication
+const publicRoutes = ["/", "/signin", "/signup", "/verify", "/verify/error"];
+
+export async function middleware(request: NextRequest) {
+ // Create a response that we'll modify as needed
+ const response = NextResponse.next();
+
+ // Get the current path
+ const path = request.nextUrl.pathname;
+
+ // Check if this is a public route or a public asset
+ const isPublicRoute = publicRoutes.some(
+ (route) => path === route || path.startsWith(`${route}/`),
+ );
+
+ // Handle Nhost authentication and token refresh
+ // Always call this to ensure session is up-to-date
+ // even for public routes, so that session changes are detected
+ const session = await handleNhostMiddleware(request, response);
+
+ // If it's a public route, allow access without checking auth
+ if (isPublicRoute) {
+ return response;
+ }
+
+ // If no session and not a public route, redirect to signin
+ if (!session) {
+ const homeUrl = new URL("/", request.url);
+ return NextResponse.redirect(homeUrl);
+ }
+
+ // Session exists, allow access to protected route
+ return response;
+}
+
+// Define which routes this middleware should run on
+export const config = {
+ matcher: [
+ /*
+ * Match all request paths except:
+ * - _next/static (static files)
+ * - _next/image (image optimization files)
+ * - favicon.ico (favicon file)
+ * - public files (public directory)
+ */
+ "/((?!_next/static|_next/image|favicon.ico|public).*)",
+ ],
+};
diff --git a/examples/tutorials/nhost-nextjs-tutorial/tsconfig.json b/examples/tutorials/nhost-nextjs-tutorial/tsconfig.json
new file mode 100644
index 000000000..c1334095f
--- /dev/null
+++ b/examples/tutorials/nhost-nextjs-tutorial/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/examples/tutorials/nhost-react-tutorial/.gitignore b/examples/tutorials/nhost-react-tutorial/.gitignore
new file mode 100644
index 000000000..a547bf36d
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/examples/tutorials/nhost-react-tutorial/README.md b/examples/tutorials/nhost-react-tutorial/README.md
new file mode 100644
index 000000000..81d5eedf9
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/README.md
@@ -0,0 +1,3 @@
+This is the result of following the tutorial at:
+
+https://docs.nhost.io/getting-started/tutorials/react/1-introduction
diff --git a/examples/tutorials/nhost-react-tutorial/index.html b/examples/tutorials/nhost-react-tutorial/index.html
new file mode 100644
index 000000000..e4b78eae1
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite + React + TS
+
+
+
+
+
+
diff --git a/examples/tutorials/nhost-react-tutorial/package.json b/examples/tutorials/nhost-react-tutorial/package.json
new file mode 100644
index 000000000..c1295e6b8
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "nhost-react-tutorial",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "generate": "echo 'Nothing to do'",
+ "test": "pnpm test:typecheck && pnpm test:lint",
+ "test:typecheck": "tsc --noEmit",
+ "test:lint": "biome check",
+ "format": "biome format --write",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@nhost/nhost-js": "workspace:^",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "react-router-dom": "^7.6.0"
+ },
+ "devDependencies": {
+ "@types/react": "^19.1.10",
+ "@types/react-dom": "^19.1.7",
+ "@vitejs/plugin-react": "^5.0.0",
+ "globals": "^16.3.0",
+ "typescript": "~5.8.3",
+ "vite": "^7.1.2"
+ }
+}
diff --git a/examples/tutorials/nhost-react-tutorial/pnpm-lock.yaml b/examples/tutorials/nhost-react-tutorial/pnpm-lock.yaml
new file mode 100644
index 000000000..194598873
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/pnpm-lock.yaml
@@ -0,0 +1,1133 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@nhost/nhost-js':
+ specifier: workspace:^
+ version: link:../../../packages/nhost-js
+ react:
+ specifier: ^19.1.1
+ version: 19.1.1
+ react-dom:
+ specifier: ^19.1.1
+ version: 19.1.1(react@19.1.1)
+ react-router-dom:
+ specifier: ^7.6.0
+ version: 7.8.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ devDependencies:
+ '@types/react':
+ specifier: ^19.1.10
+ version: 19.1.12
+ '@types/react-dom':
+ specifier: ^19.1.7
+ version: 19.1.9(@types/react@19.1.12)
+ '@vitejs/plugin-react':
+ specifier: ^5.0.0
+ version: 5.0.2(vite@7.1.5)
+ globals:
+ specifier: ^16.3.0
+ version: 16.4.0
+ typescript:
+ specifier: ~5.8.3
+ version: 5.8.3
+ vite:
+ specifier: ^7.1.2
+ version: 7.1.5
+
+packages:
+
+ '@babel/code-frame@7.27.1':
+ resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.28.4':
+ resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.28.4':
+ resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.28.3':
+ resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.27.2':
+ resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.27.1':
+ resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.3':
+ resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.27.1':
+ resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.27.1':
+ resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.4':
+ resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.4':
+ resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1':
+ resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1':
+ resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.27.2':
+ resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.28.4':
+ resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.28.4':
+ resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@esbuild/aix-ppc64@0.25.9':
+ resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.9':
+ resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.9':
+ resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.9':
+ resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.9':
+ resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.9':
+ resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.9':
+ resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.9':
+ resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.9':
+ resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.9':
+ resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.9':
+ resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.9':
+ resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.9':
+ resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.9':
+ resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.9':
+ resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.9':
+ resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.9':
+ resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.9':
+ resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.9':
+ resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.9':
+ resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.9':
+ resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.9':
+ resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.25.9':
+ resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.9':
+ resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.9':
+ resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.9':
+ resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@rolldown/pluginutils@1.0.0-beta.34':
+ resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==}
+
+ '@rollup/rollup-android-arm-eabi@4.50.1':
+ resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.50.1':
+ resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.50.1':
+ resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.50.1':
+ resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.50.1':
+ resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.50.1':
+ resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.50.1':
+ resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.50.1':
+ resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.50.1':
+ resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.50.1':
+ resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.50.1':
+ resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.50.1':
+ resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.50.1':
+ resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.50.1':
+ resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.50.1':
+ resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.50.1':
+ resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.50.1':
+ resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openharmony-arm64@4.50.1':
+ resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.50.1':
+ resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.50.1':
+ resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.50.1':
+ resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==}
+ cpu: [x64]
+ os: [win32]
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/react-dom@19.1.9':
+ resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==}
+ peerDependencies:
+ '@types/react': ^19.0.0
+
+ '@types/react@19.1.12':
+ resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==}
+
+ '@vitejs/plugin-react@5.0.2':
+ resolution: {integrity: sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+
+ browserslist@4.25.4:
+ resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ caniuse-lite@1.0.30001741:
+ resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cookie@1.0.2:
+ resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
+ engines: {node: '>=18'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ debug@4.4.1:
+ resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ electron-to-chromium@1.5.218:
+ resolution: {integrity: sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==}
+
+ esbuild@0.25.9:
+ resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ globals@16.4.0:
+ resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==}
+ engines: {node: '>=18'}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ node-releases@2.0.20:
+ resolution: {integrity: sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ react-dom@19.1.1:
+ resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==}
+ peerDependencies:
+ react: ^19.1.1
+
+ react-refresh@0.17.0:
+ resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
+ engines: {node: '>=0.10.0'}
+
+ react-router-dom@7.8.2:
+ resolution: {integrity: sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==}
+ engines: {node: '>=20.0.0'}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+
+ react-router@7.8.2:
+ resolution: {integrity: sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==}
+ engines: {node: '>=20.0.0'}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+
+ react@19.1.1:
+ resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
+ engines: {node: '>=0.10.0'}
+
+ rollup@4.50.1:
+ resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ scheduler@0.26.0:
+ resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ set-cookie-parser@2.7.1:
+ resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ typescript@5.8.3:
+ resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ update-browserslist-db@1.1.3:
+ resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ vite@7.1.5:
+ resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+snapshots:
+
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.27.1
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.28.4': {}
+
+ '@babel/core@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helpers': 7.28.4
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.1
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.28.3':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.27.2':
+ dependencies:
+ '@babel/compat-data': 7.28.4
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.25.4
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-module-imports@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.27.1': {}
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.27.1': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.4':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+
+ '@babel/parser@7.28.4':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/template@7.27.2':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+
+ '@babel/traverse@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+ debug: 4.4.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.28.4':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@esbuild/aix-ppc64@0.25.9':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/android-arm@0.25.9':
+ optional: true
+
+ '@esbuild/android-x64@0.25.9':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.9':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.9':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.9':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.9':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.9':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.9':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.9':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.9':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.9':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.9':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.9':
+ optional: true
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@rolldown/pluginutils@1.0.0-beta.34': {}
+
+ '@rollup/rollup-android-arm-eabi@4.50.1':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.50.1':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.50.1':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.50.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.50.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.50.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.50.1':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.50.1':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.50.1':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.50.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.50.1':
+ optional: true
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@types/estree@1.0.8': {}
+
+ '@types/react-dom@19.1.9(@types/react@19.1.12)':
+ dependencies:
+ '@types/react': 19.1.12
+
+ '@types/react@19.1.12':
+ dependencies:
+ csstype: 3.1.3
+
+ '@vitejs/plugin-react@5.0.2(vite@7.1.5)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4)
+ '@rolldown/pluginutils': 1.0.0-beta.34
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.17.0
+ vite: 7.1.5
+ transitivePeerDependencies:
+ - supports-color
+
+ browserslist@4.25.4:
+ dependencies:
+ caniuse-lite: 1.0.30001741
+ electron-to-chromium: 1.5.218
+ node-releases: 2.0.20
+ update-browserslist-db: 1.1.3(browserslist@4.25.4)
+
+ caniuse-lite@1.0.30001741: {}
+
+ convert-source-map@2.0.0: {}
+
+ cookie@1.0.2: {}
+
+ csstype@3.1.3: {}
+
+ debug@4.4.1:
+ dependencies:
+ ms: 2.1.3
+
+ electron-to-chromium@1.5.218: {}
+
+ esbuild@0.25.9:
+ 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
+
+ escalade@3.2.0: {}
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ fsevents@2.3.3:
+ optional: true
+
+ gensync@1.0.0-beta.2: {}
+
+ globals@16.4.0: {}
+
+ js-tokens@4.0.0: {}
+
+ jsesc@3.1.0: {}
+
+ json5@2.2.3: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.11: {}
+
+ node-releases@2.0.20: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.3: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ react-dom@19.1.1(react@19.1.1):
+ dependencies:
+ react: 19.1.1
+ scheduler: 0.26.0
+
+ react-refresh@0.17.0: {}
+
+ react-router-dom@7.8.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
+ dependencies:
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ react-router: 7.8.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+
+ react-router@7.8.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
+ dependencies:
+ cookie: 1.0.2
+ react: 19.1.1
+ set-cookie-parser: 2.7.1
+ optionalDependencies:
+ react-dom: 19.1.1(react@19.1.1)
+
+ react@19.1.1: {}
+
+ rollup@4.50.1:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.50.1
+ '@rollup/rollup-android-arm64': 4.50.1
+ '@rollup/rollup-darwin-arm64': 4.50.1
+ '@rollup/rollup-darwin-x64': 4.50.1
+ '@rollup/rollup-freebsd-arm64': 4.50.1
+ '@rollup/rollup-freebsd-x64': 4.50.1
+ '@rollup/rollup-linux-arm-gnueabihf': 4.50.1
+ '@rollup/rollup-linux-arm-musleabihf': 4.50.1
+ '@rollup/rollup-linux-arm64-gnu': 4.50.1
+ '@rollup/rollup-linux-arm64-musl': 4.50.1
+ '@rollup/rollup-linux-loongarch64-gnu': 4.50.1
+ '@rollup/rollup-linux-ppc64-gnu': 4.50.1
+ '@rollup/rollup-linux-riscv64-gnu': 4.50.1
+ '@rollup/rollup-linux-riscv64-musl': 4.50.1
+ '@rollup/rollup-linux-s390x-gnu': 4.50.1
+ '@rollup/rollup-linux-x64-gnu': 4.50.1
+ '@rollup/rollup-linux-x64-musl': 4.50.1
+ '@rollup/rollup-openharmony-arm64': 4.50.1
+ '@rollup/rollup-win32-arm64-msvc': 4.50.1
+ '@rollup/rollup-win32-ia32-msvc': 4.50.1
+ '@rollup/rollup-win32-x64-msvc': 4.50.1
+ fsevents: 2.3.3
+
+ scheduler@0.26.0: {}
+
+ semver@6.3.1: {}
+
+ set-cookie-parser@2.7.1: {}
+
+ source-map-js@1.2.1: {}
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ typescript@5.8.3: {}
+
+ update-browserslist-db@1.1.3(browserslist@4.25.4):
+ dependencies:
+ browserslist: 4.25.4
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ vite@7.1.5:
+ dependencies:
+ esbuild: 0.25.9
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.50.1
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ yallist@3.1.1: {}
diff --git a/examples/tutorials/nhost-react-tutorial/public/vite.svg b/examples/tutorials/nhost-react-tutorial/public/vite.svg
new file mode 100644
index 000000000..e7b8dfb1b
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/tutorials/nhost-react-tutorial/src/App.css b/examples/tutorials/nhost-react-tutorial/src/App.css
new file mode 100644
index 000000000..b9d355df2
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/App.css
@@ -0,0 +1,42 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/App.tsx b/examples/tutorials/nhost-react-tutorial/src/App.tsx
new file mode 100644
index 000000000..c76850ef1
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/App.tsx
@@ -0,0 +1,58 @@
+import {
+ createBrowserRouter,
+ createRoutesFromElements,
+ Navigate,
+ Outlet,
+ Route,
+ RouterProvider,
+} from "react-router-dom";
+import Navigation from "./components/Navigation";
+import ProtectedRoute from "./components/ProtectedRoute";
+import { AuthProvider } from "./lib/nhost/AuthProvider";
+import Files from "./pages/Files";
+import Home from "./pages/Home";
+import Profile from "./pages/Profile";
+import SignIn from "./pages/SignIn";
+import SignUp from "./pages/SignUp";
+import Todos from "./pages/Todos";
+import Verify from "./pages/Verify";
+
+// Root layout component to wrap all routes
+const RootLayout = () => {
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+// Create router with routes
+const router = createBrowserRouter(
+ createRoutesFromElements(
+ }>
+ } />
+ } />
+ } />
+ } />
+ }>
+ } />
+ } />
+ } />
+
+ } />
+ ,
+ ),
+);
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+export default App;
diff --git a/examples/tutorials/nhost-react-tutorial/src/assets/react.svg b/examples/tutorials/nhost-react-tutorial/src/assets/react.svg
new file mode 100644
index 000000000..6c87de9bb
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/tutorials/nhost-react-tutorial/src/components/Navigation.tsx b/examples/tutorials/nhost-react-tutorial/src/components/Navigation.tsx
new file mode 100644
index 000000000..dbbce0e01
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/components/Navigation.tsx
@@ -0,0 +1,67 @@
+import { Link, useNavigate } from "react-router-dom";
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+export default function Navigation() {
+ const { isAuthenticated, session, nhost } = useAuth();
+ const navigate = useNavigate();
+
+ const handleSignOut = async () => {
+ try {
+ if (session) {
+ await nhost.auth.signOut({
+ refreshToken: session.refreshToken,
+ });
+ }
+ navigate("/");
+ } catch (err: unknown) {
+ const message = err instanceof Error ? err.message : String(err);
+ console.error("Error signing out:", message);
+ }
+ };
+
+ return (
+
+
+
+ Nhost React Demo
+
+
+
+
+ Home
+
+
+ {isAuthenticated ? (
+ <>
+
+ Todos
+
+
+ Files
+
+
+ Profile
+
+
+ Sign Out
+
+ >
+ ) : (
+ <>
+
+ Sign In
+
+
+ Sign Up
+
+ >
+ )}
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/components/ProtectedRoute.tsx b/examples/tutorials/nhost-react-tutorial/src/components/ProtectedRoute.tsx
new file mode 100644
index 000000000..528e00fc3
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/components/ProtectedRoute.tsx
@@ -0,0 +1,29 @@
+import { Navigate, Outlet } from "react-router-dom";
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+interface ProtectedRouteProps {
+ redirectTo?: string;
+}
+
+export default function ProtectedRoute({
+ redirectTo = "/signin",
+}: ProtectedRouteProps) {
+ const { isAuthenticated, isLoading } = useAuth();
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (!isAuthenticated) {
+ return ;
+ }
+
+ return ;
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/index.css b/examples/tutorials/nhost-react-tutorial/src/index.css
new file mode 100644
index 000000000..33d33be4f
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/index.css
@@ -0,0 +1,864 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+#root {
+ width: 100%;
+ min-height: 100vh;
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+
+a:hover {
+ color: #535bf2;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+
+button:hover {
+ border-color: #646cff;
+}
+
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+input,
+textarea {
+ width: 100%;
+ padding: 0.875rem 1rem;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 8px;
+ font-size: 0.875rem;
+ transition: all 0.2s ease;
+ background: rgba(255, 255, 255, 0.05);
+ color: white;
+ box-sizing: border-box;
+ font-family: inherit;
+}
+
+input:focus,
+textarea:focus {
+ outline: none;
+ border-color: #3b82f6;
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
+ background: rgba(255, 255, 255, 0.08);
+}
+
+input::placeholder,
+textarea::placeholder {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+textarea {
+ resize: vertical;
+ min-height: 4rem;
+}
+
+label {
+ display: block;
+ margin: 0 0 0.5rem 0;
+ font-weight: 500;
+ color: rgba(255, 255, 255, 0.9);
+ font-size: 0.875rem;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+/* Global Layout */
+.app-content {
+ padding: 0 2rem 2rem;
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+.page-center {
+ text-align: center;
+ padding: 2rem;
+}
+
+.page-header {
+ margin-bottom: 2rem;
+}
+
+.page-title {
+ font-weight: 700;
+ margin: 0;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.margin-bottom {
+ margin-bottom: 1rem;
+}
+
+.margin-top {
+ margin-top: 1rem;
+}
+
+.container {
+ width: 800px;
+ max-width: calc(100vw - 4rem);
+ min-width: 320px;
+ margin: 0 auto;
+ padding: 2rem;
+ box-sizing: border-box;
+ position: relative;
+}
+
+/* Status Messages */
+.success-message {
+ padding: 1rem;
+ background-color: #d4edda;
+ color: #155724;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+}
+
+.error-message {
+ background: rgba(239, 68, 68, 0.1);
+ color: #ef4444;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ border-radius: 12px;
+ padding: 1rem 1.5rem;
+ margin: 1rem 0;
+}
+
+.help-text {
+ color: #666;
+}
+
+.verification-status {
+ color: #28a745;
+ font-size: 1.2rem;
+ font-weight: bold;
+ margin-bottom: 1rem;
+}
+
+.verification-status.error {
+ color: #dc3545;
+ font-size: 1.1rem;
+}
+
+/* Email Verification Status */
+.email-verified {
+ color: #10b981;
+ font-weight: bold;
+ margin-left: 0.5rem;
+}
+
+.email-unverified {
+ color: #ef4444;
+ font-weight: bold;
+ margin-left: 0.5rem;
+}
+
+/* Debug Info */
+.debug-panel {
+ margin-bottom: 1rem;
+ padding: 1rem;
+ background-color: #f8f9fa;
+ border-radius: 8px;
+ text-align: left;
+ max-height: 200px;
+ overflow: auto;
+}
+
+.debug-title {
+ font-weight: bold;
+ margin-bottom: 0.5rem;
+}
+
+.debug-item {
+ margin-bottom: 0.25rem;
+}
+
+.debug-key {
+ font-family: monospace;
+ color: #007bff;
+}
+
+.debug-value {
+ font-family: monospace;
+}
+
+/* Session Display */
+.session-display {
+ font-size: 0.75rem;
+ overflow: auto;
+ margin: 0;
+ line-height: 1.4;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+/* Loading Spinner */
+.spinner-verify {
+ width: 32px;
+ height: 32px;
+ border: 3px solid #f3f3f3;
+ border-top: 3px solid #007bff;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin: 0 auto;
+}
+
+/* Navigation */
+.navigation {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ margin-bottom: 2rem;
+}
+
+.nav-container {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 1rem 2rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.nav-logo {
+ font-size: 1.25rem;
+ font-weight: 700;
+ color: white;
+ text-decoration: none;
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.nav-logo:hover {
+ opacity: 0.8;
+}
+
+.nav-links {
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+}
+
+.nav-link {
+ color: rgba(255, 255, 255, 0.8);
+ text-decoration: none;
+ font-weight: 500;
+ font-size: 0.875rem;
+ padding: 0.5rem 0.75rem;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+ border: none;
+ background: none;
+ cursor: pointer;
+ font-family: inherit;
+}
+
+.nav-link:hover {
+ color: white;
+ background: rgba(255, 255, 255, 0.1);
+}
+
+.nav-button {
+ color: #ef4444;
+}
+
+.nav-button:hover {
+ background: rgba(239, 68, 68, 0.2);
+}
+
+/* Buttons */
+.btn {
+ padding: 0.75rem 1.5rem;
+ border: none;
+ border-radius: 8px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ font-size: 0.875rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ min-width: 120px;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ color: white;
+}
+
+.btn-primary:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
+}
+
+.btn-secondary {
+ background: rgba(255, 255, 255, 0.1);
+ color: white;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.btn-secondary:hover {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+.btn-cancel {
+ background: rgba(239, 68, 68, 0.1);
+ color: #ef4444;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+}
+
+.btn-cancel:hover {
+ background: rgba(239, 68, 68, 0.2);
+}
+
+/* Loading State */
+.loading-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 4rem 2rem;
+}
+
+.loading-content {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.spinner {
+ width: 2rem;
+ height: 2rem;
+ border: 3px solid rgba(59, 130, 246, 0.3);
+ border-top: 3px solid #3b82f6;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+.loading-text {
+ color: rgba(255, 255, 255, 0.7);
+ font-size: 0.875rem;
+}
+
+/* Empty State */
+.empty-state {
+ text-align: center;
+ padding: 4rem 2rem;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+}
+
+.empty-icon {
+ width: 4rem;
+ height: 4rem;
+ color: rgba(255, 255, 255, 0.4);
+ margin: 0 auto 1rem;
+}
+
+.empty-title {
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: white;
+ margin: 0 0 0.5rem 0;
+}
+
+.empty-description {
+ color: rgba(255, 255, 255, 0.6);
+ margin: 0;
+}
+
+/* Forms */
+.form-card {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+ padding: 2rem;
+ margin-bottom: 2rem;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.form-title {
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: white;
+ margin: 0 0 1.5rem 0;
+}
+
+.form-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.field-group {
+ display: flex;
+ flex-direction: column;
+}
+
+.form-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1rem;
+}
+
+/* Auth Pages */
+.auth-form {
+ max-width: 400px;
+}
+
+.auth-form-field {
+ margin-bottom: 1rem;
+}
+
+.auth-input {
+ width: 100%;
+ padding: 0.5rem;
+ margin-top: 0.25rem;
+}
+
+.auth-error {
+ color: red;
+ margin-bottom: 1rem;
+ padding: 0.5rem;
+ background-color: #fee;
+ border-radius: 4px;
+}
+
+.auth-button {
+ width: 100%;
+ padding: 0.75rem;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.auth-button:disabled {
+ cursor: not-allowed;
+}
+
+.auth-button.primary {
+ background-color: #28a745;
+}
+
+.auth-button.secondary {
+ background-color: #007bff;
+}
+
+.auth-links {
+ margin-top: 1rem;
+}
+
+/* Todos */
+
+.todo-form {
+ width: 100%;
+}
+
+/* Todo List */
+.todos-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.todo-card {
+ background: rgba(255, 255, 255, 0.03);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ transition: all 0.2s ease;
+ overflow: hidden;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.todo-card:hover {
+ border-color: rgba(255, 255, 255, 0.2);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+}
+
+.todo-card.completed {
+ opacity: 0.7;
+}
+
+/* Todo Content */
+.todo-content {
+ padding: 1rem 1.5rem;
+}
+
+.todo-edit {
+ padding: 1.5rem;
+ min-height: 200px;
+}
+
+.edit-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.edit-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1.5rem;
+}
+
+.todo-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+}
+
+.todo-title-btn {
+ background: none;
+ border: none;
+ padding: 0;
+ text-align: left;
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: white;
+ cursor: pointer;
+ transition: color 0.2s ease;
+ flex: 1;
+ line-height: 1.4;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ max-width: calc(100% - 140px);
+}
+
+.todo-title-btn:hover {
+ color: #3b82f6;
+}
+
+.todo-title-btn.completed {
+ text-decoration: line-through;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.todo-actions {
+ display: flex;
+ gap: 0.5rem;
+ flex-shrink: 0;
+ min-width: 132px;
+ justify-content: flex-end;
+}
+
+/* Action Buttons */
+.action-btn {
+ width: 40px;
+ height: 40px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ background: rgba(255, 255, 255, 0.05);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ transition: all 0.2s ease;
+ -webkit-text-fill-color: currentColor;
+}
+
+.action-btn-complete {
+ color: #10b981;
+ font-size: 20px;
+}
+
+.action-btn-complete:hover {
+ background: rgba(16, 185, 129, 0.2);
+ color: #34d399;
+}
+
+.action-btn-edit {
+ color: #3b82f6;
+}
+
+.action-btn-edit:hover {
+ background: rgba(59, 130, 246, 0.2);
+ color: #60a5fa;
+}
+
+.action-btn-delete {
+ color: #ef4444;
+}
+
+.action-btn-delete:hover {
+ background: rgba(239, 68, 68, 0.2);
+ color: #f87171;
+}
+
+/* Add Todo Button */
+.add-todo-btn {
+ width: 36px;
+ height: 36px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-size: 18px;
+ font-weight: normal;
+ -webkit-text-fill-color: white;
+ transition: all 0.2s ease;
+}
+
+.add-todo-btn:hover {
+ transform: scale(1.1);
+ box-shadow: 0 4px 20px rgba(59, 130, 246, 0.4);
+}
+
+/* Todo Details */
+.todo-details {
+ margin-top: 1rem;
+ padding-top: 1rem;
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.description {
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 8px;
+ padding: 1rem;
+ margin-bottom: 1rem;
+}
+
+.description p {
+ margin: 0;
+ color: rgba(255, 255, 255, 0.8);
+ line-height: 1.6;
+}
+
+.description.completed p {
+ text-decoration: line-through;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.todo-meta {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+}
+
+.meta-dates {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.meta-item {
+ font-size: 0.75rem;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.completion-badge {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ font-size: 0.75rem;
+ color: #10b981;
+ font-weight: 500;
+}
+
+.completion-icon {
+ width: 0.875rem;
+ height: 0.875rem;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .nav-container {
+ padding: 1rem;
+ flex-direction: column;
+ gap: 1rem;
+ }
+
+ .nav-links {
+ gap: 1rem;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+
+ .container {
+ padding: 1rem;
+ }
+
+ .form-actions {
+ flex-direction: column;
+ }
+
+ .edit-actions {
+ flex-direction: column;
+ }
+
+ .todo-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1rem;
+ }
+
+ .todo-actions {
+ align-self: stretch;
+ justify-content: center;
+ }
+
+ .meta-dates {
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+
+ .todo-meta {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ }
+}
+
+/* File Upload */
+.file-upload-btn {
+ min-height: 120px;
+ flex-direction: column;
+ gap: 0.5rem;
+ width: 100%;
+ border: 2px dashed rgba(255, 255, 255, 0.3);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.file-upload-btn:hover {
+ border-color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.file-upload-info {
+ margin-top: 0.5rem;
+ font-size: 0.875rem;
+ color: rgba(255, 255, 255, 0.8);
+}
+
+/* File Table */
+.file-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.file-table th {
+ padding: 0.75rem;
+ text-align: left;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 500;
+ font-size: 0.875rem;
+}
+
+.file-table th:last-child {
+ text-align: center;
+}
+
+.file-table td {
+ padding: 0.75rem;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.file-table tr:hover {
+ background-color: rgba(255, 255, 255, 0.02);
+}
+
+.file-name {
+ color: white;
+ font-weight: 500;
+}
+
+.file-meta {
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 0.875rem;
+}
+
+.file-actions {
+ display: flex;
+ gap: 0.5rem;
+ justify-content: center;
+}
+
+/* Responsive File Table */
+@media (max-width: 768px) {
+ .file-table {
+ font-size: 0.875rem;
+ }
+
+ .file-table th,
+ .file-table td {
+ padding: 0.5rem;
+ }
+
+ .file-actions {
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/lib/nhost/AuthProvider.tsx b/examples/tutorials/nhost-react-tutorial/src/lib/nhost/AuthProvider.tsx
new file mode 100644
index 000000000..8726cd412
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/lib/nhost/AuthProvider.tsx
@@ -0,0 +1,174 @@
+import { createClient, type NhostClient } from "@nhost/nhost-js";
+import type { Session } from "@nhost/nhost-js/auth";
+import {
+ createContext,
+ type ReactNode,
+ useCallback,
+ useContext,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from "react";
+
+/**
+ * Authentication context interface providing access to user session state and Nhost client.
+ * Used throughout the React application to access authentication-related data and operations.
+ */
+interface AuthContextType {
+ /** Current authenticated user object, null if not authenticated */
+ user: Session["user"] | null;
+ /** Current session object containing tokens and user data, null if no active session */
+ session: Session | null;
+ /** Boolean indicating if user is currently authenticated */
+ isAuthenticated: boolean;
+ /** Boolean indicating if authentication state is still loading */
+ isLoading: boolean;
+ /** Nhost client instance for making authenticated requests */
+ nhost: NhostClient;
+}
+
+// Create React context for authentication state and nhost client
+const AuthContext = createContext(null);
+
+interface AuthProviderProps {
+ children: ReactNode;
+}
+
+/**
+ * AuthProvider component that provides authentication context to the React application.
+ *
+ * This component handles:
+ * - Initializing the Nhost client with default EventEmitterStorage
+ * - Managing authentication state (user, session, loading, authenticated status)
+ * - Cross-tab session synchronization using sessionStorage.onChange events
+ * - Page visibility and focus event handling to maintain session consistency
+ * - Client-side only session management (no server-side rendering)
+ */
+export const AuthProvider = ({ children }: AuthProviderProps) => {
+ const [user, setUser] = useState(null);
+ const [session, setSession] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
+ const lastRefreshTokenIdRef = useRef(null);
+
+ // Initialize Nhost client with default SessionStorage (local storage)
+ const nhost = useMemo(
+ () =>
+ createClient({
+ region: import.meta.env.VITE_NHOST_REGION || "local",
+ subdomain: import.meta.env.VITE_NHOST_SUBDOMAIN || "local",
+ }),
+ [],
+ );
+
+ /**
+ * Handles session reload when refresh token changes.
+ * This detects when the session has been updated from other tabs.
+ *
+ * @param currentRefreshTokenId - The current refresh token ID to compare against stored value
+ */
+ const reloadSession = useCallback(
+ (currentRefreshTokenId: string | null) => {
+ if (currentRefreshTokenId !== lastRefreshTokenIdRef.current) {
+ lastRefreshTokenIdRef.current = currentRefreshTokenId;
+
+ // Update local authentication state to match current session
+ const currentSession = nhost.getUserSession();
+ setUser(currentSession?.user || null);
+ setSession(currentSession);
+ setIsAuthenticated(!!currentSession);
+ }
+ },
+ [nhost],
+ );
+
+ // Initialize authentication state and set up cross-tab session synchronization
+ useEffect(() => {
+ setIsLoading(true);
+
+ // Load initial session state from Nhost client
+ const currentSession = nhost.getUserSession();
+ setUser(currentSession?.user || null);
+ setSession(currentSession);
+ setIsAuthenticated(!!currentSession);
+ lastRefreshTokenIdRef.current = currentSession?.refreshTokenId ?? null;
+ setIsLoading(false);
+
+ // Subscribe to session changes from other browser tabs
+ // This enables real-time synchronization when user signs in/out in another tab
+ const unsubscribe = nhost.sessionStorage.onChange((session) => {
+ reloadSession(session?.refreshTokenId ?? null);
+ });
+
+ return unsubscribe;
+ }, [nhost, reloadSession]);
+
+ // Handle session changes from page focus events (for additional session consistency)
+ useEffect(() => {
+ /**
+ * Checks for session changes when page becomes visible or focused.
+ * In the React SPA context, this provides additional consistency checks
+ * though it's less critical than in the Next.js SSR version.
+ */
+ const checkSessionOnFocus = () => {
+ reloadSession(nhost.getUserSession()?.refreshTokenId ?? null);
+ };
+
+ // Monitor page visibility changes (tab switching, window minimizing)
+ document.addEventListener("visibilitychange", () => {
+ if (!document.hidden) {
+ checkSessionOnFocus();
+ }
+ });
+
+ // Monitor window focus events (clicking back into the browser window)
+ window.addEventListener("focus", checkSessionOnFocus);
+
+ // Cleanup event listeners on component unmount
+ return () => {
+ document.removeEventListener("visibilitychange", checkSessionOnFocus);
+ window.removeEventListener("focus", checkSessionOnFocus);
+ };
+ }, [nhost, reloadSession]);
+
+ const value: AuthContextType = {
+ user,
+ session,
+ isAuthenticated,
+ isLoading,
+ nhost,
+ };
+
+ return {children} ;
+};
+
+/**
+ * Custom hook to access the authentication context.
+ *
+ * Must be used within a component wrapped by AuthProvider.
+ * Provides access to current user session, authentication state, and Nhost client.
+ *
+ * @throws {Error} When used outside of AuthProvider
+ * @returns {AuthContextType} Authentication context containing user, session, and client
+ *
+ * @example
+ * ```tsx
+ * function MyComponent() {
+ * const { user, isAuthenticated, nhost } = useAuth();
+ *
+ * if (!isAuthenticated) {
+ * return Please sign in
;
+ * }
+ *
+ * return Welcome, {user?.displayName}!
;
+ * }
+ * ```
+ */
+export const useAuth = (): AuthContextType => {
+ const context = useContext(AuthContext);
+ if (!context) {
+ throw new Error("useAuth must be used within an AuthProvider");
+ }
+ return context;
+};
diff --git a/examples/tutorials/nhost-react-tutorial/src/main.tsx b/examples/tutorials/nhost-react-tutorial/src/main.tsx
new file mode 100644
index 000000000..ab6fdb86b
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import "./index.css";
+import App from "./App.tsx";
+
+createRoot(document.getElementById("root") as HTMLElement).render(
+
+
+ ,
+);
diff --git a/examples/tutorials/nhost-react-tutorial/src/pages/Files.tsx b/examples/tutorials/nhost-react-tutorial/src/pages/Files.tsx
new file mode 100644
index 000000000..0b3c9fe9a
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/pages/Files.tsx
@@ -0,0 +1,404 @@
+import type { FileMetadata } from "@nhost/nhost-js/storage";
+import { type JSX, useCallback, useEffect, useRef, useState } from "react";
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+interface DeleteStatus {
+ message: string;
+ isError: boolean;
+}
+
+interface GraphqlGetFilesResponse {
+ files: FileMetadata[];
+}
+
+function formatFileSize(bytes: number): string {
+ if (bytes === 0) return "0 Bytes";
+
+ const sizes: string[] = ["Bytes", "KB", "MB", "GB", "TB"];
+ const i: number = Math.floor(Math.log(bytes) / Math.log(1024));
+
+ return `${parseFloat((bytes / 1024 ** i).toFixed(2))} ${sizes[i]}`;
+}
+
+export default function Files(): JSX.Element {
+ const { isAuthenticated, nhost } = useAuth();
+ const fileInputRef = useRef(null);
+ const [selectedFile, setSelectedFile] = useState(null);
+ const [uploading, setUploading] = useState(false);
+ const [uploadResult, setUploadResult] = useState(null);
+ const [isFetching, setIsFetching] = useState(true);
+ const [error, setError] = useState(null);
+ const [files, setFiles] = useState([]);
+ const [viewingFile, setViewingFile] = useState(null);
+ const [deleting, setDeleting] = useState(null);
+ const [deleteStatus, setDeleteStatus] = useState(null);
+
+ const fetchFiles = useCallback(async (): Promise => {
+ setIsFetching(true);
+ setError(null);
+
+ try {
+ // Use GraphQL to fetch files from the storage system
+ // Files are automatically filtered by user permissions
+ const response = await nhost.graphql.request({
+ query: `query GetFiles {
+ files {
+ id
+ name
+ size
+ mimeType
+ bucketId
+ uploadedByUserId
+ }
+ }`,
+ });
+
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to fetch files",
+ );
+ }
+
+ setFiles(response.body.data?.files || []);
+ } catch (err) {
+ console.error("Error fetching files:", err);
+ setError("Failed to load files. Please try refreshing the page.");
+ } finally {
+ setIsFetching(false);
+ }
+ }, [nhost.graphql]);
+
+ useEffect(() => {
+ if (isAuthenticated) {
+ fetchFiles();
+ }
+ }, [isAuthenticated, fetchFiles]);
+
+ const handleFileChange = (e: React.ChangeEvent): void => {
+ if (e.target.files && e.target.files.length > 0) {
+ const file = e.target.files[0];
+ if (file) {
+ setSelectedFile(file);
+ setError(null);
+ setUploadResult(null);
+ }
+ }
+ };
+
+ const handleUpload = async (): Promise => {
+ if (!selectedFile) {
+ setError("Please select a file to upload");
+ return;
+ }
+
+ setUploading(true);
+ setError(null);
+
+ try {
+ // Upload file to the personal bucket
+ // The uploadedByUserId is automatically set by the storage permissions
+ const response = await nhost.storage.uploadFiles({
+ "bucket-id": "personal",
+ "file[]": [selectedFile],
+ });
+
+ const uploadedFile = response.body.processedFiles?.[0];
+ if (uploadedFile === undefined) {
+ throw new Error("Failed to upload file");
+ }
+ setUploadResult(uploadedFile);
+
+ // Clear the form
+ setSelectedFile(null);
+ if (fileInputRef.current) {
+ fileInputRef.current.value = "";
+ }
+
+ // Update the files list
+ setFiles((prevFiles) => [uploadedFile, ...prevFiles]);
+
+ await fetchFiles();
+
+ // Clear success message after 3 seconds
+ setTimeout(() => {
+ setUploadResult(null);
+ }, 3000);
+ } catch (err: unknown) {
+ const message = (err as Error).message || "An unknown error occurred";
+ setError(`Failed to upload file: ${message}`);
+ } finally {
+ setUploading(false);
+ }
+ };
+
+ const handleViewFile = async (
+ fileId: string,
+ fileName: string,
+ mimeType: string,
+ ): Promise => {
+ setViewingFile(fileId);
+
+ try {
+ // Get the file from storage
+ const response = await nhost.storage.getFile(fileId);
+
+ const url = URL.createObjectURL(response.body);
+
+ // Handle different file types appropriately
+ if (
+ mimeType.startsWith("image/") ||
+ mimeType === "application/pdf" ||
+ mimeType.startsWith("text/") ||
+ mimeType.startsWith("video/") ||
+ mimeType.startsWith("audio/")
+ ) {
+ // Open viewable files in new tab
+ window.open(url, "_blank");
+ } else {
+ // Download other file types
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = fileName;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+
+ // Show download confirmation
+ const newWindow = window.open("", "_blank", "width=400,height=200");
+ if (newWindow) {
+ newWindow.document.documentElement.innerHTML = `
+
+ File Download
+
+
+
+ Downloading: ${fileName}
+ Your download has started. You can close this window.
+
+ `;
+ }
+ }
+ } catch (err) {
+ const message = (err as Error).message || "An unknown error occurred";
+ setError(`Failed to view file: ${message}`);
+ console.error("Error viewing file:", err);
+ } finally {
+ setViewingFile(null);
+ }
+ };
+
+ const handleDeleteFile = async (fileId: string): Promise => {
+ if (!fileId || deleting) return;
+
+ setDeleting(fileId);
+ setError(null);
+ setDeleteStatus(null);
+
+ const fileToDelete = files.find((file) => file.id === fileId);
+ const fileName = fileToDelete?.name || "File";
+
+ try {
+ // Delete file from storage
+ // Permissions ensure users can only delete their own files
+ await nhost.storage.deleteFile(fileId);
+
+ setDeleteStatus({
+ message: `${fileName} deleted successfully`,
+ isError: false,
+ });
+
+ // Remove from local state
+ setFiles(files.filter((file) => file.id !== fileId));
+
+ await fetchFiles();
+
+ // Clear success message after 3 seconds
+ setTimeout(() => {
+ setDeleteStatus(null);
+ }, 3000);
+ } catch (err) {
+ const message = (err as Error).message || "An unknown error occurred";
+ setDeleteStatus({
+ message: `Failed to delete ${fileName}: ${message}`,
+ isError: true,
+ });
+ console.error("Error deleting file:", err);
+ } finally {
+ setDeleting(null);
+ }
+ };
+
+ return (
+
+
+
+
+
Upload a File
+
+
+
+
fileInputRef.current?.click()}
+ >
+
+
+
+ Click to select a file
+ {selectedFile && (
+
+ {selectedFile.name} ({formatFileSize(selectedFile.size)})
+
+ )}
+
+
+
+ {error &&
{error}
}
+
+ {uploadResult && (
+
File uploaded successfully!
+ )}
+
+
+ {uploading ? "Uploading..." : "Upload File"}
+
+
+
+
+
Your Files
+
+ {deleteStatus && (
+
+ {deleteStatus.message}
+
+ )}
+
+ {isFetching ? (
+
+ ) : files.length === 0 ? (
+
+
+
+
+
No files yet
+
+ Upload your first file to get started!
+
+
+ ) : (
+
+
+
+
+ Name
+ Type
+ Size
+ Actions
+
+
+
+ {files.map((file) => (
+
+ {file.name}
+ {file.mimeType}
+
+ {formatFileSize(file.size || 0)}
+
+
+
+
+ handleViewFile(
+ file.id || "unknown",
+ file.name || "unknown",
+ file.mimeType || "unknown",
+ )
+ }
+ disabled={viewingFile === file.id}
+ className="action-btn action-btn-edit"
+ title="View File"
+ >
+ {viewingFile === file.id ? "⏳" : "👁️"}
+
+ handleDeleteFile(file.id || "unknown")}
+ disabled={deleting === file.id}
+ className="action-btn action-btn-delete"
+ title="Delete File"
+ >
+ {deleting === file.id ? "⏳" : "🗑️"}
+
+
+
+
+ ))}
+
+
+
+ )}
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/pages/Home.tsx b/examples/tutorials/nhost-react-tutorial/src/pages/Home.tsx
new file mode 100644
index 000000000..d4e85bf02
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/pages/Home.tsx
@@ -0,0 +1,23 @@
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+export default function Home() {
+ const { isAuthenticated, user } = useAuth();
+
+ return (
+
+
+ Welcome to Nhost React Demo
+
+
+ {isAuthenticated ? (
+
+
Hello, {user?.displayName || user?.email}!
+
+ ) : (
+
+
You are not signed in.
+
+ )}
+
+ );
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/pages/Profile.tsx b/examples/tutorials/nhost-react-tutorial/src/pages/Profile.tsx
new file mode 100644
index 000000000..9df3c4d67
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/pages/Profile.tsx
@@ -0,0 +1,50 @@
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+export default function Profile() {
+ const { user, session } = useAuth();
+
+ return (
+
+
+
+
+
User Information
+
+
+ Display Name: {user?.displayName || "Not set"}
+
+
+ Email: {user?.email || "Not available"}
+
+
+ User ID: {user?.id || "Not available"}
+
+
+ Roles: {user?.roles?.join(", ") || "None"}
+
+
+ Email Verified:
+
+ {user?.emailVerified ? "✓ Yes" : "✗ No"}
+
+
+
+
+
+
+
Session Information
+
+
+ {JSON.stringify(session, null, 2)}
+
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/pages/SignIn.tsx b/examples/tutorials/nhost-react-tutorial/src/pages/SignIn.tsx
new file mode 100644
index 000000000..a5d527bf0
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/pages/SignIn.tsx
@@ -0,0 +1,97 @@
+import { useEffect, useId, useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+export default function SignIn() {
+ const { nhost, isAuthenticated } = useAuth();
+ const navigate = useNavigate();
+
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const emailId = useId();
+ const passwordId = useId();
+
+ // Use useEffect for navigation after authentication is confirmed
+ useEffect(() => {
+ if (isAuthenticated) {
+ navigate("/profile");
+ }
+ }, [isAuthenticated, navigate]);
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setIsLoading(true);
+ setError(null);
+
+ try {
+ // Use the signIn function from auth context
+ const response = await nhost.auth.signInEmailPassword({
+ email,
+ password,
+ });
+
+ // If we have a session, sign in was successful
+ if (response.body?.session) {
+ navigate("/profile");
+ } else {
+ setError("Failed to sign in. Please check your credentials.");
+ }
+ } catch (err) {
+ const message = (err as Error).message || "Unknown error";
+ setError(`An error occurred during sign in: ${message}`);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+
+
Sign In
+
+
+
+ Email
+ setEmail(e.target.value)}
+ required
+ className="auth-input"
+ />
+
+
+
+ Password
+ setPassword(e.target.value)}
+ required
+ className="auth-input"
+ />
+
+
+ {error && {error}
}
+
+
+ {isLoading ? "Signing In..." : "Sign In"}
+
+
+
+
+
+ Don't have an account? Sign Up
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/pages/SignUp.tsx b/examples/tutorials/nhost-react-tutorial/src/pages/SignUp.tsx
new file mode 100644
index 000000000..34c0b12c6
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/pages/SignUp.tsx
@@ -0,0 +1,140 @@
+import { useEffect, useId, useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+export default function SignUp() {
+ const { nhost, isAuthenticated } = useAuth();
+ const navigate = useNavigate();
+
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [displayName, setDisplayName] = useState("");
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [success, setSuccess] = useState(false);
+ const displayNameId = useId();
+
+ const emailId = useId();
+ const passwordId = useId();
+
+ // Redirect authenticated users to profile
+ useEffect(() => {
+ if (isAuthenticated) {
+ navigate("/profile");
+ }
+ }, [isAuthenticated, navigate]);
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setIsLoading(true);
+ setError(null);
+ setSuccess(false);
+
+ try {
+ const response = await nhost.auth.signUpEmailPassword({
+ email,
+ password,
+ options: {
+ displayName,
+ // Set the redirect URL for email verification
+ redirectTo: `${window.location.origin}/verify`,
+ },
+ });
+
+ if (response.body?.session) {
+ // Successfully signed up and automatically signed in
+ navigate("/profile");
+ } else {
+ // Verification email sent
+ setSuccess(true);
+ }
+ } catch (err) {
+ const message = (err as Error).message || "Unknown error";
+ setError(`An error occurred during sign up: ${message}`);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ if (success) {
+ return (
+
+
Check Your Email
+
+
+ We've sent a verification link to {email}
+
+
+ Please check your email and click the verification link to activate
+ your account.
+
+
+
+ Back to Sign In
+
+
+ );
+ }
+
+ return (
+
+
Sign Up
+
+
+
+ Display Name
+ setDisplayName(e.target.value)}
+ required
+ className="auth-input"
+ />
+
+
+
+ Email
+ setEmail(e.target.value)}
+ required
+ className="auth-input"
+ />
+
+
+
+ Password
+ setPassword(e.target.value)}
+ required
+ minLength={8}
+ className="auth-input"
+ />
+ Minimum 8 characters
+
+
+ {error && {error}
}
+
+
+ {isLoading ? "Creating Account..." : "Sign Up"}
+
+
+
+
+
+ Already have an account? Sign In
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/pages/Todos.tsx b/examples/tutorials/nhost-react-tutorial/src/pages/Todos.tsx
new file mode 100644
index 000000000..71ec0c137
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/pages/Todos.tsx
@@ -0,0 +1,504 @@
+import type { JSX } from "react";
+import { useCallback, useEffect, useId, useState } from "react";
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+// The interfaces below define the structure of our data
+// They are not strictly necessary but help with type safety
+
+// Represents a single todo item
+interface Todo {
+ id: string;
+ title: string;
+ details: string | null;
+ completed: boolean;
+ created_at: string;
+ updated_at: string;
+ user_id: string;
+}
+
+// This matches the GraphQL response structure for fetching todos
+// Can be used as a generic type on the request method
+interface GetTodos {
+ todos: Todo[];
+}
+
+// This matches the GraphQL response structure for inserting a todo
+// Can be used as a generic type on the request method
+interface InsertTodo {
+ insert_todos_one: Todo | null;
+}
+
+// This matches the GraphQL response structure for updating a todo
+// Can be used as a generic type on the request method
+interface UpdateTodo {
+ update_todos_by_pk: Todo | null;
+}
+
+export default function Todos(): JSX.Element {
+ const { nhost, session } = useAuth();
+ const [todos, setTodos] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [newTodoTitle, setNewTodoTitle] = useState("");
+ const [newTodoDetails, setNewTodoDetails] = useState("");
+ const [editingTodo, setEditingTodo] = useState(null);
+ const [showAddForm, setShowAddForm] = useState(false);
+ const [expandedTodos, setExpandedTodos] = useState>(new Set());
+
+ const titleId = useId();
+ const detailsId = useId();
+
+ const fetchTodos = useCallback(async () => {
+ try {
+ setLoading(true);
+ // Make GraphQL request to fetch todos using Nhost client
+ // The query automatically filters by user_id due to Hasura permissions
+ const response = await nhost.graphql.request({
+ query: `
+ query GetTodos {
+ todos(order_by: { created_at: desc }) {
+ id
+ title
+ details
+ completed
+ created_at
+ updated_at
+ user_id
+ }
+ }
+ `,
+ });
+
+ // Check for GraphQL errors in the response body
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to fetch todos",
+ );
+ }
+
+ // Extract todos from the GraphQL response data
+ setTodos(response.body?.data?.todos || []);
+ setError(null);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to fetch todos");
+ } finally {
+ setLoading(false);
+ }
+ }, [nhost.graphql]);
+
+ const addTodo = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!newTodoTitle.trim()) return;
+
+ try {
+ // Execute GraphQL mutation to insert a new todo
+ // user_id is automatically set by Hasura based on JWT token
+ const response = await nhost.graphql.request({
+ query: `
+ mutation InsertTodo($title: String!, $details: String) {
+ insert_todos_one(object: { title: $title, details: $details }) {
+ id
+ title
+ details
+ completed
+ created_at
+ updated_at
+ user_id
+ }
+ }
+ `,
+ variables: {
+ title: newTodoTitle.trim(),
+ details: newTodoDetails.trim() || null,
+ },
+ });
+
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to add todo",
+ );
+ }
+
+ if (!response.body?.data?.insert_todos_one) {
+ throw new Error("Failed to add todo");
+ }
+ setTodos([response.body?.data?.insert_todos_one, ...todos]);
+ setNewTodoTitle("");
+ setNewTodoDetails("");
+ setShowAddForm(false);
+ setError(null);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to add todo");
+ }
+ };
+
+ const updateTodo = async (
+ id: string,
+ updates: Partial>,
+ ) => {
+ try {
+ // Execute GraphQL mutation to update an existing todo by primary key
+ // Hasura permissions ensure users can only update their own todos
+ const response = await nhost.graphql.request({
+ query: `
+ mutation UpdateTodo($id: uuid!, $updates: todos_set_input!) {
+ update_todos_by_pk(pk_columns: { id: $id }, _set: $updates) {
+ id
+ title
+ details
+ completed
+ created_at
+ updated_at
+ user_id
+ }
+ }
+ `,
+ variables: {
+ id,
+ updates,
+ },
+ });
+
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to update todo",
+ );
+ }
+
+ if (!response.body?.data?.update_todos_by_pk) {
+ throw new Error("Failed to update todo");
+ }
+
+ const updatedTodo = response.body?.data?.update_todos_by_pk;
+ if (updatedTodo) {
+ setTodos(todos.map((todo) => (todo.id === id ? updatedTodo : todo)));
+ }
+ setEditingTodo(null);
+ setError(null);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to update todo");
+ }
+ };
+
+ const deleteTodo = async (id: string) => {
+ if (!confirm("Are you sure you want to delete this todo?")) return;
+
+ try {
+ // Execute GraphQL mutation to delete a todo by primary key
+ // Hasura permissions ensure users can only delete their own todos
+ const response = await nhost.graphql.request({
+ query: `
+ mutation DeleteTodo($id: uuid!) {
+ delete_todos_by_pk(id: $id) {
+ id
+ }
+ }
+ `,
+ variables: {
+ id,
+ },
+ });
+
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to delete todo",
+ );
+ }
+
+ setTodos(todos.filter((todo) => todo.id !== id));
+ setError(null);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to delete todo");
+ }
+ };
+
+ const toggleComplete = async (todo: Todo) => {
+ await updateTodo(todo.id, { completed: !todo.completed });
+ };
+
+ const saveEdit = async () => {
+ if (!editingTodo) return;
+ await updateTodo(editingTodo.id, {
+ title: editingTodo.title,
+ details: editingTodo.details,
+ });
+ };
+
+ const toggleTodoExpansion = (todoId: string) => {
+ const newExpanded = new Set(expandedTodos);
+ if (newExpanded.has(todoId)) {
+ newExpanded.delete(todoId);
+ } else {
+ newExpanded.add(todoId);
+ }
+ setExpandedTodos(newExpanded);
+ };
+
+ // Fetch todos when user session is available
+ // The session contains the JWT token needed for GraphQL authentication
+ useEffect(() => {
+ if (session) {
+ fetchTodos();
+ }
+ }, [session, fetchTodos]);
+
+ if (!session) {
+ return (
+
+
Please sign in to view your todos.
+
+ );
+ }
+
+ return (
+
+
+
+ My Todos
+ {!showAddForm && (
+ setShowAddForm(true)}
+ className="add-todo-btn"
+ title="Add a new todo"
+ >
+ +
+
+ )}
+
+
+
+ {error && (
+
+ Error: {error}
+
+ )}
+
+ {showAddForm && (
+
+
+ Add New Todo
+
+
+ Title *
+ setNewTodoTitle(e.target.value)}
+ placeholder="What needs to be done?"
+ required
+ />
+
+
+ Details
+ setNewTodoDetails(e.target.value)}
+ placeholder="Add some details (optional)..."
+ rows={3}
+ />
+
+
+
+ Add Todo
+
+ {
+ setShowAddForm(false);
+ setNewTodoTitle("");
+ setNewTodoDetails("");
+ }}
+ className="btn btn-secondary"
+ >
+ Cancel
+
+
+
+
+
+ )}
+
+ {!showAddForm &&
+ (loading ? (
+
+ ) : (
+
+ {todos.length === 0 ? (
+
+
+
+
+
No todos yet
+
+ Create your first todo to get started!
+
+
+ ) : (
+ todos.map((todo) => (
+
+ {editingTodo?.id === todo.id ? (
+
+
+
+ Title
+
+ setEditingTodo({
+ ...editingTodo,
+ title: e.target.value,
+ })
+ }
+ />
+
+
+ Details
+
+ setEditingTodo({
+ ...editingTodo,
+ details: e.target.value,
+ })
+ }
+ rows={3}
+ />
+
+
+
+ ✓ Save Changes
+
+ setEditingTodo(null)}
+ className="btn btn-cancel"
+ >
+ ✕ Cancel
+
+
+
+
+ ) : (
+
+
+
toggleTodoExpansion(todo.id)}
+ >
+ {todo.title}
+
+
+ toggleComplete(todo)}
+ className="action-btn action-btn-complete"
+ title={
+ todo.completed
+ ? "Mark as incomplete"
+ : "Mark as complete"
+ }
+ >
+ {todo.completed ? "↶" : "✓"}
+
+ setEditingTodo(todo)}
+ className="action-btn action-btn-edit"
+ title="Edit todo"
+ >
+ ✏️
+
+ deleteTodo(todo.id)}
+ className="action-btn action-btn-delete"
+ title="Delete todo"
+ >
+ 🗑️
+
+
+
+
+ {expandedTodos.has(todo.id) && (
+
+ {todo.details && (
+
+ )}
+
+
+
+
+ Created:{" "}
+ {new Date(todo.created_at).toLocaleString()}
+
+
+ Updated:{" "}
+ {new Date(todo.updated_at).toLocaleString()}
+
+
+ {todo.completed && (
+
+ )}
+
+
+ )}
+
+ )}
+
+ ))
+ )}
+
+ ))}
+
+ );
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/pages/Verify.tsx b/examples/tutorials/nhost-react-tutorial/src/pages/Verify.tsx
new file mode 100644
index 000000000..81522b102
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/pages/Verify.tsx
@@ -0,0 +1,140 @@
+import { useEffect, useState } from "react";
+import { useLocation, useNavigate } from "react-router-dom";
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+export default function Verify() {
+ const location = useLocation();
+ const navigate = useNavigate();
+
+ const [status, setStatus] = useState<"verifying" | "success" | "error">(
+ "verifying",
+ );
+ const [error, setError] = useState(null);
+ const [urlParams, setUrlParams] = useState>({});
+
+ const { nhost } = useAuth();
+
+ useEffect(() => {
+ // Extract the refresh token from the URL
+ const params = new URLSearchParams(location.search);
+ const refreshToken = params.get("refreshToken");
+
+ if (!refreshToken) {
+ // Collect all URL parameters to display for debugging
+ const allParams: Record = {};
+ params.forEach((value, key) => {
+ allParams[key] = value;
+ });
+ setUrlParams(allParams);
+
+ setStatus("error");
+ setError("No refresh token found in URL");
+ return;
+ }
+
+ // Flag to handle component unmounting during async operations
+ let isMounted = true;
+
+ async function processToken(): Promise {
+ try {
+ // First display the verifying message for at least a moment
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ if (!isMounted) return;
+
+ if (!refreshToken) {
+ // Collect all URL parameters to display
+ const allParams: Record = {};
+ params.forEach((value, key) => {
+ allParams[key] = value;
+ });
+ setUrlParams(allParams);
+
+ setStatus("error");
+ setError("No refresh token found in URL");
+ return;
+ }
+
+ // Process the token
+ await nhost.auth.refreshToken({ refreshToken });
+
+ if (!isMounted) return;
+
+ setStatus("success");
+
+ // Wait to show success message briefly, then redirect
+ setTimeout(() => {
+ if (isMounted) navigate("/profile");
+ }, 1500);
+ } catch (err) {
+ const message = (err as Error).message || "Unknown error";
+ if (!isMounted) return;
+
+ setStatus("error");
+ setError(`An error occurred during verification: ${message}`);
+ }
+ }
+
+ processToken();
+
+ // Cleanup function
+ return () => {
+ isMounted = false;
+ };
+ }, [location.search, navigate, nhost.auth]);
+
+ return (
+
+
Email Verification
+
+
+ {status === "verifying" && (
+
+
Verifying your email...
+
+
+
+ )}
+
+ {status === "success" && (
+
+
✓ Successfully verified!
+
You'll be redirected to your profile page shortly...
+
+ )}
+
+ {status === "error" && (
+
+
Verification failed
+
{error}
+
+ {Object.keys(urlParams).length > 0 && (
+
+
URL Parameters:
+ {Object.entries(urlParams).map(([key, value]) => (
+
+ {key}: {" "}
+ {value}
+
+ ))}
+
+ )}
+
+
navigate("/signin")}
+ className="auth-button secondary"
+ >
+ Back to Sign In
+
+
+ )}
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-react-tutorial/src/vite-env.d.ts b/examples/tutorials/nhost-react-tutorial/src/vite-env.d.ts
new file mode 100644
index 000000000..11f02fe2a
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/tutorials/nhost-react-tutorial/tsconfig.app.json b/examples/tutorials/nhost-react-tutorial/tsconfig.app.json
new file mode 100644
index 000000000..227a6c672
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/tsconfig.app.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/examples/tutorials/nhost-react-tutorial/tsconfig.json b/examples/tutorials/nhost-react-tutorial/tsconfig.json
new file mode 100644
index 000000000..1ffef600d
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/examples/tutorials/nhost-react-tutorial/tsconfig.node.json b/examples/tutorials/nhost-react-tutorial/tsconfig.node.json
new file mode 100644
index 000000000..f85a39906
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/tsconfig.node.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2023",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/examples/tutorials/nhost-react-tutorial/vite.config.ts b/examples/tutorials/nhost-react-tutorial/vite.config.ts
new file mode 100644
index 000000000..f16e2be3f
--- /dev/null
+++ b/examples/tutorials/nhost-react-tutorial/vite.config.ts
@@ -0,0 +1,7 @@
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/.cursor/mcp.json b/examples/tutorials/nhost-reactnative-tutorial/.cursor/mcp.json
new file mode 100644
index 000000000..182bf9372
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/.cursor/mcp.json
@@ -0,0 +1,11 @@
+{
+ "mcpServers": {
+ "RadonAi": {
+ "url": "http://127.0.0.1:63266/mcp",
+ "type": "http",
+ "headers": {
+ "nonce": "7a6ba1e0-1f15-4d68-90df-3a71371f2694"
+ }
+ }
+ }
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/.gitignore b/examples/tutorials/nhost-reactnative-tutorial/.gitignore
new file mode 100644
index 000000000..f8c6c2e83
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/.gitignore
@@ -0,0 +1,43 @@
+# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
+
+# dependencies
+node_modules/
+
+# Expo
+.expo/
+dist/
+web-build/
+expo-env.d.ts
+
+# Native
+.kotlin/
+*.orig.*
+*.jks
+*.p8
+*.p12
+*.key
+*.mobileprovision
+
+# Metro
+.metro-health-check*
+
+# debug
+npm-debug.*
+yarn-debug.*
+yarn-error.*
+
+# macOS
+.DS_Store
+*.pem
+
+# local env files
+.env*.local
+
+# typescript
+*.tsbuildinfo
+
+app-example
+
+# generated native folders
+/ios
+/android
diff --git a/examples/tutorials/nhost-reactnative-tutorial/.vscode/extensions.json b/examples/tutorials/nhost-reactnative-tutorial/.vscode/extensions.json
new file mode 100644
index 000000000..b7ed83779
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/.vscode/extensions.json
@@ -0,0 +1 @@
+{ "recommendations": ["expo.vscode-expo-tools"] }
diff --git a/examples/tutorials/nhost-reactnative-tutorial/.vscode/settings.json b/examples/tutorials/nhost-reactnative-tutorial/.vscode/settings.json
new file mode 100644
index 000000000..e2798e426
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/.vscode/settings.json
@@ -0,0 +1,7 @@
+{
+ "editor.codeActionsOnSave": {
+ "source.fixAll": "explicit",
+ "source.organizeImports": "explicit",
+ "source.sortMembers": "explicit"
+ }
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/README.md b/examples/tutorials/nhost-reactnative-tutorial/README.md
new file mode 100644
index 000000000..d074b48ae
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/README.md
@@ -0,0 +1,3 @@
+This is the result of following the tutorial at:
+
+https://docs.nhost.io/getting-started/tutorials/reactnative/1-introduction
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app.json b/examples/tutorials/nhost-reactnative-tutorial/app.json
new file mode 100644
index 000000000..0c4e78ee8
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app.json
@@ -0,0 +1,64 @@
+{
+ "expo": {
+ "name": "nhost-reactnative-tutorial",
+ "slug": "nhost-reactnative-tutorial",
+ "version": "1.0.0",
+ "orientation": "portrait",
+ "icon": "./assets/images/icon.png",
+ "scheme": "nhostreactnativetutorial",
+ "userInterfaceStyle": "automatic",
+ "newArchEnabled": true,
+ "ios": {
+ "supportsTablet": true,
+ "bundleIdentifier": "io.nhost.david-dev-test",
+ "jsEngine": "jsc",
+ "infoPlist": {
+ "NSFaceIDUsageDescription": "This app uses Face ID for signing in",
+ "CFBundleURLTypes": [
+ {
+ "CFBundleURLSchemes": ["reactnativewebdemo"]
+ }
+ ]
+ }
+ },
+ "android": {
+ "jsEngine": "jsc",
+ "adaptiveIcon": {
+ "backgroundColor": "#E6F4FE",
+ "foregroundImage": "./assets/images/android-icon-foreground.png",
+ "backgroundImage": "./assets/images/android-icon-background.png",
+ "monochromeImage": "./assets/images/android-icon-monochrome.png"
+ },
+ "edgeToEdgeEnabled": true,
+ "predictiveBackGestureEnabled": false
+ },
+ "web": {
+ "output": "static",
+ "favicon": "./assets/images/favicon.png"
+ },
+ "plugins": [
+ "expo-router",
+ [
+ "expo-splash-screen",
+ {
+ "image": "./assets/images/splash-icon.png",
+ "imageWidth": 200,
+ "resizeMode": "contain",
+ "backgroundColor": "#ffffff",
+ "dark": {
+ "backgroundColor": "#000000"
+ }
+ }
+ ],
+ ["expo-apple-authentication"]
+ ],
+ "experiments": {
+ "typedRoutes": true,
+ "reactCompiler": true
+ },
+ "extra": {
+ "NHOST_REGION": "local",
+ "NHOST_SUBDOMAIN": "192-168-1-108"
+ }
+ }
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/(tabs)/_layout.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/(tabs)/_layout.tsx
new file mode 100644
index 000000000..864c4b6e8
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/(tabs)/_layout.tsx
@@ -0,0 +1,39 @@
+import { Tabs } from "expo-router";
+
+import { HapticTab } from "@/components/haptic-tab";
+import { IconSymbol } from "@/components/ui/icon-symbol";
+import { Colors } from "@/constants/theme";
+import { useColorScheme } from "@/hooks/use-color-scheme";
+
+export default function TabLayout() {
+ const colorScheme = useColorScheme();
+
+ return (
+
+ (
+
+ ),
+ }}
+ />
+ (
+
+ ),
+ }}
+ />
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/(tabs)/explore.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/(tabs)/explore.tsx
new file mode 100644
index 000000000..bb2b49aa1
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/(tabs)/explore.tsx
@@ -0,0 +1,125 @@
+import { Image } from "expo-image";
+import { Platform, StyleSheet } from "react-native";
+import { ExternalLink } from "@/components/external-link";
+import ParallaxScrollView from "@/components/parallax-scroll-view";
+import { ThemedText } from "@/components/themed-text";
+import { ThemedView } from "@/components/themed-view";
+import { Collapsible } from "@/components/ui/collapsible";
+import { IconSymbol } from "@/components/ui/icon-symbol";
+import { Fonts } from "@/constants/theme";
+
+export default function TabTwoScreen() {
+ return (
+
+ }
+ >
+
+
+ Explore
+
+
+
+ This app includes example code to help you get started.
+
+
+
+ This app has two screens:{" "}
+ app/(tabs)/index.tsx {" "}
+ and{" "}
+ app/(tabs)/explore.tsx
+
+
+ The layout file in{" "}
+ app/(tabs)/_layout.tsx {" "}
+ sets up the tab navigator.
+
+
+ Learn more
+
+
+
+
+ You can open this project on Android, iOS, and the web. To open the
+ web version, press w {" "}
+ in the terminal running this project.
+
+
+
+
+ For static images, you can use the{" "}
+ @2x and{" "}
+ @3x suffixes to
+ provide files for different screen densities
+
+
+
+ Learn more
+
+
+
+
+ This template has light and dark mode support. The{" "}
+ useColorScheme() hook
+ lets you inspect what the user's current color scheme is, and so
+ you can adjust UI colors accordingly.
+
+
+ Learn more
+
+
+
+
+ This template includes an example of an animated component. The{" "}
+
+ components/HelloWave.tsx
+ {" "}
+ component uses the powerful{" "}
+
+ react-native-reanimated
+ {" "}
+ library to create a waving hand animation.
+
+ {Platform.select({
+ ios: (
+
+ The{" "}
+
+ components/ParallaxScrollView.tsx
+ {" "}
+ component provides a parallax effect for the header image.
+
+ ),
+ })}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ headerImage: {
+ color: "#808080",
+ bottom: -90,
+ left: -35,
+ position: "absolute",
+ },
+ titleContainer: {
+ flexDirection: "row",
+ gap: 8,
+ },
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/(tabs)/index.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/(tabs)/index.tsx
new file mode 100644
index 000000000..b3bffa160
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/(tabs)/index.tsx
@@ -0,0 +1,104 @@
+import { Image } from "expo-image";
+import { Link } from "expo-router";
+import { Platform, StyleSheet } from "react-native";
+import { HelloWave } from "@/components/hello-wave";
+import ParallaxScrollView from "@/components/parallax-scroll-view";
+import { ThemedText } from "@/components/themed-text";
+import { ThemedView } from "@/components/themed-view";
+
+export default function HomeScreen() {
+ return (
+
+ }
+ >
+
+ Welcome!
+
+
+
+ Step 1: Try it
+
+ Edit{" "}
+ app/(tabs)/index.tsx {" "}
+ to see changes. Press{" "}
+
+ {Platform.select({
+ ios: "cmd + d",
+ android: "cmd + m",
+ web: "F12",
+ })}
+ {" "}
+ to open developer tools.
+
+
+
+
+
+ Step 2: Explore
+
+
+
+ alert("Action pressed")}
+ />
+ alert("Share pressed")}
+ />
+
+ alert("Delete pressed")}
+ />
+
+
+
+
+
+ {`Tap the Explore tab to learn more about what's included in this starter app.`}
+
+
+
+ Step 3: Get a fresh start
+
+ {`When you're ready, run `}
+ npm run reset-project {" "}
+ to get a fresh app {" "}
+ directory. This will move the current{" "}
+ app to{" "}
+ app-example .
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ titleContainer: {
+ flexDirection: "row",
+ alignItems: "center",
+ gap: 8,
+ },
+ stepContainer: {
+ gap: 8,
+ marginBottom: 8,
+ },
+ reactLogo: {
+ height: 178,
+ width: 290,
+ bottom: 0,
+ left: 0,
+ position: "absolute",
+ },
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/_layout.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/_layout.tsx
new file mode 100644
index 000000000..896d16ce5
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/_layout.tsx
@@ -0,0 +1,32 @@
+import { Stack } from "expo-router";
+import { AuthProvider } from "./lib/nhost/AuthProvider";
+import { colors } from "./styles/theme";
+
+/**
+ * Root layout component that provides authentication context to the entire app.
+ * Uses Expo Router's Stack navigation for screen management.
+ */
+export default function RootLayout() {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/components/AppleSignInButton.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/components/AppleSignInButton.tsx
new file mode 100644
index 000000000..d4e6e12ce
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/components/AppleSignInButton.tsx
@@ -0,0 +1,117 @@
+import * as AppleAuthentication from "expo-apple-authentication";
+import * as Crypto from "expo-crypto";
+import { useRouter } from "expo-router";
+import { useEffect, useState } from "react";
+import { Alert, Platform, StyleSheet } from "react-native";
+import { useAuth } from "../lib/nhost/AuthProvider";
+
+interface AppleSignInButtonProps {
+ isLoading: boolean;
+ setIsLoading: (isLoading: boolean) => void;
+}
+
+export default function AppleSignInButton({
+ setIsLoading,
+}: AppleSignInButtonProps) {
+ const { nhost } = useAuth();
+ const router = useRouter();
+ const [appleAuthAvailable, setAppleAuthAvailable] = useState(false);
+
+ useEffect(() => {
+ const checkAvailability = async () => {
+ if (Platform.OS === "ios") {
+ const isAvailable = await AppleAuthentication.isAvailableAsync();
+ setAppleAuthAvailable(isAvailable);
+ }
+ };
+
+ void checkAvailability();
+ }, []);
+
+ const handleAppleSignIn = async () => {
+ try {
+ setIsLoading(true);
+
+ // Generate a random nonce for security
+ const nonce = Math.random().toString(36).substring(2, 15);
+
+ // Hash the nonce for Apple Authentication
+ const hashedNonce = await Crypto.digestStringAsync(
+ Crypto.CryptoDigestAlgorithm.SHA256,
+ nonce,
+ );
+
+ // Request Apple authentication with our hashed nonce
+ const credential = await AppleAuthentication.signInAsync({
+ requestedScopes: [
+ AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
+ AppleAuthentication.AppleAuthenticationScope.EMAIL,
+ ],
+ nonce: hashedNonce,
+ });
+
+ if (credential.identityToken) {
+ // Use the identity token to sign in with Nhost
+ // Pass the original unhashed nonce to the SDK
+ // so the server can verify it
+ const response = await nhost.auth.signInIdToken({
+ provider: "apple",
+ idToken: credential.identityToken,
+ nonce,
+ });
+
+ if (response.body?.session) {
+ router.replace("/profile");
+ } else {
+ Alert.alert(
+ "Authentication Error",
+ "Failed to authenticate with Nhost",
+ );
+ }
+ } else {
+ Alert.alert(
+ "Authentication Error",
+ "No identity token received from Apple",
+ );
+ }
+ } catch (error: unknown) {
+ // Handle user cancellation gracefully
+ if (error instanceof Error && error.message.includes("canceled")) {
+ // User cancelled the sign-in flow, don't show an error
+ return;
+ }
+
+ // Handle other errors
+ const message =
+ error instanceof Error
+ ? error.message
+ : "Failed to authenticate with Apple";
+ Alert.alert("Authentication Error", message);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ // Only show the button on iOS devices where Apple authentication is available
+ if (Platform.OS !== "ios" || !appleAuthAvailable) {
+ return null;
+ }
+
+ return (
+
+ );
+}
+
+const styles = StyleSheet.create({
+ appleButton: {
+ width: "100%",
+ height: 45,
+ marginBottom: 10,
+ },
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/components/ProtectedScreen.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/components/ProtectedScreen.tsx
new file mode 100644
index 000000000..39f3c2082
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/components/ProtectedScreen.tsx
@@ -0,0 +1,47 @@
+import { router } from "expo-router";
+import type React from "react";
+import { useEffect } from "react";
+import { ActivityIndicator, Text, View } from "react-native";
+import { useAuth } from "../lib/nhost/AuthProvider";
+import { commonStyles } from "../styles/commonStyles";
+import { colors } from "../styles/theme";
+
+type AppRoutes = "/" | "/signin" | "/signup" | "/profile";
+
+interface ProtectedScreenProps {
+ children: React.ReactNode;
+ redirectTo?: AppRoutes;
+}
+
+/**
+ * ProtectedScreen component that wraps screens requiring authentication.
+ * Automatically redirects unauthenticated users to the signin screen.
+ * Shows loading spinner while checking authentication status.
+ */
+export default function ProtectedScreen({
+ children,
+ redirectTo = "/signin",
+}: ProtectedScreenProps) {
+ const { isAuthenticated, isLoading } = useAuth();
+
+ useEffect(() => {
+ if (!isLoading && !isAuthenticated) {
+ router.replace(redirectTo);
+ }
+ }, [isAuthenticated, isLoading, redirectTo]);
+
+ if (isLoading) {
+ return (
+
+
+ Loading...
+
+ );
+ }
+
+ if (!isAuthenticated) {
+ return null; // Will redirect in useEffect
+ }
+
+ return <>{children}>;
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/files.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/files.tsx
new file mode 100644
index 000000000..011a944c0
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/files.tsx
@@ -0,0 +1,454 @@
+import type { FetchError } from "@nhost/nhost-js/fetch";
+import type { ErrorResponse, FileMetadata } from "@nhost/nhost-js/storage";
+import * as DocumentPicker from "expo-document-picker";
+import * as FileSystem from "expo-file-system";
+import { Stack } from "expo-router";
+import * as Sharing from "expo-sharing";
+import { useCallback, useEffect, useState } from "react";
+import {
+ ActivityIndicator,
+ Alert,
+ FlatList,
+ Text,
+ TouchableOpacity,
+ View,
+} from "react-native";
+import ProtectedScreen from "./components/ProtectedScreen";
+import { useAuth } from "./lib/nhost/AuthProvider";
+import { commonStyles, fileUploadStyles } from "./styles/commonStyles";
+import { colors } from "./styles/theme";
+
+interface DeleteStatus {
+ message: string;
+ isError: boolean;
+}
+
+interface GraphqlGetFilesResponse {
+ files: FileMetadata[];
+}
+
+// Utility function to format file size
+function formatFileSize(bytes: number, decimals = 2): string {
+ if (bytes === 0) return "0 Bytes";
+
+ const k = 1024;
+ const dm = decimals < 0 ? 0 : decimals;
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
+
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+
+ return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
+}
+
+// Convert Blob to Base64 for React Native file handling
+function blobToBase64(blob: Blob): Promise {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ const base64data = reader.result as string;
+ // Remove the data URL prefix (e.g., "data:application/octet-stream;base64,")
+ const base64Content = base64data.split(",")[1] || "";
+ resolve(base64Content);
+ };
+ reader.onerror = reject;
+ reader.readAsDataURL(blob);
+ });
+}
+
+export default function Files() {
+ const { nhost } = useAuth();
+ const [selectedFile, setSelectedFile] =
+ useState(null);
+ const [uploading, setUploading] = useState(false);
+ const [uploadResult, setUploadResult] = useState(null);
+ const [isFetching, setIsFetching] = useState(true);
+ const [error, setError] = useState(null);
+ const [files, setFiles] = useState([]);
+ const [viewingFile, setViewingFile] = useState(null);
+ const [deleting, setDeleting] = useState(null);
+ const [deleteStatus, setDeleteStatus] = useState(null);
+
+ const fetchFiles = useCallback(async () => {
+ setIsFetching(true);
+ setError(null);
+
+ try {
+ // Fetch files using GraphQL query
+ const response = await nhost.graphql.request({
+ query: `query GetFiles {
+ files {
+ id
+ name
+ size
+ mimeType
+ bucketId
+ uploadedByUserId
+ }
+ }`,
+ });
+
+ setFiles(response.body.data?.files || []);
+ } catch (err) {
+ const errMessage =
+ err instanceof Error ? err.message : "An unexpected error occurred";
+ setError(`Failed to fetch files: ${errMessage}`);
+ } finally {
+ setIsFetching(false);
+ }
+ }, [nhost.graphql]);
+
+ // Fetch existing files when component mounts
+ useEffect(() => {
+ void fetchFiles();
+ }, [fetchFiles]);
+
+ const pickDocument = async () => {
+ try {
+ const result = await DocumentPicker.getDocumentAsync({
+ type: "*/*", // All file types
+ copyToCacheDirectory: true,
+ });
+
+ if (!result.canceled) {
+ setSelectedFile(result);
+ setError(null);
+ setUploadResult(null);
+ }
+ } catch (err) {
+ setError("Failed to pick document");
+ console.error("DocumentPicker Error:", err);
+ }
+ };
+
+ const handleUpload = async () => {
+ if (!selectedFile || selectedFile.canceled) {
+ setError("Please select a file to upload");
+ return;
+ }
+
+ setUploading(true);
+ setError(null);
+
+ try {
+ // For React Native, we need to read the file first
+ const fileToUpload = selectedFile.assets?.[0];
+ if (!fileToUpload) {
+ throw new Error("No file selected");
+ }
+
+ const file: unknown = {
+ uri: fileToUpload.uri,
+ name: fileToUpload.name || "file",
+ type: fileToUpload.mimeType || "application/octet-stream",
+ };
+
+ // Upload file to the personal bucket
+ // The uploadedByUserId is automatically set by the storage permissions
+ const response = await nhost.storage.uploadFiles({
+ "bucket-id": "personal",
+ "file[]": [file as File],
+ });
+
+ // Get the processed file data
+ const uploadedFile = response.body.processedFiles?.[0];
+ if (uploadedFile === undefined) {
+ throw new Error("Failed to upload file");
+ }
+
+ setUploadResult(uploadedFile);
+
+ // Reset form
+ setSelectedFile(null);
+
+ // Update files list
+ setFiles((prevFiles) => [uploadedFile, ...prevFiles]);
+
+ // Refresh file list
+ await fetchFiles();
+
+ // Clear success message after 3 seconds
+ setTimeout(() => {
+ setUploadResult(null);
+ }, 3000);
+ } catch (err: unknown) {
+ const error = err as FetchError;
+ setError(`Failed to upload file: ${error.message}`);
+ console.error("Upload error:", err);
+ } finally {
+ setUploading(false);
+ }
+ };
+
+ // Function to handle viewing a file with proper authorization
+ const handleViewFile = async (
+ fileId: string,
+ fileName: string,
+ mimeType: string,
+ ) => {
+ setViewingFile(fileId);
+
+ try {
+ // Fetch the file with authentication using the SDK
+ const response = await nhost.storage.getFile(fileId);
+
+ if (!response.body) {
+ throw new Error("Failed to retrieve file contents");
+ }
+
+ // For iOS/Android, we need to save the file to the device first
+ // Create a unique temp file path with a timestamp to prevent collisions
+ const fileExtension = fileName.includes(".") ? "" : ".file";
+ const tempFileName = fileName.includes(".")
+ ? fileName
+ : `${fileName}${fileExtension}`;
+ const tempFilePath = `${FileSystem.cacheDirectory}${Date.now()}_${tempFileName}`;
+
+ // Get the blob from the response
+ const blob = response.body;
+
+ // Convert blob to base64
+ const base64Data = await blobToBase64(blob);
+
+ // Write the file to the filesystem
+ await FileSystem.writeAsStringAsync(tempFilePath, base64Data, {
+ encoding: FileSystem.EncodingType.Base64,
+ });
+
+ // Check if sharing is available (iOS & Android)
+ const isSharingAvailable = await Sharing.isAvailableAsync();
+
+ if (isSharingAvailable) {
+ // Open the file with the default app
+ await Sharing.shareAsync(tempFilePath, {
+ mimeType: mimeType || "application/octet-stream",
+ dialogTitle: `View ${fileName}`,
+ UTI: mimeType, // for iOS
+ });
+ } else {
+ throw new Error("Sharing is not available on this device");
+ }
+ } catch (err) {
+ const error = err as FetchError;
+ setError(`Failed to view file: ${error.message}`);
+ console.error("Error viewing file:", err);
+ Alert.alert("Error", `Failed to view file: ${error.message}`);
+ } finally {
+ setViewingFile(null);
+ }
+ };
+
+ // Function to handle deleting a file
+ const handleDeleteFile = (fileId: string) => {
+ if (!fileId || deleting) return;
+
+ // Confirm deletion
+ Alert.alert("Delete File", "Are you sure you want to delete this file?", [
+ {
+ text: "Cancel",
+ style: "cancel",
+ },
+ {
+ text: "Delete",
+ style: "destructive",
+ onPress: () => {
+ void (async () => {
+ setDeleting(fileId);
+ setError(null);
+ setDeleteStatus(null);
+
+ // Get the file name for the status message
+ const fileToDelete = files.find((file) => file.id === fileId);
+ const fileName = fileToDelete?.name || "File";
+
+ try {
+ // Delete the file using the Nhost storage SDK
+ // Permissions ensure users can only delete their own files
+ await nhost.storage.deleteFile(fileId);
+
+ // Show success message
+ setDeleteStatus({
+ message: `${fileName} deleted successfully`,
+ isError: false,
+ });
+
+ // Update the local files list by removing the deleted file
+ setFiles(files.filter((file) => file.id !== fileId));
+
+ // Refresh the file list
+ await fetchFiles();
+
+ // Clear the success message after 3 seconds
+ setTimeout(() => {
+ setDeleteStatus(null);
+ }, 3000);
+ } catch (err) {
+ // Show error message
+ const error = err as FetchError;
+ setDeleteStatus({
+ message: `Failed to delete ${fileName}: ${error.message}`,
+ isError: true,
+ });
+ console.error("Error deleting file:", err);
+ } finally {
+ setDeleting(null);
+ }
+ })();
+ },
+ },
+ ]);
+ };
+
+ return (
+
+
+
+ {/* Upload Form */}
+
+ Upload a File
+
+
+
+ ⬆️
+
+
+ Tap to select a file
+
+ {selectedFile &&
+ !selectedFile.canceled &&
+ selectedFile.assets?.[0] && (
+
+ {selectedFile.assets[0].name} (
+ {formatFileSize(selectedFile.assets[0].size || 0)})
+
+ )}
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {uploadResult && (
+
+
+ File uploaded successfully!
+
+
+ )}
+
+
+
+ {uploading ? "Uploading..." : "Upload File"}
+
+
+
+
+ {/* Files List */}
+
+ Your Files
+
+ {deleteStatus && (
+
+
+ {deleteStatus.message}
+
+
+ )}
+
+ {isFetching ? (
+
+
+ Loading files...
+
+ ) : files.length === 0 ? (
+
+ 📄
+ No files yet
+
+ Upload your first file to get started!
+
+
+ ) : (
+ item.id || Math.random().toString()}
+ renderItem={({ item }) => (
+
+
+
+ {item.name}
+
+
+ {item.mimeType} • {formatFileSize(item.size || 0)}
+
+
+
+
+ handleViewFile(
+ item.id || "unknown",
+ item.name || "unknown",
+ item.mimeType || "unknown",
+ )
+ }
+ disabled={viewingFile === item.id}
+ >
+ {viewingFile === item.id ? (
+ ⌛
+ ) : (
+ 👁️
+ )}
+
+ handleDeleteFile(item.id || "unknown")}
+ disabled={deleting === item.id}
+ >
+ {deleting === item.id ? (
+ ⌛
+ ) : (
+ 🗑️
+ )}
+
+
+
+ )}
+ style={fileUploadStyles.fileList}
+ />
+ )}
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/index.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/index.tsx
new file mode 100644
index 000000000..9a146974b
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/index.tsx
@@ -0,0 +1,67 @@
+import { useRouter } from "expo-router";
+import { Text, TouchableOpacity, View } from "react-native";
+import { useAuth } from "./lib/nhost/AuthProvider";
+import { commonStyles, homeStyles } from "./styles/commonStyles";
+
+export default function Index() {
+ const router = useRouter();
+ const { isAuthenticated, user } = useAuth();
+
+ return (
+
+ Welcome to Nhost React Native Demo
+
+
+ {isAuthenticated ? (
+
+
+ Hello, {user?.displayName || user?.email}!
+
+ router.push("/todos")}
+ >
+ My Todos
+
+ router.push("/files")}
+ >
+ My Files
+
+ router.push("/profile")}
+ >
+ Go to Profile
+
+
+ ) : (
+ <>
+ You are not signed in.
+
+
+ router.push("/signin")}
+ >
+ Sign In
+
+
+ router.push("/signup")}
+ >
+ Sign Up
+
+
+ >
+ )}
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/lib/nhost/AsyncStorage.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/lib/nhost/AsyncStorage.tsx
new file mode 100644
index 000000000..86d6e3489
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/lib/nhost/AsyncStorage.tsx
@@ -0,0 +1,93 @@
+import {
+ DEFAULT_SESSION_KEY,
+ type Session,
+ type SessionStorageBackend,
+} from "@nhost/nhost-js/session";
+import AsyncStorage from "@react-native-async-storage/async-storage";
+
+/**
+ * Custom storage implementation for React Native using AsyncStorage
+ * to persist the Nhost session on the device.
+ *
+ * This implementation synchronously works with the SessionStorageBackend interface
+ * while ensuring reliable persistence with AsyncStorage for Expo Go.
+ */
+export default class NhostAsyncStorage implements SessionStorageBackend {
+ private key: string;
+ private cache: Session | null = null;
+
+ constructor(key: string = DEFAULT_SESSION_KEY) {
+ this.key = key;
+
+ // Immediately try to load from AsyncStorage
+ this.loadFromAsyncStorage();
+ }
+
+ /**
+ * Load the session from AsyncStorage synchronously if possible
+ */
+ private loadFromAsyncStorage(): void {
+ // Try to get cached data from AsyncStorage immediately
+ try {
+ AsyncStorage.getItem(this.key)
+ .then((value) => {
+ if (value) {
+ try {
+ this.cache = JSON.parse(value) as Session;
+ } catch (error) {
+ console.warn("Error parsing session from AsyncStorage:", error);
+ this.cache = null;
+ }
+ }
+ })
+ .catch((error) => {
+ console.warn("Error loading from AsyncStorage:", error);
+ });
+ } catch (error) {
+ console.warn("AsyncStorage access error:", error);
+ }
+ }
+
+ /**
+ * Gets the session from the in-memory cache
+ */
+ get(): Session | null {
+ return this.cache;
+ }
+
+ /**
+ * Sets the session in the in-memory cache and persists to AsyncStorage
+ * Ensures the data gets written by using an immediately invoked async function
+ */
+ set(value: Session): void {
+ // Update cache immediately
+ this.cache = value;
+
+ // Persist to AsyncStorage with better error handling
+ void (async () => {
+ try {
+ await AsyncStorage.setItem(this.key, JSON.stringify(value));
+ } catch (error) {
+ console.warn("Error saving session to AsyncStorage:", error);
+ }
+ })();
+ }
+
+ /**
+ * Removes the session from the in-memory cache and AsyncStorage
+ * Ensures the data gets removed by using an immediately invoked async function
+ */
+ remove(): void {
+ // Clear cache immediately
+ this.cache = null;
+
+ // Remove from AsyncStorage with better error handling
+ void (async () => {
+ try {
+ await AsyncStorage.removeItem(this.key);
+ } catch (error) {
+ console.warn("Error removing session from AsyncStorage:", error);
+ }
+ })();
+ }
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/lib/nhost/AuthProvider.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/lib/nhost/AuthProvider.tsx
new file mode 100644
index 000000000..7773ca37f
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/lib/nhost/AuthProvider.tsx
@@ -0,0 +1,148 @@
+import { createClient, type NhostClient } from "@nhost/nhost-js";
+import type { Session } from "@nhost/nhost-js/session";
+import Constants from "expo-constants";
+import {
+ createContext,
+ type ReactNode,
+ useContext,
+ useEffect,
+ useMemo,
+ useState,
+} from "react";
+import NhostAsyncStorage from "./AsyncStorage";
+
+/**
+ * Authentication context interface providing access to user session state and Nhost client.
+ * Used throughout the React Native application to access authentication-related data and operations.
+ */
+interface AuthContextType {
+ /** Current authenticated user object, null if not authenticated */
+ user: Session["user"] | null;
+ /** Current session object containing tokens and user data, null if no active session */
+ session: Session | null;
+ /** Boolean indicating if user is currently authenticated */
+ isAuthenticated: boolean;
+ /** Boolean indicating if authentication state is still loading */
+ isLoading: boolean;
+ /** Nhost client instance for making authenticated requests */
+ nhost: NhostClient;
+}
+
+// Create React context for authentication state and nhost client
+const AuthContext = createContext(null);
+
+interface AuthProviderProps {
+ children: ReactNode;
+}
+
+/**
+ * AuthProvider component that provides authentication context to the React Native application.
+ *
+ * This component handles:
+ * - Initializing the Nhost client with AsyncStorage for persistent storage
+ * - Managing authentication state (user, session, loading, authenticated status)
+ * - Cross-device session synchronization using sessionStorage.onChange events
+ * - Async session initialization to work with React Native's AsyncStorage
+ */
+export const AuthProvider = ({ children }: AuthProviderProps) => {
+ const [user, setUser] = useState(null);
+ const [session, setSession] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
+
+ // Create the nhost client with persistent storage
+ const nhost = useMemo(() => {
+ // Get configuration values with type assertion
+ const subdomain =
+ (Constants.expoConfig?.extra?.["NHOST_SUBDOMAIN"] as string) || "local";
+ const region =
+ (Constants.expoConfig?.extra?.["NHOST_REGION"] as string) || "local";
+
+ return createClient({
+ subdomain,
+ region,
+ storage: new NhostAsyncStorage(),
+ });
+ }, []);
+
+ useEffect(() => {
+ // Initialize authentication state
+ setIsLoading(true);
+
+ // Allow enough time for AsyncStorage to be read and session to be restored
+ const initializeSession = async () => {
+ try {
+ // Let's wait a bit to ensure AsyncStorage has been read
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ // Now try to get the current session
+ const currentSession = nhost.getUserSession();
+
+ setUser(currentSession?.user || null);
+ setSession(currentSession);
+ setIsAuthenticated(!!currentSession);
+ } catch (error) {
+ console.warn("Error initializing session:", error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ void initializeSession();
+
+ // Listen for session changes
+ const unsubscribe = nhost.sessionStorage.onChange((currentSession) => {
+ setUser(currentSession?.user || null);
+ setSession(currentSession);
+ setIsAuthenticated(!!currentSession);
+ });
+
+ // Clean up subscription on unmount
+ return () => {
+ unsubscribe();
+ };
+ }, [nhost]);
+
+ // Context value with nhost client directly exposed
+ const value: AuthContextType = {
+ user,
+ session,
+ isAuthenticated,
+ isLoading,
+ nhost,
+ };
+
+ return {children} ;
+};
+
+/**
+ * Custom hook to access the authentication context.
+ *
+ * Must be used within a component wrapped by AuthProvider.
+ * Provides access to current user session, authentication state, and Nhost client.
+ *
+ * @throws {Error} When used outside of AuthProvider
+ * @returns {AuthContextType} Authentication context containing user, session, and client
+ *
+ * @example
+ * ```tsx
+ * function MyComponent() {
+ * const { user, isAuthenticated, nhost } = useAuth();
+ *
+ * if (!isAuthenticated) {
+ * return Please sign in ;
+ * }
+ *
+ * return Welcome, {user?.displayName}! ;
+ * }
+ * ```
+ */
+export const useAuth = (): AuthContextType => {
+ const context = useContext(AuthContext);
+ if (!context) {
+ throw new Error("useAuth must be used within an AuthProvider");
+ }
+ return context;
+};
+
+export default AuthProvider;
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/modal.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/modal.tsx
new file mode 100644
index 000000000..6c278bd8a
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/modal.tsx
@@ -0,0 +1,29 @@
+import { Link } from "expo-router";
+import { StyleSheet } from "react-native";
+
+import { ThemedText } from "@/components/themed-text";
+import { ThemedView } from "@/components/themed-view";
+
+export default function ModalScreen() {
+ return (
+
+ This is a modal
+
+ Go to home screen
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: "center",
+ justifyContent: "center",
+ padding: 20,
+ },
+ link: {
+ marginTop: 15,
+ paddingVertical: 15,
+ },
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/profile.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/profile.tsx
new file mode 100644
index 000000000..ce1c940b6
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/profile.tsx
@@ -0,0 +1,112 @@
+import { useRouter } from "expo-router";
+import { Alert, ScrollView, Text, TouchableOpacity, View } from "react-native";
+import ProtectedScreen from "./components/ProtectedScreen";
+import { useAuth } from "./lib/nhost/AuthProvider";
+import { commonStyles, profileStyles } from "./styles/commonStyles";
+
+export default function Profile() {
+ const router = useRouter();
+ const { user, session, nhost } = useAuth();
+
+ const handleSignOut = async () => {
+ Alert.alert("Sign Out", "Are you sure you want to sign out?", [
+ { text: "Cancel", style: "cancel" },
+ {
+ text: "Sign Out",
+ style: "destructive",
+ onPress: async () => {
+ try {
+ if (session) {
+ await nhost.auth.signOut({
+ refreshToken: session.refreshToken,
+ });
+ }
+ router.replace("/");
+ } catch (err: unknown) {
+ const message = err instanceof Error ? err.message : String(err);
+ Alert.alert("Error", `Failed to sign out: ${message}`);
+ }
+ },
+ },
+ ]);
+ };
+
+ return (
+
+
+ Your Profile
+
+
+ User Information
+
+
+ Display Name:
+
+ {user?.displayName || "Not set"}
+
+
+
+
+ Email:
+
+ {user?.email || "Not available"}
+
+
+
+
+ User ID:
+
+ {user?.id || "Not available"}
+
+
+
+
+ Roles:
+
+ {user?.roles?.join(", ") || "None"}
+
+
+
+
+ Email Verified:
+
+ {user?.emailVerified ? "✓ Yes" : "✗ No"}
+
+
+
+
+
+ Session Information
+
+
+ {JSON.stringify(session, null, 2)}
+
+
+
+
+
+ Sign Out
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/signin.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/signin.tsx
new file mode 100644
index 000000000..147cbe972
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/signin.tsx
@@ -0,0 +1,138 @@
+import { Link, router } from "expo-router";
+import { useEffect, useState } from "react";
+import {
+ ActivityIndicator,
+ KeyboardAvoidingView,
+ Platform,
+ ScrollView,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ View,
+} from "react-native";
+import AppleSignInButton from "./components/AppleSignInButton";
+import { useAuth } from "./lib/nhost/AuthProvider";
+import { commonStyles } from "./styles/commonStyles";
+import { colors } from "./styles/theme";
+
+export default function SignIn() {
+ const { nhost, isAuthenticated } = useAuth();
+
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ // Use useEffect for navigation after authentication is confirmed
+ useEffect(() => {
+ if (isAuthenticated) {
+ router.replace("/profile");
+ }
+ }, [isAuthenticated]);
+
+ const handleSubmit = async () => {
+ setIsLoading(true);
+ setError(null);
+
+ try {
+ // Use the signIn function from auth context
+ const response = await nhost.auth.signInEmailPassword({
+ email,
+ password,
+ });
+
+ // If we have a session, sign in was successful
+ if (response.body?.session) {
+ router.replace("/profile");
+ } else {
+ setError("Failed to sign in. Please check your credentials.");
+ }
+ } catch (err) {
+ const message = (err as Error).message || "Unknown error";
+ setError(`An error occurred during sign in: ${message}`);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+
+
+ Sign In
+
+
+ {/* Apple Sign In Button */}
+
+
+ {/* Divider */}
+
+
+ or
+
+
+
+
+ Email
+
+
+
+
+ Password
+
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+ {isLoading ? (
+
+ ) : (
+ Sign In
+ )}
+
+
+
+
+
+ Don't have an account?{" "}
+
+ Sign Up
+
+
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/signup.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/signup.tsx
new file mode 100644
index 000000000..81764cb04
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/signup.tsx
@@ -0,0 +1,183 @@
+import * as Linking from "expo-linking";
+import { Link, router } from "expo-router";
+import { useEffect, useState } from "react";
+import {
+ ActivityIndicator,
+ KeyboardAvoidingView,
+ Platform,
+ ScrollView,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ View,
+} from "react-native";
+import AppleSignInButton from "./components/AppleSignInButton";
+import { useAuth } from "./lib/nhost/AuthProvider";
+import { commonStyles } from "./styles/commonStyles";
+import { colors } from "./styles/theme";
+
+export default function SignUp() {
+ const { nhost, isAuthenticated } = useAuth();
+
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [displayName, setDisplayName] = useState("");
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [success, setSuccess] = useState(false);
+
+ // Redirect authenticated users to profile
+ useEffect(() => {
+ if (isAuthenticated) {
+ router.replace("/profile");
+ }
+ }, [isAuthenticated]);
+
+ const handleSubmit = async () => {
+ setIsLoading(true);
+ setError(null);
+ setSuccess(false);
+
+ try {
+ const response = await nhost.auth.signUpEmailPassword({
+ email,
+ password,
+ options: {
+ displayName,
+ // Set the redirect URL for email verification
+ redirectTo: Linking.createURL("verify"),
+ },
+ });
+
+ if (response.body?.session) {
+ // Successfully signed up and automatically signed in
+ router.replace("/profile");
+ } else {
+ // Verification email sent
+ setSuccess(true);
+ }
+ } catch (err) {
+ const message = (err as Error).message || "Unknown error";
+ setError(`An error occurred during sign up: ${message}`);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ if (success) {
+ return (
+
+ Check Your Email
+
+
+ We've sent a verification link to{" "}
+ {email}
+
+
+ Please check your email and click the verification link to activate
+ your account.
+
+
+ router.replace("/signin")}
+ >
+ Back to Sign In
+
+
+ );
+ }
+
+ return (
+
+
+ Sign Up
+
+
+ {/* Apple Sign In Button */}
+
+
+ {/* Divider */}
+
+
+ or
+
+
+
+
+ Display Name
+
+
+
+
+ Email
+
+
+
+
+ Password
+
+ Minimum 8 characters
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+ {isLoading ? (
+
+ ) : (
+ Sign Up
+ )}
+
+
+
+
+
+ Already have an account?{" "}
+
+ Sign In
+
+
+
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/styles/commonStyles.ts b/examples/tutorials/nhost-reactnative-tutorial/app/styles/commonStyles.ts
new file mode 100644
index 000000000..4983b041e
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/styles/commonStyles.ts
@@ -0,0 +1,667 @@
+import { StyleSheet } from "react-native";
+import { borderRadius, colors, shadows, spacing, typography } from "./theme";
+
+/**
+ * Common styles used across multiple components.
+ * This promotes consistency and reduces code duplication.
+ */
+
+export const commonStyles = StyleSheet.create({
+ // Layout styles
+ container: {
+ flex: 1,
+ backgroundColor: colors.background,
+ },
+
+ contentContainer: {
+ padding: spacing.xl,
+ paddingBottom: spacing.xxxl + spacing.md,
+ },
+
+ centerContent: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ padding: spacing.xl,
+ },
+
+ // Card styles
+ card: {
+ backgroundColor: colors.surface,
+ borderRadius: borderRadius.lg,
+ padding: spacing.lg,
+ marginBottom: spacing.xl,
+ width: "100%",
+ ...shadows.medium,
+ },
+
+ cardTitle: {
+ fontSize: typography.sizes.lg,
+ fontWeight: typography.weights.bold,
+ color: colors.text,
+ marginBottom: spacing.md,
+ },
+
+ // Button styles
+ button: {
+ backgroundColor: colors.primary,
+ paddingVertical: spacing.md,
+ paddingHorizontal: spacing.xl,
+ borderRadius: borderRadius.md,
+ alignItems: "center",
+ justifyContent: "center",
+ minHeight: 48,
+ },
+
+ buttonSecondary: {
+ backgroundColor: colors.secondary,
+ },
+
+ buttonText: {
+ color: colors.surface,
+ fontSize: typography.sizes.md,
+ fontWeight: typography.weights.semibold,
+ textAlign: "center",
+ },
+
+ // Text styles
+ title: {
+ fontSize: typography.sizes.xxl,
+ fontWeight: typography.weights.bold,
+ color: colors.text,
+ textAlign: "center",
+ marginBottom: spacing.xl,
+ lineHeight: typography.sizes.xxl * typography.lineHeights.tight,
+ },
+
+ subtitle: {
+ fontSize: typography.sizes.lg,
+ fontWeight: typography.weights.semibold,
+ color: colors.text,
+ marginBottom: spacing.lg,
+ },
+
+ bodyText: {
+ fontSize: typography.sizes.md,
+ color: colors.textLight,
+ lineHeight: typography.sizes.md * typography.lineHeights.normal,
+ },
+
+ labelText: {
+ fontSize: typography.sizes.md,
+ fontWeight: typography.weights.semibold,
+ color: colors.text,
+ marginBottom: spacing.xs,
+ },
+
+ valueText: {
+ fontSize: typography.sizes.md,
+ color: colors.textLight,
+ },
+
+ // Status text styles
+ successText: {
+ color: colors.success,
+ fontWeight: typography.weights.semibold,
+ },
+
+ errorText: {
+ color: colors.error,
+ fontWeight: typography.weights.semibold,
+ },
+
+ // Loading styles
+ loadingContainer: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ backgroundColor: colors.background,
+ padding: spacing.xl,
+ },
+
+ loadingText: {
+ marginTop: spacing.md,
+ color: colors.textLight,
+ fontSize: typography.sizes.md,
+ textAlign: "center",
+ },
+
+ // Form styles
+ formField: {
+ marginBottom: spacing.lg,
+ },
+
+ fieldGroup: {
+ paddingVertical: spacing.md,
+ borderBottomWidth: 1,
+ borderBottomColor: colors.borderLight,
+ },
+
+ // Input styles
+ input: {
+ borderWidth: 1,
+ borderColor: colors.border,
+ borderRadius: borderRadius.md,
+ paddingVertical: spacing.md,
+ paddingHorizontal: spacing.lg,
+ fontSize: typography.sizes.md,
+ color: colors.text,
+ backgroundColor: colors.surface,
+ minHeight: 48,
+ },
+
+ helperText: {
+ fontSize: typography.sizes.sm,
+ color: colors.textLight,
+ marginTop: spacing.xs,
+ },
+
+ // Link styles
+ linkContainer: {
+ marginTop: spacing.xl,
+ alignItems: "center",
+ },
+
+ linkText: {
+ fontSize: typography.sizes.md,
+ color: colors.textLight,
+ textAlign: "center",
+ },
+
+ link: {
+ color: colors.primary,
+ fontWeight: typography.weights.semibold,
+ },
+
+ // Container styles
+ successContainer: {
+ backgroundColor: colors.background,
+ borderColor: colors.success,
+ borderWidth: 1,
+ borderRadius: borderRadius.md,
+ padding: spacing.lg,
+ marginBottom: spacing.lg,
+ },
+
+ errorContainer: {
+ backgroundColor: colors.background,
+ borderColor: colors.error,
+ borderWidth: 1,
+ borderRadius: borderRadius.md,
+ padding: spacing.md,
+ marginBottom: spacing.md,
+ },
+
+ debugContainer: {
+ backgroundColor: colors.background,
+ borderColor: colors.warning,
+ borderWidth: 1,
+ borderRadius: borderRadius.md,
+ padding: spacing.md,
+ marginBottom: spacing.md,
+ },
+
+ debugTitle: {
+ fontSize: typography.sizes.sm,
+ fontWeight: typography.weights.semibold,
+ color: colors.text,
+ marginBottom: spacing.xs,
+ },
+
+ debugItem: {
+ flexDirection: "row",
+ marginBottom: spacing.xs,
+ },
+
+ debugKey: {
+ fontSize: typography.sizes.sm,
+ fontWeight: typography.weights.medium,
+ color: colors.info,
+ marginRight: spacing.sm,
+ fontFamily: "monospace",
+ },
+
+ debugValue: {
+ fontSize: typography.sizes.sm,
+ color: colors.textLight,
+ fontFamily: "monospace",
+ flex: 1,
+ },
+
+ emailText: {
+ fontWeight: typography.weights.bold,
+ color: colors.text,
+ },
+
+ // Session info styles
+ sessionInfo: {
+ backgroundColor: colors.background,
+ padding: spacing.md,
+ borderRadius: borderRadius.sm,
+ marginTop: spacing.sm,
+ },
+
+ sessionValue: {
+ fontSize: typography.sizes.sm,
+ color: colors.textLight,
+ marginBottom: spacing.md,
+ fontFamily: "monospace",
+ },
+
+ // Utility styles
+ row: {
+ flexDirection: "row",
+ alignItems: "center",
+ },
+
+ spaceBetween: {
+ justifyContent: "space-between",
+ },
+
+ alignCenter: {
+ alignItems: "center",
+ },
+
+ textCenter: {
+ textAlign: "center",
+ },
+
+ marginBottom: {
+ marginBottom: spacing.md,
+ },
+
+ fullWidth: {
+ width: "100%",
+ },
+
+ // Todo-specific styles
+ todoCard: {
+ backgroundColor: colors.surface,
+ borderRadius: borderRadius.md,
+ padding: spacing.lg,
+ marginBottom: spacing.md,
+ borderWidth: 1,
+ borderColor: colors.border,
+ ...shadows.small,
+ },
+
+ todoCompleted: {
+ opacity: 0.7,
+ borderColor: colors.success,
+ },
+
+ todoEditForm: {
+ padding: spacing.md,
+ },
+
+ todoHeader: {
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "space-between",
+ },
+
+ todoTitleContainer: {
+ flex: 1,
+ marginRight: spacing.md,
+ },
+
+ todoTitle: {
+ fontSize: typography.sizes.md,
+ fontWeight: typography.weights.medium,
+ color: colors.text,
+ lineHeight: typography.sizes.md * typography.lineHeights.normal,
+ },
+
+ todoTitleCompleted: {
+ textDecorationLine: "line-through",
+ color: colors.textLight,
+ },
+
+ todoActions: {
+ flexDirection: "row",
+ alignItems: "center",
+ },
+
+ actionButton: {
+ paddingHorizontal: spacing.sm,
+ paddingVertical: spacing.xs,
+ marginLeft: spacing.xs,
+ borderRadius: borderRadius.sm,
+ backgroundColor: colors.background,
+ minWidth: 32,
+ alignItems: "center",
+ justifyContent: "center",
+ },
+
+ completeButton: {
+ backgroundColor: `${colors.success}20`,
+ },
+
+ editButton: {
+ backgroundColor: `${colors.info}20`,
+ },
+
+ deleteButton: {
+ backgroundColor: `${colors.error}20`,
+ },
+
+ actionButtonText: {
+ fontSize: typography.sizes.md,
+ },
+
+ todoDetails: {
+ marginTop: spacing.md,
+ paddingTop: spacing.md,
+ borderTopWidth: 1,
+ borderTopColor: colors.borderLight,
+ },
+
+ todoDescription: {
+ fontSize: typography.sizes.sm,
+ color: colors.textLight,
+ lineHeight: typography.sizes.sm * typography.lineHeights.normal,
+ marginBottom: spacing.sm,
+ },
+
+ todoDescriptionCompleted: {
+ textDecorationLine: "line-through",
+ },
+
+ todoMeta: {
+ marginTop: spacing.sm,
+ },
+
+ metaText: {
+ fontSize: typography.sizes.xs,
+ color: colors.textLight,
+ marginBottom: spacing.xs,
+ },
+
+ completionBadge: {
+ marginTop: spacing.xs,
+ alignSelf: "flex-start",
+ },
+
+ completionText: {
+ fontSize: typography.sizes.xs,
+ color: colors.success,
+ fontWeight: typography.weights.medium,
+ },
+
+ // Page layout styles
+ pageHeader: {
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "space-between",
+ paddingHorizontal: spacing.lg,
+ paddingVertical: spacing.md,
+ },
+
+ pageTitle: {
+ fontSize: typography.sizes.xl,
+ fontWeight: typography.weights.bold,
+ color: colors.text,
+ flex: 1,
+ },
+
+ addButton: {
+ backgroundColor: colors.primary,
+ width: 44,
+ height: 44,
+ borderRadius: 22,
+ alignItems: "center",
+ justifyContent: "center",
+ ...shadows.medium,
+ },
+
+ addButtonText: {
+ fontSize: typography.sizes.xl,
+ color: colors.surface,
+ fontWeight: typography.weights.bold,
+ },
+
+ // Form styles
+ formFields: {
+ marginTop: spacing.sm,
+ },
+
+ inputLabel: {
+ fontSize: typography.sizes.sm,
+ fontWeight: typography.weights.medium,
+ color: colors.text,
+ marginBottom: spacing.xs,
+ },
+
+ textArea: {
+ minHeight: 80,
+ textAlignVertical: "top",
+ },
+
+ buttonGroup: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ marginTop: spacing.lg,
+ gap: spacing.md,
+ },
+
+ primaryButton: {
+ backgroundColor: colors.primary,
+ flex: 1,
+ },
+
+ secondaryButton: {
+ backgroundColor: colors.background,
+ borderWidth: 1,
+ borderColor: colors.border,
+ flex: 1,
+ },
+
+ secondaryButtonText: {
+ color: colors.text,
+ },
+
+ // Content layout styles
+ contentSection: {
+ flex: 1,
+ paddingHorizontal: spacing.lg,
+ },
+
+ emptyState: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ paddingHorizontal: spacing.xl,
+ paddingVertical: spacing.xxxl,
+ },
+
+ emptyStateTitle: {
+ fontSize: typography.sizes.lg,
+ fontWeight: typography.weights.semibold,
+ color: colors.text,
+ textAlign: "center",
+ marginBottom: spacing.sm,
+ },
+
+ emptyStateText: {
+ fontSize: typography.sizes.md,
+ color: colors.textLight,
+ textAlign: "center",
+ lineHeight: typography.sizes.md * typography.lineHeights.normal,
+ },
+
+ listContainer: {
+ paddingBottom: spacing.xl,
+ },
+
+ dividerContainer: {
+ flexDirection: "row" as const,
+ alignItems: "center" as const,
+ marginVertical: 15,
+ },
+
+ divider: {
+ flex: 1,
+ height: 1,
+ backgroundColor: colors.border,
+ },
+
+ dividerText: {
+ marginHorizontal: 16,
+ fontSize: 14,
+ color: colors.textLight,
+ fontWeight: "500" as const,
+ },
+});
+
+// Specific component styles that might be reused
+export const profileStyles = StyleSheet.create({
+ profileItem: {
+ paddingVertical: spacing.md,
+ borderBottomWidth: 1,
+ borderBottomColor: colors.borderLight,
+ },
+
+ profileItemLast: {
+ borderBottomWidth: 0,
+ },
+});
+
+export const homeStyles = StyleSheet.create({
+ welcomeCard: {
+ width: "100%",
+ maxWidth: 400,
+ backgroundColor: colors.surface,
+ borderRadius: borderRadius.lg,
+ padding: spacing.xl,
+ alignItems: "center",
+ ...shadows.medium,
+ },
+
+ welcomeText: {
+ fontSize: typography.sizes.lg,
+ marginBottom: spacing.xl,
+ textAlign: "center",
+ color: colors.text,
+ lineHeight: typography.sizes.lg * typography.lineHeights.normal,
+ },
+
+ authMessage: {
+ fontSize: typography.sizes.md,
+ textAlign: "center",
+ color: colors.textLight,
+ lineHeight: typography.sizes.md * typography.lineHeights.normal,
+ },
+});
+
+// File upload specific styles
+export const fileUploadStyles = StyleSheet.create({
+ fileUpload: {
+ borderWidth: 2,
+ borderColor: colors.border,
+ borderStyle: "dashed",
+ borderRadius: borderRadius.lg,
+ padding: spacing.xl,
+ alignItems: "center",
+ justifyContent: "center",
+ backgroundColor: colors.background,
+ marginBottom: spacing.lg,
+ },
+
+ uploadIcon: {
+ marginBottom: spacing.md,
+ },
+
+ uploadIconText: {
+ fontSize: typography.sizes.xxxl,
+ },
+
+ uploadText: {
+ fontSize: typography.sizes.md,
+ color: colors.textLight,
+ textAlign: "center",
+ },
+
+ fileName: {
+ marginTop: spacing.sm,
+ color: colors.primary,
+ fontSize: typography.sizes.sm,
+ textAlign: "center",
+ },
+
+ buttonDisabled: {
+ backgroundColor: colors.textPlaceholder,
+ },
+
+ emptyState: {
+ alignItems: "center",
+ padding: spacing.xl,
+ },
+
+ emptyIcon: {
+ fontSize: typography.sizes.xxxl,
+ marginBottom: spacing.md,
+ },
+
+ emptyTitle: {
+ fontSize: typography.sizes.lg,
+ fontWeight: typography.weights.semibold,
+ color: colors.text,
+ marginBottom: spacing.xs,
+ },
+
+ emptyDescription: {
+ fontSize: typography.sizes.md,
+ color: colors.textLight,
+ textAlign: "center",
+ },
+
+ fileList: {
+ maxHeight: 300,
+ },
+
+ fileItem: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ paddingVertical: spacing.md,
+ paddingHorizontal: spacing.sm,
+ borderBottomWidth: 1,
+ borderBottomColor: colors.borderLight,
+ },
+
+ fileInfo: {
+ flex: 1,
+ paddingRight: spacing.md,
+ },
+
+ fileNameText: {
+ fontSize: typography.sizes.md,
+ fontWeight: typography.weights.medium,
+ color: colors.text,
+ marginBottom: spacing.xs,
+ },
+
+ fileDetails: {
+ fontSize: typography.sizes.sm,
+ color: colors.textLight,
+ },
+
+ fileActions: {
+ flexDirection: "row",
+ },
+
+ actionButton: {
+ padding: spacing.sm,
+ marginHorizontal: spacing.xs,
+ borderRadius: borderRadius.round,
+ backgroundColor: colors.borderLight,
+ },
+
+ deleteButton: {
+ backgroundColor: colors.errorLight,
+ },
+
+ actionText: {
+ fontSize: typography.sizes.md,
+ },
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/styles/theme.ts b/examples/tutorials/nhost-reactnative-tutorial/app/styles/theme.ts
new file mode 100644
index 000000000..1b3f8bfc6
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/styles/theme.ts
@@ -0,0 +1,108 @@
+/**
+ * Design system constants for consistent theming across the app.
+ * These values are used throughout the application for colors, spacing, and typography.
+ */
+
+export const colors = {
+ // Primary brand colors
+ primary: "#6366f1",
+ primaryHover: "#5855eb",
+ secondary: "#818cf8",
+
+ // Background colors
+ background: "#f5f5f5",
+ surface: "#ffffff",
+ overlay: "rgba(0, 0, 0, 0.5)",
+
+ // Text colors
+ text: "#333333",
+ textLight: "#666666",
+ textDark: "#1a1a1a",
+ textPlaceholder: "#999999",
+
+ // Status colors
+ success: "#10b981",
+ successLight: "#34d399",
+ error: "#ef4444",
+ errorLight: "#f87171",
+ warning: "#f59e0b",
+ info: "#3b82f6",
+
+ // Border and divider colors
+ border: "#e5e5e5",
+ borderLight: "#f0f0f0",
+ divider: "#e0e0e0",
+
+ // Shadow color
+ shadow: "#000000",
+};
+
+export const spacing = {
+ xs: 4,
+ sm: 8,
+ md: 12,
+ lg: 16,
+ xl: 20,
+ xxl: 24,
+ xxxl: 32,
+};
+
+export const borderRadius = {
+ sm: 6,
+ md: 8,
+ lg: 10,
+ xl: 12,
+ round: 50,
+};
+
+export const typography = {
+ // Font sizes
+ sizes: {
+ xs: 12,
+ sm: 14,
+ md: 16,
+ lg: 18,
+ xl: 20,
+ xxl: 24,
+ xxxl: 28,
+ },
+
+ // Font weights
+ weights: {
+ normal: "400" as const,
+ medium: "500" as const,
+ semibold: "600" as const,
+ bold: "700" as const,
+ },
+
+ // Line heights
+ lineHeights: {
+ tight: 1.2,
+ normal: 1.4,
+ relaxed: 1.6,
+ },
+};
+
+export const shadows = {
+ small: {
+ shadowColor: colors.shadow,
+ shadowOffset: { width: 0, height: 1 },
+ shadowOpacity: 0.1,
+ shadowRadius: 2,
+ elevation: 2,
+ },
+ medium: {
+ shadowColor: colors.shadow,
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.1,
+ shadowRadius: 3.84,
+ elevation: 5,
+ },
+ large: {
+ shadowColor: colors.shadow,
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 0.15,
+ shadowRadius: 8,
+ elevation: 8,
+ },
+};
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/todos.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/todos.tsx
new file mode 100644
index 000000000..9fa60c3ca
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/todos.tsx
@@ -0,0 +1,561 @@
+import { router, Stack } from "expo-router";
+import { useCallback, useEffect, useState } from "react";
+import {
+ ActivityIndicator,
+ Alert,
+ FlatList,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ View,
+} from "react-native";
+import ProtectedScreen from "./components/ProtectedScreen";
+import { useAuth } from "./lib/nhost/AuthProvider";
+import { commonStyles } from "./styles/commonStyles";
+
+// The interfaces below define the structure of our data
+// They are not strictly necessary but help with type safety
+
+// Represents a single todo item
+interface Todo {
+ id: string;
+ title: string;
+ details: string | null;
+ completed: boolean;
+ created_at: string;
+ updated_at: string;
+ user_id: string;
+}
+
+// This matches the GraphQL response structure for fetching todos
+// Can be used as a generic type on the request method
+interface GetTodos {
+ todos: Todo[];
+}
+
+// This matches the GraphQL response structure for inserting a todo
+// Can be used as a generic type on the request method
+interface InsertTodo {
+ insert_todos_one: Todo | null;
+}
+
+// This matches the GraphQL response structure for updating a todo
+// Can be used as a generic type on the request method
+interface UpdateTodo {
+ update_todos_by_pk: Todo | null;
+}
+
+export default function Todos() {
+ const { nhost, session } = useAuth();
+ const [todos, setTodos] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [newTodoTitle, setNewTodoTitle] = useState("");
+ const [newTodoDetails, setNewTodoDetails] = useState("");
+ const [editingTodo, setEditingTodo] = useState(null);
+ const [showAddForm, setShowAddForm] = useState(false);
+ const [expandedTodos, setExpandedTodos] = useState>(new Set());
+ const [addingTodo, setAddingTodo] = useState(false);
+ const [updatingTodos, setUpdatingTodos] = useState>(new Set());
+
+ // Redirect to sign in if not authenticated
+ useEffect(() => {
+ if (!session) {
+ router.replace("/signin");
+ }
+ }, [session]);
+
+ const fetchTodos = useCallback(async () => {
+ try {
+ setLoading(true);
+ // Make GraphQL request to fetch todos using Nhost client
+ // The query automatically filters by user_id due to Hasura permissions
+ const response = await nhost.graphql.request({
+ query: `
+ query GetTodos {
+ todos(order_by: { created_at: desc }) {
+ id
+ title
+ details
+ completed
+ created_at
+ updated_at
+ user_id
+ }
+ }
+ `,
+ });
+
+ // Check for GraphQL errors in the response body
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to fetch todos",
+ );
+ }
+
+ // Extract todos from the GraphQL response data
+ setTodos(response.body?.data?.todos || []);
+ setError(null);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to fetch todos");
+ } finally {
+ setLoading(false);
+ }
+ }, [nhost.graphql]);
+
+ const addTodo = async () => {
+ if (!newTodoTitle.trim()) return;
+
+ try {
+ setAddingTodo(true);
+ // Execute GraphQL mutation to insert a new todo
+ // user_id is automatically set by Hasura based on JWT token
+ const response = await nhost.graphql.request({
+ query: `
+ mutation InsertTodo($title: String!, $details: String) {
+ insert_todos_one(object: { title: $title, details: $details }) {
+ id
+ title
+ details
+ completed
+ created_at
+ updated_at
+ user_id
+ }
+ }
+ `,
+ variables: {
+ title: newTodoTitle.trim(),
+ details: newTodoDetails.trim() || null,
+ },
+ });
+
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to add todo",
+ );
+ }
+
+ if (!response.body?.data?.insert_todos_one) {
+ throw new Error("Failed to add todo");
+ }
+ setTodos([response.body?.data?.insert_todos_one, ...todos]);
+ setNewTodoTitle("");
+ setNewTodoDetails("");
+ setShowAddForm(false);
+ setError(null);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to add todo");
+ Alert.alert(
+ "Error",
+ err instanceof Error ? err.message : "Failed to add todo",
+ );
+ } finally {
+ setAddingTodo(false);
+ }
+ };
+
+ const updateTodo = async (
+ id: string,
+ updates: Partial>,
+ ) => {
+ try {
+ setUpdatingTodos((prev) => new Set([...prev, id]));
+ // Execute GraphQL mutation to update an existing todo by primary key
+ // Hasura permissions ensure users can only update their own todos
+ const response = await nhost.graphql.request({
+ query: `
+ mutation UpdateTodo($id: uuid!, $updates: todos_set_input!) {
+ update_todos_by_pk(pk_columns: { id: $id }, _set: $updates) {
+ id
+ title
+ details
+ completed
+ created_at
+ updated_at
+ user_id
+ }
+ }
+ `,
+ variables: {
+ id,
+ updates,
+ },
+ });
+
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to update todo",
+ );
+ }
+
+ if (!response.body?.data?.update_todos_by_pk) {
+ throw new Error("Failed to update todo");
+ }
+
+ const updatedTodo = response.body?.data?.update_todos_by_pk;
+ if (updatedTodo) {
+ setTodos(todos.map((todo) => (todo.id === id ? updatedTodo : todo)));
+ }
+ setEditingTodo(null);
+ setError(null);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to update todo");
+ Alert.alert(
+ "Error",
+ err instanceof Error ? err.message : "Failed to update todo",
+ );
+ } finally {
+ setUpdatingTodos((prev) => {
+ const newSet = new Set(prev);
+ newSet.delete(id);
+ return newSet;
+ });
+ }
+ };
+
+ const deleteTodo = async (id: string) => {
+ Alert.alert("Delete Todo", "Are you sure you want to delete this todo?", [
+ { text: "Cancel", style: "cancel" },
+ {
+ text: "Delete",
+ style: "destructive",
+ onPress: async () => {
+ try {
+ setUpdatingTodos((prev) => new Set([...prev, id]));
+ // Execute GraphQL mutation to delete a todo by primary key
+ // Hasura permissions ensure users can only delete their own todos
+ const response = await nhost.graphql.request({
+ query: `
+ mutation DeleteTodo($id: uuid!) {
+ delete_todos_by_pk(id: $id) {
+ id
+ }
+ }
+ `,
+ variables: {
+ id,
+ },
+ });
+
+ if (response.body.errors) {
+ throw new Error(
+ response.body.errors[0]?.message || "Failed to delete todo",
+ );
+ }
+
+ setTodos(todos.filter((todo) => todo.id !== id));
+ setError(null);
+ } catch (err) {
+ setError(
+ err instanceof Error ? err.message : "Failed to delete todo",
+ );
+ Alert.alert(
+ "Error",
+ err instanceof Error ? err.message : "Failed to delete todo",
+ );
+ } finally {
+ setUpdatingTodos((prev) => {
+ const newSet = new Set(prev);
+ newSet.delete(id);
+ return newSet;
+ });
+ }
+ },
+ },
+ ]);
+ };
+
+ const toggleComplete = async (todo: Todo) => {
+ await updateTodo(todo.id, { completed: !todo.completed });
+ };
+
+ const saveEdit = async () => {
+ if (!editingTodo) return;
+ await updateTodo(editingTodo.id, {
+ title: editingTodo.title,
+ details: editingTodo.details,
+ });
+ };
+
+ const toggleTodoExpansion = (todoId: string) => {
+ const newExpanded = new Set(expandedTodos);
+ if (newExpanded.has(todoId)) {
+ newExpanded.delete(todoId);
+ } else {
+ newExpanded.add(todoId);
+ }
+ setExpandedTodos(newExpanded);
+ };
+
+ // Fetch todos when user session is available
+ // The session contains the JWT token needed for GraphQL authentication
+ useEffect(() => {
+ if (session) {
+ fetchTodos();
+ }
+ }, [session, fetchTodos]);
+
+ if (!session) {
+ return null; // Will redirect to sign in
+ }
+
+ const renderTodoItem = ({ item: todo }: { item: Todo }) => {
+ const isUpdating = updatingTodos.has(todo.id);
+ const isExpanded = expandedTodos.has(todo.id);
+
+ return (
+
+ {editingTodo?.id === todo.id ? (
+
+ Title
+
+ setEditingTodo({
+ ...editingTodo,
+ title: text,
+ })
+ }
+ placeholder="Enter todo title"
+ />
+ Details
+
+ setEditingTodo({
+ ...editingTodo,
+ details: text,
+ })
+ }
+ placeholder="Enter details (optional)"
+ multiline
+ numberOfLines={3}
+ />
+
+
+
+ {isUpdating ? "Saving..." : "Save"}
+
+
+ setEditingTodo(null)}
+ >
+
+ Cancel
+
+
+
+
+ ) : (
+
+
+ toggleTodoExpansion(todo.id)}
+ >
+
+ {todo.title}
+
+
+
+ toggleComplete(todo)}
+ disabled={isUpdating}
+ >
+
+ {isUpdating ? "⌛" : todo.completed ? "↶" : "✓"}
+
+
+ setEditingTodo(todo)}
+ >
+ ✏️
+
+ deleteTodo(todo.id)}
+ disabled={isUpdating}
+ >
+ 🗑️
+
+
+
+ {isExpanded && (
+
+ {todo.details && (
+
+ {todo.details}
+
+ )}
+
+
+ Created: {new Date(todo.created_at).toLocaleString()}
+
+
+ Updated: {new Date(todo.updated_at).toLocaleString()}
+
+ {todo.completed && (
+
+
+ ✅ Completed
+
+
+ )}
+
+
+ )}
+
+ )}
+
+ );
+ };
+
+ const renderHeader = () => (
+ <>
+
+ My Todos
+ {!showAddForm && (
+ setShowAddForm(true)}
+ >
+ +
+
+ )}
+
+
+ {error && (
+
+ Error: {error}
+
+ )}
+
+ {showAddForm && (
+
+ Add New Todo
+
+
+ Title *
+
+
+
+ Details
+
+
+
+
+
+ {addingTodo ? "Adding..." : "Add Todo"}
+
+
+ {
+ setShowAddForm(false);
+ setNewTodoTitle("");
+ setNewTodoDetails("");
+ }}
+ >
+
+ Cancel
+
+
+
+
+
+ )}
+ >
+ );
+
+ const renderEmptyState = () => (
+
+ No todos yet
+
+ Create your first todo to get started!
+
+
+ );
+
+ if (loading) {
+ return (
+
+
+
+
+ Loading todos...
+
+
+ );
+ }
+
+ return (
+
+
+
+ item.id}
+ ListHeaderComponent={renderHeader}
+ ListEmptyComponent={!showAddForm ? renderEmptyState : null}
+ showsVerticalScrollIndicator={false}
+ contentContainerStyle={commonStyles.listContainer}
+ />
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/app/verify.tsx b/examples/tutorials/nhost-reactnative-tutorial/app/verify.tsx
new file mode 100644
index 000000000..45848406e
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/app/verify.tsx
@@ -0,0 +1,146 @@
+import { router, useLocalSearchParams } from "expo-router";
+import { useEffect, useState } from "react";
+import { ActivityIndicator, Text, TouchableOpacity, View } from "react-native";
+import { useAuth } from "./lib/nhost/AuthProvider";
+import { commonStyles } from "./styles/commonStyles";
+import { colors } from "./styles/theme";
+
+export default function Verify() {
+ const params = useLocalSearchParams();
+
+ const [status, setStatus] = useState<"verifying" | "success" | "error">(
+ "verifying",
+ );
+ const [error, setError] = useState(null);
+ const [urlParams, setUrlParams] = useState>({});
+
+ const { nhost } = useAuth();
+
+ useEffect(() => {
+ // Extract the refresh token from the URL
+ const refreshToken = params.refreshToken as string;
+
+ if (!refreshToken) {
+ // Collect all URL parameters to display for debugging
+ const allParams: Record = {};
+ Object.entries(params).forEach(([key, value]) => {
+ if (typeof value === "string") {
+ allParams[key] = value;
+ }
+ });
+ setUrlParams(allParams);
+
+ setStatus("error");
+ setError("No refresh token found in URL");
+ return;
+ }
+
+ // Flag to handle component unmounting during async operations
+ let isMounted = true;
+
+ async function processToken(): Promise {
+ try {
+ // First display the verifying message for at least a moment
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ if (!isMounted) return;
+
+ if (!refreshToken) {
+ // Collect all URL parameters to display
+ const allParams: Record = {};
+ Object.entries(params).forEach(([key, value]) => {
+ if (typeof value === "string") {
+ allParams[key] = value;
+ }
+ });
+ setUrlParams(allParams);
+
+ setStatus("error");
+ setError("No refresh token found in URL");
+ return;
+ }
+
+ // Process the token
+ await nhost.auth.refreshToken({ refreshToken });
+
+ if (!isMounted) return;
+
+ setStatus("success");
+
+ // Wait to show success message briefly, then redirect
+ setTimeout(() => {
+ if (isMounted) router.replace("/profile");
+ }, 1500);
+ } catch (err) {
+ const message = (err as Error).message || "Unknown error";
+ if (!isMounted) return;
+
+ setStatus("error");
+ setError(`An error occurred during verification: ${message}`);
+ }
+ }
+
+ processToken();
+
+ // Cleanup function
+ return () => {
+ isMounted = false;
+ };
+ }, [params, nhost.auth]);
+
+ return (
+
+ Email Verification
+
+
+ {status === "verifying" && (
+
+
+ Verifying your email...
+
+
+
+ )}
+
+ {status === "success" && (
+
+
+ ✓ Successfully verified!
+
+
+ You'll be redirected to your profile page shortly...
+
+
+ )}
+
+ {status === "error" && (
+
+ Verification failed
+
+ {error}
+
+
+ {Object.keys(urlParams).length > 0 && (
+
+ URL Parameters:
+ {Object.entries(urlParams).map(([key, value]) => (
+
+ {key}:
+ {value}
+
+ ))}
+
+ )}
+
+ router.replace("/signin")}
+ >
+ Back to Sign In
+
+
+ )}
+
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/android-icon-background.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/android-icon-background.png
new file mode 100644
index 000000000..5ffefc5bb
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/android-icon-background.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/android-icon-foreground.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/android-icon-foreground.png
new file mode 100644
index 000000000..3a9e5016d
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/android-icon-foreground.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/android-icon-monochrome.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/android-icon-monochrome.png
new file mode 100644
index 000000000..77484ebdb
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/android-icon-monochrome.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/favicon.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/favicon.png
new file mode 100644
index 000000000..408bd7466
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/favicon.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/icon.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/icon.png
new file mode 100644
index 000000000..7165a53c7
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/icon.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/partial-react-logo.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/partial-react-logo.png
new file mode 100644
index 000000000..66fd9570e
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/partial-react-logo.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/react-logo.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/react-logo.png
new file mode 100644
index 000000000..9d72a9ffc
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/react-logo.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/react-logo@2x.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/react-logo@2x.png
new file mode 100644
index 000000000..2229b130a
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/react-logo@2x.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/react-logo@3x.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/react-logo@3x.png
new file mode 100644
index 000000000..a99b20322
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/react-logo@3x.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/assets/images/splash-icon.png b/examples/tutorials/nhost-reactnative-tutorial/assets/images/splash-icon.png
new file mode 100644
index 000000000..03d6f6b6c
Binary files /dev/null and b/examples/tutorials/nhost-reactnative-tutorial/assets/images/splash-icon.png differ
diff --git a/examples/tutorials/nhost-reactnative-tutorial/components/external-link.tsx b/examples/tutorials/nhost-reactnative-tutorial/components/external-link.tsx
new file mode 100644
index 000000000..6b5bc5800
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/components/external-link.tsx
@@ -0,0 +1,30 @@
+import { type Href, Link } from "expo-router";
+import {
+ openBrowserAsync,
+ WebBrowserPresentationStyle,
+} from "expo-web-browser";
+import type { ComponentProps } from "react";
+
+type Props = Omit, "href"> & {
+ href: Href & string;
+};
+
+export function ExternalLink({ href, ...rest }: Props) {
+ return (
+ {
+ if (process.env.EXPO_OS !== "web") {
+ // Prevent the default behavior of linking to the default browser on native.
+ event.preventDefault();
+ // Open the link in an in-app browser.
+ await openBrowserAsync(href, {
+ presentationStyle: WebBrowserPresentationStyle.AUTOMATIC,
+ });
+ }
+ }}
+ />
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/components/haptic-tab.tsx b/examples/tutorials/nhost-reactnative-tutorial/components/haptic-tab.tsx
new file mode 100644
index 000000000..e0d64e962
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/components/haptic-tab.tsx
@@ -0,0 +1,18 @@
+import type { BottomTabBarButtonProps } from "@react-navigation/bottom-tabs";
+import { PlatformPressable } from "@react-navigation/elements";
+import * as Haptics from "expo-haptics";
+
+export function HapticTab(props: BottomTabBarButtonProps) {
+ return (
+ {
+ if (process.env.EXPO_OS === "ios") {
+ // Add a soft haptic feedback when pressing down on the tabs.
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
+ }
+ props.onPressIn?.(ev);
+ }}
+ />
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/components/hello-wave.tsx b/examples/tutorials/nhost-reactnative-tutorial/components/hello-wave.tsx
new file mode 100644
index 000000000..af14648cb
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/components/hello-wave.tsx
@@ -0,0 +1,20 @@
+import Animated from "react-native-reanimated";
+
+export function HelloWave() {
+ return (
+
+ 👋
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/components/parallax-scroll-view.tsx b/examples/tutorials/nhost-reactnative-tutorial/components/parallax-scroll-view.tsx
new file mode 100644
index 000000000..19f7f8181
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/components/parallax-scroll-view.tsx
@@ -0,0 +1,85 @@
+import type { PropsWithChildren, ReactElement } from "react";
+import { StyleSheet } from "react-native";
+import Animated, {
+ interpolate,
+ useAnimatedRef,
+ useAnimatedStyle,
+ useScrollOffset,
+} from "react-native-reanimated";
+
+import { ThemedView } from "@/components/themed-view";
+import { useColorScheme } from "@/hooks/use-color-scheme";
+import { useThemeColor } from "@/hooks/use-theme-color";
+
+const HEADER_HEIGHT = 250;
+
+type Props = PropsWithChildren<{
+ headerImage: ReactElement;
+ headerBackgroundColor: { dark: string; light: string };
+}>;
+
+export default function ParallaxScrollView({
+ children,
+ headerImage,
+ headerBackgroundColor,
+}: Props) {
+ const backgroundColor = useThemeColor({}, "background");
+ const colorScheme = useColorScheme() ?? "light";
+ const scrollRef = useAnimatedRef();
+ const scrollOffset = useScrollOffset(scrollRef);
+ const headerAnimatedStyle = useAnimatedStyle(() => {
+ return {
+ transform: [
+ {
+ translateY: interpolate(
+ scrollOffset.value,
+ [-HEADER_HEIGHT, 0, HEADER_HEIGHT],
+ [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75],
+ ),
+ },
+ {
+ scale: interpolate(
+ scrollOffset.value,
+ [-HEADER_HEIGHT, 0, HEADER_HEIGHT],
+ [2, 1, 1],
+ ),
+ },
+ ],
+ };
+ });
+
+ return (
+
+
+ {headerImage}
+
+ {children}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ header: {
+ height: HEADER_HEIGHT,
+ overflow: "hidden",
+ },
+ content: {
+ flex: 1,
+ padding: 32,
+ gap: 16,
+ overflow: "hidden",
+ },
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/components/themed-text.tsx b/examples/tutorials/nhost-reactnative-tutorial/components/themed-text.tsx
new file mode 100644
index 000000000..71112d095
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/components/themed-text.tsx
@@ -0,0 +1,60 @@
+import { StyleSheet, Text, type TextProps } from "react-native";
+
+import { useThemeColor } from "@/hooks/use-theme-color";
+
+export type ThemedTextProps = TextProps & {
+ lightColor?: string;
+ darkColor?: string;
+ type?: "default" | "title" | "defaultSemiBold" | "subtitle" | "link";
+};
+
+export function ThemedText({
+ style,
+ lightColor,
+ darkColor,
+ type = "default",
+ ...rest
+}: ThemedTextProps) {
+ const color = useThemeColor({ light: lightColor, dark: darkColor }, "text");
+
+ return (
+
+ );
+}
+
+const styles = StyleSheet.create({
+ default: {
+ fontSize: 16,
+ lineHeight: 24,
+ },
+ defaultSemiBold: {
+ fontSize: 16,
+ lineHeight: 24,
+ fontWeight: "600",
+ },
+ title: {
+ fontSize: 32,
+ fontWeight: "bold",
+ lineHeight: 32,
+ },
+ subtitle: {
+ fontSize: 20,
+ fontWeight: "bold",
+ },
+ link: {
+ lineHeight: 30,
+ fontSize: 16,
+ color: "#0a7ea4",
+ },
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/components/themed-view.tsx b/examples/tutorials/nhost-reactnative-tutorial/components/themed-view.tsx
new file mode 100644
index 000000000..fac619889
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/components/themed-view.tsx
@@ -0,0 +1,22 @@
+import { View, type ViewProps } from "react-native";
+
+import { useThemeColor } from "@/hooks/use-theme-color";
+
+export type ThemedViewProps = ViewProps & {
+ lightColor?: string;
+ darkColor?: string;
+};
+
+export function ThemedView({
+ style,
+ lightColor,
+ darkColor,
+ ...otherProps
+}: ThemedViewProps) {
+ const backgroundColor = useThemeColor(
+ { light: lightColor, dark: darkColor },
+ "background",
+ );
+
+ return ;
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/components/ui/collapsible.tsx b/examples/tutorials/nhost-reactnative-tutorial/components/ui/collapsible.tsx
new file mode 100644
index 000000000..1c17b94e9
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/components/ui/collapsible.tsx
@@ -0,0 +1,49 @@
+import { type PropsWithChildren, useState } from "react";
+import { StyleSheet, TouchableOpacity } from "react-native";
+
+import { ThemedText } from "@/components/themed-text";
+import { ThemedView } from "@/components/themed-view";
+import { IconSymbol } from "@/components/ui/icon-symbol";
+import { Colors } from "@/constants/theme";
+import { useColorScheme } from "@/hooks/use-color-scheme";
+
+export function Collapsible({
+ children,
+ title,
+}: PropsWithChildren & { title: string }) {
+ const [isOpen, setIsOpen] = useState(false);
+ const theme = useColorScheme() ?? "light";
+
+ return (
+
+ setIsOpen((value) => !value)}
+ activeOpacity={0.8}
+ >
+
+
+ {title}
+
+ {isOpen && {children} }
+
+ );
+}
+
+const styles = StyleSheet.create({
+ heading: {
+ flexDirection: "row",
+ alignItems: "center",
+ gap: 6,
+ },
+ content: {
+ marginTop: 6,
+ marginLeft: 24,
+ },
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/components/ui/icon-symbol.ios.tsx b/examples/tutorials/nhost-reactnative-tutorial/components/ui/icon-symbol.ios.tsx
new file mode 100644
index 000000000..8e5d26ae1
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/components/ui/icon-symbol.ios.tsx
@@ -0,0 +1,36 @@
+import {
+ SymbolView,
+ type SymbolViewProps,
+ type SymbolWeight,
+} from "expo-symbols";
+import type { StyleProp, ViewStyle } from "react-native";
+
+export function IconSymbol({
+ name,
+ size = 24,
+ color,
+ style,
+ weight = "regular",
+}: {
+ name: SymbolViewProps["name"];
+ size?: number;
+ color: string;
+ style?: StyleProp;
+ weight?: SymbolWeight;
+}) {
+ return (
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/components/ui/icon-symbol.tsx b/examples/tutorials/nhost-reactnative-tutorial/components/ui/icon-symbol.tsx
new file mode 100644
index 000000000..35f08deaf
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/components/ui/icon-symbol.tsx
@@ -0,0 +1,51 @@
+// Fallback for using MaterialIcons on Android and web.
+
+import MaterialIcons from "@expo/vector-icons/MaterialIcons";
+import type { SymbolViewProps, SymbolWeight } from "expo-symbols";
+import type { ComponentProps } from "react";
+import type { OpaqueColorValue, StyleProp, TextStyle } from "react-native";
+
+type IconMapping = Record<
+ SymbolViewProps["name"],
+ ComponentProps["name"]
+>;
+type IconSymbolName = keyof typeof MAPPING;
+
+/**
+ * Add your SF Symbols to Material Icons mappings here.
+ * - see Material Icons in the [Icons Directory](https://icons.expo.fyi).
+ * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app.
+ */
+const MAPPING = {
+ "house.fill": "home",
+ "paperplane.fill": "send",
+ "chevron.left.forwardslash.chevron.right": "code",
+ "chevron.right": "chevron-right",
+} as IconMapping;
+
+/**
+ * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web.
+ * This ensures a consistent look across platforms, and optimal resource usage.
+ * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons.
+ */
+export function IconSymbol({
+ name,
+ size = 24,
+ color,
+ style,
+}: {
+ name: IconSymbolName;
+ size?: number;
+ color: string | OpaqueColorValue;
+ style?: StyleProp;
+ weight?: SymbolWeight;
+}) {
+ return (
+
+ );
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/constants/theme.ts b/examples/tutorials/nhost-reactnative-tutorial/constants/theme.ts
new file mode 100644
index 000000000..7b8642a70
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/constants/theme.ts
@@ -0,0 +1,54 @@
+/**
+ * Below are the colors that are used in the app. The colors are defined in the light and dark mode.
+ * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
+ */
+
+import { Platform } from "react-native";
+
+const tintColorLight = "#0a7ea4";
+const tintColorDark = "#fff";
+
+export const Colors = {
+ light: {
+ text: "#11181C",
+ background: "#fff",
+ tint: tintColorLight,
+ icon: "#687076",
+ tabIconDefault: "#687076",
+ tabIconSelected: tintColorLight,
+ },
+ dark: {
+ text: "#ECEDEE",
+ background: "#151718",
+ tint: tintColorDark,
+ icon: "#9BA1A6",
+ tabIconDefault: "#9BA1A6",
+ tabIconSelected: tintColorDark,
+ },
+};
+
+export const Fonts = Platform.select({
+ ios: {
+ /** iOS `UIFontDescriptorSystemDesignDefault` */
+ sans: "system-ui",
+ /** iOS `UIFontDescriptorSystemDesignSerif` */
+ serif: "ui-serif",
+ /** iOS `UIFontDescriptorSystemDesignRounded` */
+ rounded: "ui-rounded",
+ /** iOS `UIFontDescriptorSystemDesignMonospaced` */
+ mono: "ui-monospace",
+ },
+ default: {
+ sans: "normal",
+ serif: "serif",
+ rounded: "normal",
+ mono: "monospace",
+ },
+ web: {
+ sans: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
+ serif: "Georgia, 'Times New Roman', serif",
+ rounded:
+ "'SF Pro Rounded', 'Hiragino Maru Gothic ProN', Meiryo, 'MS PGothic', sans-serif",
+ mono: "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
+ },
+});
diff --git a/examples/tutorials/nhost-reactnative-tutorial/hooks/use-color-scheme.ts b/examples/tutorials/nhost-reactnative-tutorial/hooks/use-color-scheme.ts
new file mode 100644
index 000000000..93a8fde95
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/hooks/use-color-scheme.ts
@@ -0,0 +1 @@
+export { useColorScheme } from "react-native";
diff --git a/examples/tutorials/nhost-reactnative-tutorial/hooks/use-color-scheme.web.ts b/examples/tutorials/nhost-reactnative-tutorial/hooks/use-color-scheme.web.ts
new file mode 100644
index 000000000..66cccac5e
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/hooks/use-color-scheme.web.ts
@@ -0,0 +1,21 @@
+import { useEffect, useState } from "react";
+import { useColorScheme as useRNColorScheme } from "react-native";
+
+/**
+ * To support static rendering, this value needs to be re-calculated on the client side for web
+ */
+export function useColorScheme() {
+ const [hasHydrated, setHasHydrated] = useState(false);
+
+ useEffect(() => {
+ setHasHydrated(true);
+ }, []);
+
+ const colorScheme = useRNColorScheme();
+
+ if (hasHydrated) {
+ return colorScheme;
+ }
+
+ return "light";
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/hooks/use-theme-color.ts b/examples/tutorials/nhost-reactnative-tutorial/hooks/use-theme-color.ts
new file mode 100644
index 000000000..36b012ca9
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/hooks/use-theme-color.ts
@@ -0,0 +1,21 @@
+/**
+ * Learn more about light and dark modes:
+ * https://docs.expo.dev/guides/color-schemes/
+ */
+
+import { Colors } from "@/constants/theme";
+import { useColorScheme } from "@/hooks/use-color-scheme";
+
+export function useThemeColor(
+ props: { light?: string; dark?: string },
+ colorName: keyof typeof Colors.light & keyof typeof Colors.dark,
+) {
+ const theme = useColorScheme() ?? "light";
+ const colorFromProps = props[theme];
+
+ if (colorFromProps) {
+ return colorFromProps;
+ } else {
+ return Colors[theme][colorName];
+ }
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/package.json b/examples/tutorials/nhost-reactnative-tutorial/package.json
new file mode 100644
index 000000000..a86d7d67c
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/package.json
@@ -0,0 +1,58 @@
+{
+ "name": "nhost-reactnative-tutorial",
+ "main": "expo-router/entry",
+ "version": "1.0.0",
+ "scripts": {
+ "start": "expo start",
+ "reset-project": "node ./scripts/reset-project.js",
+ "generate": "echo 'Nothing to do'",
+ "test": "pnpm test:typecheck && pnpm test:lint",
+ "test:typecheck": "tsc --noEmit",
+ "test:lint": "biome check",
+ "format": "biome format --write",
+ "build": "pnpm expo export -p ios -p android",
+ "android": "expo start --android",
+ "ios": "expo start --ios"
+ },
+ "dependencies": {
+ "@expo/vector-icons": "^15.0.2",
+ "@nhost/nhost-js": "workspace:^",
+ "@react-native-async-storage/async-storage": "2.2.0",
+ "@react-navigation/bottom-tabs": "^7.4.0",
+ "@react-navigation/elements": "^2.6.3",
+ "@react-navigation/native": "^7.1.8",
+ "expo": "~54.0.7",
+ "expo-apple-authentication": "^7.2.4",
+ "expo-constants": "~18.0.8",
+ "expo-crypto": "14",
+ "expo-document-picker": "~13.1.6",
+ "expo-file-system": "~18.1.11",
+ "expo-font": "~14.0.8",
+ "expo-haptics": "~15.0.7",
+ "expo-image": "~3.0.8",
+ "expo-linking": "~8.0.8",
+ "expo-router": "~6.0.4",
+ "expo-sharing": "~13.1.5",
+ "expo-splash-screen": "~31.0.10",
+ "expo-status-bar": "~3.0.8",
+ "expo-symbols": "~1.0.7",
+ "expo-system-ui": "~6.0.7",
+ "expo-web-browser": "~15.0.7",
+ "react": "19.1.0",
+ "react-dom": "19.1.0",
+ "react-native": "0.81.4",
+ "react-native-gesture-handler": "~2.28.0",
+ "react-native-reanimated": "~4.1.0",
+ "react-native-safe-area-context": "~5.6.0",
+ "react-native-screens": "~4.16.0",
+ "react-native-web": "~0.21.0",
+ "react-native-worklets": "0.5.1"
+ },
+ "devDependencies": {
+ "@types/react": "~19.1.0",
+ "eslint": "^9.25.0",
+ "eslint-config-expo": "~10.0.0",
+ "typescript": "~5.9.2"
+ },
+ "private": true
+}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/pnpm-lock.yaml b/examples/tutorials/nhost-reactnative-tutorial/pnpm-lock.yaml
new file mode 100644
index 000000000..550f976a8
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/pnpm-lock.yaml
@@ -0,0 +1,9269 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@expo/vector-icons':
+ specifier: ^15.0.2
+ version: 15.0.2(expo-font@14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@nhost/nhost-js':
+ specifier: workspace:^
+ version: link:../../../packages/nhost-js
+ '@react-native-async-storage/async-storage':
+ specifier: 2.2.0
+ version: 2.2.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ '@react-navigation/bottom-tabs':
+ specifier: ^7.4.0
+ version: 7.4.7(@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@react-navigation/elements':
+ specifier: ^2.6.3
+ version: 2.6.4(@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@react-navigation/native':
+ specifier: ^7.1.8
+ version: 7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo:
+ specifier: ~54.0.7
+ version: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-apple-authentication:
+ specifier: ^7.2.4
+ version: 7.2.4(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ expo-constants:
+ specifier: ~18.0.8
+ version: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ expo-crypto:
+ specifier: '14'
+ version: 14.1.5(expo@54.0.7)
+ expo-document-picker:
+ specifier: ~13.1.6
+ version: 13.1.6(expo@54.0.7)
+ expo-file-system:
+ specifier: ~18.1.11
+ version: 18.1.11(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ expo-font:
+ specifier: ~14.0.8
+ version: 14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-haptics:
+ specifier: ~15.0.7
+ version: 15.0.7(expo@54.0.7)
+ expo-image:
+ specifier: ~3.0.8
+ version: 3.0.8(expo@54.0.7)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-linking:
+ specifier: ~8.0.8
+ version: 8.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-router:
+ specifier: ~6.0.4
+ version: 6.0.4(@expo/metro-runtime@6.1.2)(@types/react@19.1.13)(expo-constants@18.0.8)(expo-linking@8.0.8)(expo@54.0.7)(react-dom@19.1.0(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.0(@babel/core@7.28.4)(react-native-worklets@0.5.1(@babel/core@7.28.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-sharing:
+ specifier: ~13.1.5
+ version: 13.1.5(expo@54.0.7)
+ expo-splash-screen:
+ specifier: ~31.0.10
+ version: 31.0.10(expo@54.0.7)
+ expo-status-bar:
+ specifier: ~3.0.8
+ version: 3.0.8(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-symbols:
+ specifier: ~1.0.7
+ version: 1.0.7(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ expo-system-ui:
+ specifier: ~6.0.7
+ version: 6.0.7(expo@54.0.7)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ expo-web-browser:
+ specifier: ~15.0.7
+ version: 15.0.7(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ react:
+ specifier: 19.1.0
+ version: 19.1.0
+ react-dom:
+ specifier: 19.1.0
+ version: 19.1.0(react@19.1.0)
+ react-native:
+ specifier: 0.81.4
+ version: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ react-native-gesture-handler:
+ specifier: ~2.28.0
+ version: 2.28.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-reanimated:
+ specifier: ~4.1.0
+ version: 4.1.0(@babel/core@7.28.4)(react-native-worklets@0.5.1(@babel/core@7.28.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-safe-area-context:
+ specifier: ~5.6.0
+ version: 5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-screens:
+ specifier: ~4.16.0
+ version: 4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-web:
+ specifier: ~0.21.0
+ version: 0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react-native-worklets:
+ specifier: 0.5.1
+ version: 0.5.1(@babel/core@7.28.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ devDependencies:
+ '@types/react':
+ specifier: ~19.1.0
+ version: 19.1.13
+ eslint:
+ specifier: ^9.25.0
+ version: 9.35.0
+ eslint-config-expo:
+ specifier: ~10.0.0
+ version: 10.0.0(eslint@9.35.0)(typescript@5.9.2)
+ typescript:
+ specifier: ~5.9.2
+ version: 5.9.2
+
+packages:
+
+ '@0no-co/graphql.web@1.2.0':
+ resolution: {integrity: sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==}
+ peerDependencies:
+ graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
+ peerDependenciesMeta:
+ graphql:
+ optional: true
+
+ '@babel/code-frame@7.10.4':
+ resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==}
+
+ '@babel/code-frame@7.27.1':
+ resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.28.4':
+ resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.28.4':
+ resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.28.3':
+ resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.27.2':
+ resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-create-class-features-plugin@7.28.3':
+ resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-create-regexp-features-plugin@7.27.1':
+ resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-define-polyfill-provider@0.6.5':
+ resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-member-expression-to-functions@7.27.1':
+ resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.27.1':
+ resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.3':
+ resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-plugin-utils@7.27.1':
+ resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-remap-async-to-generator@7.27.1':
+ resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-replace-supers@7.27.1':
+ resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.27.1':
+ resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-wrap-function@7.28.3':
+ resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.4':
+ resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/highlight@7.25.9':
+ resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.4':
+ resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-proposal-decorators@7.28.0':
+ resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-proposal-export-default-from@7.27.1':
+ resolution: {integrity: sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-async-generators@7.8.4':
+ resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-bigint@7.8.3':
+ resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-class-properties@7.12.13':
+ resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-class-static-block@7.14.5':
+ resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-decorators@7.27.1':
+ resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-dynamic-import@7.8.3':
+ resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-export-default-from@7.27.1':
+ resolution: {integrity: sha512-eBC/3KSekshx19+N40MzjWqJd7KTEdOoLesAfa4IDFI8eRz5a47i5Oszus6zG/cwIXN63YhgLOMSSNJx49sENg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-flow@7.27.1':
+ resolution: {integrity: sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-attributes@7.27.1':
+ resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-meta@7.10.4':
+ resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-json-strings@7.8.3':
+ resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-jsx@7.27.1':
+ resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-logical-assignment-operators@7.10.4':
+ resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
+ resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-numeric-separator@7.10.4':
+ resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-object-rest-spread@7.8.3':
+ resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-optional-catch-binding@7.8.3':
+ resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-optional-chaining@7.8.3':
+ resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-private-property-in-object@7.14.5':
+ resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-top-level-await@7.14.5':
+ resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-typescript@7.27.1':
+ resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-arrow-functions@7.27.1':
+ resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-async-generator-functions@7.28.0':
+ resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-async-to-generator@7.27.1':
+ resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-block-scoping@7.28.4':
+ resolution: {integrity: sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-class-properties@7.27.1':
+ resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-class-static-block@7.28.3':
+ resolution: {integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.12.0
+
+ '@babel/plugin-transform-classes@7.28.4':
+ resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-computed-properties@7.27.1':
+ resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-destructuring@7.28.0':
+ resolution: {integrity: sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-export-namespace-from@7.27.1':
+ resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-flow-strip-types@7.27.1':
+ resolution: {integrity: sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-for-of@7.27.1':
+ resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-function-name@7.27.1':
+ resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-literals@7.27.1':
+ resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-logical-assignment-operators@7.27.1':
+ resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-modules-commonjs@7.27.1':
+ resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-named-capturing-groups-regex@7.27.1':
+ resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-transform-nullish-coalescing-operator@7.27.1':
+ resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-numeric-separator@7.27.1':
+ resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-object-rest-spread@7.28.4':
+ resolution: {integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-optional-catch-binding@7.27.1':
+ resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-optional-chaining@7.27.1':
+ resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-parameters@7.27.7':
+ resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-private-methods@7.27.1':
+ resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-private-property-in-object@7.27.1':
+ resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-display-name@7.28.0':
+ resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-development@7.27.1':
+ resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1':
+ resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1':
+ resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx@7.27.1':
+ resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-pure-annotations@7.27.1':
+ resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-regenerator@7.28.4':
+ resolution: {integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-runtime@7.28.3':
+ resolution: {integrity: sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-shorthand-properties@7.27.1':
+ resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-spread@7.27.1':
+ resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-sticky-regex@7.27.1':
+ resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-template-literals@7.27.1':
+ resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-typescript@7.28.0':
+ resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-unicode-regex@7.27.1':
+ resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/preset-react@7.27.1':
+ resolution: {integrity: sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/preset-typescript@7.27.1':
+ resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/runtime@7.28.4':
+ resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.27.2':
+ resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.28.4':
+ resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.28.4':
+ resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@egjs/hammerjs@2.0.17':
+ resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==}
+ engines: {node: '>=0.8.0'}
+
+ '@emnapi/core@1.5.0':
+ resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==}
+
+ '@emnapi/runtime@1.5.0':
+ resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==}
+
+ '@emnapi/wasi-threads@1.1.0':
+ resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
+
+ '@eslint-community/eslint-utils@4.9.0':
+ resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.21.0':
+ resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-helpers@0.3.1':
+ resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.15.2':
+ resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.3.1':
+ resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.35.0':
+ resolution: {integrity: sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.6':
+ resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.3.5':
+ resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@expo/cli@54.0.5':
+ resolution: {integrity: sha512-8MZOZKHfHRHTBQu2/PXBi7eCKc2aF1i1JsZweL/P7aX8nivhrP6KV6An5PtO1/rrdnS9z7pmX2KwMygvvaFNhg==}
+ hasBin: true
+ peerDependencies:
+ expo: '*'
+ expo-router: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ expo-router:
+ optional: true
+ react-native:
+ optional: true
+
+ '@expo/code-signing-certificates@0.0.5':
+ resolution: {integrity: sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw==}
+
+ '@expo/config-plugins@54.0.1':
+ resolution: {integrity: sha512-NyBChhiWFL6VqSgU+LzK4R1vC397tEG2XFewVt4oMr4Pnalq/mJxBANQrR+dyV1RHhSyhy06RNiJIkQyngVWeg==}
+
+ '@expo/config-types@54.0.8':
+ resolution: {integrity: sha512-lyIn/x/Yz0SgHL7IGWtgTLg6TJWC9vL7489++0hzCHZ4iGjVcfZmPTUfiragZ3HycFFj899qN0jlhl49IHa94A==}
+
+ '@expo/config@12.0.9':
+ resolution: {integrity: sha512-HiDVVaXYKY57+L1MxSF3TaYjX6zZlGBnuWnOKZG+7mtsLD+aNTtW4bZM0pZqZfoRumyOU0SfTCwT10BWtUUiJQ==}
+
+ '@expo/devcert@1.2.0':
+ resolution: {integrity: sha512-Uilcv3xGELD5t/b0eM4cxBFEKQRIivB3v7i+VhWLV/gL98aw810unLKKJbGAxAIhY6Ipyz8ChWibFsKFXYwstA==}
+
+ '@expo/devtools@0.1.7':
+ resolution: {integrity: sha512-dfIa9qMyXN+0RfU6SN4rKeXZyzKWsnz6xBSDccjL4IRiE+fQ0t84zg0yxgN4t/WK2JU5v6v4fby7W7Crv9gJvA==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ react:
+ optional: true
+ react-native:
+ optional: true
+
+ '@expo/env@2.0.7':
+ resolution: {integrity: sha512-BNETbLEohk3HQ2LxwwezpG8pq+h7Fs7/vAMP3eAtFT1BCpprLYoBBFZH7gW4aqGfqOcVP4Lc91j014verrYNGg==}
+
+ '@expo/fingerprint@0.15.0':
+ resolution: {integrity: sha512-PrLA6fxScZfnLy7OHZ2GHXsDG9YbE7L5DbNhion6j/U/O+FQgz4VbxJarW5C00kMg1ll2u6EghB7ENAvL1T4qg==}
+ hasBin: true
+
+ '@expo/image-utils@0.8.7':
+ resolution: {integrity: sha512-SXOww4Wq3RVXLyOaXiCCuQFguCDh8mmaHBv54h/R29wGl4jRY8GEyQEx8SypV/iHt1FbzsU/X3Qbcd9afm2W2w==}
+
+ '@expo/json-file@10.0.7':
+ resolution: {integrity: sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw==}
+
+ '@expo/metro-config@54.0.3':
+ resolution: {integrity: sha512-TQ5MKSGFB6zJxi+Yr8VYXQFHzRXgvSJzNsHX1otTqnxjXbptwYiXhljAqGSjr3pByq4+sHX/GifMk6fGgAANmA==}
+ peerDependencies:
+ expo: '*'
+ peerDependenciesMeta:
+ expo:
+ optional: true
+
+ '@expo/metro-runtime@6.1.2':
+ resolution: {integrity: sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-dom: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+
+ '@expo/metro@0.1.1':
+ resolution: {integrity: sha512-zvA9BE6myFoCxeiw/q3uE/kVkIwLTy27a+fDoEl7WQ7EvKfFeiXnRVhUplDMLGZIHH8VC38Gay6RBtVhnmOm5w==}
+
+ '@expo/osascript@2.3.7':
+ resolution: {integrity: sha512-IClSOXxR0YUFxIriUJVqyYki7lLMIHrrzOaP01yxAL1G8pj2DWV5eW1y5jSzIcIfSCNhtGsshGd1tU/AYup5iQ==}
+ engines: {node: '>=12'}
+
+ '@expo/package-manager@1.9.7':
+ resolution: {integrity: sha512-k3uky8Qzlv21rxuPvP2KUTAy8NI0b/LP7BSXcwJpS/rH7RmiAqUXgzPar3I1OmKGgxpod78Y9Mae//F8d3aiOQ==}
+
+ '@expo/plist@0.4.7':
+ resolution: {integrity: sha512-dGxqHPvCZKeRKDU1sJZMmuyVtcASuSYh1LPFVaM1DuffqPL36n6FMEL0iUqq2Tx3xhWk8wCnWl34IKplUjJDdA==}
+
+ '@expo/prebuild-config@54.0.3':
+ resolution: {integrity: sha512-okf6Umaz1VniKmm+pA37QHBzB9XlRHvO1Qh3VbUezy07LTkz87kXUW7uLMmrA319WLavWSVORTXeR0jBRihObA==}
+ peerDependencies:
+ expo: '*'
+
+ '@expo/schema-utils@0.1.7':
+ resolution: {integrity: sha512-jWHoSuwRb5ZczjahrychMJ3GWZu54jK9ulNdh1d4OzAEq672K9E5yOlnlBsfIHWHGzUAT+0CL7Yt1INiXTz68g==}
+
+ '@expo/sdk-runtime-versions@1.0.0':
+ resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==}
+
+ '@expo/server@0.7.4':
+ resolution: {integrity: sha512-8bfRzL7h1Qgrmf3auR71sPAcAuxnmNkRJs+8enL8vZi2+hihevLhrayDu7P0A/XGEq7wySAGvBBFfIB00Et/AA==}
+ engines: {node: '>=20.16.0'}
+
+ '@expo/spawn-async@1.7.2':
+ resolution: {integrity: sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==}
+ engines: {node: '>=12'}
+
+ '@expo/sudo-prompt@9.3.2':
+ resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==}
+
+ '@expo/vector-icons@15.0.2':
+ resolution: {integrity: sha512-IiBjg7ZikueuHNf40wSGCf0zS73a3guJLdZzKnDUxsauB8VWPLMeWnRIupc+7cFhLUkqyvyo0jLNlcxG5xPOuQ==}
+ peerDependencies:
+ expo-font: '>=14.0.4'
+ react: '*'
+ react-native: '*'
+
+ '@expo/ws-tunnel@1.0.6':
+ resolution: {integrity: sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==}
+
+ '@expo/xcpretty@4.3.2':
+ resolution: {integrity: sha512-ReZxZ8pdnoI3tP/dNnJdnmAk7uLT4FjsKDGW7YeDdvdOMz2XCQSmSCM9IWlrXuWtMF9zeSB6WJtEhCQ41gQOfw==}
+ hasBin: true
+
+ '@humanfs/core@0.19.1':
+ resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.7':
+ resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@isaacs/fs-minipass@4.0.1':
+ resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+ engines: {node: '>=18.0.0'}
+
+ '@isaacs/ttlcache@1.4.1':
+ resolution: {integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==}
+ engines: {node: '>=12'}
+
+ '@istanbuljs/load-nyc-config@1.1.0':
+ resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
+ engines: {node: '>=8'}
+
+ '@istanbuljs/schema@0.1.3':
+ resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+ engines: {node: '>=8'}
+
+ '@jest/create-cache-key-function@29.7.0':
+ resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/environment@29.7.0':
+ resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/fake-timers@29.7.0':
+ resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/schemas@29.6.3':
+ resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/transform@29.7.0':
+ resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/types@29.6.3':
+ resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/source-map@0.3.11':
+ resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@napi-rs/wasm-runtime@0.2.12':
+ resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@nolyfill/is-core-module@1.0.39':
+ resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
+ engines: {node: '>=12.4.0'}
+
+ '@pkgjs/parseargs@0.11.0':
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+
+ '@radix-ui/primitive@1.1.3':
+ resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
+
+ '@radix-ui/react-collection@1.1.7':
+ resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-compose-refs@1.1.2':
+ resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-context@1.1.2':
+ resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-dialog@1.1.15':
+ resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-direction@1.1.1':
+ resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-dismissable-layer@1.1.11':
+ resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-focus-guards@1.1.3':
+ resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-focus-scope@1.1.7':
+ resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-id@1.1.1':
+ resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-portal@1.1.9':
+ resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-presence@1.1.5':
+ resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-primitive@2.1.3':
+ resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-roving-focus@1.1.11':
+ resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slot@1.2.0':
+ resolution: {integrity: sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-slot@1.2.3':
+ resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-tabs@1.1.13':
+ resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-use-callback-ref@1.1.1':
+ resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-controllable-state@1.2.2':
+ resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-effect-event@0.0.2':
+ resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-escape-keydown@1.1.1':
+ resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-layout-effect@1.1.1':
+ resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@react-native-async-storage/async-storage@2.2.0':
+ resolution: {integrity: sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==}
+ peerDependencies:
+ react-native: ^0.0.0-0 || >=0.65 <1.0
+
+ '@react-native/assets-registry@0.81.4':
+ resolution: {integrity: sha512-AMcDadefBIjD10BRqkWw+W/VdvXEomR6aEZ0fhQRAv7igrBzb4PTn4vHKYg+sUK0e3wa74kcMy2DLc/HtnGcMA==}
+ engines: {node: '>= 20.19.4'}
+
+ '@react-native/babel-plugin-codegen@0.81.4':
+ resolution: {integrity: sha512-6ztXf2Tl2iWznyI/Da/N2Eqymt0Mnn69GCLnEFxFbNdk0HxHPZBNWU9shTXhsLWOL7HATSqwg/bB1+3kY1q+mA==}
+ engines: {node: '>= 20.19.4'}
+
+ '@react-native/babel-preset@0.81.4':
+ resolution: {integrity: sha512-VYj0c/cTjQJn/RJ5G6P0L9wuYSbU9yGbPYDHCKstlQZQWkk+L9V8ZDbxdJBTIei9Xl3KPQ1odQ4QaeW+4v+AZg==}
+ engines: {node: '>= 20.19.4'}
+ peerDependencies:
+ '@babel/core': '*'
+
+ '@react-native/codegen@0.81.4':
+ resolution: {integrity: sha512-LWTGUTzFu+qOQnvkzBP52B90Ym3stZT8IFCzzUrppz8Iwglg83FCtDZAR4yLHI29VY/x/+pkcWAMCl3739XHdw==}
+ engines: {node: '>= 20.19.4'}
+ peerDependencies:
+ '@babel/core': '*'
+
+ '@react-native/community-cli-plugin@0.81.4':
+ resolution: {integrity: sha512-8mpnvfcLcnVh+t1ok6V9eozWo8Ut+TZhz8ylJ6gF9d6q9EGDQX6s8jenan5Yv/pzN4vQEKI4ib2pTf/FELw+SA==}
+ engines: {node: '>= 20.19.4'}
+ peerDependencies:
+ '@react-native-community/cli': '*'
+ '@react-native/metro-config': '*'
+ peerDependenciesMeta:
+ '@react-native-community/cli':
+ optional: true
+ '@react-native/metro-config':
+ optional: true
+
+ '@react-native/debugger-frontend@0.81.4':
+ resolution: {integrity: sha512-SU05w1wD0nKdQFcuNC9D6De0ITnINCi8MEnx9RsTD2e4wN83ukoC7FpXaPCYyP6+VjFt5tUKDPgP1O7iaNXCqg==}
+ engines: {node: '>= 20.19.4'}
+
+ '@react-native/dev-middleware@0.81.4':
+ resolution: {integrity: sha512-hu1Wu5R28FT7nHXs2wWXvQ++7W7zq5GPY83llajgPlYKznyPLAY/7bArc5rAzNB7b0kwnlaoPQKlvD/VP9LZug==}
+ engines: {node: '>= 20.19.4'}
+
+ '@react-native/gradle-plugin@0.81.4':
+ resolution: {integrity: sha512-T7fPcQvDDCSusZFVSg6H1oVDKb/NnVYLnsqkcHsAF2C2KGXyo3J7slH/tJAwNfj/7EOA2OgcWxfC1frgn9TQvw==}
+ engines: {node: '>= 20.19.4'}
+
+ '@react-native/js-polyfills@0.81.4':
+ resolution: {integrity: sha512-sr42FaypKXJHMVHhgSbu2f/ZJfrLzgaoQ+HdpRvKEiEh2mhFf6XzZwecyLBvWqf2pMPZa+CpPfNPiejXjKEy8w==}
+ engines: {node: '>= 20.19.4'}
+
+ '@react-native/normalize-colors@0.74.89':
+ resolution: {integrity: sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==}
+
+ '@react-native/normalize-colors@0.81.4':
+ resolution: {integrity: sha512-9nRRHO1H+tcFqjb9gAM105Urtgcanbta2tuqCVY0NATHeFPDEAB7gPyiLxCHKMi1NbhP6TH0kxgSWXKZl1cyRg==}
+
+ '@react-native/virtualized-lists@0.81.4':
+ resolution: {integrity: sha512-hBM+rMyL6Wm1Q4f/WpqGsaCojKSNUBqAXLABNGoWm1vabZ7cSnARMxBvA/2vo3hLcoR4v7zDK8tkKm9+O0LjVA==}
+ engines: {node: '>= 20.19.4'}
+ peerDependencies:
+ '@types/react': ^19.1.0
+ react: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@react-navigation/bottom-tabs@7.4.7':
+ resolution: {integrity: sha512-SQ4KuYV9yr3SV/thefpLWhAD0CU2CrBMG1l0w/QKl3GYuGWdN5OQmdQdmaPZGtsjjVOb+N9Qo7Tf6210P4TlpA==}
+ peerDependencies:
+ '@react-navigation/native': ^7.1.17
+ react: '>= 18.2.0'
+ react-native: '*'
+ react-native-safe-area-context: '>= 4.0.0'
+ react-native-screens: '>= 4.0.0'
+
+ '@react-navigation/core@7.12.4':
+ resolution: {integrity: sha512-xLFho76FA7v500XID5z/8YfGTvjQPw7/fXsq4BIrVSqetNe/o/v+KAocEw4ots6kyv3XvSTyiWKh2g3pN6xZ9Q==}
+ peerDependencies:
+ react: '>= 18.2.0'
+
+ '@react-navigation/elements@2.6.4':
+ resolution: {integrity: sha512-O3X9vWXOEhAO56zkQS7KaDzL8BvjlwZ0LGSteKpt1/k6w6HONG+2Wkblrb057iKmehTkEkQMzMLkXiuLmN5x9Q==}
+ peerDependencies:
+ '@react-native-masked-view/masked-view': '>= 0.2.0'
+ '@react-navigation/native': ^7.1.17
+ react: '>= 18.2.0'
+ react-native: '*'
+ react-native-safe-area-context: '>= 4.0.0'
+ peerDependenciesMeta:
+ '@react-native-masked-view/masked-view':
+ optional: true
+
+ '@react-navigation/native-stack@7.3.26':
+ resolution: {integrity: sha512-EjaBWzLZ76HJGOOcWCFf+h/M+Zg7M1RalYioDOb6ZdXHz7AwYNidruT3OUAQgSzg3gVLqvu5OYO0jFsNDPCZxQ==}
+ peerDependencies:
+ '@react-navigation/native': ^7.1.17
+ react: '>= 18.2.0'
+ react-native: '*'
+ react-native-safe-area-context: '>= 4.0.0'
+ react-native-screens: '>= 4.0.0'
+
+ '@react-navigation/native@7.1.17':
+ resolution: {integrity: sha512-uEcYWi1NV+2Qe1oELfp9b5hTYekqWATv2cuwcOAg5EvsIsUPtzFrKIasgUXLBRGb9P7yR5ifoJ+ug4u6jdqSTQ==}
+ peerDependencies:
+ react: '>= 18.2.0'
+ react-native: '*'
+
+ '@react-navigation/routers@7.5.1':
+ resolution: {integrity: sha512-pxipMW/iEBSUrjxz2cDD7fNwkqR4xoi0E/PcfTQGCcdJwLoaxzab5kSadBLj1MTJyT0YRrOXL9umHpXtp+Dv4w==}
+
+ '@rtsao/scc@1.1.0':
+ resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
+
+ '@sinclair/typebox@0.27.8':
+ resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
+
+ '@sinonjs/commons@3.0.1':
+ resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==}
+
+ '@sinonjs/fake-timers@10.3.0':
+ resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
+
+ '@tybys/wasm-util@0.10.1':
+ resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/graceful-fs@4.1.9':
+ resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
+
+ '@types/hammerjs@2.0.46':
+ resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==}
+
+ '@types/istanbul-lib-coverage@2.0.6':
+ resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
+
+ '@types/istanbul-lib-report@3.0.3':
+ resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
+
+ '@types/istanbul-reports@3.0.4':
+ resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/json5@0.0.29':
+ resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+
+ '@types/node@24.5.0':
+ resolution: {integrity: sha512-y1dMvuvJspJiPSDZUQ+WMBvF7dpnEqN4x9DDC9ie5Fs/HUZJA3wFp7EhHoVaKX/iI0cRoECV8X2jL8zi0xrHCg==}
+
+ '@types/react@19.1.13':
+ resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==}
+
+ '@types/stack-utils@2.0.3':
+ resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
+
+ '@types/yargs-parser@21.0.3':
+ resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
+
+ '@types/yargs@17.0.33':
+ resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==}
+
+ '@typescript-eslint/eslint-plugin@8.44.0':
+ resolution: {integrity: sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.44.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/parser@8.44.0':
+ resolution: {integrity: sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/project-service@8.44.0':
+ resolution: {integrity: sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/scope-manager@8.44.0':
+ resolution: {integrity: sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.44.0':
+ resolution: {integrity: sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/type-utils@8.44.0':
+ resolution: {integrity: sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/types@8.44.0':
+ resolution: {integrity: sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.44.0':
+ resolution: {integrity: sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/utils@8.44.0':
+ resolution: {integrity: sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/visitor-keys@8.44.0':
+ resolution: {integrity: sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@ungap/structured-clone@1.3.0':
+ resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
+
+ '@unrs/resolver-binding-android-arm-eabi@1.11.1':
+ resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
+ cpu: [arm]
+ os: [android]
+
+ '@unrs/resolver-binding-android-arm64@1.11.1':
+ resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==}
+ cpu: [arm64]
+ os: [android]
+
+ '@unrs/resolver-binding-darwin-arm64@1.11.1':
+ resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-darwin-x64@1.11.1':
+ resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-freebsd-x64@1.11.1':
+ resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
+ resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
+ resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
+ resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
+ resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
+ resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
+ resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
+ resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
+ resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
+ resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
+ cpu: [x64]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-x64-musl@1.11.1':
+ resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@unrs/resolver-binding-wasm32-wasi@1.11.1':
+ resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
+ resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
+ resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
+ resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==}
+ cpu: [x64]
+ os: [win32]
+
+ '@urql/core@5.2.0':
+ resolution: {integrity: sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==}
+
+ '@urql/exchange-retry@1.3.2':
+ resolution: {integrity: sha512-TQMCz2pFJMfpNxmSfX1VSfTjwUIFx/mL+p1bnfM1xjjdla7Z+KnGMW/EhFbpckp3LyWAH4PgOsMwOMnIN+MBFg==}
+ peerDependencies:
+ '@urql/core': ^5.0.0
+
+ '@xmldom/xmldom@0.8.11':
+ resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
+ engines: {node: '>=10.0.0'}
+
+ abort-controller@3.0.0:
+ resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
+ engines: {node: '>=6.5'}
+
+ accepts@1.3.8:
+ resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+ engines: {node: '>= 0.6'}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ anser@1.4.10:
+ resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==}
+
+ ansi-escapes@4.3.2:
+ resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@4.1.1:
+ resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==}
+ engines: {node: '>=6'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.2.2:
+ resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+ engines: {node: '>=12'}
+
+ ansi-styles@3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ any-promise@1.3.0:
+ resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ arg@5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+ argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
+ engines: {node: '>=10'}
+
+ array-buffer-byte-length@1.0.2:
+ resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
+ engines: {node: '>= 0.4'}
+
+ array-includes@3.1.9:
+ resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlast@1.2.5:
+ resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlastindex@1.2.6:
+ resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flat@1.3.3:
+ resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flatmap@1.3.3:
+ resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.tosorted@1.1.4:
+ resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
+ engines: {node: '>= 0.4'}
+
+ arraybuffer.prototype.slice@1.0.4:
+ resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
+ engines: {node: '>= 0.4'}
+
+ asap@2.0.6:
+ resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
+
+ async-function@1.0.0:
+ resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
+ engines: {node: '>= 0.4'}
+
+ async-limiter@1.0.1:
+ resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==}
+
+ available-typed-arrays@1.0.7:
+ resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+ engines: {node: '>= 0.4'}
+
+ babel-jest@29.7.0:
+ resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ peerDependencies:
+ '@babel/core': ^7.8.0
+
+ babel-plugin-istanbul@6.1.1:
+ resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
+ engines: {node: '>=8'}
+
+ babel-plugin-jest-hoist@29.6.3:
+ resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ babel-plugin-polyfill-corejs2@0.4.14:
+ resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ babel-plugin-polyfill-corejs3@0.13.0:
+ resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ babel-plugin-polyfill-regenerator@0.6.5:
+ resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ babel-plugin-react-compiler@19.1.0-rc.3:
+ resolution: {integrity: sha512-mjRn69WuTz4adL0bXGx8Rsyk1086zFJeKmes6aK0xPuK3aaXmDJdLHqwKKMrpm6KAI1MCoUK72d2VeqQbu8YIA==}
+
+ babel-plugin-react-native-web@0.21.1:
+ resolution: {integrity: sha512-7XywfJ5QIRMwjOL+pwJt2w47Jmi5fFLvK7/So4fV4jIN6PcRbylCp9/l3cJY4VJbSz3lnWTeHDTD1LKIc1C09Q==}
+
+ babel-plugin-syntax-hermes-parser@0.29.1:
+ resolution: {integrity: sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==}
+
+ babel-plugin-transform-flow-enums@0.0.2:
+ resolution: {integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==}
+
+ babel-preset-current-node-syntax@1.2.0:
+ resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0 || ^8.0.0-0
+
+ babel-preset-expo@54.0.1:
+ resolution: {integrity: sha512-ziLpj+I/IxQdblHCpuzcyukTpzunq6h/QFsbWhk5DTd4suqB+Vl0Neacd+e38YeKXBabmxCOv8VJN3qk39Md4w==}
+ peerDependencies:
+ '@babel/runtime': ^7.20.0
+ expo: '*'
+ react-refresh: '>=0.14.0 <1.0.0'
+ peerDependenciesMeta:
+ '@babel/runtime':
+ optional: true
+ expo:
+ optional: true
+
+ babel-preset-jest@29.6.3:
+ resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+ baseline-browser-mapping@2.8.4:
+ resolution: {integrity: sha512-L+YvJwGAgwJBV1p6ffpSTa2KRc69EeeYGYjRVWKs0GKrK+LON0GC0gV+rKSNtALEDvMDqkvCFq9r1r94/Gjwxw==}
+ hasBin: true
+
+ better-opn@3.0.2:
+ resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==}
+ engines: {node: '>=12.0.0'}
+
+ big-integer@1.6.52:
+ resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
+ engines: {node: '>=0.6'}
+
+ bplist-creator@0.1.0:
+ resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==}
+
+ bplist-parser@0.3.1:
+ resolution: {integrity: sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==}
+ engines: {node: '>= 5.10.0'}
+
+ bplist-parser@0.3.2:
+ resolution: {integrity: sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==}
+ engines: {node: '>= 5.10.0'}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.26.2:
+ resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ bser@2.1.1:
+ resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ buffer@5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+ bytes@3.1.2:
+ resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+ engines: {node: '>= 0.8'}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bind@1.0.8:
+ resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
+ caller-callsite@2.0.0:
+ resolution: {integrity: sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==}
+ engines: {node: '>=4'}
+
+ caller-path@2.0.0:
+ resolution: {integrity: sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==}
+ engines: {node: '>=4'}
+
+ callsites@2.0.0:
+ resolution: {integrity: sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==}
+ engines: {node: '>=4'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ camelcase@5.3.1:
+ resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+ engines: {node: '>=6'}
+
+ camelcase@6.3.0:
+ resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+ engines: {node: '>=10'}
+
+ caniuse-lite@1.0.30001743:
+ resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==}
+
+ chalk@2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ chownr@3.0.0:
+ resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+ engines: {node: '>=18'}
+
+ chrome-launcher@0.15.2:
+ resolution: {integrity: sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==}
+ engines: {node: '>=12.13.0'}
+ hasBin: true
+
+ chromium-edge-launcher@0.2.0:
+ resolution: {integrity: sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==}
+
+ ci-info@2.0.0:
+ resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
+
+ ci-info@3.9.0:
+ resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
+ engines: {node: '>=8'}
+
+ cli-cursor@2.1.0:
+ resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==}
+ engines: {node: '>=4'}
+
+ cli-spinners@2.9.2:
+ resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
+ engines: {node: '>=6'}
+
+ client-only@0.0.1:
+ resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
+ clone@1.0.4:
+ resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
+ engines: {node: '>=0.8'}
+
+ color-convert@1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.3:
+ resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
+ color@4.2.3:
+ resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
+ engines: {node: '>=12.5.0'}
+
+ commander@12.1.0:
+ resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
+ engines: {node: '>=18'}
+
+ commander@2.20.3:
+ resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+ commander@4.1.1:
+ resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+ engines: {node: '>= 6'}
+
+ commander@7.2.0:
+ resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+ engines: {node: '>= 10'}
+
+ compressible@2.0.18:
+ resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
+ engines: {node: '>= 0.6'}
+
+ compression@1.8.1:
+ resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==}
+ engines: {node: '>= 0.8.0'}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ connect@3.7.0:
+ resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==}
+ engines: {node: '>= 0.10.0'}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ core-js-compat@3.45.1:
+ resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==}
+
+ cosmiconfig@5.2.1:
+ resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==}
+ engines: {node: '>=4'}
+
+ cross-fetch@3.2.0:
+ resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ crypto-random-string@2.0.0:
+ resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
+ engines: {node: '>=8'}
+
+ css-in-js-utils@3.1.0:
+ resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ data-view-buffer@1.0.2:
+ resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-length@1.0.2:
+ resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-offset@1.0.1:
+ resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
+ engines: {node: '>= 0.4'}
+
+ debug@2.6.9:
+ resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@3.2.7:
+ resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decode-uri-component@0.2.2:
+ resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
+ engines: {node: '>=0.10'}
+
+ deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
+ defaults@1.0.4:
+ resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
+
+ define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+
+ define-lazy-prop@2.0.0:
+ resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
+ engines: {node: '>=8'}
+
+ define-properties@1.2.1:
+ resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+ engines: {node: '>= 0.4'}
+
+ depd@2.0.0:
+ resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+ engines: {node: '>= 0.8'}
+
+ destroy@1.2.0:
+ resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+ engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+ detect-libc@2.1.0:
+ resolution: {integrity: sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==}
+ engines: {node: '>=8'}
+
+ detect-node-es@1.1.0:
+ resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
+
+ doctrine@2.1.0:
+ resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+ engines: {node: '>=0.10.0'}
+
+ dotenv-expand@11.0.7:
+ resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==}
+ engines: {node: '>=12'}
+
+ dotenv@16.4.7:
+ resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==}
+ engines: {node: '>=12'}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ ee-first@1.1.1:
+ resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+
+ electron-to-chromium@1.5.218:
+ resolution: {integrity: sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ encodeurl@1.0.2:
+ resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+ engines: {node: '>= 0.8'}
+
+ encodeurl@2.0.0:
+ resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
+ engines: {node: '>= 0.8'}
+
+ env-editor@0.4.2:
+ resolution: {integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==}
+ engines: {node: '>=8'}
+
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
+ error-stack-parser@2.1.4:
+ resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==}
+
+ es-abstract@1.24.0:
+ resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==}
+ engines: {node: '>= 0.4'}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-iterator-helpers@1.2.1:
+ resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ es-shim-unscopables@1.1.0:
+ resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
+ engines: {node: '>= 0.4'}
+
+ es-to-primitive@1.3.0:
+ resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
+ engines: {node: '>= 0.4'}
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-html@1.0.3:
+ resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+ escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
+ escape-string-regexp@2.0.0:
+ resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+ engines: {node: '>=8'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-config-expo@10.0.0:
+ resolution: {integrity: sha512-/XC/DvniUWTzU7Ypb/cLDhDD4DXqEio4lug1ObD/oQ9Hcx3OVOR8Mkp4u6U4iGoZSJyIQmIk3WVHe/P1NYUXKw==}
+ peerDependencies:
+ eslint: '>=8.10'
+
+ eslint-import-resolver-node@0.3.9:
+ resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+
+ eslint-import-resolver-typescript@3.10.1:
+ resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '*'
+ eslint-plugin-import: '*'
+ eslint-plugin-import-x: '*'
+ peerDependenciesMeta:
+ eslint-plugin-import:
+ optional: true
+ eslint-plugin-import-x:
+ optional: true
+
+ eslint-module-utils@2.12.1:
+ resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: '*'
+ eslint-import-resolver-node: '*'
+ eslint-import-resolver-typescript: '*'
+ eslint-import-resolver-webpack: '*'
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+ eslint:
+ optional: true
+ eslint-import-resolver-node:
+ optional: true
+ eslint-import-resolver-typescript:
+ optional: true
+ eslint-import-resolver-webpack:
+ optional: true
+
+ eslint-plugin-expo@1.0.0:
+ resolution: {integrity: sha512-qLtunR+cNFtC+jwYCBia5c/PJurMjSLMOV78KrEOyQK02ohZapU4dCFFnS2hfrJuw0zxfsjVkjqg3QBqi933QA==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ eslint: '>=8.10'
+
+ eslint-plugin-import@2.32.0:
+ resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+
+ eslint-plugin-react-hooks@5.2.0:
+ resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+
+ eslint-plugin-react@7.37.5:
+ resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
+
+ eslint-scope@8.4.0:
+ resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.1:
+ resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.35.0:
+ resolution: {integrity: sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.4.0:
+ resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ etag@1.8.1:
+ resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+ engines: {node: '>= 0.6'}
+
+ event-target-shim@5.0.1:
+ resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
+ engines: {node: '>=6'}
+
+ exec-async@2.2.0:
+ resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==}
+
+ expo-apple-authentication@7.2.4:
+ resolution: {integrity: sha512-T2agaLLPT4Ax97FeXImB7BCCEzEJ0gB+ZwlFa/FXBtbp6WFKcGRlTVKiX2YPYLZzN5QjXcmQ9HHJ17jRthNHMg==}
+ peerDependencies:
+ expo: '*'
+ react-native: '*'
+
+ expo-asset@12.0.8:
+ resolution: {integrity: sha512-jj2U8zw9+7orST2rlQGULYiqPoECOuUyffs2NguGrq84bYbkM041T7TOMXH2raPVJnM9lEAP54ezI6XL+GVYqw==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-native: '*'
+
+ expo-constants@18.0.8:
+ resolution: {integrity: sha512-Tetphsx6RVImCTZeBAclRQMy0WOODY3y6qrUoc88YGUBVm8fAKkErCSWxLTCc6nFcJxdoOMYi62LgNIUFjZCLA==}
+ peerDependencies:
+ expo: '*'
+ react-native: '*'
+
+ expo-crypto@14.1.5:
+ resolution: {integrity: sha512-ZXJoUMoUeiMNEoSD4itItFFz3cKrit6YJ/BR0hjuwNC+NczbV9rorvhvmeJmrU9O2cFQHhJQQR1fjQnt45Vu4Q==}
+ peerDependencies:
+ expo: '*'
+
+ expo-document-picker@13.1.6:
+ resolution: {integrity: sha512-8FTQPDOkyCvFN/i4xyqzH7ELW4AsB6B3XBZQjn1FEdqpozo6rpNJRr7sWFU/93WrLgA9FJEKpKbyr6XxczK6BA==}
+ peerDependencies:
+ expo: '*'
+
+ expo-file-system@18.1.11:
+ resolution: {integrity: sha512-HJw/m0nVOKeqeRjPjGdvm+zBi5/NxcdPf8M8P3G2JFvH5Z8vBWqVDic2O58jnT1OFEy0XXzoH9UqFu7cHg9DTQ==}
+ peerDependencies:
+ expo: '*'
+ react-native: '*'
+
+ expo-file-system@19.0.14:
+ resolution: {integrity: sha512-0CA7O5IYhab11TlxQlJAx0Xm9pdkk/zEHNiW+Hh/T4atWi9U/J38CIp7iNYSrBvy9dC3rJbze5D1ANcKKr4mSQ==}
+ peerDependencies:
+ expo: '*'
+ react-native: '*'
+
+ expo-font@14.0.8:
+ resolution: {integrity: sha512-bTUHaJWRZ7ywP8dg3f+wfOwv6RwMV3mWT2CDUIhsK70GjNGlCtiWOCoHsA5Od/esPaVxqc37cCBvQGQRFStRlA==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-native: '*'
+
+ expo-haptics@15.0.7:
+ resolution: {integrity: sha512-7flWsYPrwjJxZ8x82RiJtzsnk1Xp9ahnbd9PhCy3NnsemyMApoWIEUr4waPqFr80DtiLZfhD9VMLL1CKa8AImQ==}
+ peerDependencies:
+ expo: '*'
+
+ expo-image@3.0.8:
+ resolution: {integrity: sha512-L83fTHVjvE5hACxUXPk3dpABteI/IypeqxKMeOAAcT2eB/jbqT53ddsYKEvKAP86eoByQ7+TCtw9AOUizEtaTQ==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+ react-native: '*'
+ react-native-web: '*'
+ peerDependenciesMeta:
+ react-native-web:
+ optional: true
+
+ expo-keep-awake@15.0.7:
+ resolution: {integrity: sha512-CgBNcWVPnrIVII5G54QDqoE125l+zmqR4HR8q+MQaCfHet+dYpS5vX5zii/RMayzGN4jPgA4XYIQ28ePKFjHoA==}
+ peerDependencies:
+ expo: '*'
+ react: '*'
+
+ expo-linking@8.0.8:
+ resolution: {integrity: sha512-MyeMcbFDKhXh4sDD1EHwd0uxFQNAc6VCrwBkNvvvufUsTYFq3glTA9Y8a+x78CPpjNqwNAamu74yIaIz7IEJyg==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
+ expo-modules-autolinking@3.0.10:
+ resolution: {integrity: sha512-6pwaz9H7aK/iYraHbX7zjg8QFTUuMfGEs8Vyc6bAoBd8Rovtb91WX955Kq5sazwNrQjs3WePwQ23LEAmls3u5g==}
+ hasBin: true
+
+ expo-modules-core@3.0.15:
+ resolution: {integrity: sha512-vGI7osd0/IjprldD08k4bckWSu7ID4HhZNP68l/UtilONQ8XZig8mWJd/Fm7i7KGvE3HyuF+HOXE9l671no42Q==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
+ expo-router@6.0.4:
+ resolution: {integrity: sha512-RZRHAhUCCU6QNTnVhoy0PAJxbLy7OF78F4/4fwJna3cHGTtokPvJYUwz8kOZOOub/7l8jqgxnT7eKAk1dw9uXQ==}
+ peerDependencies:
+ '@expo/metro-runtime': ^6.1.2
+ '@react-navigation/drawer': ^7.5.0
+ '@testing-library/react-native': '>= 12.0.0'
+ expo: '*'
+ expo-constants: ^18.0.8
+ expo-linking: ^8.0.8
+ react: '*'
+ react-dom: '*'
+ react-native: '*'
+ react-native-gesture-handler: '*'
+ react-native-reanimated: '*'
+ react-native-safe-area-context: '>= 5.4.0'
+ react-native-screens: '*'
+ react-native-web: '*'
+ react-server-dom-webpack: '>= 19.0.0'
+ peerDependenciesMeta:
+ '@react-navigation/drawer':
+ optional: true
+ '@testing-library/react-native':
+ optional: true
+ react-dom:
+ optional: true
+ react-native-gesture-handler:
+ optional: true
+ react-native-reanimated:
+ optional: true
+ react-native-web:
+ optional: true
+ react-server-dom-webpack:
+ optional: true
+
+ expo-sharing@13.1.5:
+ resolution: {integrity: sha512-X/5sAEiWXL2kdoGE3NO5KmbfcmaCWuWVZXHu8OQef7Yig4ZgHFkGD11HKJ5KqDrDg+SRZe4ISd6MxE7vGUgm4w==}
+ peerDependencies:
+ expo: '*'
+
+ expo-splash-screen@31.0.10:
+ resolution: {integrity: sha512-i6g9IK798mae4yvflstQ1HkgahIJ6exzTCTw4vEdxV0J2SwiW3Tj+CwRjf0te7Zsb+7dDQhBTmGZwdv00VER2A==}
+ peerDependencies:
+ expo: '*'
+
+ expo-status-bar@3.0.8:
+ resolution: {integrity: sha512-L248XKPhum7tvREoS1VfE0H6dPCaGtoUWzRsUv7hGKdiB4cus33Rc0sxkWkoQ77wE8stlnUlL5lvmT0oqZ3ZBw==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
+ expo-symbols@1.0.7:
+ resolution: {integrity: sha512-ZqFUeTXbwO6BrE00n37wTXYfJmsjFrfB446jeB9k9w7aA8a6eugNUIzNsUIUfbFWoOiY4wrGmpLSLPBwk4PH+g==}
+ peerDependencies:
+ expo: '*'
+ react-native: '*'
+
+ expo-system-ui@6.0.7:
+ resolution: {integrity: sha512-NT+/r/BOg08lFI9SZO2WFi9X1ZmawkVStknioWzQq6Mt4KinoMS6yl3eLbyOLM3LoptN13Ywfo4W5KHA6TV9Ow==}
+ peerDependencies:
+ expo: '*'
+ react-native: '*'
+ react-native-web: '*'
+ peerDependenciesMeta:
+ react-native-web:
+ optional: true
+
+ expo-web-browser@15.0.7:
+ resolution: {integrity: sha512-eXnfO3FQ2WthTA8uEPNJ7SDRfPaLIU/P2k082HGEYIHAFZMwh2o9Wo+SDVytO3E95TAv1qwhggUjOrczYzxteQ==}
+ peerDependencies:
+ expo: '*'
+ react-native: '*'
+
+ expo@54.0.7:
+ resolution: {integrity: sha512-DftN6nMdpHYUCw5Xnh7+h7wnusjtly4JzQknvuD7MzIvqoyJL9uffQyMQrmZkXrUbgm+cKBm47vtooIz4qj0Qg==}
+ hasBin: true
+ peerDependencies:
+ '@expo/dom-webview': '*'
+ '@expo/metro-runtime': '*'
+ react: '*'
+ react-native: '*'
+ react-native-webview: '*'
+ peerDependenciesMeta:
+ '@expo/dom-webview':
+ optional: true
+ '@expo/metro-runtime':
+ optional: true
+ react-native-webview:
+ optional: true
+
+ exponential-backoff@3.1.2:
+ resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.19.1:
+ resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+
+ fb-watchman@2.0.2:
+ resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
+
+ fbjs-css-vars@1.0.2:
+ resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==}
+
+ fbjs@3.0.5:
+ resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ filter-obj@1.1.0:
+ resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==}
+ engines: {node: '>=0.10.0'}
+
+ finalhandler@1.1.2:
+ resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
+ engines: {node: '>= 0.8'}
+
+ find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.3.3:
+ resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+
+ flow-enums-runtime@0.0.6:
+ resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==}
+
+ fontfaceobserver@2.3.0:
+ resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==}
+
+ for-each@0.3.5:
+ resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
+ engines: {node: '>= 0.4'}
+
+ foreground-child@3.3.1:
+ resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
+ engines: {node: '>=14'}
+
+ freeport-async@2.0.0:
+ resolution: {integrity: sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==}
+ engines: {node: '>=8'}
+
+ fresh@0.5.2:
+ resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
+ engines: {node: '>= 0.6'}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ function.prototype.name@1.1.8:
+ resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
+ engines: {node: '>= 0.4'}
+
+ functions-have-names@1.2.3:
+ resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-nonce@1.0.1:
+ resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+ engines: {node: '>=6'}
+
+ get-package-type@0.1.0:
+ resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
+ engines: {node: '>=8.0.0'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-symbol-description@1.1.0:
+ resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
+ engines: {node: '>= 0.4'}
+
+ get-tsconfig@4.10.1:
+ resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
+
+ getenv@2.0.0:
+ resolution: {integrity: sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==}
+ engines: {node: '>=6'}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@10.4.5:
+ resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ hasBin: true
+
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ global-dirs@0.1.1:
+ resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==}
+ engines: {node: '>=4'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globals@16.4.0:
+ resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==}
+ engines: {node: '>=18'}
+
+ globalthis@1.0.4:
+ resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
+ engines: {node: '>= 0.4'}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ has-bigints@1.1.0:
+ resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
+ engines: {node: '>= 0.4'}
+
+ has-flag@3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
+ has-proto@1.2.0:
+ resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
+ engines: {node: '>= 0.4'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ hermes-estree@0.29.1:
+ resolution: {integrity: sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==}
+
+ hermes-parser@0.29.1:
+ resolution: {integrity: sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==}
+
+ hoist-non-react-statics@3.3.2:
+ resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
+
+ hosted-git-info@7.0.2:
+ resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
+ engines: {node: ^16.14.0 || >=18.0.0}
+
+ http-errors@2.0.0:
+ resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+ engines: {node: '>= 0.8'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ hyphenate-style-name@1.1.0:
+ resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==}
+
+ ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ ignore@7.0.5:
+ resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
+ image-size@1.2.1:
+ resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==}
+ engines: {node: '>=16.x'}
+ hasBin: true
+
+ import-fresh@2.0.0:
+ resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==}
+ engines: {node: '>=4'}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ inline-style-prefixer@7.0.1:
+ resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==}
+
+ internal-slot@1.1.0:
+ resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
+ engines: {node: '>= 0.4'}
+
+ invariant@2.2.4:
+ resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
+
+ is-array-buffer@3.0.5:
+ resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
+ engines: {node: '>= 0.4'}
+
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ is-arrayish@0.3.4:
+ resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==}
+
+ is-async-function@2.1.1:
+ resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
+ engines: {node: '>= 0.4'}
+
+ is-bigint@1.1.0:
+ resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
+ engines: {node: '>= 0.4'}
+
+ is-boolean-object@1.2.2:
+ resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
+ engines: {node: '>= 0.4'}
+
+ is-bun-module@2.0.0:
+ resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==}
+
+ is-callable@1.2.7:
+ resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+ engines: {node: '>= 0.4'}
+
+ is-core-module@2.16.1:
+ resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+ engines: {node: '>= 0.4'}
+
+ is-data-view@1.0.2:
+ resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
+ engines: {node: '>= 0.4'}
+
+ is-date-object@1.1.0:
+ resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
+ engines: {node: '>= 0.4'}
+
+ is-directory@0.3.1:
+ resolution: {integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==}
+ engines: {node: '>=0.10.0'}
+
+ is-docker@2.2.1:
+ resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-finalizationregistry@1.1.1:
+ resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
+ engines: {node: '>= 0.4'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-generator-function@1.1.0:
+ resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
+ engines: {node: '>= 0.4'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-map@2.0.3:
+ resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
+ engines: {node: '>= 0.4'}
+
+ is-negative-zero@2.0.3:
+ resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
+ engines: {node: '>= 0.4'}
+
+ is-number-object@1.1.1:
+ resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
+ engines: {node: '>= 0.4'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-plain-obj@2.1.0:
+ resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
+ engines: {node: '>=8'}
+
+ is-regex@1.2.1:
+ resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
+ engines: {node: '>= 0.4'}
+
+ is-set@2.0.3:
+ resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
+ engines: {node: '>= 0.4'}
+
+ is-shared-array-buffer@1.0.4:
+ resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
+ engines: {node: '>= 0.4'}
+
+ is-string@1.1.1:
+ resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
+ engines: {node: '>= 0.4'}
+
+ is-symbol@1.1.1:
+ resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
+ engines: {node: '>= 0.4'}
+
+ is-typed-array@1.1.15:
+ resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
+ engines: {node: '>= 0.4'}
+
+ is-weakmap@2.0.2:
+ resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
+ engines: {node: '>= 0.4'}
+
+ is-weakref@1.1.1:
+ resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
+ engines: {node: '>= 0.4'}
+
+ is-weakset@2.0.4:
+ resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
+ engines: {node: '>= 0.4'}
+
+ is-wsl@2.2.0:
+ resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
+ engines: {node: '>=8'}
+
+ isarray@2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-instrument@5.2.1:
+ resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
+ engines: {node: '>=8'}
+
+ iterator.prototype@1.1.5:
+ resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
+ engines: {node: '>= 0.4'}
+
+ jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+ jest-environment-node@29.7.0:
+ resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-get-type@29.6.3:
+ resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-haste-map@29.7.0:
+ resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-message-util@29.7.0:
+ resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-mock@29.7.0:
+ resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-regex-util@29.6.3:
+ resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-util@29.7.0:
+ resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-validate@29.7.0:
+ resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-worker@29.7.0:
+ resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jimp-compact@0.16.1:
+ resolution: {integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@3.14.1:
+ resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+ hasBin: true
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsc-safe-url@0.2.4:
+ resolution: {integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==}
+
+ jsesc@3.0.2:
+ resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-parse-better-errors@1.0.2:
+ resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json5@1.0.2:
+ resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+ hasBin: true
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsx-ast-utils@3.3.5:
+ resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
+ engines: {node: '>=4.0'}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ kleur@3.0.3:
+ resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+ engines: {node: '>=6'}
+
+ lan-network@0.1.7:
+ resolution: {integrity: sha512-mnIlAEMu4OyEvUNdzco9xpuB9YVcPkQec+QsgycBCtPZvEqWPCDPfbAE4OJMdBBWpZWtpCn1xw9jJYlwjWI5zQ==}
+ hasBin: true
+
+ leven@3.1.0:
+ resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
+ engines: {node: '>=6'}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lighthouse-logger@1.4.2:
+ resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==}
+
+ lightningcss-darwin-arm64@1.30.1:
+ resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.30.1:
+ resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.30.1:
+ resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.30.1:
+ resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.30.1:
+ resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
+ engines: {node: '>= 12.0.0'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.debounce@4.0.8:
+ resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash.throttle@4.1.1:
+ resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
+
+ log-symbols@2.2.0:
+ resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==}
+ engines: {node: '>=4'}
+
+ loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ makeerror@1.0.12:
+ resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+
+ marky@1.3.0:
+ resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ memoize-one@5.2.1:
+ resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
+
+ memoize-one@6.0.0:
+ resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
+
+ merge-options@3.0.4:
+ resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==}
+ engines: {node: '>=10'}
+
+ merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ metro-babel-transformer@0.83.1:
+ resolution: {integrity: sha512-r3xAD3964E8dwDBaZNSO2aIIvWXjIK80uO2xo0/pi3WI8XWT9h5SCjtGWtMtE5PRWw+t20TN0q1WMRsjvhC1rQ==}
+ engines: {node: '>=20.19.4'}
+
+ metro-cache-key@0.83.1:
+ resolution: {integrity: sha512-ZUs+GD5CNeDLxx5UUWmfg26IL+Dnbryd+TLqTlZnDEgehkIa11kUSvgF92OFfJhONeXzV4rZDRGNXoo6JT+8Gg==}
+ engines: {node: '>=20.19.4'}
+
+ metro-cache@0.83.1:
+ resolution: {integrity: sha512-7N/Ad1PHa1YMWDNiyynTPq34Op2qIE68NWryGEQ4TSE3Zy6a8GpsYnEEZE4Qi6aHgsE+yZHKkRczeBgxhnFIxQ==}
+ engines: {node: '>=20.19.4'}
+
+ metro-config@0.83.1:
+ resolution: {integrity: sha512-HJhpZx3wyOkux/jeF1o7akFJzZFdbn6Zf7UQqWrvp7gqFqNulQ8Mju09raBgPmmSxKDl4LbbNeigkX0/nKY1QA==}
+ engines: {node: '>=20.19.4'}
+
+ metro-core@0.83.1:
+ resolution: {integrity: sha512-uVL1eAJcMFd2o2Q7dsbpg8COaxjZBBGaXqO2OHnivpCdfanraVL8dPmY6It9ZeqWLOihUKZ2yHW4b6soVCzH/Q==}
+ engines: {node: '>=20.19.4'}
+
+ metro-file-map@0.83.1:
+ resolution: {integrity: sha512-Yu429lnexKl44PttKw3nhqgmpBR+6UQ/tRaYcxPeEShtcza9DWakCn7cjqDTQZtWR2A8xSNv139izJMyQ4CG+w==}
+ engines: {node: '>=20.19.4'}
+
+ metro-minify-terser@0.83.1:
+ resolution: {integrity: sha512-kmooOxXLvKVxkh80IVSYO4weBdJDhCpg5NSPkjzzAnPJP43u6+usGXobkTWxxrAlq900bhzqKek4pBsUchlX6A==}
+ engines: {node: '>=20.19.4'}
+
+ metro-resolver@0.83.1:
+ resolution: {integrity: sha512-t8j46kiILAqqFS5RNa+xpQyVjULxRxlvMidqUswPEk5nQVNdlJslqizDm/Et3v/JKwOtQGkYAQCHxP1zGStR/g==}
+ engines: {node: '>=20.19.4'}
+
+ metro-runtime@0.83.1:
+ resolution: {integrity: sha512-3Ag8ZS4IwafL/JUKlaeM6/CbkooY+WcVeqdNlBG0m4S0Qz0om3rdFdy1y6fYBpl6AwXJwWeMuXrvZdMuByTcRA==}
+ engines: {node: '>=20.19.4'}
+
+ metro-source-map@0.83.1:
+ resolution: {integrity: sha512-De7Vbeo96fFZ2cqmI0fWwVJbtHIwPZv++LYlWSwzTiCzxBDJORncN0LcT48Vi2UlQLzXJg+/CuTAcy7NBVh69A==}
+ engines: {node: '>=20.19.4'}
+
+ metro-symbolicate@0.83.1:
+ resolution: {integrity: sha512-wPxYkONlq/Sv8Ji7vHEx5OzFouXAMQJjpcPW41ySKMLP/Ir18SsiJK2h4YkdKpYrTS1+0xf8oqF6nxCsT3uWtg==}
+ engines: {node: '>=20.19.4'}
+ hasBin: true
+
+ metro-transform-plugins@0.83.1:
+ resolution: {integrity: sha512-1Y+I8oozXwhuS0qwC+ezaHXBf0jXW4oeYn4X39XWbZt9X2HfjodqY9bH9r6RUTsoiK7S4j8Ni2C91bUC+sktJQ==}
+ engines: {node: '>=20.19.4'}
+
+ metro-transform-worker@0.83.1:
+ resolution: {integrity: sha512-owCrhPyUxdLgXEEEAL2b14GWTPZ2zYuab1VQXcfEy0sJE71iciD7fuMcrngoufh7e7UHDZ56q4ktXg8wgiYA1Q==}
+ engines: {node: '>=20.19.4'}
+
+ metro@0.83.1:
+ resolution: {integrity: sha512-UGKepmTxoGD4HkQV8YWvpvwef7fUujNtTgG4Ygf7m/M0qjvb9VuDmAsEU+UdriRX7F61pnVK/opz89hjKlYTXA==}
+ engines: {node: '>=20.19.4'}
+ hasBin: true
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-db@1.54.0:
+ resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ mime@1.6.0:
+ resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ mimic-fn@1.2.0:
+ resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==}
+ engines: {node: '>=4'}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@3.0.2:
+ resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
+ engines: {node: '>= 18'}
+
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ mkdirp@3.0.1:
+ resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ ms@2.0.0:
+ resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ mz@2.7.0:
+ resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ napi-postinstall@0.3.3:
+ resolution: {integrity: sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ negotiator@0.6.3:
+ resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
+ engines: {node: '>= 0.6'}
+
+ negotiator@0.6.4:
+ resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
+ engines: {node: '>= 0.6'}
+
+ nested-error-stacks@2.0.1:
+ resolution: {integrity: sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==}
+
+ node-fetch@2.7.0:
+ resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+
+ node-forge@1.3.1:
+ resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
+ engines: {node: '>= 6.13.0'}
+
+ node-int64@0.4.0:
+ resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+
+ node-releases@2.0.21:
+ resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ npm-package-arg@11.0.3:
+ resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==}
+ engines: {node: ^16.14.0 || >=18.0.0}
+
+ nullthrows@1.1.1:
+ resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==}
+
+ ob1@0.83.1:
+ resolution: {integrity: sha512-ngwqewtdUzFyycomdbdIhFLjePPSOt1awKMUXQ0L7iLHgWEPF3DsCerblzjzfAUHaXuvE9ccJymWQ/4PNNqvnQ==}
+ engines: {node: '>=20.19.4'}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ object-keys@1.1.1:
+ resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+ engines: {node: '>= 0.4'}
+
+ object.assign@4.1.7:
+ resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
+ engines: {node: '>= 0.4'}
+
+ object.entries@1.1.9:
+ resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
+ engines: {node: '>= 0.4'}
+
+ object.fromentries@2.0.8:
+ resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
+ engines: {node: '>= 0.4'}
+
+ object.groupby@1.0.3:
+ resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
+ engines: {node: '>= 0.4'}
+
+ object.values@1.2.1:
+ resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
+ engines: {node: '>= 0.4'}
+
+ on-finished@2.3.0:
+ resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==}
+ engines: {node: '>= 0.8'}
+
+ on-finished@2.4.1:
+ resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+ engines: {node: '>= 0.8'}
+
+ on-headers@1.1.0:
+ resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==}
+ engines: {node: '>= 0.8'}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ onetime@2.0.1:
+ resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==}
+ engines: {node: '>=4'}
+
+ open@7.4.2:
+ resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==}
+ engines: {node: '>=8'}
+
+ open@8.4.2:
+ resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
+ engines: {node: '>=12'}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ ora@3.4.0:
+ resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==}
+ engines: {node: '>=6'}
+
+ own-keys@1.0.1:
+ resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
+ engines: {node: '>= 0.4'}
+
+ p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse-json@4.0.0:
+ resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==}
+ engines: {node: '>=4'}
+
+ parse-png@2.1.0:
+ resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==}
+ engines: {node: '>=10'}
+
+ parseurl@1.3.3:
+ resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+ engines: {node: '>= 0.8'}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@3.0.1:
+ resolution: {integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==}
+ engines: {node: '>=10'}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ pirates@4.0.7:
+ resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+ engines: {node: '>= 6'}
+
+ plist@3.1.0:
+ resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==}
+ engines: {node: '>=10.4.0'}
+
+ pngjs@3.4.0:
+ resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
+ engines: {node: '>=4.0.0'}
+
+ possible-typed-array-names@1.1.0:
+ resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
+ engines: {node: '>= 0.4'}
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.4.49:
+ resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ pretty-bytes@5.6.0:
+ resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
+ engines: {node: '>=6'}
+
+ pretty-format@29.7.0:
+ resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ proc-log@4.2.0:
+ resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ progress@2.0.3:
+ resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
+ engines: {node: '>=0.4.0'}
+
+ promise@7.3.1:
+ resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==}
+
+ promise@8.3.0:
+ resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==}
+
+ prompts@2.4.2:
+ resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+ engines: {node: '>= 6'}
+
+ prop-types@15.8.1:
+ resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ qrcode-terminal@0.11.0:
+ resolution: {integrity: sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==}
+ hasBin: true
+
+ query-string@7.1.3:
+ resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==}
+ engines: {node: '>=6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ queue@6.0.2:
+ resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==}
+
+ range-parser@1.2.1:
+ resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+ engines: {node: '>= 0.6'}
+
+ rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+
+ react-devtools-core@6.1.5:
+ resolution: {integrity: sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==}
+
+ react-dom@19.1.0:
+ resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
+ peerDependencies:
+ react: ^19.1.0
+
+ react-fast-compare@3.2.2:
+ resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
+
+ react-freeze@1.0.4:
+ resolution: {integrity: sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ react: '>=17.0.0'
+
+ react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+ react-is@18.3.1:
+ resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+
+ react-is@19.1.1:
+ resolution: {integrity: sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==}
+
+ react-native-gesture-handler@2.28.0:
+ resolution: {integrity: sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
+ react-native-is-edge-to-edge@1.2.1:
+ resolution: {integrity: sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
+ react-native-reanimated@4.1.0:
+ resolution: {integrity: sha512-L8FqZn8VjZyBaCUMYFyx1Y+T+ZTbblaudpxReOXJ66RnOf52g6UM4Pa/IjwLD1XAw1FUxLRQrtpdjbkEc74FiQ==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ react: '*'
+ react-native: '*'
+ react-native-worklets: '>=0.5.0'
+
+ react-native-safe-area-context@5.6.1:
+ resolution: {integrity: sha512-/wJE58HLEAkATzhhX1xSr+fostLsK8Q97EfpfMDKo8jlOc1QKESSX/FQrhk7HhQH/2uSaox4Y86sNaI02kteiA==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
+ react-native-screens@4.16.0:
+ resolution: {integrity: sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+
+ react-native-web@0.21.1:
+ resolution: {integrity: sha512-BeNsgwwe4AXUFPAoFU+DKjJ+CVQa3h54zYX77p7GVZrXiiNo3vl03WYDYVEy5R2J2HOPInXtQZB5gmj3vuzrKg==}
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+
+ react-native-worklets@0.5.1:
+ resolution: {integrity: sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ react: '*'
+ react-native: '*'
+
+ react-native@0.81.4:
+ resolution: {integrity: sha512-bt5bz3A/+Cv46KcjV0VQa+fo7MKxs17RCcpzjftINlen4ZDUl0I6Ut+brQ2FToa5oD0IB0xvQHfmsg2EDqsZdQ==}
+ engines: {node: '>= 20.19.4'}
+ hasBin: true
+ peerDependencies:
+ '@types/react': ^19.1.0
+ react: ^19.1.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-refresh@0.14.2:
+ resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
+ engines: {node: '>=0.10.0'}
+
+ react-remove-scroll-bar@2.3.8:
+ resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-remove-scroll@2.7.1:
+ resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-style-singleton@2.2.3:
+ resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react@19.1.0:
+ resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
+ engines: {node: '>=0.10.0'}
+
+ reflect.getprototypeof@1.0.10:
+ resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
+ engines: {node: '>= 0.4'}
+
+ regenerate-unicode-properties@10.2.2:
+ resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==}
+ engines: {node: '>=4'}
+
+ regenerate@1.4.2:
+ resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
+
+ regenerator-runtime@0.13.11:
+ resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+
+ regexp.prototype.flags@1.5.4:
+ resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
+ engines: {node: '>= 0.4'}
+
+ regexpu-core@6.3.1:
+ resolution: {integrity: sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ==}
+ engines: {node: '>=4'}
+
+ regjsgen@0.8.0:
+ resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==}
+
+ regjsparser@0.12.0:
+ resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==}
+ hasBin: true
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ requireg@0.2.2:
+ resolution: {integrity: sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==}
+ engines: {node: '>= 4.0.0'}
+
+ resolve-from@3.0.0:
+ resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==}
+ engines: {node: '>=4'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
+ resolve-global@1.0.0:
+ resolution: {integrity: sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==}
+ engines: {node: '>=8'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ resolve-workspace-root@2.0.0:
+ resolution: {integrity: sha512-IsaBUZETJD5WsI11Wt8PKHwaIe45or6pwNc8yflvLJ4DWtImK9kuLoH5kUva/2Mmx/RdIyr4aONNSa2v9LTJsw==}
+
+ resolve.exports@2.0.3:
+ resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==}
+ engines: {node: '>=10'}
+
+ resolve@1.22.10:
+ resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ resolve@1.7.1:
+ resolution: {integrity: sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==}
+
+ resolve@2.0.0-next.5:
+ resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
+ hasBin: true
+
+ restore-cursor@2.0.0:
+ resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==}
+ engines: {node: '>=4'}
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rimraf@3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
+ hasBin: true
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ safe-array-concat@1.1.3:
+ resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
+ engines: {node: '>=0.4'}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safe-push-apply@1.0.0:
+ resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
+ engines: {node: '>= 0.4'}
+
+ safe-regex-test@1.1.0:
+ resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
+ engines: {node: '>= 0.4'}
+
+ sax@1.4.1:
+ resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
+
+ scheduler@0.26.0:
+ resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.6.3:
+ resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ semver@7.7.2:
+ resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ send@0.19.0:
+ resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
+ engines: {node: '>= 0.8.0'}
+
+ send@0.19.1:
+ resolution: {integrity: sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==}
+ engines: {node: '>= 0.8.0'}
+
+ serialize-error@2.1.0:
+ resolution: {integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==}
+ engines: {node: '>=0.10.0'}
+
+ serve-static@1.16.2:
+ resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
+ engines: {node: '>= 0.8.0'}
+
+ server-only@0.0.1:
+ resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
+
+ set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+
+ set-function-name@2.0.2:
+ resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
+ engines: {node: '>= 0.4'}
+
+ set-proto@1.0.0:
+ resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
+ engines: {node: '>= 0.4'}
+
+ setimmediate@1.0.5:
+ resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
+
+ setprototypeof@1.2.0:
+ resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+
+ sf-symbols-typescript@2.1.0:
+ resolution: {integrity: sha512-ezT7gu/SHTPIOEEoG6TF+O0m5eewl0ZDAO4AtdBi5HjsrUI6JdCG17+Q8+aKp0heM06wZKApRCn5olNbs0Wb/A==}
+ engines: {node: '>=10'}
+
+ shallowequal@1.1.0:
+ resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ shell-quote@1.8.3:
+ resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-list@1.0.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.0:
+ resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+ engines: {node: '>= 0.4'}
+
+ signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ simple-plist@1.3.1:
+ resolution: {integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==}
+
+ simple-swizzle@0.2.4:
+ resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
+
+ sisteransi@1.0.5:
+ resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+ slash@3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+
+ slugify@1.6.6:
+ resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==}
+ engines: {node: '>=8.0.0'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+ source-map@0.5.7:
+ resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ split-on-first@1.1.0:
+ resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==}
+ engines: {node: '>=6'}
+
+ sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+ stable-hash@0.0.5:
+ resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
+
+ stack-utils@2.0.6:
+ resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+ engines: {node: '>=10'}
+
+ stackframe@1.3.4:
+ resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==}
+
+ stacktrace-parser@0.1.11:
+ resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==}
+ engines: {node: '>=6'}
+
+ statuses@1.5.0:
+ resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
+ engines: {node: '>= 0.6'}
+
+ statuses@2.0.1:
+ resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+ engines: {node: '>= 0.8'}
+
+ stop-iteration-iterator@1.1.0:
+ resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
+ engines: {node: '>= 0.4'}
+
+ stream-buffers@2.2.0:
+ resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==}
+ engines: {node: '>= 0.10.0'}
+
+ strict-uri-encode@2.0.0:
+ resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
+ engines: {node: '>=4'}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ string.prototype.matchall@4.0.12:
+ resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.repeat@1.0.0:
+ resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
+
+ string.prototype.trim@1.2.10:
+ resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimend@1.0.9:
+ resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimstart@1.0.8:
+ resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+ engines: {node: '>= 0.4'}
+
+ strip-ansi@5.2.0:
+ resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==}
+ engines: {node: '>=6'}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.1.2:
+ resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
+ engines: {node: '>=12'}
+
+ strip-bom@3.0.0:
+ resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+ engines: {node: '>=4'}
+
+ strip-json-comments@2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ structured-headers@0.4.1:
+ resolution: {integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==}
+
+ styleq@0.1.3:
+ resolution: {integrity: sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==}
+
+ sucrase@3.35.0:
+ resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
+ supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-color@8.1.1:
+ resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+ engines: {node: '>=10'}
+
+ supports-hyperlinks@2.3.0:
+ resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
+ engines: {node: '>=8'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ tar@7.4.3:
+ resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
+ engines: {node: '>=18'}
+
+ temp-dir@2.0.0:
+ resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
+ engines: {node: '>=8'}
+
+ terminal-link@2.1.1:
+ resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
+ engines: {node: '>=8'}
+
+ terser@5.44.0:
+ resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ test-exclude@6.0.0:
+ resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
+ engines: {node: '>=8'}
+
+ thenify-all@1.6.0:
+ resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+ engines: {node: '>=0.8'}
+
+ thenify@3.3.1:
+ resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+ throat@5.0.0:
+ resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ tmpl@1.0.5:
+ resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ toidentifier@1.0.1:
+ resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+ engines: {node: '>=0.6'}
+
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
+ ts-api-utils@2.1.0:
+ resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ ts-interface-checker@0.1.13:
+ resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+ tsconfig-paths@3.15.0:
+ resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ type-detect@4.0.8:
+ resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+ engines: {node: '>=4'}
+
+ type-fest@0.21.3:
+ resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+ engines: {node: '>=10'}
+
+ type-fest@0.7.1:
+ resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==}
+ engines: {node: '>=8'}
+
+ typed-array-buffer@1.0.3:
+ resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-length@1.0.3:
+ resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-offset@1.0.4:
+ resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-length@1.0.7:
+ resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
+ engines: {node: '>= 0.4'}
+
+ typescript@5.9.2:
+ resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ ua-parser-js@1.0.41:
+ resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==}
+ hasBin: true
+
+ unbox-primitive@1.1.0:
+ resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
+ engines: {node: '>= 0.4'}
+
+ undici-types@7.12.0:
+ resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==}
+
+ undici@6.21.3:
+ resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==}
+ engines: {node: '>=18.17'}
+
+ unicode-canonical-property-names-ecmascript@2.0.1:
+ resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==}
+ engines: {node: '>=4'}
+
+ unicode-match-property-ecmascript@2.0.0:
+ resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
+ engines: {node: '>=4'}
+
+ unicode-match-property-value-ecmascript@2.2.1:
+ resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==}
+ engines: {node: '>=4'}
+
+ unicode-property-aliases-ecmascript@2.2.0:
+ resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==}
+ engines: {node: '>=4'}
+
+ unique-string@2.0.0:
+ resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
+ engines: {node: '>=8'}
+
+ unpipe@1.0.0:
+ resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+ engines: {node: '>= 0.8'}
+
+ unrs-resolver@1.11.1:
+ resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
+
+ update-browserslist-db@1.1.3:
+ resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ use-callback-ref@1.3.3:
+ resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ use-latest-callback@0.2.4:
+ resolution: {integrity: sha512-LS2s2n1usUUnDq4oVh1ca6JFX9uSqUncTfAm44WMg0v6TxL7POUTk1B044NH8TeLkFbNajIsgDHcgNpNzZucdg==}
+ peerDependencies:
+ react: '>=16.8'
+
+ use-sidecar@1.1.3:
+ resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ use-sync-external-store@1.5.0:
+ resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ utils-merge@1.0.1:
+ resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
+ engines: {node: '>= 0.4.0'}
+
+ uuid@7.0.3:
+ resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==}
+ hasBin: true
+
+ validate-npm-package-name@5.0.1:
+ resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ vary@1.1.2:
+ resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+ engines: {node: '>= 0.8'}
+
+ vaul@1.1.2:
+ resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+
+ vlq@1.0.1:
+ resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==}
+
+ walker@1.0.8:
+ resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+
+ warn-once@0.1.1:
+ resolution: {integrity: sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==}
+
+ wcwidth@1.0.1:
+ resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
+
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+ webidl-conversions@5.0.0:
+ resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==}
+ engines: {node: '>=8'}
+
+ whatwg-fetch@3.6.20:
+ resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==}
+
+ whatwg-url-without-unicode@8.0.0-3:
+ resolution: {integrity: sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==}
+ engines: {node: '>=10'}
+
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
+ which-boxed-primitive@1.1.1:
+ resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
+ engines: {node: '>= 0.4'}
+
+ which-builtin-type@1.2.1:
+ resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
+ engines: {node: '>= 0.4'}
+
+ which-collection@1.0.2:
+ resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
+ engines: {node: '>= 0.4'}
+
+ which-typed-array@1.1.19:
+ resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
+ engines: {node: '>= 0.4'}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ wonka@6.3.5:
+ resolution: {integrity: sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==}
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ write-file-atomic@4.0.2:
+ resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
+ ws@6.2.3:
+ resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@7.5.10:
+ resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
+ engines: {node: '>=8.3.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@8.18.3:
+ resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+ 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
+
+ xcode@3.0.1:
+ resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==}
+ engines: {node: '>=10.0.0'}
+
+ xml2js@0.6.0:
+ resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==}
+ engines: {node: '>=4.0.0'}
+
+ xmlbuilder@11.0.1:
+ resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
+ engines: {node: '>=4.0'}
+
+ xmlbuilder@15.1.1:
+ resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
+ engines: {node: '>=8.0'}
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yallist@5.0.0:
+ resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+ engines: {node: '>=18'}
+
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+snapshots:
+
+ '@0no-co/graphql.web@1.2.0': {}
+
+ '@babel/code-frame@7.10.4':
+ dependencies:
+ '@babel/highlight': 7.25.9
+
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.27.1
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.28.4': {}
+
+ '@babel/core@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helpers': 7.28.4
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.28.3':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/helper-compilation-targets@7.27.2':
+ dependencies:
+ '@babel/compat-data': 7.28.4
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.26.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-member-expression-to-functions': 7.27.1
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/traverse': 7.28.4
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ regexpu-core: 6.3.1
+ semver: 6.3.1
+
+ '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-plugin-utils': 7.27.1
+ debug: 4.4.3
+ lodash.debounce: 4.0.8
+ resolve: 1.22.10
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-member-expression-to-functions@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-imports@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/helper-plugin-utils@7.27.1': {}
+
+ '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-wrap-function': 7.28.3
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-member-expression-to-functions': 7.27.1
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.27.1': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helper-wrap-function@7.28.3':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helpers@7.28.4':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+
+ '@babel/highlight@7.25.9':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.27.1
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/parser@7.28.4':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-export-default-from@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4)
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-block-scoping@7.28.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-globals': 7.28.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4)
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/template': 7.27.2
+
+ '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-flow': 7.27.1(@babel/core@7.28.4)
+
+ '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4)
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4)
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-runtime@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.4)
+ babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.4)
+ babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.4)
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/preset-react@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-validator-option': 7.27.1
+ '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/preset-typescript@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-validator-option': 7.27.1
+ '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/runtime@7.28.4': {}
+
+ '@babel/template@7.27.2':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+
+ '@babel/traverse@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.28.4':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@egjs/hammerjs@2.0.17':
+ dependencies:
+ '@types/hammerjs': 2.0.46
+
+ '@emnapi/core@1.5.0':
+ dependencies:
+ '@emnapi/wasi-threads': 1.1.0
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.5.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.1.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@eslint-community/eslint-utils@4.9.0(eslint@9.35.0)':
+ dependencies:
+ eslint: 9.35.0
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/config-array@0.21.0':
+ dependencies:
+ '@eslint/object-schema': 2.1.6
+ debug: 4.4.3
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.3.1': {}
+
+ '@eslint/core@0.15.2':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.3.1':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.3
+ espree: 10.4.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.35.0': {}
+
+ '@eslint/object-schema@2.1.6': {}
+
+ '@eslint/plugin-kit@0.3.5':
+ dependencies:
+ '@eslint/core': 0.15.2
+ levn: 0.4.1
+
+ '@expo/cli@54.0.5(expo-router@6.0.4)(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))':
+ dependencies:
+ '@0no-co/graphql.web': 1.2.0
+ '@expo/code-signing-certificates': 0.0.5
+ '@expo/config': 12.0.9
+ '@expo/config-plugins': 54.0.1
+ '@expo/devcert': 1.2.0
+ '@expo/env': 2.0.7
+ '@expo/image-utils': 0.8.7
+ '@expo/json-file': 10.0.7
+ '@expo/metro': 0.1.1
+ '@expo/metro-config': 54.0.3(expo@54.0.7)
+ '@expo/osascript': 2.3.7
+ '@expo/package-manager': 1.9.7
+ '@expo/plist': 0.4.7
+ '@expo/prebuild-config': 54.0.3(expo@54.0.7)
+ '@expo/schema-utils': 0.1.7
+ '@expo/server': 0.7.4
+ '@expo/spawn-async': 1.7.2
+ '@expo/ws-tunnel': 1.0.6
+ '@expo/xcpretty': 4.3.2
+ '@react-native/dev-middleware': 0.81.4
+ '@urql/core': 5.2.0
+ '@urql/exchange-retry': 1.3.2(@urql/core@5.2.0)
+ accepts: 1.3.8
+ arg: 5.0.2
+ better-opn: 3.0.2
+ bplist-creator: 0.1.0
+ bplist-parser: 0.3.2
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ compression: 1.8.1
+ connect: 3.7.0
+ debug: 4.4.3
+ env-editor: 0.4.2
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ freeport-async: 2.0.0
+ getenv: 2.0.0
+ glob: 10.4.5
+ lan-network: 0.1.7
+ minimatch: 9.0.5
+ node-forge: 1.3.1
+ npm-package-arg: 11.0.3
+ ora: 3.4.0
+ picomatch: 3.0.1
+ pretty-bytes: 5.6.0
+ pretty-format: 29.7.0
+ progress: 2.0.3
+ prompts: 2.4.2
+ qrcode-terminal: 0.11.0
+ require-from-string: 2.0.2
+ requireg: 0.2.2
+ resolve: 1.22.10
+ resolve-from: 5.0.0
+ resolve.exports: 2.0.3
+ semver: 7.7.2
+ send: 0.19.1
+ slugify: 1.6.6
+ source-map-support: 0.5.21
+ stacktrace-parser: 0.1.11
+ structured-headers: 0.4.1
+ tar: 7.4.3
+ terminal-link: 2.1.1
+ undici: 6.21.3
+ wrap-ansi: 7.0.0
+ ws: 8.18.3
+ optionalDependencies:
+ expo-router: 6.0.4(@expo/metro-runtime@6.1.2)(@types/react@19.1.13)(expo-constants@18.0.8)(expo-linking@8.0.8)(expo@54.0.7)(react-dom@19.1.0(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.0(@babel/core@7.28.4)(react-native-worklets@0.5.1(@babel/core@7.28.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ transitivePeerDependencies:
+ - bufferutil
+ - graphql
+ - supports-color
+ - utf-8-validate
+
+ '@expo/code-signing-certificates@0.0.5':
+ dependencies:
+ node-forge: 1.3.1
+ nullthrows: 1.1.1
+
+ '@expo/config-plugins@54.0.1':
+ dependencies:
+ '@expo/config-types': 54.0.8
+ '@expo/json-file': 10.0.7
+ '@expo/plist': 0.4.7
+ '@expo/sdk-runtime-versions': 1.0.0
+ chalk: 4.1.2
+ debug: 4.4.3
+ getenv: 2.0.0
+ glob: 10.4.5
+ resolve-from: 5.0.0
+ semver: 7.7.2
+ slash: 3.0.0
+ slugify: 1.6.6
+ xcode: 3.0.1
+ xml2js: 0.6.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/config-types@54.0.8': {}
+
+ '@expo/config@12.0.9':
+ dependencies:
+ '@babel/code-frame': 7.10.4
+ '@expo/config-plugins': 54.0.1
+ '@expo/config-types': 54.0.8
+ '@expo/json-file': 10.0.7
+ deepmerge: 4.3.1
+ getenv: 2.0.0
+ glob: 10.4.5
+ require-from-string: 2.0.2
+ resolve-from: 5.0.0
+ resolve-workspace-root: 2.0.0
+ semver: 7.7.2
+ slugify: 1.6.6
+ sucrase: 3.35.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/devcert@1.2.0':
+ dependencies:
+ '@expo/sudo-prompt': 9.3.2
+ debug: 3.2.7
+ glob: 10.4.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/devtools@0.1.7(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ chalk: 4.1.2
+ optionalDependencies:
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ '@expo/env@2.0.7':
+ dependencies:
+ chalk: 4.1.2
+ debug: 4.4.3
+ dotenv: 16.4.7
+ dotenv-expand: 11.0.7
+ getenv: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/fingerprint@0.15.0':
+ dependencies:
+ '@expo/spawn-async': 1.7.2
+ arg: 5.0.2
+ chalk: 4.1.2
+ debug: 4.4.3
+ getenv: 2.0.0
+ glob: 10.4.5
+ ignore: 5.3.2
+ minimatch: 9.0.5
+ p-limit: 3.1.0
+ resolve-from: 5.0.0
+ semver: 7.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/image-utils@0.8.7':
+ dependencies:
+ '@expo/spawn-async': 1.7.2
+ chalk: 4.1.2
+ getenv: 2.0.0
+ jimp-compact: 0.16.1
+ parse-png: 2.1.0
+ resolve-from: 5.0.0
+ resolve-global: 1.0.0
+ semver: 7.7.2
+ temp-dir: 2.0.0
+ unique-string: 2.0.0
+
+ '@expo/json-file@10.0.7':
+ dependencies:
+ '@babel/code-frame': 7.10.4
+ json5: 2.2.3
+
+ '@expo/metro-config@54.0.3(expo@54.0.7)':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/core': 7.28.4
+ '@babel/generator': 7.28.3
+ '@expo/config': 12.0.9
+ '@expo/env': 2.0.7
+ '@expo/json-file': 10.0.7
+ '@expo/metro': 0.1.1
+ '@expo/spawn-async': 1.7.2
+ browserslist: 4.26.2
+ chalk: 4.1.2
+ debug: 4.4.3
+ dotenv: 16.4.7
+ dotenv-expand: 11.0.7
+ getenv: 2.0.0
+ glob: 10.4.5
+ hermes-parser: 0.29.1
+ jsc-safe-url: 0.2.4
+ lightningcss: 1.30.1
+ minimatch: 9.0.5
+ postcss: 8.4.49
+ resolve-from: 5.0.0
+ optionalDependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ '@expo/metro-runtime@6.1.2(expo@54.0.7)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ anser: 1.4.10
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ pretty-format: 29.7.0
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ stacktrace-parser: 0.1.11
+ whatwg-fetch: 3.6.20
+ optionalDependencies:
+ react-dom: 19.1.0(react@19.1.0)
+
+ '@expo/metro@0.1.1':
+ dependencies:
+ metro: 0.83.1
+ metro-babel-transformer: 0.83.1
+ metro-cache: 0.83.1
+ metro-cache-key: 0.83.1
+ metro-config: 0.83.1
+ metro-core: 0.83.1
+ metro-file-map: 0.83.1
+ metro-resolver: 0.83.1
+ metro-runtime: 0.83.1
+ metro-source-map: 0.83.1
+ metro-transform-plugins: 0.83.1
+ metro-transform-worker: 0.83.1
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ '@expo/osascript@2.3.7':
+ dependencies:
+ '@expo/spawn-async': 1.7.2
+ exec-async: 2.2.0
+
+ '@expo/package-manager@1.9.7':
+ dependencies:
+ '@expo/json-file': 10.0.7
+ '@expo/spawn-async': 1.7.2
+ chalk: 4.1.2
+ npm-package-arg: 11.0.3
+ ora: 3.4.0
+ resolve-workspace-root: 2.0.0
+
+ '@expo/plist@0.4.7':
+ dependencies:
+ '@xmldom/xmldom': 0.8.11
+ base64-js: 1.5.1
+ xmlbuilder: 15.1.1
+
+ '@expo/prebuild-config@54.0.3(expo@54.0.7)':
+ dependencies:
+ '@expo/config': 12.0.9
+ '@expo/config-plugins': 54.0.1
+ '@expo/config-types': 54.0.8
+ '@expo/image-utils': 0.8.7
+ '@expo/json-file': 10.0.7
+ '@react-native/normalize-colors': 0.81.4
+ debug: 4.4.3
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ resolve-from: 5.0.0
+ semver: 7.7.2
+ xml2js: 0.6.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/schema-utils@0.1.7': {}
+
+ '@expo/sdk-runtime-versions@1.0.0': {}
+
+ '@expo/server@0.7.4':
+ dependencies:
+ abort-controller: 3.0.0
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@expo/spawn-async@1.7.2':
+ dependencies:
+ cross-spawn: 7.0.6
+
+ '@expo/sudo-prompt@9.3.2': {}
+
+ '@expo/vector-icons@15.0.2(expo-font@14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ expo-font: 14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ '@expo/ws-tunnel@1.0.6': {}
+
+ '@expo/xcpretty@4.3.2':
+ dependencies:
+ '@babel/code-frame': 7.10.4
+ chalk: 4.1.2
+ find-up: 5.0.0
+ js-yaml: 4.1.0
+
+ '@humanfs/core@0.19.1': {}
+
+ '@humanfs/node@0.16.7':
+ dependencies:
+ '@humanfs/core': 0.19.1
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.1.2
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@isaacs/fs-minipass@4.0.1':
+ dependencies:
+ minipass: 7.1.2
+
+ '@isaacs/ttlcache@1.4.1': {}
+
+ '@istanbuljs/load-nyc-config@1.1.0':
+ dependencies:
+ camelcase: 5.3.1
+ find-up: 4.1.0
+ get-package-type: 0.1.0
+ js-yaml: 3.14.1
+ resolve-from: 5.0.0
+
+ '@istanbuljs/schema@0.1.3': {}
+
+ '@jest/create-cache-key-function@29.7.0':
+ dependencies:
+ '@jest/types': 29.6.3
+
+ '@jest/environment@29.7.0':
+ dependencies:
+ '@jest/fake-timers': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 24.5.0
+ jest-mock: 29.7.0
+
+ '@jest/fake-timers@29.7.0':
+ dependencies:
+ '@jest/types': 29.6.3
+ '@sinonjs/fake-timers': 10.3.0
+ '@types/node': 24.5.0
+ jest-message-util: 29.7.0
+ jest-mock: 29.7.0
+ jest-util: 29.7.0
+
+ '@jest/schemas@29.6.3':
+ dependencies:
+ '@sinclair/typebox': 0.27.8
+
+ '@jest/transform@29.7.0':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@jest/types': 29.6.3
+ '@jridgewell/trace-mapping': 0.3.31
+ babel-plugin-istanbul: 6.1.1
+ chalk: 4.1.2
+ convert-source-map: 2.0.0
+ fast-json-stable-stringify: 2.1.0
+ graceful-fs: 4.2.11
+ jest-haste-map: 29.7.0
+ jest-regex-util: 29.6.3
+ jest-util: 29.7.0
+ micromatch: 4.0.8
+ pirates: 4.0.7
+ slash: 3.0.0
+ write-file-atomic: 4.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/types@29.6.3':
+ dependencies:
+ '@jest/schemas': 29.6.3
+ '@types/istanbul-lib-coverage': 2.0.6
+ '@types/istanbul-reports': 3.0.4
+ '@types/node': 24.5.0
+ '@types/yargs': 17.0.33
+ chalk: 4.1.2
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/source-map@0.3.11':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@napi-rs/wasm-runtime@0.2.12':
+ dependencies:
+ '@emnapi/core': 1.5.0
+ '@emnapi/runtime': 1.5.0
+ '@tybys/wasm-util': 0.10.1
+ optional: true
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.19.1
+
+ '@nolyfill/is-core-module@1.0.39': {}
+
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
+
+ '@radix-ui/primitive@1.1.3': {}
+
+ '@radix-ui/react-collection@1.1.7(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-context@1.1.2(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-dialog@1.1.15(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.13)(react@19.1.0)
+ aria-hidden: 1.2.6
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ react-remove-scroll: 2.7.1(@types/react@19.1.13)(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-direction@1.1.1(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-dismissable-layer@1.1.11(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-focus-guards@1.1.3(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-focus-scope@1.1.7(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-id@1.1.1(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-portal@1.1.9(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-presence@1.1.5(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-primitive@2.1.3(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-roving-focus@1.1.11(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-collection': 1.1.7(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-slot@1.2.0(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-slot@1.2.3(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-tabs@1.1.13(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-direction': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-roving-focus': 1.1.11(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.0)
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.13)(react@19.1.0)':
+ dependencies:
+ react: 19.1.0
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@react-native-async-storage/async-storage@2.2.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))':
+ dependencies:
+ merge-options: 3.0.4
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ '@react-native/assets-registry@0.81.4': {}
+
+ '@react-native/babel-plugin-codegen@0.81.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@react-native/codegen': 0.81.4(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - supports-color
+
+ '@react-native/babel-preset@0.81.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.28.4)
+ '@babel/plugin-syntax-export-default-from': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4)
+ '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-block-scoping': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4)
+ '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-runtime': 7.28.3(@babel/core@7.28.4)
+ '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/template': 7.27.2
+ '@react-native/babel-plugin-codegen': 0.81.4(@babel/core@7.28.4)
+ babel-plugin-syntax-hermes-parser: 0.29.1
+ babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.28.4)
+ react-refresh: 0.14.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@react-native/codegen@0.81.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/parser': 7.28.4
+ glob: 7.2.3
+ hermes-parser: 0.29.1
+ invariant: 2.2.4
+ nullthrows: 1.1.1
+ yargs: 17.7.2
+
+ '@react-native/community-cli-plugin@0.81.4':
+ dependencies:
+ '@react-native/dev-middleware': 0.81.4
+ debug: 4.4.3
+ invariant: 2.2.4
+ metro: 0.83.1
+ metro-config: 0.83.1
+ metro-core: 0.83.1
+ semver: 7.7.2
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ '@react-native/debugger-frontend@0.81.4': {}
+
+ '@react-native/dev-middleware@0.81.4':
+ dependencies:
+ '@isaacs/ttlcache': 1.4.1
+ '@react-native/debugger-frontend': 0.81.4
+ chrome-launcher: 0.15.2
+ chromium-edge-launcher: 0.2.0
+ connect: 3.7.0
+ debug: 4.4.3
+ invariant: 2.2.4
+ nullthrows: 1.1.1
+ open: 7.4.2
+ serve-static: 1.16.2
+ ws: 6.2.3
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ '@react-native/gradle-plugin@0.81.4': {}
+
+ '@react-native/js-polyfills@0.81.4': {}
+
+ '@react-native/normalize-colors@0.74.89': {}
+
+ '@react-native/normalize-colors@0.81.4': {}
+
+ '@react-native/virtualized-lists@0.81.4(@types/react@19.1.13)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ invariant: 2.2.4
+ nullthrows: 1.1.1
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ '@react-navigation/bottom-tabs@7.4.7(@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@react-navigation/elements': 2.6.4(@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@react-navigation/native': 7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ color: 4.2.3
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ react-native-safe-area-context: 5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-screens: 4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ transitivePeerDependencies:
+ - '@react-native-masked-view/masked-view'
+
+ '@react-navigation/core@7.12.4(react@19.1.0)':
+ dependencies:
+ '@react-navigation/routers': 7.5.1
+ escape-string-regexp: 4.0.0
+ nanoid: 3.3.11
+ query-string: 7.1.3
+ react: 19.1.0
+ react-is: 19.1.1
+ use-latest-callback: 0.2.4(react@19.1.0)
+ use-sync-external-store: 1.5.0(react@19.1.0)
+
+ '@react-navigation/elements@2.6.4(@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@react-navigation/native': 7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ color: 4.2.3
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ react-native-safe-area-context: 5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ use-latest-callback: 0.2.4(react@19.1.0)
+ use-sync-external-store: 1.5.0(react@19.1.0)
+
+ '@react-navigation/native-stack@7.3.26(@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@react-navigation/elements': 2.6.4(@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@react-navigation/native': 7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ react-native-safe-area-context: 5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-screens: 4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ warn-once: 0.1.1
+ transitivePeerDependencies:
+ - '@react-native-masked-view/masked-view'
+
+ '@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@react-navigation/core': 7.12.4(react@19.1.0)
+ escape-string-regexp: 4.0.0
+ fast-deep-equal: 3.1.3
+ nanoid: 3.3.11
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ use-latest-callback: 0.2.4(react@19.1.0)
+
+ '@react-navigation/routers@7.5.1':
+ dependencies:
+ nanoid: 3.3.11
+
+ '@rtsao/scc@1.1.0': {}
+
+ '@sinclair/typebox@0.27.8': {}
+
+ '@sinonjs/commons@3.0.1':
+ dependencies:
+ type-detect: 4.0.8
+
+ '@sinonjs/fake-timers@10.3.0':
+ dependencies:
+ '@sinonjs/commons': 3.0.1
+
+ '@tybys/wasm-util@0.10.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@types/estree@1.0.8': {}
+
+ '@types/graceful-fs@4.1.9':
+ dependencies:
+ '@types/node': 24.5.0
+
+ '@types/hammerjs@2.0.46': {}
+
+ '@types/istanbul-lib-coverage@2.0.6': {}
+
+ '@types/istanbul-lib-report@3.0.3':
+ dependencies:
+ '@types/istanbul-lib-coverage': 2.0.6
+
+ '@types/istanbul-reports@3.0.4':
+ dependencies:
+ '@types/istanbul-lib-report': 3.0.3
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/json5@0.0.29': {}
+
+ '@types/node@24.5.0':
+ dependencies:
+ undici-types: 7.12.0
+
+ '@types/react@19.1.13':
+ dependencies:
+ csstype: 3.1.3
+
+ '@types/stack-utils@2.0.3': {}
+
+ '@types/yargs-parser@21.0.3': {}
+
+ '@types/yargs@17.0.33':
+ dependencies:
+ '@types/yargs-parser': 21.0.3
+
+ '@typescript-eslint/eslint-plugin@8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(typescript@5.9.2)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.44.0(eslint@9.35.0)(typescript@5.9.2)
+ '@typescript-eslint/scope-manager': 8.44.0
+ '@typescript-eslint/type-utils': 8.44.0(eslint@9.35.0)(typescript@5.9.2)
+ '@typescript-eslint/utils': 8.44.0(eslint@9.35.0)(typescript@5.9.2)
+ '@typescript-eslint/visitor-keys': 8.44.0
+ eslint: 9.35.0
+ graphemer: 1.4.0
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.44.0
+ '@typescript-eslint/types': 8.44.0
+ '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2)
+ '@typescript-eslint/visitor-keys': 8.44.0
+ debug: 4.4.3
+ eslint: 9.35.0
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.44.0(typescript@5.9.2)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.44.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.44.0
+ debug: 4.4.3
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.44.0':
+ dependencies:
+ '@typescript-eslint/types': 8.44.0
+ '@typescript-eslint/visitor-keys': 8.44.0
+
+ '@typescript-eslint/tsconfig-utils@8.44.0(typescript@5.9.2)':
+ dependencies:
+ typescript: 5.9.2
+
+ '@typescript-eslint/type-utils@8.44.0(eslint@9.35.0)(typescript@5.9.2)':
+ dependencies:
+ '@typescript-eslint/types': 8.44.0
+ '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2)
+ '@typescript-eslint/utils': 8.44.0(eslint@9.35.0)(typescript@5.9.2)
+ debug: 4.4.3
+ eslint: 9.35.0
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.44.0': {}
+
+ '@typescript-eslint/typescript-estree@8.44.0(typescript@5.9.2)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.44.0(typescript@5.9.2)
+ '@typescript-eslint/tsconfig-utils': 8.44.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.44.0
+ '@typescript-eslint/visitor-keys': 8.44.0
+ debug: 4.4.3
+ fast-glob: 3.3.3
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.2
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.44.0(eslint@9.35.0)(typescript@5.9.2)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0)
+ '@typescript-eslint/scope-manager': 8.44.0
+ '@typescript-eslint/types': 8.44.0
+ '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2)
+ eslint: 9.35.0
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.44.0':
+ dependencies:
+ '@typescript-eslint/types': 8.44.0
+ eslint-visitor-keys: 4.2.1
+
+ '@ungap/structured-clone@1.3.0': {}
+
+ '@unrs/resolver-binding-android-arm-eabi@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-android-arm64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-darwin-arm64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-darwin-x64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-freebsd-x64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-wasm32-wasi@1.11.1':
+ dependencies:
+ '@napi-rs/wasm-runtime': 0.2.12
+ optional: true
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
+ optional: true
+
+ '@urql/core@5.2.0':
+ dependencies:
+ '@0no-co/graphql.web': 1.2.0
+ wonka: 6.3.5
+ transitivePeerDependencies:
+ - graphql
+
+ '@urql/exchange-retry@1.3.2(@urql/core@5.2.0)':
+ dependencies:
+ '@urql/core': 5.2.0
+ wonka: 6.3.5
+
+ '@xmldom/xmldom@0.8.11': {}
+
+ abort-controller@3.0.0:
+ dependencies:
+ event-target-shim: 5.0.1
+
+ accepts@1.3.8:
+ dependencies:
+ mime-types: 2.1.35
+ negotiator: 0.6.3
+
+ acorn-jsx@5.3.2(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
+ acorn@8.15.0: {}
+
+ agent-base@7.1.4: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ anser@1.4.10: {}
+
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
+ ansi-regex@4.1.1: {}
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.2.2: {}
+
+ ansi-styles@3.2.1:
+ dependencies:
+ color-convert: 1.9.3
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@5.2.0: {}
+
+ ansi-styles@6.2.3: {}
+
+ any-promise@1.3.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ arg@5.0.2: {}
+
+ argparse@1.0.10:
+ dependencies:
+ sprintf-js: 1.0.3
+
+ argparse@2.0.1: {}
+
+ aria-hidden@1.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ array-buffer-byte-length@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ is-array-buffer: 3.0.5
+
+ array-includes@3.1.9:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ is-string: 1.1.1
+ math-intrinsics: 1.1.0
+
+ array.prototype.findlast@1.2.5:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.findlastindex@1.2.6:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.flat@1.3.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.flatmap@1.3.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.tosorted@1.1.4:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-errors: 1.3.0
+ es-shim-unscopables: 1.1.0
+
+ arraybuffer.prototype.slice@1.0.4:
+ dependencies:
+ array-buffer-byte-length: 1.0.2
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ is-array-buffer: 3.0.5
+
+ asap@2.0.6: {}
+
+ async-function@1.0.0: {}
+
+ async-limiter@1.0.1: {}
+
+ available-typed-arrays@1.0.7:
+ dependencies:
+ possible-typed-array-names: 1.1.0
+
+ babel-jest@29.7.0(@babel/core@7.28.4):
+ dependencies:
+ '@babel/core': 7.28.4
+ '@jest/transform': 29.7.0
+ '@types/babel__core': 7.20.5
+ babel-plugin-istanbul: 6.1.1
+ babel-preset-jest: 29.6.3(@babel/core@7.28.4)
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ slash: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-istanbul@6.1.1:
+ dependencies:
+ '@babel/helper-plugin-utils': 7.27.1
+ '@istanbuljs/load-nyc-config': 1.1.0
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-instrument: 5.2.1
+ test-exclude: 6.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-jest-hoist@29.6.3:
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+ '@types/babel__core': 7.20.5
+ '@types/babel__traverse': 7.28.0
+
+ babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4):
+ dependencies:
+ '@babel/compat-data': 7.28.4
+ '@babel/core': 7.28.4
+ '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4)
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.4):
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4)
+ core-js-compat: 3.45.1
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.4):
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-react-compiler@19.1.0-rc.3:
+ dependencies:
+ '@babel/types': 7.28.4
+
+ babel-plugin-react-native-web@0.21.1: {}
+
+ babel-plugin-syntax-hermes-parser@0.29.1:
+ dependencies:
+ hermes-parser: 0.29.1
+
+ babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.28.4):
+ dependencies:
+ '@babel/plugin-syntax-flow': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - '@babel/core'
+
+ babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.4):
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.4)
+ '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4)
+ '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.4)
+ '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.4)
+ '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4)
+ '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.4)
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.4)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4)
+ '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4)
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.4)
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.4)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4)
+ '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4)
+ '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.4)
+
+ babel-preset-expo@54.0.1(@babel/core@7.28.4)(@babel/runtime@7.28.4)(expo@54.0.7)(react-refresh@0.14.2):
+ dependencies:
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-syntax-export-default-from': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.4)
+ '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4)
+ '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-runtime': 7.28.3(@babel/core@7.28.4)
+ '@babel/preset-react': 7.27.1(@babel/core@7.28.4)
+ '@babel/preset-typescript': 7.27.1(@babel/core@7.28.4)
+ '@react-native/babel-preset': 0.81.4(@babel/core@7.28.4)
+ babel-plugin-react-compiler: 19.1.0-rc.3
+ babel-plugin-react-native-web: 0.21.1
+ babel-plugin-syntax-hermes-parser: 0.29.1
+ babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.28.4)
+ debug: 4.4.3
+ react-refresh: 0.14.2
+ resolve-from: 5.0.0
+ optionalDependencies:
+ '@babel/runtime': 7.28.4
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - supports-color
+
+ babel-preset-jest@29.6.3(@babel/core@7.28.4):
+ dependencies:
+ '@babel/core': 7.28.4
+ babel-plugin-jest-hoist: 29.6.3
+ babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4)
+
+ balanced-match@1.0.2: {}
+
+ base64-js@1.5.1: {}
+
+ baseline-browser-mapping@2.8.4: {}
+
+ better-opn@3.0.2:
+ dependencies:
+ open: 8.4.2
+
+ big-integer@1.6.52: {}
+
+ bplist-creator@0.1.0:
+ dependencies:
+ stream-buffers: 2.2.0
+
+ bplist-parser@0.3.1:
+ dependencies:
+ big-integer: 1.6.52
+
+ bplist-parser@0.3.2:
+ dependencies:
+ big-integer: 1.6.52
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.26.2:
+ dependencies:
+ baseline-browser-mapping: 2.8.4
+ caniuse-lite: 1.0.30001743
+ electron-to-chromium: 1.5.218
+ node-releases: 2.0.21
+ update-browserslist-db: 1.1.3(browserslist@4.26.2)
+
+ bser@2.1.1:
+ dependencies:
+ node-int64: 0.4.0
+
+ buffer-from@1.1.2: {}
+
+ buffer@5.7.1:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+
+ bytes@3.1.2: {}
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bind@1.0.8:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ get-intrinsic: 1.3.0
+ set-function-length: 1.2.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
+ caller-callsite@2.0.0:
+ dependencies:
+ callsites: 2.0.0
+
+ caller-path@2.0.0:
+ dependencies:
+ caller-callsite: 2.0.0
+
+ callsites@2.0.0: {}
+
+ callsites@3.1.0: {}
+
+ camelcase@5.3.1: {}
+
+ camelcase@6.3.0: {}
+
+ caniuse-lite@1.0.30001743: {}
+
+ chalk@2.4.2:
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chownr@3.0.0: {}
+
+ chrome-launcher@0.15.2:
+ dependencies:
+ '@types/node': 24.5.0
+ escape-string-regexp: 4.0.0
+ is-wsl: 2.2.0
+ lighthouse-logger: 1.4.2
+ transitivePeerDependencies:
+ - supports-color
+
+ chromium-edge-launcher@0.2.0:
+ dependencies:
+ '@types/node': 24.5.0
+ escape-string-regexp: 4.0.0
+ is-wsl: 2.2.0
+ lighthouse-logger: 1.4.2
+ mkdirp: 1.0.4
+ rimraf: 3.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ ci-info@2.0.0: {}
+
+ ci-info@3.9.0: {}
+
+ cli-cursor@2.1.0:
+ dependencies:
+ restore-cursor: 2.0.0
+
+ cli-spinners@2.9.2: {}
+
+ client-only@0.0.1: {}
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ clone@1.0.4: {}
+
+ color-convert@1.9.3:
+ dependencies:
+ color-name: 1.1.3
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.3: {}
+
+ color-name@1.1.4: {}
+
+ color-string@1.9.1:
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.4
+
+ color@4.2.3:
+ dependencies:
+ color-convert: 2.0.1
+ color-string: 1.9.1
+
+ commander@12.1.0: {}
+
+ commander@2.20.3: {}
+
+ commander@4.1.1: {}
+
+ commander@7.2.0: {}
+
+ compressible@2.0.18:
+ dependencies:
+ mime-db: 1.54.0
+
+ compression@1.8.1:
+ dependencies:
+ bytes: 3.1.2
+ compressible: 2.0.18
+ debug: 2.6.9
+ negotiator: 0.6.4
+ on-headers: 1.1.0
+ safe-buffer: 5.2.1
+ vary: 1.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ concat-map@0.0.1: {}
+
+ connect@3.7.0:
+ dependencies:
+ debug: 2.6.9
+ finalhandler: 1.1.2
+ parseurl: 1.3.3
+ utils-merge: 1.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ convert-source-map@2.0.0: {}
+
+ core-js-compat@3.45.1:
+ dependencies:
+ browserslist: 4.26.2
+
+ cosmiconfig@5.2.1:
+ dependencies:
+ import-fresh: 2.0.0
+ is-directory: 0.3.1
+ js-yaml: 3.14.1
+ parse-json: 4.0.0
+
+ cross-fetch@3.2.0:
+ dependencies:
+ node-fetch: 2.7.0
+ transitivePeerDependencies:
+ - encoding
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ crypto-random-string@2.0.0: {}
+
+ css-in-js-utils@3.1.0:
+ dependencies:
+ hyphenate-style-name: 1.1.0
+
+ csstype@3.1.3: {}
+
+ data-view-buffer@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ data-view-byte-length@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ data-view-byte-offset@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ debug@2.6.9:
+ dependencies:
+ ms: 2.0.0
+
+ debug@3.2.7:
+ dependencies:
+ ms: 2.1.3
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decode-uri-component@0.2.2: {}
+
+ deep-extend@0.6.0: {}
+
+ deep-is@0.1.4: {}
+
+ deepmerge@4.3.1: {}
+
+ defaults@1.0.4:
+ dependencies:
+ clone: 1.0.4
+
+ define-data-property@1.1.4:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ define-lazy-prop@2.0.0: {}
+
+ define-properties@1.2.1:
+ dependencies:
+ define-data-property: 1.1.4
+ has-property-descriptors: 1.0.2
+ object-keys: 1.1.1
+
+ depd@2.0.0: {}
+
+ destroy@1.2.0: {}
+
+ detect-libc@2.1.0: {}
+
+ detect-node-es@1.1.0: {}
+
+ doctrine@2.1.0:
+ dependencies:
+ esutils: 2.0.3
+
+ dotenv-expand@11.0.7:
+ dependencies:
+ dotenv: 16.4.7
+
+ dotenv@16.4.7: {}
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ eastasianwidth@0.2.0: {}
+
+ ee-first@1.1.1: {}
+
+ electron-to-chromium@1.5.218: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ encodeurl@1.0.2: {}
+
+ encodeurl@2.0.0: {}
+
+ env-editor@0.4.2: {}
+
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
+ error-stack-parser@2.1.4:
+ dependencies:
+ stackframe: 1.3.4
+
+ es-abstract@1.24.0:
+ dependencies:
+ array-buffer-byte-length: 1.0.2
+ arraybuffer.prototype.slice: 1.0.4
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ data-view-buffer: 1.0.2
+ data-view-byte-length: 1.0.2
+ data-view-byte-offset: 1.0.1
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ es-set-tostringtag: 2.1.0
+ es-to-primitive: 1.3.0
+ function.prototype.name: 1.1.8
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ get-symbol-description: 1.1.0
+ globalthis: 1.0.4
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+ has-proto: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ internal-slot: 1.1.0
+ is-array-buffer: 3.0.5
+ is-callable: 1.2.7
+ is-data-view: 1.0.2
+ is-negative-zero: 2.0.3
+ is-regex: 1.2.1
+ is-set: 2.0.3
+ is-shared-array-buffer: 1.0.4
+ is-string: 1.1.1
+ is-typed-array: 1.1.15
+ is-weakref: 1.1.1
+ math-intrinsics: 1.1.0
+ object-inspect: 1.13.4
+ object-keys: 1.1.1
+ object.assign: 4.1.7
+ own-keys: 1.0.1
+ regexp.prototype.flags: 1.5.4
+ safe-array-concat: 1.1.3
+ safe-push-apply: 1.0.0
+ safe-regex-test: 1.1.0
+ set-proto: 1.0.0
+ stop-iteration-iterator: 1.1.0
+ string.prototype.trim: 1.2.10
+ string.prototype.trimend: 1.0.9
+ string.prototype.trimstart: 1.0.8
+ typed-array-buffer: 1.0.3
+ typed-array-byte-length: 1.0.3
+ typed-array-byte-offset: 1.0.4
+ typed-array-length: 1.0.7
+ unbox-primitive: 1.1.0
+ which-typed-array: 1.1.19
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-iterator-helpers@1.2.1:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-errors: 1.3.0
+ es-set-tostringtag: 2.1.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ globalthis: 1.0.4
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+ has-proto: 1.2.0
+ has-symbols: 1.1.0
+ internal-slot: 1.1.0
+ iterator.prototype: 1.1.5
+ safe-array-concat: 1.1.3
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ es-shim-unscopables@1.1.0:
+ dependencies:
+ hasown: 2.0.2
+
+ es-to-primitive@1.3.0:
+ dependencies:
+ is-callable: 1.2.7
+ is-date-object: 1.1.0
+ is-symbol: 1.1.1
+
+ escalade@3.2.0: {}
+
+ escape-html@1.0.3: {}
+
+ escape-string-regexp@1.0.5: {}
+
+ escape-string-regexp@2.0.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-config-expo@10.0.0(eslint@9.35.0)(typescript@5.9.2):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(typescript@5.9.2)
+ '@typescript-eslint/parser': 8.44.0(eslint@9.35.0)(typescript@5.9.2)
+ eslint: 9.35.0
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.35.0)
+ eslint-plugin-expo: 1.0.0(eslint@9.35.0)(typescript@5.9.2)
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.35.0)
+ eslint-plugin-react: 7.37.5(eslint@9.35.0)
+ eslint-plugin-react-hooks: 5.2.0(eslint@9.35.0)
+ globals: 16.4.0
+ transitivePeerDependencies:
+ - eslint-import-resolver-webpack
+ - eslint-plugin-import-x
+ - supports-color
+ - typescript
+
+ eslint-import-resolver-node@0.3.9:
+ dependencies:
+ debug: 3.2.7
+ is-core-module: 2.16.1
+ resolve: 1.22.10
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.35.0):
+ dependencies:
+ '@nolyfill/is-core-module': 1.0.39
+ debug: 4.4.3
+ eslint: 9.35.0
+ get-tsconfig: 4.10.1
+ is-bun-module: 2.0.0
+ stable-hash: 0.0.5
+ tinyglobby: 0.2.15
+ unrs-resolver: 1.11.1
+ optionalDependencies:
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.35.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.35.0):
+ dependencies:
+ debug: 3.2.7
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.44.0(eslint@9.35.0)(typescript@5.9.2)
+ eslint: 9.35.0
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.35.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-expo@1.0.0(eslint@9.35.0)(typescript@5.9.2):
+ dependencies:
+ '@typescript-eslint/types': 8.44.0
+ '@typescript-eslint/utils': 8.44.0(eslint@9.35.0)(typescript@5.9.2)
+ eslint: 9.35.0
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.35.0):
+ dependencies:
+ '@rtsao/scc': 1.1.0
+ array-includes: 3.1.9
+ array.prototype.findlastindex: 1.2.6
+ array.prototype.flat: 1.3.3
+ array.prototype.flatmap: 1.3.3
+ debug: 3.2.7
+ doctrine: 2.1.0
+ eslint: 9.35.0
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.35.0)
+ hasown: 2.0.2
+ is-core-module: 2.16.1
+ is-glob: 4.0.3
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ object.groupby: 1.0.3
+ object.values: 1.2.1
+ semver: 6.3.1
+ string.prototype.trimend: 1.0.9
+ tsconfig-paths: 3.15.0
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.44.0(eslint@9.35.0)(typescript@5.9.2)
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-plugin-react-hooks@5.2.0(eslint@9.35.0):
+ dependencies:
+ eslint: 9.35.0
+
+ eslint-plugin-react@7.37.5(eslint@9.35.0):
+ dependencies:
+ array-includes: 3.1.9
+ array.prototype.findlast: 1.2.5
+ array.prototype.flatmap: 1.3.3
+ array.prototype.tosorted: 1.1.4
+ doctrine: 2.1.0
+ es-iterator-helpers: 1.2.1
+ eslint: 9.35.0
+ estraverse: 5.3.0
+ hasown: 2.0.2
+ jsx-ast-utils: 3.3.5
+ minimatch: 3.1.2
+ object.entries: 1.1.9
+ object.fromentries: 2.0.8
+ object.values: 1.2.1
+ prop-types: 15.8.1
+ resolve: 2.0.0-next.5
+ semver: 6.3.1
+ string.prototype.matchall: 4.0.12
+ string.prototype.repeat: 1.0.0
+
+ eslint-scope@8.4.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.1: {}
+
+ eslint@9.35.0:
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0)
+ '@eslint-community/regexpp': 4.12.1
+ '@eslint/config-array': 0.21.0
+ '@eslint/config-helpers': 0.3.1
+ '@eslint/core': 0.15.2
+ '@eslint/eslintrc': 3.3.1
+ '@eslint/js': 9.35.0
+ '@eslint/plugin-kit': 0.3.5
+ '@humanfs/node': 0.16.7
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.8
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.4.0:
+ dependencies:
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 4.2.1
+
+ esprima@4.0.1: {}
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ esutils@2.0.3: {}
+
+ etag@1.8.1: {}
+
+ event-target-shim@5.0.1: {}
+
+ exec-async@2.2.0: {}
+
+ expo-apple-authentication@7.2.4(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ expo-asset@12.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@expo/image-utils': 0.8.7
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-constants: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ expo-constants@18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)):
+ dependencies:
+ '@expo/config': 12.0.9
+ '@expo/env': 2.0.7
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ expo-crypto@14.1.5(expo@54.0.7):
+ dependencies:
+ base64-js: 1.5.1
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+
+ expo-document-picker@13.1.6(expo@54.0.7):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+
+ expo-file-system@18.1.11(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ expo-file-system@19.0.14(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ expo-font@14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ fontfaceobserver: 2.3.0
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ expo-haptics@15.0.7(expo@54.0.7):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+
+ expo-image@3.0.8(expo@54.0.7)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ optionalDependencies:
+ react-native-web: 0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+
+ expo-keep-awake@15.0.7(expo@54.0.7)(react@19.1.0):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+
+ expo-linking@8.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ expo-constants: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ invariant: 2.2.4
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ transitivePeerDependencies:
+ - expo
+ - supports-color
+
+ expo-modules-autolinking@3.0.10:
+ dependencies:
+ '@expo/spawn-async': 1.7.2
+ chalk: 4.1.2
+ commander: 7.2.0
+ glob: 10.4.5
+ require-from-string: 2.0.2
+ resolve-from: 5.0.0
+
+ expo-modules-core@3.0.15(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ invariant: 2.2.4
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ expo-router@6.0.4(@expo/metro-runtime@6.1.2)(@types/react@19.1.13)(expo-constants@18.0.8)(expo-linking@8.0.8)(expo@54.0.7)(react-dom@19.1.0(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.0(@babel/core@7.28.4)(react-native-worklets@0.5.1(@babel/core@7.28.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@expo/metro-runtime': 6.1.2(expo@54.0.7)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@expo/schema-utils': 0.1.7
+ '@expo/server': 0.7.4
+ '@radix-ui/react-slot': 1.2.0(@types/react@19.1.13)(react@19.1.0)
+ '@radix-ui/react-tabs': 1.1.13(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@react-navigation/bottom-tabs': 7.4.7(@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@react-navigation/native': 7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@react-navigation/native-stack': 7.3.26(@react-navigation/native@7.1.17(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ client-only: 0.0.1
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-constants: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ expo-linking: 8.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ fast-deep-equal: 3.1.3
+ invariant: 2.2.4
+ nanoid: 3.3.11
+ query-string: 7.1.3
+ react: 19.1.0
+ react-fast-compare: 3.2.2
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-safe-area-context: 5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-screens: 4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ semver: 7.6.3
+ server-only: 0.0.1
+ sf-symbols-typescript: 2.1.0
+ shallowequal: 1.1.0
+ use-latest-callback: 0.2.4(react@19.1.0)
+ vaul: 1.1.2(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ optionalDependencies:
+ react-dom: 19.1.0(react@19.1.0)
+ react-native-gesture-handler: 2.28.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-reanimated: 4.1.0(@babel/core@7.28.4)(react-native-worklets@0.5.1(@babel/core@7.28.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-web: 0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ transitivePeerDependencies:
+ - '@react-native-masked-view/masked-view'
+ - '@types/react'
+ - '@types/react-dom'
+ - supports-color
+
+ expo-sharing@13.1.5(expo@54.0.7):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+
+ expo-splash-screen@31.0.10(expo@54.0.7):
+ dependencies:
+ '@expo/prebuild-config': 54.0.3(expo@54.0.7)
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ expo-status-bar@3.0.8(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+
+ expo-symbols@1.0.7(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ sf-symbols-typescript: 2.1.0
+
+ expo-system-ui@6.0.7(expo@54.0.7)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)):
+ dependencies:
+ '@react-native/normalize-colors': 0.81.4
+ debug: 4.4.3
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ optionalDependencies:
+ react-native-web: 0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ expo-web-browser@15.0.7(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)):
+ dependencies:
+ expo: 54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ expo@54.0.7(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@babel/runtime': 7.28.4
+ '@expo/cli': 54.0.5(expo-router@6.0.4)(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ '@expo/config': 12.0.9
+ '@expo/config-plugins': 54.0.1
+ '@expo/devtools': 0.1.7(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@expo/fingerprint': 0.15.0
+ '@expo/metro': 0.1.1
+ '@expo/metro-config': 54.0.3(expo@54.0.7)
+ '@expo/vector-icons': 15.0.2(expo-font@14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ '@ungap/structured-clone': 1.3.0
+ babel-preset-expo: 54.0.1(@babel/core@7.28.4)(@babel/runtime@7.28.4)(expo@54.0.7)(react-refresh@0.14.2)
+ expo-asset: 12.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-constants: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ expo-file-system: 19.0.14(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))
+ expo-font: 14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ expo-keep-awake: 15.0.7(expo@54.0.7)(react@19.1.0)
+ expo-modules-autolinking: 3.0.10
+ expo-modules-core: 3.0.15(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ pretty-format: 29.7.0
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ react-refresh: 0.14.2
+ whatwg-url-without-unicode: 8.0.0-3
+ optionalDependencies:
+ '@expo/metro-runtime': 6.1.2(expo@54.0.7)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - bufferutil
+ - expo-router
+ - graphql
+ - supports-color
+ - utf-8-validate
+
+ exponential-backoff@3.1.2: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.19.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fb-watchman@2.0.2:
+ dependencies:
+ bser: 2.1.1
+
+ fbjs-css-vars@1.0.2: {}
+
+ fbjs@3.0.5:
+ dependencies:
+ cross-fetch: 3.2.0
+ fbjs-css-vars: 1.0.2
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ promise: 7.3.1
+ setimmediate: 1.0.5
+ ua-parser-js: 1.0.41
+ transitivePeerDependencies:
+ - encoding
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ filter-obj@1.1.0: {}
+
+ finalhandler@1.1.2:
+ dependencies:
+ debug: 2.6.9
+ encodeurl: 1.0.2
+ escape-html: 1.0.3
+ on-finished: 2.3.0
+ parseurl: 1.3.3
+ statuses: 1.5.0
+ unpipe: 1.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.3
+ keyv: 4.5.4
+
+ flatted@3.3.3: {}
+
+ flow-enums-runtime@0.0.6: {}
+
+ fontfaceobserver@2.3.0: {}
+
+ for-each@0.3.5:
+ dependencies:
+ is-callable: 1.2.7
+
+ foreground-child@3.3.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+
+ freeport-async@2.0.0: {}
+
+ fresh@0.5.2: {}
+
+ fs.realpath@1.0.0: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ function.prototype.name@1.1.8:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ functions-have-names: 1.2.3
+ hasown: 2.0.2
+ is-callable: 1.2.7
+
+ functions-have-names@1.2.3: {}
+
+ gensync@1.0.0-beta.2: {}
+
+ get-caller-file@2.0.5: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-nonce@1.0.1: {}
+
+ get-package-type@0.1.0: {}
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-symbol-description@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+
+ get-tsconfig@4.10.1:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ getenv@2.0.0: {}
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@10.4.5:
+ dependencies:
+ foreground-child: 3.3.1
+ jackspeak: 3.4.3
+ minimatch: 9.0.5
+ minipass: 7.1.2
+ package-json-from-dist: 1.0.1
+ path-scurry: 1.11.1
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ global-dirs@0.1.1:
+ dependencies:
+ ini: 1.3.8
+
+ globals@14.0.0: {}
+
+ globals@16.4.0: {}
+
+ globalthis@1.0.4:
+ dependencies:
+ define-properties: 1.2.1
+ gopd: 1.2.0
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ graphemer@1.4.0: {}
+
+ has-bigints@1.1.0: {}
+
+ has-flag@3.0.0: {}
+
+ has-flag@4.0.0: {}
+
+ has-property-descriptors@1.0.2:
+ dependencies:
+ es-define-property: 1.0.1
+
+ has-proto@1.2.0:
+ dependencies:
+ dunder-proto: 1.0.1
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ hermes-estree@0.29.1: {}
+
+ hermes-parser@0.29.1:
+ dependencies:
+ hermes-estree: 0.29.1
+
+ hoist-non-react-statics@3.3.2:
+ dependencies:
+ react-is: 16.13.1
+
+ hosted-git-info@7.0.2:
+ dependencies:
+ lru-cache: 10.4.3
+
+ http-errors@2.0.0:
+ dependencies:
+ depd: 2.0.0
+ inherits: 2.0.4
+ setprototypeof: 1.2.0
+ statuses: 2.0.1
+ toidentifier: 1.0.1
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ hyphenate-style-name@1.1.0: {}
+
+ ieee754@1.2.1: {}
+
+ ignore@5.3.2: {}
+
+ ignore@7.0.5: {}
+
+ image-size@1.2.1:
+ dependencies:
+ queue: 6.0.2
+
+ import-fresh@2.0.0:
+ dependencies:
+ caller-path: 2.0.0
+ resolve-from: 3.0.0
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ini@1.3.8: {}
+
+ inline-style-prefixer@7.0.1:
+ dependencies:
+ css-in-js-utils: 3.1.0
+
+ internal-slot@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ hasown: 2.0.2
+ side-channel: 1.1.0
+
+ invariant@2.2.4:
+ dependencies:
+ loose-envify: 1.4.0
+
+ is-array-buffer@3.0.5:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+
+ is-arrayish@0.2.1: {}
+
+ is-arrayish@0.3.4: {}
+
+ is-async-function@2.1.1:
+ dependencies:
+ async-function: 1.0.0
+ call-bound: 1.0.4
+ get-proto: 1.0.1
+ has-tostringtag: 1.0.2
+ safe-regex-test: 1.1.0
+
+ is-bigint@1.1.0:
+ dependencies:
+ has-bigints: 1.1.0
+
+ is-boolean-object@1.2.2:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-bun-module@2.0.0:
+ dependencies:
+ semver: 7.7.2
+
+ is-callable@1.2.7: {}
+
+ is-core-module@2.16.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-data-view@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+ is-typed-array: 1.1.15
+
+ is-date-object@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-directory@0.3.1: {}
+
+ is-docker@2.2.1: {}
+
+ is-extglob@2.1.1: {}
+
+ is-finalizationregistry@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-generator-function@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ get-proto: 1.0.1
+ has-tostringtag: 1.0.2
+ safe-regex-test: 1.1.0
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-map@2.0.3: {}
+
+ is-negative-zero@2.0.3: {}
+
+ is-number-object@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-number@7.0.0: {}
+
+ is-plain-obj@2.1.0: {}
+
+ is-regex@1.2.1:
+ dependencies:
+ call-bound: 1.0.4
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ is-set@2.0.3: {}
+
+ is-shared-array-buffer@1.0.4:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-string@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-symbol@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-symbols: 1.1.0
+ safe-regex-test: 1.1.0
+
+ is-typed-array@1.1.15:
+ dependencies:
+ which-typed-array: 1.1.19
+
+ is-weakmap@2.0.2: {}
+
+ is-weakref@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-weakset@2.0.4:
+ dependencies:
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+
+ is-wsl@2.2.0:
+ dependencies:
+ is-docker: 2.2.1
+
+ isarray@2.0.5: {}
+
+ isexe@2.0.0: {}
+
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-instrument@5.2.1:
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/parser': 7.28.4
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-coverage: 3.2.2
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ iterator.prototype@1.1.5:
+ dependencies:
+ define-data-property: 1.1.4
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ has-symbols: 1.1.0
+ set-function-name: 2.0.2
+
+ jackspeak@3.4.3:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ jest-environment-node@29.7.0:
+ dependencies:
+ '@jest/environment': 29.7.0
+ '@jest/fake-timers': 29.7.0
+ '@jest/types': 29.6.3
+ '@types/node': 24.5.0
+ jest-mock: 29.7.0
+ jest-util: 29.7.0
+
+ jest-get-type@29.6.3: {}
+
+ jest-haste-map@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ '@types/graceful-fs': 4.1.9
+ '@types/node': 24.5.0
+ anymatch: 3.1.3
+ fb-watchman: 2.0.2
+ graceful-fs: 4.2.11
+ jest-regex-util: 29.6.3
+ jest-util: 29.7.0
+ jest-worker: 29.7.0
+ micromatch: 4.0.8
+ walker: 1.0.8
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ jest-message-util@29.7.0:
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@jest/types': 29.6.3
+ '@types/stack-utils': 2.0.3
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ micromatch: 4.0.8
+ pretty-format: 29.7.0
+ slash: 3.0.0
+ stack-utils: 2.0.6
+
+ jest-mock@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ '@types/node': 24.5.0
+ jest-util: 29.7.0
+
+ jest-regex-util@29.6.3: {}
+
+ jest-util@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ '@types/node': 24.5.0
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ graceful-fs: 4.2.11
+ picomatch: 2.3.1
+
+ jest-validate@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ camelcase: 6.3.0
+ chalk: 4.1.2
+ jest-get-type: 29.6.3
+ leven: 3.1.0
+ pretty-format: 29.7.0
+
+ jest-worker@29.7.0:
+ dependencies:
+ '@types/node': 24.5.0
+ jest-util: 29.7.0
+ merge-stream: 2.0.0
+ supports-color: 8.1.1
+
+ jimp-compact@0.16.1: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@3.14.1:
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsc-safe-url@0.2.4: {}
+
+ jsesc@3.0.2: {}
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-parse-better-errors@1.0.2: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@1.0.2:
+ dependencies:
+ minimist: 1.2.8
+
+ json5@2.2.3: {}
+
+ jsx-ast-utils@3.3.5:
+ dependencies:
+ array-includes: 3.1.9
+ array.prototype.flat: 1.3.3
+ object.assign: 4.1.7
+ object.values: 1.2.1
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ kleur@3.0.3: {}
+
+ lan-network@0.1.7: {}
+
+ leven@3.1.0: {}
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lighthouse-logger@1.4.2:
+ dependencies:
+ debug: 2.6.9
+ marky: 1.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ lightningcss-darwin-arm64@1.30.1:
+ optional: true
+
+ lightningcss-darwin-x64@1.30.1:
+ optional: true
+
+ lightningcss-freebsd-x64@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.30.1:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ optional: true
+
+ lightningcss@1.30.1:
+ dependencies:
+ detect-libc: 2.1.0
+ optionalDependencies:
+ lightningcss-darwin-arm64: 1.30.1
+ lightningcss-darwin-x64: 1.30.1
+ lightningcss-freebsd-x64: 1.30.1
+ lightningcss-linux-arm-gnueabihf: 1.30.1
+ lightningcss-linux-arm64-gnu: 1.30.1
+ lightningcss-linux-arm64-musl: 1.30.1
+ lightningcss-linux-x64-gnu: 1.30.1
+ lightningcss-linux-x64-musl: 1.30.1
+ lightningcss-win32-arm64-msvc: 1.30.1
+ lightningcss-win32-x64-msvc: 1.30.1
+
+ lines-and-columns@1.2.4: {}
+
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.debounce@4.0.8: {}
+
+ lodash.merge@4.6.2: {}
+
+ lodash.throttle@4.1.1: {}
+
+ log-symbols@2.2.0:
+ dependencies:
+ chalk: 2.4.2
+
+ loose-envify@1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+
+ lru-cache@10.4.3: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ makeerror@1.0.12:
+ dependencies:
+ tmpl: 1.0.5
+
+ marky@1.3.0: {}
+
+ math-intrinsics@1.1.0: {}
+
+ memoize-one@5.2.1: {}
+
+ memoize-one@6.0.0: {}
+
+ merge-options@3.0.4:
+ dependencies:
+ is-plain-obj: 2.1.0
+
+ merge-stream@2.0.0: {}
+
+ merge2@1.4.1: {}
+
+ metro-babel-transformer@0.83.1:
+ dependencies:
+ '@babel/core': 7.28.4
+ flow-enums-runtime: 0.0.6
+ hermes-parser: 0.29.1
+ nullthrows: 1.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ metro-cache-key@0.83.1:
+ dependencies:
+ flow-enums-runtime: 0.0.6
+
+ metro-cache@0.83.1:
+ dependencies:
+ exponential-backoff: 3.1.2
+ flow-enums-runtime: 0.0.6
+ https-proxy-agent: 7.0.6
+ metro-core: 0.83.1
+ transitivePeerDependencies:
+ - supports-color
+
+ metro-config@0.83.1:
+ dependencies:
+ connect: 3.7.0
+ cosmiconfig: 5.2.1
+ flow-enums-runtime: 0.0.6
+ jest-validate: 29.7.0
+ metro: 0.83.1
+ metro-cache: 0.83.1
+ metro-core: 0.83.1
+ metro-runtime: 0.83.1
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ metro-core@0.83.1:
+ dependencies:
+ flow-enums-runtime: 0.0.6
+ lodash.throttle: 4.1.1
+ metro-resolver: 0.83.1
+
+ metro-file-map@0.83.1:
+ dependencies:
+ debug: 4.4.3
+ fb-watchman: 2.0.2
+ flow-enums-runtime: 0.0.6
+ graceful-fs: 4.2.11
+ invariant: 2.2.4
+ jest-worker: 29.7.0
+ micromatch: 4.0.8
+ nullthrows: 1.1.1
+ walker: 1.0.8
+ transitivePeerDependencies:
+ - supports-color
+
+ metro-minify-terser@0.83.1:
+ dependencies:
+ flow-enums-runtime: 0.0.6
+ terser: 5.44.0
+
+ metro-resolver@0.83.1:
+ dependencies:
+ flow-enums-runtime: 0.0.6
+
+ metro-runtime@0.83.1:
+ dependencies:
+ '@babel/runtime': 7.28.4
+ flow-enums-runtime: 0.0.6
+
+ metro-source-map@0.83.1:
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/traverse--for-generate-function-map': '@babel/traverse@7.28.4'
+ '@babel/types': 7.28.4
+ flow-enums-runtime: 0.0.6
+ invariant: 2.2.4
+ metro-symbolicate: 0.83.1
+ nullthrows: 1.1.1
+ ob1: 0.83.1
+ source-map: 0.5.7
+ vlq: 1.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ metro-symbolicate@0.83.1:
+ dependencies:
+ flow-enums-runtime: 0.0.6
+ invariant: 2.2.4
+ metro-source-map: 0.83.1
+ nullthrows: 1.1.1
+ source-map: 0.5.7
+ vlq: 1.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ metro-transform-plugins@0.83.1:
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/generator': 7.28.3
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ flow-enums-runtime: 0.0.6
+ nullthrows: 1.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ metro-transform-worker@0.83.1:
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/generator': 7.28.3
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+ flow-enums-runtime: 0.0.6
+ metro: 0.83.1
+ metro-babel-transformer: 0.83.1
+ metro-cache: 0.83.1
+ metro-cache-key: 0.83.1
+ metro-minify-terser: 0.83.1
+ metro-source-map: 0.83.1
+ metro-transform-plugins: 0.83.1
+ nullthrows: 1.1.1
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ metro@0.83.1:
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/core': 7.28.4
+ '@babel/generator': 7.28.3
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ accepts: 1.3.8
+ chalk: 4.1.2
+ ci-info: 2.0.0
+ connect: 3.7.0
+ debug: 4.4.3
+ error-stack-parser: 2.1.4
+ flow-enums-runtime: 0.0.6
+ graceful-fs: 4.2.11
+ hermes-parser: 0.29.1
+ image-size: 1.2.1
+ invariant: 2.2.4
+ jest-worker: 29.7.0
+ jsc-safe-url: 0.2.4
+ lodash.throttle: 4.1.1
+ metro-babel-transformer: 0.83.1
+ metro-cache: 0.83.1
+ metro-cache-key: 0.83.1
+ metro-config: 0.83.1
+ metro-core: 0.83.1
+ metro-file-map: 0.83.1
+ metro-resolver: 0.83.1
+ metro-runtime: 0.83.1
+ metro-source-map: 0.83.1
+ metro-symbolicate: 0.83.1
+ metro-transform-plugins: 0.83.1
+ metro-transform-worker: 0.83.1
+ mime-types: 2.1.35
+ nullthrows: 1.1.1
+ serialize-error: 2.1.0
+ source-map: 0.5.7
+ throat: 5.0.0
+ ws: 7.5.10
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime-db@1.52.0: {}
+
+ mime-db@1.54.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mime@1.6.0: {}
+
+ mimic-fn@1.2.0: {}
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimist@1.2.8: {}
+
+ minipass@7.1.2: {}
+
+ minizlib@3.0.2:
+ dependencies:
+ minipass: 7.1.2
+
+ mkdirp@1.0.4: {}
+
+ mkdirp@3.0.1: {}
+
+ ms@2.0.0: {}
+
+ ms@2.1.3: {}
+
+ mz@2.7.0:
+ dependencies:
+ any-promise: 1.3.0
+ object-assign: 4.1.1
+ thenify-all: 1.6.0
+
+ nanoid@3.3.11: {}
+
+ napi-postinstall@0.3.3: {}
+
+ natural-compare@1.4.0: {}
+
+ negotiator@0.6.3: {}
+
+ negotiator@0.6.4: {}
+
+ nested-error-stacks@2.0.1: {}
+
+ node-fetch@2.7.0:
+ dependencies:
+ whatwg-url: 5.0.0
+
+ node-forge@1.3.1: {}
+
+ node-int64@0.4.0: {}
+
+ node-releases@2.0.21: {}
+
+ normalize-path@3.0.0: {}
+
+ npm-package-arg@11.0.3:
+ dependencies:
+ hosted-git-info: 7.0.2
+ proc-log: 4.2.0
+ semver: 7.7.2
+ validate-npm-package-name: 5.0.1
+
+ nullthrows@1.1.1: {}
+
+ ob1@0.83.1:
+ dependencies:
+ flow-enums-runtime: 0.0.6
+
+ object-assign@4.1.1: {}
+
+ object-inspect@1.13.4: {}
+
+ object-keys@1.1.1: {}
+
+ object.assign@4.1.7:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+ has-symbols: 1.1.0
+ object-keys: 1.1.1
+
+ object.entries@1.1.9:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ object.fromentries@2.0.8:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-object-atoms: 1.1.1
+
+ object.groupby@1.0.3:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+
+ object.values@1.2.1:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ on-finished@2.3.0:
+ dependencies:
+ ee-first: 1.1.1
+
+ on-finished@2.4.1:
+ dependencies:
+ ee-first: 1.1.1
+
+ on-headers@1.1.0: {}
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ onetime@2.0.1:
+ dependencies:
+ mimic-fn: 1.2.0
+
+ open@7.4.2:
+ dependencies:
+ is-docker: 2.2.1
+ is-wsl: 2.2.0
+
+ open@8.4.2:
+ dependencies:
+ define-lazy-prop: 2.0.0
+ is-docker: 2.2.1
+ is-wsl: 2.2.0
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ ora@3.4.0:
+ dependencies:
+ chalk: 2.4.2
+ cli-cursor: 2.1.0
+ cli-spinners: 2.9.2
+ log-symbols: 2.2.0
+ strip-ansi: 5.2.0
+ wcwidth: 1.0.1
+
+ own-keys@1.0.1:
+ dependencies:
+ get-intrinsic: 1.3.0
+ object-keys: 1.1.1
+ safe-push-apply: 1.0.0
+
+ p-limit@2.3.0:
+ dependencies:
+ p-try: 2.2.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ p-try@2.2.0: {}
+
+ package-json-from-dist@1.0.1: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse-json@4.0.0:
+ dependencies:
+ error-ex: 1.3.4
+ json-parse-better-errors: 1.0.2
+
+ parse-png@2.1.0:
+ dependencies:
+ pngjs: 3.4.0
+
+ parseurl@1.3.3: {}
+
+ path-exists@4.0.0: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-parse@1.0.7: {}
+
+ path-scurry@1.11.1:
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.2
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@3.0.1: {}
+
+ picomatch@4.0.3: {}
+
+ pirates@4.0.7: {}
+
+ plist@3.1.0:
+ dependencies:
+ '@xmldom/xmldom': 0.8.11
+ base64-js: 1.5.1
+ xmlbuilder: 15.1.1
+
+ pngjs@3.4.0: {}
+
+ possible-typed-array-names@1.1.0: {}
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.4.49:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ pretty-bytes@5.6.0: {}
+
+ pretty-format@29.7.0:
+ dependencies:
+ '@jest/schemas': 29.6.3
+ ansi-styles: 5.2.0
+ react-is: 18.3.1
+
+ proc-log@4.2.0: {}
+
+ progress@2.0.3: {}
+
+ promise@7.3.1:
+ dependencies:
+ asap: 2.0.6
+
+ promise@8.3.0:
+ dependencies:
+ asap: 2.0.6
+
+ prompts@2.4.2:
+ dependencies:
+ kleur: 3.0.3
+ sisteransi: 1.0.5
+
+ prop-types@15.8.1:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react-is: 16.13.1
+
+ punycode@2.3.1: {}
+
+ qrcode-terminal@0.11.0: {}
+
+ query-string@7.1.3:
+ dependencies:
+ decode-uri-component: 0.2.2
+ filter-obj: 1.1.0
+ split-on-first: 1.1.0
+ strict-uri-encode: 2.0.0
+
+ queue-microtask@1.2.3: {}
+
+ queue@6.0.2:
+ dependencies:
+ inherits: 2.0.4
+
+ range-parser@1.2.1: {}
+
+ rc@1.2.8:
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+
+ react-devtools-core@6.1.5:
+ dependencies:
+ shell-quote: 1.8.3
+ ws: 7.5.10
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ react-dom@19.1.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ scheduler: 0.26.0
+
+ react-fast-compare@3.2.2: {}
+
+ react-freeze@1.0.4(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
+ react-is@16.13.1: {}
+
+ react-is@18.3.1: {}
+
+ react-is@19.1.1: {}
+
+ react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@egjs/hammerjs': 2.0.17
+ hoist-non-react-statics: 3.3.2
+ invariant: 2.2.4
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ react-native-is-edge-to-edge@1.2.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ react-native-reanimated@4.1.0(@babel/core@7.28.4)(react-native-worklets@0.5.1(@babel/core@7.28.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@babel/core': 7.28.4
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ react-native-worklets: 0.5.1(@babel/core@7.28.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ semver: 7.7.2
+
+ react-native-safe-area-context@5.6.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+
+ react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ react-freeze: 1.0.4(react@19.1.0)
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ warn-once: 0.1.1
+
+ react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@babel/runtime': 7.28.4
+ '@react-native/normalize-colors': 0.74.89
+ fbjs: 3.0.5
+ inline-style-prefixer: 7.0.1
+ memoize-one: 6.0.0
+ nullthrows: 1.1.1
+ postcss-value-parser: 4.2.0
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ styleq: 0.1.3
+ transitivePeerDependencies:
+ - encoding
+
+ react-native-worklets@0.5.1(@babel/core@7.28.4)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/preset-typescript': 7.27.1(@babel/core@7.28.4)
+ convert-source-map: 2.0.0
+ react: 19.1.0
+ react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0)
+ semver: 7.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0):
+ dependencies:
+ '@jest/create-cache-key-function': 29.7.0
+ '@react-native/assets-registry': 0.81.4
+ '@react-native/codegen': 0.81.4(@babel/core@7.28.4)
+ '@react-native/community-cli-plugin': 0.81.4
+ '@react-native/gradle-plugin': 0.81.4
+ '@react-native/js-polyfills': 0.81.4
+ '@react-native/normalize-colors': 0.81.4
+ '@react-native/virtualized-lists': 0.81.4(@types/react@19.1.13)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0)
+ abort-controller: 3.0.0
+ anser: 1.4.10
+ ansi-regex: 5.0.1
+ babel-jest: 29.7.0(@babel/core@7.28.4)
+ babel-plugin-syntax-hermes-parser: 0.29.1
+ base64-js: 1.5.1
+ commander: 12.1.0
+ flow-enums-runtime: 0.0.6
+ glob: 7.2.3
+ invariant: 2.2.4
+ jest-environment-node: 29.7.0
+ memoize-one: 5.2.1
+ metro-runtime: 0.83.1
+ metro-source-map: 0.83.1
+ nullthrows: 1.1.1
+ pretty-format: 29.7.0
+ promise: 8.3.0
+ react: 19.1.0
+ react-devtools-core: 6.1.5
+ react-refresh: 0.14.2
+ regenerator-runtime: 0.13.11
+ scheduler: 0.26.0
+ semver: 7.7.2
+ stacktrace-parser: 0.1.11
+ whatwg-fetch: 3.6.20
+ ws: 6.2.3
+ yargs: 17.7.2
+ optionalDependencies:
+ '@types/react': 19.1.13
+ transitivePeerDependencies:
+ - '@babel/core'
+ - '@react-native-community/cli'
+ - '@react-native/metro-config'
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ react-refresh@0.14.2: {}
+
+ react-remove-scroll-bar@2.3.8(@types/react@19.1.13)(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ react-style-singleton: 2.2.3(@types/react@19.1.13)(react@19.1.0)
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ react-remove-scroll@2.7.1(@types/react@19.1.13)(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ react-remove-scroll-bar: 2.3.8(@types/react@19.1.13)(react@19.1.0)
+ react-style-singleton: 2.2.3(@types/react@19.1.13)(react@19.1.0)
+ tslib: 2.8.1
+ use-callback-ref: 1.3.3(@types/react@19.1.13)(react@19.1.0)
+ use-sidecar: 1.1.3(@types/react@19.1.13)(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ react-style-singleton@2.2.3(@types/react@19.1.13)(react@19.1.0):
+ dependencies:
+ get-nonce: 1.0.1
+ react: 19.1.0
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ react@19.1.0: {}
+
+ reflect.getprototypeof@1.0.10:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ which-builtin-type: 1.2.1
+
+ regenerate-unicode-properties@10.2.2:
+ dependencies:
+ regenerate: 1.4.2
+
+ regenerate@1.4.2: {}
+
+ regenerator-runtime@0.13.11: {}
+
+ regexp.prototype.flags@1.5.4:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-errors: 1.3.0
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ set-function-name: 2.0.2
+
+ regexpu-core@6.3.1:
+ dependencies:
+ regenerate: 1.4.2
+ regenerate-unicode-properties: 10.2.2
+ regjsgen: 0.8.0
+ regjsparser: 0.12.0
+ unicode-match-property-ecmascript: 2.0.0
+ unicode-match-property-value-ecmascript: 2.2.1
+
+ regjsgen@0.8.0: {}
+
+ regjsparser@0.12.0:
+ dependencies:
+ jsesc: 3.0.2
+
+ require-directory@2.1.1: {}
+
+ require-from-string@2.0.2: {}
+
+ requireg@0.2.2:
+ dependencies:
+ nested-error-stacks: 2.0.1
+ rc: 1.2.8
+ resolve: 1.7.1
+
+ resolve-from@3.0.0: {}
+
+ resolve-from@4.0.0: {}
+
+ resolve-from@5.0.0: {}
+
+ resolve-global@1.0.0:
+ dependencies:
+ global-dirs: 0.1.1
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve-workspace-root@2.0.0: {}
+
+ resolve.exports@2.0.3: {}
+
+ resolve@1.22.10:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ resolve@1.7.1:
+ dependencies:
+ path-parse: 1.0.7
+
+ resolve@2.0.0-next.5:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ restore-cursor@2.0.0:
+ dependencies:
+ onetime: 2.0.1
+ signal-exit: 3.0.7
+
+ reusify@1.1.0: {}
+
+ rimraf@3.0.2:
+ dependencies:
+ glob: 7.2.3
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ safe-array-concat@1.1.3:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+ has-symbols: 1.1.0
+ isarray: 2.0.5
+
+ safe-buffer@5.2.1: {}
+
+ safe-push-apply@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ isarray: 2.0.5
+
+ safe-regex-test@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-regex: 1.2.1
+
+ sax@1.4.1: {}
+
+ scheduler@0.26.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.6.3: {}
+
+ semver@7.7.2: {}
+
+ send@0.19.0:
+ dependencies:
+ debug: 2.6.9
+ depd: 2.0.0
+ destroy: 1.2.0
+ encodeurl: 1.0.2
+ escape-html: 1.0.3
+ etag: 1.8.1
+ fresh: 0.5.2
+ http-errors: 2.0.0
+ mime: 1.6.0
+ ms: 2.1.3
+ on-finished: 2.4.1
+ range-parser: 1.2.1
+ statuses: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ send@0.19.1:
+ dependencies:
+ debug: 2.6.9
+ depd: 2.0.0
+ destroy: 1.2.0
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ fresh: 0.5.2
+ http-errors: 2.0.0
+ mime: 1.6.0
+ ms: 2.1.3
+ on-finished: 2.4.1
+ range-parser: 1.2.1
+ statuses: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ serialize-error@2.1.0: {}
+
+ serve-static@1.16.2:
+ dependencies:
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ parseurl: 1.3.3
+ send: 0.19.0
+ transitivePeerDependencies:
+ - supports-color
+
+ server-only@0.0.1: {}
+
+ set-function-length@1.2.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+
+ set-function-name@2.0.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ functions-have-names: 1.2.3
+ has-property-descriptors: 1.0.2
+
+ set-proto@1.0.0:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+
+ setimmediate@1.0.5: {}
+
+ setprototypeof@1.2.0: {}
+
+ sf-symbols-typescript@2.1.0: {}
+
+ shallowequal@1.1.0: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ shell-quote@1.8.3: {}
+
+ side-channel-list@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.0
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
+ signal-exit@3.0.7: {}
+
+ signal-exit@4.1.0: {}
+
+ simple-plist@1.3.1:
+ dependencies:
+ bplist-creator: 0.1.0
+ bplist-parser: 0.3.1
+ plist: 3.1.0
+
+ simple-swizzle@0.2.4:
+ dependencies:
+ is-arrayish: 0.3.4
+
+ sisteransi@1.0.5: {}
+
+ slash@3.0.0: {}
+
+ slugify@1.6.6: {}
+
+ source-map-js@1.2.1: {}
+
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ source-map@0.5.7: {}
+
+ source-map@0.6.1: {}
+
+ split-on-first@1.1.0: {}
+
+ sprintf-js@1.0.3: {}
+
+ stable-hash@0.0.5: {}
+
+ stack-utils@2.0.6:
+ dependencies:
+ escape-string-regexp: 2.0.0
+
+ stackframe@1.3.4: {}
+
+ stacktrace-parser@0.1.11:
+ dependencies:
+ type-fest: 0.7.1
+
+ statuses@1.5.0: {}
+
+ statuses@2.0.1: {}
+
+ stop-iteration-iterator@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ internal-slot: 1.1.0
+
+ stream-buffers@2.2.0: {}
+
+ strict-uri-encode@2.0.0: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.2
+
+ string.prototype.matchall@4.0.12:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ internal-slot: 1.1.0
+ regexp.prototype.flags: 1.5.4
+ set-function-name: 2.0.2
+ side-channel: 1.1.0
+
+ string.prototype.repeat@1.0.0:
+ dependencies:
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+
+ string.prototype.trim@1.2.10:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-data-property: 1.1.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.0
+ es-object-atoms: 1.1.1
+ has-property-descriptors: 1.0.2
+
+ string.prototype.trimend@1.0.9:
+ dependencies:
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ string.prototype.trimstart@1.0.8:
+ dependencies:
+ call-bind: 1.0.8
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.1
+
+ strip-ansi@5.2.0:
+ dependencies:
+ ansi-regex: 4.1.1
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.1.2:
+ dependencies:
+ ansi-regex: 6.2.2
+
+ strip-bom@3.0.0: {}
+
+ strip-json-comments@2.0.1: {}
+
+ strip-json-comments@3.1.1: {}
+
+ structured-headers@0.4.1: {}
+
+ styleq@0.1.3: {}
+
+ sucrase@3.35.0:
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ commander: 4.1.1
+ glob: 10.4.5
+ lines-and-columns: 1.2.4
+ mz: 2.7.0
+ pirates: 4.0.7
+ ts-interface-checker: 0.1.13
+
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-color@8.1.1:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-hyperlinks@2.3.0:
+ dependencies:
+ has-flag: 4.0.0
+ supports-color: 7.2.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ tar@7.4.3:
+ dependencies:
+ '@isaacs/fs-minipass': 4.0.1
+ chownr: 3.0.0
+ minipass: 7.1.2
+ minizlib: 3.0.2
+ mkdirp: 3.0.1
+ yallist: 5.0.0
+
+ temp-dir@2.0.0: {}
+
+ terminal-link@2.1.1:
+ dependencies:
+ ansi-escapes: 4.3.2
+ supports-hyperlinks: 2.3.0
+
+ terser@5.44.0:
+ dependencies:
+ '@jridgewell/source-map': 0.3.11
+ acorn: 8.15.0
+ commander: 2.20.3
+ source-map-support: 0.5.21
+
+ test-exclude@6.0.0:
+ dependencies:
+ '@istanbuljs/schema': 0.1.3
+ glob: 7.2.3
+ minimatch: 3.1.2
+
+ thenify-all@1.6.0:
+ dependencies:
+ thenify: 3.3.1
+
+ thenify@3.3.1:
+ dependencies:
+ any-promise: 1.3.0
+
+ throat@5.0.0: {}
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ tmpl@1.0.5: {}
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ toidentifier@1.0.1: {}
+
+ tr46@0.0.3: {}
+
+ ts-api-utils@2.1.0(typescript@5.9.2):
+ dependencies:
+ typescript: 5.9.2
+
+ ts-interface-checker@0.1.13: {}
+
+ tsconfig-paths@3.15.0:
+ dependencies:
+ '@types/json5': 0.0.29
+ json5: 1.0.2
+ minimist: 1.2.8
+ strip-bom: 3.0.0
+
+ tslib@2.8.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ type-detect@4.0.8: {}
+
+ type-fest@0.21.3: {}
+
+ type-fest@0.7.1: {}
+
+ typed-array-buffer@1.0.3:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-typed-array: 1.1.15
+
+ typed-array-byte-length@1.0.3:
+ dependencies:
+ call-bind: 1.0.8
+ for-each: 0.3.5
+ gopd: 1.2.0
+ has-proto: 1.2.0
+ is-typed-array: 1.1.15
+
+ typed-array-byte-offset@1.0.4:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ for-each: 0.3.5
+ gopd: 1.2.0
+ has-proto: 1.2.0
+ is-typed-array: 1.1.15
+ reflect.getprototypeof: 1.0.10
+
+ typed-array-length@1.0.7:
+ dependencies:
+ call-bind: 1.0.8
+ for-each: 0.3.5
+ gopd: 1.2.0
+ is-typed-array: 1.1.15
+ possible-typed-array-names: 1.1.0
+ reflect.getprototypeof: 1.0.10
+
+ typescript@5.9.2: {}
+
+ ua-parser-js@1.0.41: {}
+
+ unbox-primitive@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ has-bigints: 1.1.0
+ has-symbols: 1.1.0
+ which-boxed-primitive: 1.1.1
+
+ undici-types@7.12.0: {}
+
+ undici@6.21.3: {}
+
+ unicode-canonical-property-names-ecmascript@2.0.1: {}
+
+ unicode-match-property-ecmascript@2.0.0:
+ dependencies:
+ unicode-canonical-property-names-ecmascript: 2.0.1
+ unicode-property-aliases-ecmascript: 2.2.0
+
+ unicode-match-property-value-ecmascript@2.2.1: {}
+
+ unicode-property-aliases-ecmascript@2.2.0: {}
+
+ unique-string@2.0.0:
+ dependencies:
+ crypto-random-string: 2.0.0
+
+ unpipe@1.0.0: {}
+
+ unrs-resolver@1.11.1:
+ dependencies:
+ napi-postinstall: 0.3.3
+ optionalDependencies:
+ '@unrs/resolver-binding-android-arm-eabi': 1.11.1
+ '@unrs/resolver-binding-android-arm64': 1.11.1
+ '@unrs/resolver-binding-darwin-arm64': 1.11.1
+ '@unrs/resolver-binding-darwin-x64': 1.11.1
+ '@unrs/resolver-binding-freebsd-x64': 1.11.1
+ '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1
+ '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1
+ '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-arm64-musl': 1.11.1
+ '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1
+ '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-x64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-x64-musl': 1.11.1
+ '@unrs/resolver-binding-wasm32-wasi': 1.11.1
+ '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1
+ '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1
+ '@unrs/resolver-binding-win32-x64-msvc': 1.11.1
+
+ update-browserslist-db@1.1.3(browserslist@4.26.2):
+ dependencies:
+ browserslist: 4.26.2
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ use-callback-ref@1.3.3(@types/react@19.1.13)(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ use-latest-callback@0.2.4(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
+ use-sidecar@1.1.3(@types/react@19.1.13)(react@19.1.0):
+ dependencies:
+ detect-node-es: 1.1.0
+ react: 19.1.0
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.1.13
+
+ use-sync-external-store@1.5.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
+ utils-merge@1.0.1: {}
+
+ uuid@7.0.3: {}
+
+ validate-npm-package-name@5.0.1: {}
+
+ vary@1.1.2: {}
+
+ vaul@1.1.2(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@radix-ui/react-dialog': 1.1.15(@types/react@19.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+
+ vlq@1.0.1: {}
+
+ walker@1.0.8:
+ dependencies:
+ makeerror: 1.0.12
+
+ warn-once@0.1.1: {}
+
+ wcwidth@1.0.1:
+ dependencies:
+ defaults: 1.0.4
+
+ webidl-conversions@3.0.1: {}
+
+ webidl-conversions@5.0.0: {}
+
+ whatwg-fetch@3.6.20: {}
+
+ whatwg-url-without-unicode@8.0.0-3:
+ dependencies:
+ buffer: 5.7.1
+ punycode: 2.3.1
+ webidl-conversions: 5.0.0
+
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
+ which-boxed-primitive@1.1.1:
+ dependencies:
+ is-bigint: 1.1.0
+ is-boolean-object: 1.2.2
+ is-number-object: 1.1.1
+ is-string: 1.1.1
+ is-symbol: 1.1.1
+
+ which-builtin-type@1.2.1:
+ dependencies:
+ call-bound: 1.0.4
+ function.prototype.name: 1.1.8
+ has-tostringtag: 1.0.2
+ is-async-function: 2.1.1
+ is-date-object: 1.1.0
+ is-finalizationregistry: 1.1.1
+ is-generator-function: 1.1.0
+ is-regex: 1.2.1
+ is-weakref: 1.1.1
+ isarray: 2.0.5
+ which-boxed-primitive: 1.1.1
+ which-collection: 1.0.2
+ which-typed-array: 1.1.19
+
+ which-collection@1.0.2:
+ dependencies:
+ is-map: 2.0.3
+ is-set: 2.0.3
+ is-weakmap: 2.0.2
+ is-weakset: 2.0.4
+
+ which-typed-array@1.1.19:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ for-each: 0.3.5
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ wonka@6.3.5: {}
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 5.1.2
+ strip-ansi: 7.1.2
+
+ wrappy@1.0.2: {}
+
+ write-file-atomic@4.0.2:
+ dependencies:
+ imurmurhash: 0.1.4
+ signal-exit: 3.0.7
+
+ ws@6.2.3:
+ dependencies:
+ async-limiter: 1.0.1
+
+ ws@7.5.10: {}
+
+ ws@8.18.3: {}
+
+ xcode@3.0.1:
+ dependencies:
+ simple-plist: 1.3.1
+ uuid: 7.0.3
+
+ xml2js@0.6.0:
+ dependencies:
+ sax: 1.4.1
+ xmlbuilder: 11.0.1
+
+ xmlbuilder@11.0.1: {}
+
+ xmlbuilder@15.1.1: {}
+
+ y18n@5.0.8: {}
+
+ yallist@3.1.1: {}
+
+ yallist@5.0.0: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ yocto-queue@0.1.0: {}
diff --git a/examples/tutorials/nhost-reactnative-tutorial/scripts/reset-project.js b/examples/tutorials/nhost-reactnative-tutorial/scripts/reset-project.js
new file mode 100755
index 000000000..f16cf44a1
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/scripts/reset-project.js
@@ -0,0 +1,112 @@
+#!/usr/bin/env node
+
+/**
+ * This script is used to reset the project to a blank state.
+ * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file.
+ * You can remove the `reset-project` script from package.json and safely delete this file after running it.
+ */
+
+const fs = require("node:fs");
+const path = require("node:path");
+const readline = require("node:readline");
+
+const root = process.cwd();
+const oldDirs = ["app", "components", "hooks", "constants", "scripts"];
+const exampleDir = "app-example";
+const newAppDir = "app";
+const exampleDirPath = path.join(root, exampleDir);
+
+const indexContent = `import { Text, View } from "react-native";
+
+export default function Index() {
+ return (
+
+ Edit app/index.tsx to edit this screen.
+
+ );
+}
+`;
+
+const layoutContent = `import { Stack } from "expo-router";
+
+export default function RootLayout() {
+ return ;
+}
+`;
+
+const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+});
+
+const moveDirectories = async (userInput) => {
+ try {
+ if (userInput === "y") {
+ // Create the app-example directory
+ await fs.promises.mkdir(exampleDirPath, { recursive: true });
+ console.log(`📁 /${exampleDir} directory created.`);
+ }
+
+ // Move old directories to new app-example directory or delete them
+ for (const dir of oldDirs) {
+ const oldDirPath = path.join(root, dir);
+ if (fs.existsSync(oldDirPath)) {
+ if (userInput === "y") {
+ const newDirPath = path.join(root, exampleDir, dir);
+ await fs.promises.rename(oldDirPath, newDirPath);
+ console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`);
+ } else {
+ await fs.promises.rm(oldDirPath, { recursive: true, force: true });
+ console.log(`❌ /${dir} deleted.`);
+ }
+ } else {
+ console.log(`➡️ /${dir} does not exist, skipping.`);
+ }
+ }
+
+ // Create new /app directory
+ const newAppDirPath = path.join(root, newAppDir);
+ await fs.promises.mkdir(newAppDirPath, { recursive: true });
+ console.log("\n📁 New /app directory created.");
+
+ // Create index.tsx
+ const indexPath = path.join(newAppDirPath, "index.tsx");
+ await fs.promises.writeFile(indexPath, indexContent);
+ console.log("📄 app/index.tsx created.");
+
+ // Create _layout.tsx
+ const layoutPath = path.join(newAppDirPath, "_layout.tsx");
+ await fs.promises.writeFile(layoutPath, layoutContent);
+ console.log("📄 app/_layout.tsx created.");
+
+ console.log("\n✅ Project reset complete. Next steps:");
+ console.log(
+ `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${
+ userInput === "y"
+ ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.`
+ : ""
+ }`,
+ );
+ } catch (error) {
+ console.error(`❌ Error during script execution: ${error.message}`);
+ }
+};
+
+rl.question(
+ "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ",
+ (answer) => {
+ const userInput = answer.trim().toLowerCase() || "y";
+ if (userInput === "y" || userInput === "n") {
+ moveDirectories(userInput).finally(() => rl.close());
+ } else {
+ console.log("❌ Invalid input. Please enter 'Y' or 'N'.");
+ rl.close();
+ }
+ },
+);
diff --git a/examples/tutorials/nhost-reactnative-tutorial/tsconfig.json b/examples/tutorials/nhost-reactnative-tutorial/tsconfig.json
new file mode 100644
index 000000000..ce27fee39
--- /dev/null
+++ b/examples/tutorials/nhost-reactnative-tutorial/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "expo/tsconfig.base",
+ "compilerOptions": {
+ "strict": true,
+ "paths": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
+}
diff --git a/examples/tutorials/nhost-svelte-tutorial/.gitignore b/examples/tutorials/nhost-svelte-tutorial/.gitignore
new file mode 100644
index 000000000..3b462cb0c
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/.gitignore
@@ -0,0 +1,23 @@
+node_modules
+
+# Output
+.output
+.vercel
+.netlify
+.wrangler
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/examples/tutorials/nhost-svelte-tutorial/.npmrc b/examples/tutorials/nhost-svelte-tutorial/.npmrc
new file mode 100644
index 000000000..b6f27f135
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/examples/tutorials/nhost-svelte-tutorial/README.md b/examples/tutorials/nhost-svelte-tutorial/README.md
new file mode 100644
index 000000000..4c7551866
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/README.md
@@ -0,0 +1,3 @@
+This is the result of following the tutorial at:
+
+https://docs.nhost.io/getting-started/tutorials/svelte/1-introduction
diff --git a/examples/tutorials/nhost-svelte-tutorial/package.json b/examples/tutorials/nhost-svelte-tutorial/package.json
new file mode 100644
index 000000000..0437a26fe
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "nhost-svelte-tutorial",
+ "private": true,
+ "version": "0.0.1",
+ "type": "module",
+ "scripts": {
+ "dev": "vite dev",
+ "build": "pnpm sync && vite build",
+ "preview": "vite preview",
+ "generate": "echo 'Nothing to do'",
+ "test": "pnpm sync && pnpm test:typecheck && pnpm test:lint && pnpm check",
+ "test:typecheck": "tsc --noEmit",
+ "test:lint": "biome check",
+ "format": "biome format --write",
+ "sync": "svelte-kit sync",
+ "check": "svelte-check --tsconfig ./tsconfig.json",
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
+ },
+ "dependencies": {
+ "@nhost/nhost-js": "workspace:^"
+ },
+ "devDependencies": {
+ "@sveltejs/adapter-auto": "^6.0.0",
+ "@sveltejs/kit": "^2.22.0",
+ "@sveltejs/vite-plugin-svelte": "^6.0.0",
+ "svelte": "^5.0.0",
+ "svelte-check": "^4.0.0",
+ "typescript": "^5.0.0",
+ "vite": "^7.0.4"
+ }
+}
diff --git a/examples/tutorials/nhost-svelte-tutorial/pnpm-lock.yaml b/examples/tutorials/nhost-svelte-tutorial/pnpm-lock.yaml
new file mode 100644
index 000000000..5c301710f
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/pnpm-lock.yaml
@@ -0,0 +1,980 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@nhost/nhost-js':
+ specifier: workspace:^
+ version: link:../../../packages/nhost-js
+ devDependencies:
+ '@sveltejs/adapter-auto':
+ specifier: ^6.0.0
+ version: 6.1.0(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5))
+ '@sveltejs/kit':
+ specifier: ^2.22.0
+ version: 2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)
+ '@sveltejs/vite-plugin-svelte':
+ specifier: ^6.0.0
+ version: 6.2.0(svelte@5.38.10)(vite@7.1.5)
+ svelte:
+ specifier: ^5.0.0
+ version: 5.38.10
+ svelte-check:
+ specifier: ^4.0.0
+ version: 4.3.1(picomatch@4.0.3)(svelte@5.38.10)(typescript@5.9.2)
+ typescript:
+ specifier: ^5.0.0
+ version: 5.9.2
+ vite:
+ specifier: ^7.0.4
+ version: 7.1.5
+
+packages:
+
+ '@esbuild/aix-ppc64@0.25.9':
+ resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.9':
+ resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.9':
+ resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.9':
+ resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.9':
+ resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.9':
+ resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.9':
+ resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.9':
+ resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.9':
+ resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.9':
+ resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.9':
+ resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.9':
+ resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.9':
+ resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.9':
+ resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.9':
+ resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.9':
+ resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.9':
+ resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.9':
+ resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.9':
+ resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.9':
+ resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.9':
+ resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.9':
+ resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.25.9':
+ resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.9':
+ resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.9':
+ resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.9':
+ resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@polka/url@1.0.0-next.29':
+ resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
+ '@rollup/rollup-android-arm-eabi@4.50.2':
+ resolution: {integrity: sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.50.2':
+ resolution: {integrity: sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.50.2':
+ resolution: {integrity: sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.50.2':
+ resolution: {integrity: sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.50.2':
+ resolution: {integrity: sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.50.2':
+ resolution: {integrity: sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.50.2':
+ resolution: {integrity: sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.50.2':
+ resolution: {integrity: sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.50.2':
+ resolution: {integrity: sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.50.2':
+ resolution: {integrity: sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.50.2':
+ resolution: {integrity: sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.50.2':
+ resolution: {integrity: sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.50.2':
+ resolution: {integrity: sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.50.2':
+ resolution: {integrity: sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.50.2':
+ resolution: {integrity: sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.50.2':
+ resolution: {integrity: sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.50.2':
+ resolution: {integrity: sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openharmony-arm64@4.50.2':
+ resolution: {integrity: sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.50.2':
+ resolution: {integrity: sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.50.2':
+ resolution: {integrity: sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.50.2':
+ resolution: {integrity: sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==}
+ cpu: [x64]
+ os: [win32]
+
+ '@standard-schema/spec@1.0.0':
+ resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
+
+ '@sveltejs/acorn-typescript@1.0.5':
+ resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==}
+ peerDependencies:
+ acorn: ^8.9.0
+
+ '@sveltejs/adapter-auto@6.1.0':
+ resolution: {integrity: sha512-shOuLI5D2s+0zTv2ab5M5PqfknXqWbKi+0UwB9yLTRIdzsK1R93JOO8jNhIYSHdW+IYXIYnLniu+JZqXs7h9Wg==}
+ peerDependencies:
+ '@sveltejs/kit': ^2.0.0
+
+ '@sveltejs/kit@2.41.0':
+ resolution: {integrity: sha512-dCLIRufAoc3bbKtHCSzDr3g41sPY0bNpxYbMd6XD2D7xSeVLgPvZ0qFtK8W2hhwr7oj8i/6NtlcB0EKFjpMMUg==}
+ engines: {node: '>=18.13'}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.0.0
+ '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0
+ svelte: ^4.0.0 || ^5.0.0-next.0
+ vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.1':
+ resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==}
+ engines: {node: ^20.19 || ^22.12 || >=24}
+ peerDependencies:
+ '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0
+ svelte: ^5.0.0
+ vite: ^6.3.0 || ^7.0.0
+
+ '@sveltejs/vite-plugin-svelte@6.2.0':
+ resolution: {integrity: sha512-nJsV36+o7rZUDlrnSduMNl11+RoDE1cKqOI0yUEBCcqFoAZOk47TwD3dPKS2WmRutke9StXnzsPBslY7prDM9w==}
+ engines: {node: ^20.19 || ^22.12 || >=24}
+ peerDependencies:
+ svelte: ^5.0.0
+ vite: ^6.3.0 || ^7.0.0
+
+ '@types/cookie@0.6.0':
+ resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ acorn@8.15.0:
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ axobject-query@4.1.0:
+ resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
+ engines: {node: '>= 0.4'}
+
+ chokidar@4.0.3:
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+ engines: {node: '>= 14.16.0'}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ cookie@0.6.0:
+ resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
+ engines: {node: '>= 0.6'}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
+ devalue@5.3.2:
+ resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==}
+
+ esbuild@0.25.9:
+ resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ esm-env@1.2.2:
+ resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
+
+ esrap@2.1.0:
+ resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ is-reference@3.0.3:
+ resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
+
+ kleur@4.1.5:
+ resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
+ engines: {node: '>=6'}
+
+ locate-character@3.0.0:
+ resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
+
+ magic-string@0.30.19:
+ resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
+
+ mri@1.2.0:
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+ engines: {node: '>=4'}
+
+ mrmime@2.0.1:
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+ engines: {node: '>=10'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ readdirp@4.1.2:
+ resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+ engines: {node: '>= 14.18.0'}
+
+ rollup@4.50.2:
+ resolution: {integrity: sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ sade@1.8.1:
+ resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
+ engines: {node: '>=6'}
+
+ set-cookie-parser@2.7.1:
+ resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
+
+ sirv@3.0.2:
+ resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
+ engines: {node: '>=18'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ svelte-check@4.3.1:
+ resolution: {integrity: sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg==}
+ engines: {node: '>= 18.0.0'}
+ hasBin: true
+ peerDependencies:
+ svelte: ^4.0.0 || ^5.0.0-next.0
+ typescript: '>=5.0.0'
+
+ svelte@5.38.10:
+ resolution: {integrity: sha512-UY+OhrWK7WI22bCZ00P/M3HtyWgwJPi9IxSRkoAE2MeAy6kl7ZlZWJZ8RaB+X4KD/G+wjis+cGVnVYaoqbzBqg==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ totalist@3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+
+ typescript@5.9.2:
+ resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ vite@7.1.5:
+ resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vitefu@1.1.1:
+ resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==}
+ peerDependencies:
+ vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0
+ peerDependenciesMeta:
+ vite:
+ optional: true
+
+ zimmerframe@1.1.4:
+ resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==}
+
+snapshots:
+
+ '@esbuild/aix-ppc64@0.25.9':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/android-arm@0.25.9':
+ optional: true
+
+ '@esbuild/android-x64@0.25.9':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.9':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.9':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.9':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.9':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.9':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.9':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.9':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.9':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.9':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.9':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.9':
+ optional: true
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@polka/url@1.0.0-next.29': {}
+
+ '@rollup/rollup-android-arm-eabi@4.50.2':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.50.2':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.50.2':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.50.2':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.50.2':
+ optional: true
+
+ '@standard-schema/spec@1.0.0': {}
+
+ '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)':
+ dependencies:
+ acorn: 8.15.0
+
+ '@sveltejs/adapter-auto@6.1.0(@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5))':
+ dependencies:
+ '@sveltejs/kit': 2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)
+
+ '@sveltejs/kit@2.41.0(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)':
+ dependencies:
+ '@standard-schema/spec': 1.0.0
+ '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
+ '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5)
+ '@types/cookie': 0.6.0
+ acorn: 8.15.0
+ cookie: 0.6.0
+ devalue: 5.3.2
+ esm-env: 1.2.2
+ kleur: 4.1.5
+ magic-string: 0.30.19
+ mrmime: 2.0.1
+ sade: 1.8.1
+ set-cookie-parser: 2.7.1
+ sirv: 3.0.2
+ svelte: 5.38.10
+ vite: 7.1.5
+
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte': 6.2.0(svelte@5.38.10)(vite@7.1.5)
+ debug: 4.4.3
+ svelte: 5.38.10
+ vite: 7.1.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5)':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.0(svelte@5.38.10)(vite@7.1.5))(svelte@5.38.10)(vite@7.1.5)
+ debug: 4.4.3
+ deepmerge: 4.3.1
+ magic-string: 0.30.19
+ svelte: 5.38.10
+ vite: 7.1.5
+ vitefu: 1.1.1(vite@7.1.5)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@types/cookie@0.6.0': {}
+
+ '@types/estree@1.0.8': {}
+
+ acorn@8.15.0: {}
+
+ aria-query@5.3.2: {}
+
+ axobject-query@4.1.0: {}
+
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
+ clsx@2.1.1: {}
+
+ cookie@0.6.0: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ deepmerge@4.3.1: {}
+
+ devalue@5.3.2: {}
+
+ esbuild@0.25.9:
+ 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
+
+ esm-env@1.2.2: {}
+
+ esrap@2.1.0:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ fsevents@2.3.3:
+ optional: true
+
+ is-reference@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
+ kleur@4.1.5: {}
+
+ locate-character@3.0.0: {}
+
+ magic-string@0.30.19:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ mri@1.2.0: {}
+
+ mrmime@2.0.1: {}
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.11: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.3: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ readdirp@4.1.2: {}
+
+ rollup@4.50.2:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.50.2
+ '@rollup/rollup-android-arm64': 4.50.2
+ '@rollup/rollup-darwin-arm64': 4.50.2
+ '@rollup/rollup-darwin-x64': 4.50.2
+ '@rollup/rollup-freebsd-arm64': 4.50.2
+ '@rollup/rollup-freebsd-x64': 4.50.2
+ '@rollup/rollup-linux-arm-gnueabihf': 4.50.2
+ '@rollup/rollup-linux-arm-musleabihf': 4.50.2
+ '@rollup/rollup-linux-arm64-gnu': 4.50.2
+ '@rollup/rollup-linux-arm64-musl': 4.50.2
+ '@rollup/rollup-linux-loong64-gnu': 4.50.2
+ '@rollup/rollup-linux-ppc64-gnu': 4.50.2
+ '@rollup/rollup-linux-riscv64-gnu': 4.50.2
+ '@rollup/rollup-linux-riscv64-musl': 4.50.2
+ '@rollup/rollup-linux-s390x-gnu': 4.50.2
+ '@rollup/rollup-linux-x64-gnu': 4.50.2
+ '@rollup/rollup-linux-x64-musl': 4.50.2
+ '@rollup/rollup-openharmony-arm64': 4.50.2
+ '@rollup/rollup-win32-arm64-msvc': 4.50.2
+ '@rollup/rollup-win32-ia32-msvc': 4.50.2
+ '@rollup/rollup-win32-x64-msvc': 4.50.2
+ fsevents: 2.3.3
+
+ sade@1.8.1:
+ dependencies:
+ mri: 1.2.0
+
+ set-cookie-parser@2.7.1: {}
+
+ sirv@3.0.2:
+ dependencies:
+ '@polka/url': 1.0.0-next.29
+ mrmime: 2.0.1
+ totalist: 3.0.1
+
+ source-map-js@1.2.1: {}
+
+ svelte-check@4.3.1(picomatch@4.0.3)(svelte@5.38.10)(typescript@5.9.2):
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ chokidar: 4.0.3
+ fdir: 6.5.0(picomatch@4.0.3)
+ picocolors: 1.1.1
+ sade: 1.8.1
+ svelte: 5.38.10
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - picomatch
+
+ svelte@5.38.10:
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
+ '@types/estree': 1.0.8
+ acorn: 8.15.0
+ aria-query: 5.3.2
+ axobject-query: 4.1.0
+ clsx: 2.1.1
+ esm-env: 1.2.2
+ esrap: 2.1.0
+ is-reference: 3.0.3
+ locate-character: 3.0.0
+ magic-string: 0.30.19
+ zimmerframe: 1.1.4
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ totalist@3.0.1: {}
+
+ typescript@5.9.2: {}
+
+ vite@7.1.5:
+ dependencies:
+ esbuild: 0.25.9
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.50.2
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ vitefu@1.1.1(vite@7.1.5):
+ optionalDependencies:
+ vite: 7.1.5
+
+ zimmerframe@1.1.4: {}
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/app.css b/examples/tutorials/nhost-svelte-tutorial/src/app.css
new file mode 100644
index 000000000..33d33be4f
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/app.css
@@ -0,0 +1,864 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+#root {
+ width: 100%;
+ min-height: 100vh;
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+
+a:hover {
+ color: #535bf2;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+
+button:hover {
+ border-color: #646cff;
+}
+
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+input,
+textarea {
+ width: 100%;
+ padding: 0.875rem 1rem;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 8px;
+ font-size: 0.875rem;
+ transition: all 0.2s ease;
+ background: rgba(255, 255, 255, 0.05);
+ color: white;
+ box-sizing: border-box;
+ font-family: inherit;
+}
+
+input:focus,
+textarea:focus {
+ outline: none;
+ border-color: #3b82f6;
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
+ background: rgba(255, 255, 255, 0.08);
+}
+
+input::placeholder,
+textarea::placeholder {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+textarea {
+ resize: vertical;
+ min-height: 4rem;
+}
+
+label {
+ display: block;
+ margin: 0 0 0.5rem 0;
+ font-weight: 500;
+ color: rgba(255, 255, 255, 0.9);
+ font-size: 0.875rem;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+/* Global Layout */
+.app-content {
+ padding: 0 2rem 2rem;
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+.page-center {
+ text-align: center;
+ padding: 2rem;
+}
+
+.page-header {
+ margin-bottom: 2rem;
+}
+
+.page-title {
+ font-weight: 700;
+ margin: 0;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.margin-bottom {
+ margin-bottom: 1rem;
+}
+
+.margin-top {
+ margin-top: 1rem;
+}
+
+.container {
+ width: 800px;
+ max-width: calc(100vw - 4rem);
+ min-width: 320px;
+ margin: 0 auto;
+ padding: 2rem;
+ box-sizing: border-box;
+ position: relative;
+}
+
+/* Status Messages */
+.success-message {
+ padding: 1rem;
+ background-color: #d4edda;
+ color: #155724;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+}
+
+.error-message {
+ background: rgba(239, 68, 68, 0.1);
+ color: #ef4444;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ border-radius: 12px;
+ padding: 1rem 1.5rem;
+ margin: 1rem 0;
+}
+
+.help-text {
+ color: #666;
+}
+
+.verification-status {
+ color: #28a745;
+ font-size: 1.2rem;
+ font-weight: bold;
+ margin-bottom: 1rem;
+}
+
+.verification-status.error {
+ color: #dc3545;
+ font-size: 1.1rem;
+}
+
+/* Email Verification Status */
+.email-verified {
+ color: #10b981;
+ font-weight: bold;
+ margin-left: 0.5rem;
+}
+
+.email-unverified {
+ color: #ef4444;
+ font-weight: bold;
+ margin-left: 0.5rem;
+}
+
+/* Debug Info */
+.debug-panel {
+ margin-bottom: 1rem;
+ padding: 1rem;
+ background-color: #f8f9fa;
+ border-radius: 8px;
+ text-align: left;
+ max-height: 200px;
+ overflow: auto;
+}
+
+.debug-title {
+ font-weight: bold;
+ margin-bottom: 0.5rem;
+}
+
+.debug-item {
+ margin-bottom: 0.25rem;
+}
+
+.debug-key {
+ font-family: monospace;
+ color: #007bff;
+}
+
+.debug-value {
+ font-family: monospace;
+}
+
+/* Session Display */
+.session-display {
+ font-size: 0.75rem;
+ overflow: auto;
+ margin: 0;
+ line-height: 1.4;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+/* Loading Spinner */
+.spinner-verify {
+ width: 32px;
+ height: 32px;
+ border: 3px solid #f3f3f3;
+ border-top: 3px solid #007bff;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin: 0 auto;
+}
+
+/* Navigation */
+.navigation {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ margin-bottom: 2rem;
+}
+
+.nav-container {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 1rem 2rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.nav-logo {
+ font-size: 1.25rem;
+ font-weight: 700;
+ color: white;
+ text-decoration: none;
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.nav-logo:hover {
+ opacity: 0.8;
+}
+
+.nav-links {
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+}
+
+.nav-link {
+ color: rgba(255, 255, 255, 0.8);
+ text-decoration: none;
+ font-weight: 500;
+ font-size: 0.875rem;
+ padding: 0.5rem 0.75rem;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+ border: none;
+ background: none;
+ cursor: pointer;
+ font-family: inherit;
+}
+
+.nav-link:hover {
+ color: white;
+ background: rgba(255, 255, 255, 0.1);
+}
+
+.nav-button {
+ color: #ef4444;
+}
+
+.nav-button:hover {
+ background: rgba(239, 68, 68, 0.2);
+}
+
+/* Buttons */
+.btn {
+ padding: 0.75rem 1.5rem;
+ border: none;
+ border-radius: 8px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ font-size: 0.875rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ min-width: 120px;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ color: white;
+}
+
+.btn-primary:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
+}
+
+.btn-secondary {
+ background: rgba(255, 255, 255, 0.1);
+ color: white;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.btn-secondary:hover {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+.btn-cancel {
+ background: rgba(239, 68, 68, 0.1);
+ color: #ef4444;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+}
+
+.btn-cancel:hover {
+ background: rgba(239, 68, 68, 0.2);
+}
+
+/* Loading State */
+.loading-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 4rem 2rem;
+}
+
+.loading-content {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.spinner {
+ width: 2rem;
+ height: 2rem;
+ border: 3px solid rgba(59, 130, 246, 0.3);
+ border-top: 3px solid #3b82f6;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+.loading-text {
+ color: rgba(255, 255, 255, 0.7);
+ font-size: 0.875rem;
+}
+
+/* Empty State */
+.empty-state {
+ text-align: center;
+ padding: 4rem 2rem;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+}
+
+.empty-icon {
+ width: 4rem;
+ height: 4rem;
+ color: rgba(255, 255, 255, 0.4);
+ margin: 0 auto 1rem;
+}
+
+.empty-title {
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: white;
+ margin: 0 0 0.5rem 0;
+}
+
+.empty-description {
+ color: rgba(255, 255, 255, 0.6);
+ margin: 0;
+}
+
+/* Forms */
+.form-card {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+ padding: 2rem;
+ margin-bottom: 2rem;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.form-title {
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: white;
+ margin: 0 0 1.5rem 0;
+}
+
+.form-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.field-group {
+ display: flex;
+ flex-direction: column;
+}
+
+.form-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1rem;
+}
+
+/* Auth Pages */
+.auth-form {
+ max-width: 400px;
+}
+
+.auth-form-field {
+ margin-bottom: 1rem;
+}
+
+.auth-input {
+ width: 100%;
+ padding: 0.5rem;
+ margin-top: 0.25rem;
+}
+
+.auth-error {
+ color: red;
+ margin-bottom: 1rem;
+ padding: 0.5rem;
+ background-color: #fee;
+ border-radius: 4px;
+}
+
+.auth-button {
+ width: 100%;
+ padding: 0.75rem;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.auth-button:disabled {
+ cursor: not-allowed;
+}
+
+.auth-button.primary {
+ background-color: #28a745;
+}
+
+.auth-button.secondary {
+ background-color: #007bff;
+}
+
+.auth-links {
+ margin-top: 1rem;
+}
+
+/* Todos */
+
+.todo-form {
+ width: 100%;
+}
+
+/* Todo List */
+.todos-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.todo-card {
+ background: rgba(255, 255, 255, 0.03);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ transition: all 0.2s ease;
+ overflow: hidden;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.todo-card:hover {
+ border-color: rgba(255, 255, 255, 0.2);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+}
+
+.todo-card.completed {
+ opacity: 0.7;
+}
+
+/* Todo Content */
+.todo-content {
+ padding: 1rem 1.5rem;
+}
+
+.todo-edit {
+ padding: 1.5rem;
+ min-height: 200px;
+}
+
+.edit-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.edit-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1.5rem;
+}
+
+.todo-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+}
+
+.todo-title-btn {
+ background: none;
+ border: none;
+ padding: 0;
+ text-align: left;
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: white;
+ cursor: pointer;
+ transition: color 0.2s ease;
+ flex: 1;
+ line-height: 1.4;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ max-width: calc(100% - 140px);
+}
+
+.todo-title-btn:hover {
+ color: #3b82f6;
+}
+
+.todo-title-btn.completed {
+ text-decoration: line-through;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.todo-actions {
+ display: flex;
+ gap: 0.5rem;
+ flex-shrink: 0;
+ min-width: 132px;
+ justify-content: flex-end;
+}
+
+/* Action Buttons */
+.action-btn {
+ width: 40px;
+ height: 40px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ background: rgba(255, 255, 255, 0.05);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ transition: all 0.2s ease;
+ -webkit-text-fill-color: currentColor;
+}
+
+.action-btn-complete {
+ color: #10b981;
+ font-size: 20px;
+}
+
+.action-btn-complete:hover {
+ background: rgba(16, 185, 129, 0.2);
+ color: #34d399;
+}
+
+.action-btn-edit {
+ color: #3b82f6;
+}
+
+.action-btn-edit:hover {
+ background: rgba(59, 130, 246, 0.2);
+ color: #60a5fa;
+}
+
+.action-btn-delete {
+ color: #ef4444;
+}
+
+.action-btn-delete:hover {
+ background: rgba(239, 68, 68, 0.2);
+ color: #f87171;
+}
+
+/* Add Todo Button */
+.add-todo-btn {
+ width: 36px;
+ height: 36px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-size: 18px;
+ font-weight: normal;
+ -webkit-text-fill-color: white;
+ transition: all 0.2s ease;
+}
+
+.add-todo-btn:hover {
+ transform: scale(1.1);
+ box-shadow: 0 4px 20px rgba(59, 130, 246, 0.4);
+}
+
+/* Todo Details */
+.todo-details {
+ margin-top: 1rem;
+ padding-top: 1rem;
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.description {
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 8px;
+ padding: 1rem;
+ margin-bottom: 1rem;
+}
+
+.description p {
+ margin: 0;
+ color: rgba(255, 255, 255, 0.8);
+ line-height: 1.6;
+}
+
+.description.completed p {
+ text-decoration: line-through;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.todo-meta {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+}
+
+.meta-dates {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.meta-item {
+ font-size: 0.75rem;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.completion-badge {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ font-size: 0.75rem;
+ color: #10b981;
+ font-weight: 500;
+}
+
+.completion-icon {
+ width: 0.875rem;
+ height: 0.875rem;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .nav-container {
+ padding: 1rem;
+ flex-direction: column;
+ gap: 1rem;
+ }
+
+ .nav-links {
+ gap: 1rem;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+
+ .container {
+ padding: 1rem;
+ }
+
+ .form-actions {
+ flex-direction: column;
+ }
+
+ .edit-actions {
+ flex-direction: column;
+ }
+
+ .todo-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1rem;
+ }
+
+ .todo-actions {
+ align-self: stretch;
+ justify-content: center;
+ }
+
+ .meta-dates {
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+
+ .todo-meta {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ }
+}
+
+/* File Upload */
+.file-upload-btn {
+ min-height: 120px;
+ flex-direction: column;
+ gap: 0.5rem;
+ width: 100%;
+ border: 2px dashed rgba(255, 255, 255, 0.3);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.file-upload-btn:hover {
+ border-color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.file-upload-info {
+ margin-top: 0.5rem;
+ font-size: 0.875rem;
+ color: rgba(255, 255, 255, 0.8);
+}
+
+/* File Table */
+.file-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.file-table th {
+ padding: 0.75rem;
+ text-align: left;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 500;
+ font-size: 0.875rem;
+}
+
+.file-table th:last-child {
+ text-align: center;
+}
+
+.file-table td {
+ padding: 0.75rem;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.file-table tr:hover {
+ background-color: rgba(255, 255, 255, 0.02);
+}
+
+.file-name {
+ color: white;
+ font-weight: 500;
+}
+
+.file-meta {
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 0.875rem;
+}
+
+.file-actions {
+ display: flex;
+ gap: 0.5rem;
+ justify-content: center;
+}
+
+/* Responsive File Table */
+@media (max-width: 768px) {
+ .file-table {
+ font-size: 0.875rem;
+ }
+
+ .file-table th,
+ .file-table td {
+ padding: 0.5rem;
+ }
+
+ .file-actions {
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+}
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/app.d.ts b/examples/tutorials/nhost-svelte-tutorial/src/app.d.ts
new file mode 100644
index 000000000..520c4217a
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/app.d.ts
@@ -0,0 +1,13 @@
+// See https://svelte.dev/docs/kit/types#app.d.ts
+// for information about these interfaces
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/app.html b/examples/tutorials/nhost-svelte-tutorial/src/app.html
new file mode 100644
index 000000000..f273cc58f
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/app.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ %sveltekit.head%
+
+
+ %sveltekit.body%
+
+
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/lib/assets/favicon.svg b/examples/tutorials/nhost-svelte-tutorial/src/lib/assets/favicon.svg
new file mode 100644
index 000000000..cc5dc66a3
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/lib/assets/favicon.svg
@@ -0,0 +1 @@
+svelte-logo
\ No newline at end of file
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/lib/index.ts b/examples/tutorials/nhost-svelte-tutorial/src/lib/index.ts
new file mode 100644
index 000000000..856f2b6c3
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/lib/index.ts
@@ -0,0 +1 @@
+// place files you want to import through the `$lib` alias in this folder.
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/lib/nhost/auth.ts b/examples/tutorials/nhost-svelte-tutorial/src/lib/nhost/auth.ts
new file mode 100644
index 000000000..d0291521b
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/lib/nhost/auth.ts
@@ -0,0 +1,119 @@
+import { createClient, type NhostClient } from "@nhost/nhost-js";
+import type { Session } from "@nhost/nhost-js/auth";
+import { derived, type Readable, writable } from "svelte/store";
+import { browser } from "$app/environment";
+
+/**
+ * Authentication store interface providing access to user session state and Nhost client.
+ * Used throughout the SvelteKit application to access authentication-related data and operations.
+ */
+interface AuthStore {
+ /** Current authenticated user object, null if not authenticated */
+ user: Session["user"] | null;
+ /** Current session object containing tokens and user data, null if no active session */
+ session: Session | null;
+ /** Boolean indicating if user is currently authenticated */
+ isAuthenticated: boolean;
+ /** Boolean indicating if authentication state is still loading */
+ isLoading: boolean;
+ /** Nhost client instance for making authenticated requests */
+ nhost: NhostClient;
+}
+
+// Initialize Nhost client with default SessionStorage (local storage)
+export const nhost = createClient({
+ region: import.meta.env.VITE_NHOST_REGION || "local",
+ subdomain: import.meta.env.VITE_NHOST_SUBDOMAIN || "local",
+});
+
+// Create writable stores for authentication state
+const userStore = writable(null);
+const sessionStore = writable(null);
+const isLoadingStore = writable(true);
+
+// Derived store for authentication status
+export const isAuthenticated = derived(sessionStore, ($session) => !!$session);
+
+// Combined auth store
+export const auth: Readable = derived(
+ [userStore, sessionStore, isLoadingStore, isAuthenticated],
+ ([$user, $session, $isLoading, $isAuthenticated]) => ({
+ user: $user,
+ session: $session,
+ isAuthenticated: $isAuthenticated,
+ isLoading: $isLoading,
+ nhost,
+ }),
+);
+
+// Individual store exports for convenience
+export const user = userStore;
+export const session = sessionStore;
+export const isLoading = isLoadingStore;
+
+let lastRefreshTokenId: string | null = null;
+
+/**
+ * Handles session reload when refresh token changes.
+ * This detects when the session has been updated from other tabs.
+ *
+ * @param currentRefreshTokenId - The current refresh token ID to compare against stored value
+ */
+function reloadSession(currentRefreshTokenId: string | null) {
+ if (currentRefreshTokenId !== lastRefreshTokenId) {
+ lastRefreshTokenId = currentRefreshTokenId;
+
+ // Update local authentication state to match current session
+ const currentSession = nhost.getUserSession();
+ userStore.set(currentSession?.user || null);
+ sessionStore.set(currentSession);
+ }
+}
+
+/**
+ * Initialize authentication state and set up cross-tab session synchronization.
+ * This function should be called once when the application starts (browser only).
+ */
+export function initializeAuth() {
+ if (!browser) return;
+
+ isLoadingStore.set(true);
+
+ // Load initial session state from Nhost client
+ const currentSession = nhost.getUserSession();
+ userStore.set(currentSession?.user || null);
+ sessionStore.set(currentSession);
+ lastRefreshTokenId = currentSession?.refreshTokenId ?? null;
+ isLoadingStore.set(false);
+
+ // Subscribe to session changes from other browser tabs
+ // This enables real-time synchronization when user signs in/out in another tab
+ const unsubscribe = nhost.sessionStorage.onChange((session) => {
+ reloadSession(session?.refreshTokenId ?? null);
+ });
+
+ /**
+ * Checks for session changes when page becomes visible or focused.
+ * Provides additional consistency checks for session state.
+ */
+ const checkSessionOnFocus = () => {
+ reloadSession(nhost.getUserSession()?.refreshTokenId ?? null);
+ };
+
+ // Monitor page visibility changes (tab switching, window minimizing)
+ document.addEventListener("visibilitychange", () => {
+ if (!document.hidden) {
+ checkSessionOnFocus();
+ }
+ });
+
+ // Monitor window focus events (clicking back into the browser window)
+ window.addEventListener("focus", checkSessionOnFocus);
+
+ // Return cleanup function
+ return () => {
+ unsubscribe();
+ document.removeEventListener("visibilitychange", checkSessionOnFocus);
+ window.removeEventListener("focus", checkSessionOnFocus);
+ };
+}
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/routes/+layout.svelte b/examples/tutorials/nhost-svelte-tutorial/src/routes/+layout.svelte
new file mode 100644
index 000000000..b3176ffc3
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/routes/+layout.svelte
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+ {#if children}
+ {@render children()}
+ {/if}
+
+
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/routes/+page.svelte b/examples/tutorials/nhost-svelte-tutorial/src/routes/+page.svelte
new file mode 100644
index 000000000..0be3e5bdc
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/routes/+page.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+
+ {#if $auth.isAuthenticated}
+
+
Hello, {$auth.user?.displayName || $auth.user?.email}!
+
+ {:else}
+
+
You are not signed in.
+
+ {/if}
+
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/routes/files/+page.svelte b/examples/tutorials/nhost-svelte-tutorial/src/routes/files/+page.svelte
new file mode 100644
index 000000000..935f932e7
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/routes/files/+page.svelte
@@ -0,0 +1,403 @@
+
+
+{#if !$auth.session}
+
+
Please sign in to access file uploads.
+
+{:else}
+
+{/if}
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/routes/profile/+page.svelte b/examples/tutorials/nhost-svelte-tutorial/src/routes/profile/+page.svelte
new file mode 100644
index 000000000..4e3e99996
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/routes/profile/+page.svelte
@@ -0,0 +1,64 @@
+
+
+{#if $auth.isLoading}
+
+{:else if $auth.isAuthenticated}
+
+{:else}
+
+
+
Access Denied
+
You must be signed in to view this page.
+
+
+{/if}
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/routes/signin/+page.svelte b/examples/tutorials/nhost-svelte-tutorial/src/routes/signin/+page.svelte
new file mode 100644
index 000000000..e3a1a95d6
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/routes/signin/+page.svelte
@@ -0,0 +1,92 @@
+
+
+
+
Sign In
+
+
+
+ Email
+
+
+
+
+ Password
+
+
+
+ {#if error}
+
+ {error}
+
+ {/if}
+
+
+ {isLoading ? "Signing In..." : "Sign In"}
+
+
+
+
+
+ Don't have an account? Sign Up
+
+
+
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/routes/signup/+page.svelte b/examples/tutorials/nhost-svelte-tutorial/src/routes/signup/+page.svelte
new file mode 100644
index 000000000..5a8e00cdf
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/routes/signup/+page.svelte
@@ -0,0 +1,130 @@
+
+
+{#if success}
+
+
Check Your Email
+
+
+ We've sent a verification link to {email}
+
+
+ Please check your email and click the verification link to activate your account.
+
+
+
+ Back to Sign In
+
+
+{:else}
+
+
Sign Up
+
+
+
+ Display Name
+
+
+
+
+ Email
+
+
+
+
+ Password
+
+ Minimum 8 characters
+
+
+ {#if error}
+
+ {error}
+
+ {/if}
+
+
+ {isLoading ? "Creating Account..." : "Sign Up"}
+
+
+
+
+
+ Already have an account? Sign In
+
+
+
+{/if}
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/routes/todos/+page.svelte b/examples/tutorials/nhost-svelte-tutorial/src/routes/todos/+page.svelte
new file mode 100644
index 000000000..c8fd945ce
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/routes/todos/+page.svelte
@@ -0,0 +1,478 @@
+
+
+{#if !$auth.session}
+
+
Please sign in to view your todos.
+
+{:else}
+
+
+
+ {#if error}
+
+ Error: {error}
+
+ {/if}
+
+ {#if showAddForm}
+
+ {/if}
+
+ {#if !showAddForm}
+ {#if loading}
+
+ {:else}
+
+ {#if todos.length === 0}
+
+
+
+
+
No todos yet
+
+ Create your first todo to get started!
+
+
+ {:else}
+ {#each todos as todo (todo.id)}
+
+ {#if editingTodo?.id === todo.id}
+
+
+
+ Title
+
+
+
+ Details
+
+
+
+
+ ✓ Save Changes
+
+ (editingTodo = null)}
+ class="btn btn-cancel"
+ >
+ ✕ Cancel
+
+
+
+
+ {:else}
+
+
+
+ {#if expandedTodos.has(todo.id)}
+
+ {#if todo.details}
+
+ {/if}
+
+
+
+ {/if}
+
+ {/if}
+
+ {/each}
+ {/if}
+
+ {/if}
+ {/if}
+
+{/if}
diff --git a/examples/tutorials/nhost-svelte-tutorial/src/routes/verify/+page.svelte b/examples/tutorials/nhost-svelte-tutorial/src/routes/verify/+page.svelte
new file mode 100644
index 000000000..0bbcd6f8a
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/src/routes/verify/+page.svelte
@@ -0,0 +1,153 @@
+
+
+
+
Email Verification
+
+
+ {#if status === "verifying"}
+
+
Verifying your email...
+
+
+ {/if}
+
+ {#if status === "success"}
+
+
+ ✓ Successfully verified!
+
+
You'll be redirected to your profile page shortly...
+
+ {/if}
+
+ {#if status === "error"}
+
+
+ Verification failed
+
+
{error}
+
+ {#if Object.keys(urlParams).length > 0}
+
+
+ URL Parameters:
+
+ {#each Object.entries(urlParams) as [key, value] (key)}
+
+
+ {key}:
+
+ {value}
+
+ {/each}
+
+ {/if}
+
+
goto("/signin")}
+ class="auth-button secondary"
+ >
+ Back to Sign In
+
+
+ {/if}
+
+
+
+
diff --git a/examples/tutorials/nhost-svelte-tutorial/static/robots.txt b/examples/tutorials/nhost-svelte-tutorial/static/robots.txt
new file mode 100644
index 000000000..b6dd6670c
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/static/robots.txt
@@ -0,0 +1,3 @@
+# allow crawling everything by default
+User-agent: *
+Disallow:
diff --git a/examples/tutorials/nhost-svelte-tutorial/svelte.config.js b/examples/tutorials/nhost-svelte-tutorial/svelte.config.js
new file mode 100644
index 000000000..a8bb58ace
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/svelte.config.js
@@ -0,0 +1,18 @@
+import adapter from "@sveltejs/adapter-auto";
+import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ // Consult https://svelte.dev/docs/kit/integrations
+ // for more information about preprocessors
+ preprocess: vitePreprocess(),
+
+ kit: {
+ // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
+ // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
+ // See https://svelte.dev/docs/kit/adapters for more information about adapters.
+ adapter: adapter(),
+ },
+};
+
+export default config;
diff --git a/examples/tutorials/nhost-svelte-tutorial/tsconfig.json b/examples/tutorials/nhost-svelte-tutorial/tsconfig.json
new file mode 100644
index 000000000..e3898cb22
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "./.svelte-kit/tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "moduleResolution": "bundler"
+ }
+ // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
+ // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
+ //
+ // To make changes to top-level options such as include and exclude, we recommend extending
+ // the generated config; see https://svelte.dev/docs/kit/configuration#typescript
+}
diff --git a/examples/tutorials/nhost-svelte-tutorial/vite.config.ts b/examples/tutorials/nhost-svelte-tutorial/vite.config.ts
new file mode 100644
index 000000000..80864b9de
--- /dev/null
+++ b/examples/tutorials/nhost-svelte-tutorial/vite.config.ts
@@ -0,0 +1,6 @@
+import { sveltekit } from "@sveltejs/kit/vite";
+import { defineConfig } from "vite";
+
+export default defineConfig({
+ plugins: [sveltekit()],
+});
diff --git a/examples/tutorials/nhost-vue-tutorial/.gitignore b/examples/tutorials/nhost-vue-tutorial/.gitignore
new file mode 100644
index 000000000..8ee54e8d3
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/.gitignore
@@ -0,0 +1,30 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+*.tsbuildinfo
diff --git a/examples/tutorials/nhost-vue-tutorial/.vscode/extensions.json b/examples/tutorials/nhost-vue-tutorial/.vscode/extensions.json
new file mode 100644
index 000000000..a7cea0b06
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["Vue.volar"]
+}
diff --git a/examples/tutorials/nhost-vue-tutorial/README.md b/examples/tutorials/nhost-vue-tutorial/README.md
new file mode 100644
index 000000000..1bb2c7a37
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/README.md
@@ -0,0 +1,3 @@
+This is the result of following the tutorial at:
+
+https://docs.nhost.io/getting-started/tutorials/vue/1-introduction
diff --git a/examples/tutorials/nhost-vue-tutorial/env.d.ts b/examples/tutorials/nhost-vue-tutorial/env.d.ts
new file mode 100644
index 000000000..11f02fe2a
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/tutorials/nhost-vue-tutorial/index.html b/examples/tutorials/nhost-vue-tutorial/index.html
new file mode 100644
index 000000000..9e5fc8f06
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite App
+
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/package.json b/examples/tutorials/nhost-vue-tutorial/package.json
new file mode 100644
index 000000000..ebcadb1ae
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "nhost-vue-tutorial",
+ "version": "0.0.0",
+ "private": true,
+ "type": "module",
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "scripts": {
+ "dev": "vite",
+ "build": "vue-tsc && vite build",
+ "generate": "echo 'Nothing to do'",
+ "test": "pnpm test:typecheck && pnpm test:lint",
+ "test:typecheck": "vue-tsc --noEmit",
+ "test:lint": "biome check",
+ "format": "biome format --write",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@nhost/nhost-js": "workspace:^",
+ "vue": "^3.5.18",
+ "vue-router": "^4.5.1"
+ },
+ "devDependencies": {
+ "@tsconfig/node22": "^22.0.2",
+ "@types/node": "^22.16.5",
+ "@vitejs/plugin-vue": "^6.0.1",
+ "@vue/tsconfig": "^0.7.0",
+ "npm-run-all2": "^8.0.4",
+ "typescript": "~5.8.0",
+ "vite": "^7.0.6",
+ "vite-plugin-vue-devtools": "^8.0.0",
+ "vue-tsc": "^3.0.4"
+ }
+}
diff --git a/examples/tutorials/nhost-vue-tutorial/pnpm-lock.yaml b/examples/tutorials/nhost-vue-tutorial/pnpm-lock.yaml
new file mode 100644
index 000000000..42e5bf189
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/pnpm-lock.yaml
@@ -0,0 +1,2027 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@nhost/nhost-js':
+ specifier: workspace:^
+ version: link:../../../packages/nhost-js
+ vue:
+ specifier: ^3.5.18
+ version: 3.5.21(typescript@5.8.3)
+ vue-router:
+ specifier: ^4.5.1
+ version: 4.5.1(vue@3.5.21(typescript@5.8.3))
+ devDependencies:
+ '@tsconfig/node22':
+ specifier: ^22.0.2
+ version: 22.0.2
+ '@types/node':
+ specifier: ^22.16.5
+ version: 22.18.3
+ '@vitejs/plugin-vue':
+ specifier: ^6.0.1
+ version: 6.0.1(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))
+ '@vue/tsconfig':
+ specifier: ^0.7.0
+ version: 0.7.0(typescript@5.8.3)(vue@3.5.21(typescript@5.8.3))
+ npm-run-all2:
+ specifier: ^8.0.4
+ version: 8.0.4
+ typescript:
+ specifier: ~5.8.0
+ version: 5.8.3
+ vite:
+ specifier: ^7.0.6
+ version: 7.1.5(@types/node@22.18.3)
+ vite-plugin-vue-devtools:
+ specifier: ^8.0.0
+ version: 8.0.2(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))
+ vue-tsc:
+ specifier: ^3.0.4
+ version: 3.0.7(typescript@5.8.3)
+
+packages:
+
+ '@babel/code-frame@7.27.1':
+ resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.28.4':
+ resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.28.4':
+ resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.28.3':
+ resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.27.2':
+ resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-create-class-features-plugin@7.28.3':
+ resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-member-expression-to-functions@7.27.1':
+ resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.27.1':
+ resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.3':
+ resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-plugin-utils@7.27.1':
+ resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-replace-supers@7.27.1':
+ resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.27.1':
+ resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.4':
+ resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.4':
+ resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-proposal-decorators@7.28.0':
+ resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-decorators@7.27.1':
+ resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-attributes@7.27.1':
+ resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-meta@7.10.4':
+ resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-jsx@7.27.1':
+ resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-typescript@7.27.1':
+ resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-typescript@7.28.0':
+ resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.27.2':
+ resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.28.4':
+ resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.28.4':
+ resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@esbuild/aix-ppc64@0.25.9':
+ resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.9':
+ resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.9':
+ resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.9':
+ resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.9':
+ resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.9':
+ resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.9':
+ resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.9':
+ resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.9':
+ resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.9':
+ resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.9':
+ resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.9':
+ resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.9':
+ resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.9':
+ resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.9':
+ resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.9':
+ resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.9':
+ resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.9':
+ resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.9':
+ resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.9':
+ resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.9':
+ resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.9':
+ resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.25.9':
+ resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.9':
+ resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.9':
+ resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.9':
+ resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@polka/url@1.0.0-next.29':
+ resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
+ '@rolldown/pluginutils@1.0.0-beta.29':
+ resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
+
+ '@rollup/rollup-android-arm-eabi@4.50.2':
+ resolution: {integrity: sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.50.2':
+ resolution: {integrity: sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.50.2':
+ resolution: {integrity: sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.50.2':
+ resolution: {integrity: sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.50.2':
+ resolution: {integrity: sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.50.2':
+ resolution: {integrity: sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.50.2':
+ resolution: {integrity: sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.50.2':
+ resolution: {integrity: sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.50.2':
+ resolution: {integrity: sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.50.2':
+ resolution: {integrity: sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.50.2':
+ resolution: {integrity: sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.50.2':
+ resolution: {integrity: sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.50.2':
+ resolution: {integrity: sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.50.2':
+ resolution: {integrity: sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.50.2':
+ resolution: {integrity: sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.50.2':
+ resolution: {integrity: sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.50.2':
+ resolution: {integrity: sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openharmony-arm64@4.50.2':
+ resolution: {integrity: sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.50.2':
+ resolution: {integrity: sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.50.2':
+ resolution: {integrity: sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.50.2':
+ resolution: {integrity: sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==}
+ cpu: [x64]
+ os: [win32]
+
+ '@sec-ant/readable-stream@0.4.1':
+ resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
+
+ '@sindresorhus/merge-streams@4.0.0':
+ resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
+ engines: {node: '>=18'}
+
+ '@tsconfig/node22@22.0.2':
+ resolution: {integrity: sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/node@22.18.3':
+ resolution: {integrity: sha512-gTVM8js2twdtqM+AE2PdGEe9zGQY4UvmFjan9rZcVb6FGdStfjWoWejdmy4CfWVO9rh5MiYQGZloKAGkJt8lMw==}
+
+ '@vitejs/plugin-vue@6.0.1':
+ resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ peerDependencies:
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+ vue: ^3.2.25
+
+ '@volar/language-core@2.4.23':
+ resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==}
+
+ '@volar/source-map@2.4.23':
+ resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==}
+
+ '@volar/typescript@2.4.23':
+ resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
+
+ '@vue/babel-helper-vue-transform-on@1.5.0':
+ resolution: {integrity: sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==}
+
+ '@vue/babel-plugin-jsx@1.5.0':
+ resolution: {integrity: sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+
+ '@vue/babel-plugin-resolve-type@1.5.0':
+ resolution: {integrity: sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@vue/compiler-core@3.5.21':
+ resolution: {integrity: sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==}
+
+ '@vue/compiler-dom@3.5.21':
+ resolution: {integrity: sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==}
+
+ '@vue/compiler-sfc@3.5.21':
+ resolution: {integrity: sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==}
+
+ '@vue/compiler-ssr@3.5.21':
+ resolution: {integrity: sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==}
+
+ '@vue/compiler-vue2@2.7.16':
+ resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
+
+ '@vue/devtools-api@6.6.4':
+ resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+ '@vue/devtools-core@8.0.2':
+ resolution: {integrity: sha512-V7eKTTHoS6KfK8PSGMLZMhGv/9yNDrmv6Qc3r71QILulnzPnqK2frsTyx3e2MrhdUZnENPEm6hcb4z0GZOqNhw==}
+ peerDependencies:
+ vue: ^3.0.0
+
+ '@vue/devtools-kit@8.0.2':
+ resolution: {integrity: sha512-yjZKdEmhJzQqbOh4KFBfTOQjDPMrjjBNCnHBvnTGJX+YLAqoUtY2J+cg7BE+EA8KUv8LprECq04ts75wCoIGWA==}
+
+ '@vue/devtools-shared@8.0.2':
+ resolution: {integrity: sha512-mLU0QVdy5Lp40PMGSixDw/Kbd6v5dkQXltd2r+mdVQV7iUog2NlZuLxFZApFZ/mObUBDhoCpf0T3zF2FWWdeHw==}
+
+ '@vue/language-core@3.0.7':
+ resolution: {integrity: sha512-0sqqyqJ0Gn33JH3TdIsZLCZZ8Gr4kwlg8iYOnOrDDkJKSjFurlQY/bEFQx5zs7SX2C/bjMkmPYq/NiyY1fTOkw==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@vue/reactivity@3.5.21':
+ resolution: {integrity: sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==}
+
+ '@vue/runtime-core@3.5.21':
+ resolution: {integrity: sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==}
+
+ '@vue/runtime-dom@3.5.21':
+ resolution: {integrity: sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==}
+
+ '@vue/server-renderer@3.5.21':
+ resolution: {integrity: sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==}
+ peerDependencies:
+ vue: 3.5.21
+
+ '@vue/shared@3.5.21':
+ resolution: {integrity: sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==}
+
+ '@vue/tsconfig@0.7.0':
+ resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==}
+ peerDependencies:
+ typescript: 5.x
+ vue: ^3.4.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ vue:
+ optional: true
+
+ alien-signals@2.0.7:
+ resolution: {integrity: sha512-wE7y3jmYeb0+h6mr5BOovuqhFv22O/MV9j5p0ndJsa7z1zJNPGQ4ph5pQk/kTTCWRC3xsA4SmtwmkzQO+7NCNg==}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ ansis@4.1.0:
+ resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==}
+ engines: {node: '>=14'}
+
+ baseline-browser-mapping@2.8.4:
+ resolution: {integrity: sha512-L+YvJwGAgwJBV1p6ffpSTa2KRc69EeeYGYjRVWKs0GKrK+LON0GC0gV+rKSNtALEDvMDqkvCFq9r1r94/Gjwxw==}
+ hasBin: true
+
+ birpc@2.5.0:
+ resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==}
+
+ browserslist@4.26.0:
+ resolution: {integrity: sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ bundle-name@4.1.0:
+ resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
+ engines: {node: '>=18'}
+
+ caniuse-lite@1.0.30001741:
+ resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ copy-anything@3.0.5:
+ resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
+ engines: {node: '>=12.13'}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ de-indent@1.0.2:
+ resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ default-browser-id@5.0.0:
+ resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==}
+ engines: {node: '>=18'}
+
+ default-browser@5.2.1:
+ resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==}
+ engines: {node: '>=18'}
+
+ define-lazy-prop@3.0.0:
+ resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
+ engines: {node: '>=12'}
+
+ electron-to-chromium@1.5.218:
+ resolution: {integrity: sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ error-stack-parser-es@1.0.5:
+ resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
+
+ esbuild@0.25.9:
+ resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ execa@9.6.0:
+ resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==}
+ engines: {node: ^18.19.0 || >=20.5.0}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ figures@6.1.0:
+ resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
+ engines: {node: '>=18'}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-stream@9.0.1:
+ resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
+ engines: {node: '>=18'}
+
+ he@1.2.0:
+ resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+ hasBin: true
+
+ hookable@5.5.3:
+ resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
+ human-signals@8.0.1:
+ resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==}
+ engines: {node: '>=18.18.0'}
+
+ is-docker@3.0.0:
+ resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ hasBin: true
+
+ is-inside-container@1.0.0:
+ resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
+ engines: {node: '>=14.16'}
+ hasBin: true
+
+ is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+
+ is-stream@4.0.1:
+ resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
+ engines: {node: '>=18'}
+
+ is-unicode-supported@2.1.0:
+ resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==}
+ engines: {node: '>=18'}
+
+ is-what@4.1.16:
+ resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
+ engines: {node: '>=12.13'}
+
+ is-wsl@3.1.0:
+ resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
+ engines: {node: '>=16'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ isexe@3.1.1:
+ resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
+ engines: {node: '>=16'}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-parse-even-better-errors@4.0.0:
+ resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==}
+ engines: {node: ^18.17.0 || >=20.5.0}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ kolorist@1.8.0:
+ resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ magic-string@0.30.19:
+ resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
+
+ memorystream@0.3.1:
+ resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
+ engines: {node: '>= 0.10.0'}
+
+ mitt@3.0.1:
+ resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
+ mrmime@2.0.1:
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+ engines: {node: '>=10'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ muggle-string@0.4.1:
+ resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ nanoid@5.1.5:
+ resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
+ engines: {node: ^18 || >=20}
+ hasBin: true
+
+ node-releases@2.0.21:
+ resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==}
+
+ npm-normalize-package-bin@4.0.0:
+ resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==}
+ engines: {node: ^18.17.0 || >=20.5.0}
+
+ npm-run-all2@8.0.4:
+ resolution: {integrity: sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==}
+ engines: {node: ^20.5.0 || >=22.0.0, npm: '>= 10'}
+ hasBin: true
+
+ npm-run-path@6.0.0:
+ resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
+ engines: {node: '>=18'}
+
+ ohash@2.0.11:
+ resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
+
+ open@10.2.0:
+ resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
+ engines: {node: '>=18'}
+
+ parse-ms@4.0.0:
+ resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
+ engines: {node: '>=18'}
+
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-key@4.0.0:
+ resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+ engines: {node: '>=12'}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ perfect-debounce@2.0.0:
+ resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ pidtree@0.6.0:
+ resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ pretty-ms@9.3.0:
+ resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==}
+ engines: {node: '>=18'}
+
+ read-package-json-fast@4.0.0:
+ resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
+ engines: {node: ^18.17.0 || >=20.5.0}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rollup@4.50.2:
+ resolution: {integrity: sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ run-applescript@7.1.0:
+ resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==}
+ engines: {node: '>=18'}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ shell-quote@1.8.3:
+ resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
+ engines: {node: '>= 0.4'}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ sirv@3.0.2:
+ resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
+ engines: {node: '>=18'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ speakingurl@14.0.1:
+ resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+ engines: {node: '>=0.10.0'}
+
+ strip-final-newline@4.0.0:
+ resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
+ engines: {node: '>=18'}
+
+ superjson@2.2.2:
+ resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
+ engines: {node: '>=16'}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ totalist@3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+
+ typescript@5.8.3:
+ resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+ unicorn-magic@0.3.0:
+ resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
+ engines: {node: '>=18'}
+
+ unplugin-utils@0.3.0:
+ resolution: {integrity: sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg==}
+ engines: {node: '>=20.19.0'}
+
+ update-browserslist-db@1.1.3:
+ resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ vite-dev-rpc@1.1.0:
+ resolution: {integrity: sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==}
+ peerDependencies:
+ vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0
+
+ vite-hot-client@2.1.0:
+ resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==}
+ peerDependencies:
+ vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+ vite-plugin-inspect@11.3.3:
+ resolution: {integrity: sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@nuxt/kit': '*'
+ vite: ^6.0.0 || ^7.0.0-0
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+
+ vite-plugin-vue-devtools@8.0.2:
+ resolution: {integrity: sha512-1069qvMBcyAu3yXQlvYrkwoyLOk0lSSR/gTKy/vy+Det7TXnouGei6ZcKwr5TIe938v/14oLlp0ow6FSJkkORA==}
+ engines: {node: '>=v14.21.3'}
+ peerDependencies:
+ vite: ^6.0.0 || ^7.0.0-0
+
+ vite-plugin-vue-inspector@5.3.2:
+ resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==}
+ peerDependencies:
+ vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+ vite@7.1.5:
+ resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vscode-uri@3.1.0:
+ resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+
+ vue-router@4.5.1:
+ resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==}
+ peerDependencies:
+ vue: ^3.2.0
+
+ vue-tsc@3.0.7:
+ resolution: {integrity: sha512-BSMmW8GGEgHykrv7mRk6zfTdK+tw4MBZY/x6fFa7IkdXK3s/8hQRacPjG9/8YKFDIWGhBocwi6PlkQQ/93OgIQ==}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=5.0.0'
+
+ vue@3.5.21:
+ resolution: {integrity: sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ which@5.0.0:
+ resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==}
+ engines: {node: ^18.17.0 || >=20.5.0}
+ hasBin: true
+
+ wsl-utils@0.1.0:
+ resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
+ engines: {node: '>=18'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yoctocolors@2.1.2:
+ resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==}
+ engines: {node: '>=18'}
+
+snapshots:
+
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.27.1
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.28.4': {}
+
+ '@babel/core@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helpers': 7.28.4
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.28.3':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/helper-compilation-targets@7.27.2':
+ dependencies:
+ '@babel/compat-data': 7.28.4
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.26.0
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-member-expression-to-functions': 7.27.1
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/traverse': 7.28.4
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-member-expression-to-functions@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-imports@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/helper-plugin-utils@7.27.1': {}
+
+ '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-member-expression-to-functions': 7.27.1
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.27.1': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.4':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+
+ '@babel/parser@7.28.4':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/template@7.27.2':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+
+ '@babel/traverse@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.28.4':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@esbuild/aix-ppc64@0.25.9':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/android-arm@0.25.9':
+ optional: true
+
+ '@esbuild/android-x64@0.25.9':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.9':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.9':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.9':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.9':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.9':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.9':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.9':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.9':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.9':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.9':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.9':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.9':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.9':
+ optional: true
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@polka/url@1.0.0-next.29': {}
+
+ '@rolldown/pluginutils@1.0.0-beta.29': {}
+
+ '@rollup/rollup-android-arm-eabi@4.50.2':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.50.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.50.2':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.50.2':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.50.2':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.50.2':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.50.2':
+ optional: true
+
+ '@sec-ant/readable-stream@0.4.1': {}
+
+ '@sindresorhus/merge-streams@4.0.0': {}
+
+ '@tsconfig/node22@22.0.2': {}
+
+ '@types/estree@1.0.8': {}
+
+ '@types/node@22.18.3':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@vitejs/plugin-vue@6.0.1(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.0-beta.29
+ vite: 7.1.5(@types/node@22.18.3)
+ vue: 3.5.21(typescript@5.8.3)
+
+ '@volar/language-core@2.4.23':
+ dependencies:
+ '@volar/source-map': 2.4.23
+
+ '@volar/source-map@2.4.23': {}
+
+ '@volar/typescript@2.4.23':
+ dependencies:
+ '@volar/language-core': 2.4.23
+ path-browserify: 1.0.1
+ vscode-uri: 3.1.0
+
+ '@vue/babel-helper-vue-transform-on@1.5.0': {}
+
+ '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4)
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ '@vue/babel-helper-vue-transform-on': 1.5.0
+ '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.28.4)
+ '@vue/shared': 3.5.21
+ optionalDependencies:
+ '@babel/core': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/parser': 7.28.4
+ '@vue/compiler-sfc': 3.5.21
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vue/compiler-core@3.5.21':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@vue/shared': 3.5.21
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.21':
+ dependencies:
+ '@vue/compiler-core': 3.5.21
+ '@vue/shared': 3.5.21
+
+ '@vue/compiler-sfc@3.5.21':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@vue/compiler-core': 3.5.21
+ '@vue/compiler-dom': 3.5.21
+ '@vue/compiler-ssr': 3.5.21
+ '@vue/shared': 3.5.21
+ estree-walker: 2.0.2
+ magic-string: 0.30.19
+ postcss: 8.5.6
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.21':
+ dependencies:
+ '@vue/compiler-dom': 3.5.21
+ '@vue/shared': 3.5.21
+
+ '@vue/compiler-vue2@2.7.16':
+ dependencies:
+ de-indent: 1.0.2
+ he: 1.2.0
+
+ '@vue/devtools-api@6.6.4': {}
+
+ '@vue/devtools-core@8.0.2(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))':
+ dependencies:
+ '@vue/devtools-kit': 8.0.2
+ '@vue/devtools-shared': 8.0.2
+ mitt: 3.0.1
+ nanoid: 5.1.5
+ pathe: 2.0.3
+ vite-hot-client: 2.1.0(vite@7.1.5(@types/node@22.18.3))
+ vue: 3.5.21(typescript@5.8.3)
+ transitivePeerDependencies:
+ - vite
+
+ '@vue/devtools-kit@8.0.2':
+ dependencies:
+ '@vue/devtools-shared': 8.0.2
+ birpc: 2.5.0
+ hookable: 5.5.3
+ mitt: 3.0.1
+ perfect-debounce: 2.0.0
+ speakingurl: 14.0.1
+ superjson: 2.2.2
+
+ '@vue/devtools-shared@8.0.2':
+ dependencies:
+ rfdc: 1.4.1
+
+ '@vue/language-core@3.0.7(typescript@5.8.3)':
+ dependencies:
+ '@volar/language-core': 2.4.23
+ '@vue/compiler-dom': 3.5.21
+ '@vue/compiler-vue2': 2.7.16
+ '@vue/shared': 3.5.21
+ alien-signals: 2.0.7
+ muggle-string: 0.4.1
+ path-browserify: 1.0.1
+ picomatch: 4.0.3
+ optionalDependencies:
+ typescript: 5.8.3
+
+ '@vue/reactivity@3.5.21':
+ dependencies:
+ '@vue/shared': 3.5.21
+
+ '@vue/runtime-core@3.5.21':
+ dependencies:
+ '@vue/reactivity': 3.5.21
+ '@vue/shared': 3.5.21
+
+ '@vue/runtime-dom@3.5.21':
+ dependencies:
+ '@vue/reactivity': 3.5.21
+ '@vue/runtime-core': 3.5.21
+ '@vue/shared': 3.5.21
+ csstype: 3.1.3
+
+ '@vue/server-renderer@3.5.21(vue@3.5.21(typescript@5.8.3))':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.21
+ '@vue/shared': 3.5.21
+ vue: 3.5.21(typescript@5.8.3)
+
+ '@vue/shared@3.5.21': {}
+
+ '@vue/tsconfig@0.7.0(typescript@5.8.3)(vue@3.5.21(typescript@5.8.3))':
+ optionalDependencies:
+ typescript: 5.8.3
+ vue: 3.5.21(typescript@5.8.3)
+
+ alien-signals@2.0.7: {}
+
+ ansi-styles@6.2.3: {}
+
+ ansis@4.1.0: {}
+
+ baseline-browser-mapping@2.8.4: {}
+
+ birpc@2.5.0: {}
+
+ browserslist@4.26.0:
+ dependencies:
+ baseline-browser-mapping: 2.8.4
+ caniuse-lite: 1.0.30001741
+ electron-to-chromium: 1.5.218
+ node-releases: 2.0.21
+ update-browserslist-db: 1.1.3(browserslist@4.26.0)
+
+ bundle-name@4.1.0:
+ dependencies:
+ run-applescript: 7.1.0
+
+ caniuse-lite@1.0.30001741: {}
+
+ convert-source-map@2.0.0: {}
+
+ copy-anything@3.0.5:
+ dependencies:
+ is-what: 4.1.16
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ csstype@3.1.3: {}
+
+ de-indent@1.0.2: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ default-browser-id@5.0.0: {}
+
+ default-browser@5.2.1:
+ dependencies:
+ bundle-name: 4.1.0
+ default-browser-id: 5.0.0
+
+ define-lazy-prop@3.0.0: {}
+
+ electron-to-chromium@1.5.218: {}
+
+ entities@4.5.0: {}
+
+ error-stack-parser-es@1.0.5: {}
+
+ esbuild@0.25.9:
+ 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
+
+ escalade@3.2.0: {}
+
+ estree-walker@2.0.2: {}
+
+ execa@9.6.0:
+ dependencies:
+ '@sindresorhus/merge-streams': 4.0.0
+ cross-spawn: 7.0.6
+ figures: 6.1.0
+ get-stream: 9.0.1
+ human-signals: 8.0.1
+ is-plain-obj: 4.1.0
+ is-stream: 4.0.1
+ npm-run-path: 6.0.0
+ pretty-ms: 9.3.0
+ signal-exit: 4.1.0
+ strip-final-newline: 4.0.0
+ yoctocolors: 2.1.2
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ figures@6.1.0:
+ dependencies:
+ is-unicode-supported: 2.1.0
+
+ fsevents@2.3.3:
+ optional: true
+
+ gensync@1.0.0-beta.2: {}
+
+ get-stream@9.0.1:
+ dependencies:
+ '@sec-ant/readable-stream': 0.4.1
+ is-stream: 4.0.1
+
+ he@1.2.0: {}
+
+ hookable@5.5.3: {}
+
+ human-signals@8.0.1: {}
+
+ is-docker@3.0.0: {}
+
+ is-inside-container@1.0.0:
+ dependencies:
+ is-docker: 3.0.0
+
+ is-plain-obj@4.1.0: {}
+
+ is-stream@4.0.1: {}
+
+ is-unicode-supported@2.1.0: {}
+
+ is-what@4.1.16: {}
+
+ is-wsl@3.1.0:
+ dependencies:
+ is-inside-container: 1.0.0
+
+ isexe@2.0.0: {}
+
+ isexe@3.1.1: {}
+
+ js-tokens@4.0.0: {}
+
+ jsesc@3.1.0: {}
+
+ json-parse-even-better-errors@4.0.0: {}
+
+ json5@2.2.3: {}
+
+ kolorist@1.8.0: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ magic-string@0.30.19:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ memorystream@0.3.1: {}
+
+ mitt@3.0.1: {}
+
+ mrmime@2.0.1: {}
+
+ ms@2.1.3: {}
+
+ muggle-string@0.4.1: {}
+
+ nanoid@3.3.11: {}
+
+ nanoid@5.1.5: {}
+
+ node-releases@2.0.21: {}
+
+ npm-normalize-package-bin@4.0.0: {}
+
+ npm-run-all2@8.0.4:
+ dependencies:
+ ansi-styles: 6.2.3
+ cross-spawn: 7.0.6
+ memorystream: 0.3.1
+ picomatch: 4.0.3
+ pidtree: 0.6.0
+ read-package-json-fast: 4.0.0
+ shell-quote: 1.8.3
+ which: 5.0.0
+
+ npm-run-path@6.0.0:
+ dependencies:
+ path-key: 4.0.0
+ unicorn-magic: 0.3.0
+
+ ohash@2.0.11: {}
+
+ open@10.2.0:
+ dependencies:
+ default-browser: 5.2.1
+ define-lazy-prop: 3.0.0
+ is-inside-container: 1.0.0
+ wsl-utils: 0.1.0
+
+ parse-ms@4.0.0: {}
+
+ path-browserify@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-key@4.0.0: {}
+
+ pathe@2.0.3: {}
+
+ perfect-debounce@2.0.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.3: {}
+
+ pidtree@0.6.0: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ pretty-ms@9.3.0:
+ dependencies:
+ parse-ms: 4.0.0
+
+ read-package-json-fast@4.0.0:
+ dependencies:
+ json-parse-even-better-errors: 4.0.0
+ npm-normalize-package-bin: 4.0.0
+
+ rfdc@1.4.1: {}
+
+ rollup@4.50.2:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.50.2
+ '@rollup/rollup-android-arm64': 4.50.2
+ '@rollup/rollup-darwin-arm64': 4.50.2
+ '@rollup/rollup-darwin-x64': 4.50.2
+ '@rollup/rollup-freebsd-arm64': 4.50.2
+ '@rollup/rollup-freebsd-x64': 4.50.2
+ '@rollup/rollup-linux-arm-gnueabihf': 4.50.2
+ '@rollup/rollup-linux-arm-musleabihf': 4.50.2
+ '@rollup/rollup-linux-arm64-gnu': 4.50.2
+ '@rollup/rollup-linux-arm64-musl': 4.50.2
+ '@rollup/rollup-linux-loong64-gnu': 4.50.2
+ '@rollup/rollup-linux-ppc64-gnu': 4.50.2
+ '@rollup/rollup-linux-riscv64-gnu': 4.50.2
+ '@rollup/rollup-linux-riscv64-musl': 4.50.2
+ '@rollup/rollup-linux-s390x-gnu': 4.50.2
+ '@rollup/rollup-linux-x64-gnu': 4.50.2
+ '@rollup/rollup-linux-x64-musl': 4.50.2
+ '@rollup/rollup-openharmony-arm64': 4.50.2
+ '@rollup/rollup-win32-arm64-msvc': 4.50.2
+ '@rollup/rollup-win32-ia32-msvc': 4.50.2
+ '@rollup/rollup-win32-x64-msvc': 4.50.2
+ fsevents: 2.3.3
+
+ run-applescript@7.1.0: {}
+
+ semver@6.3.1: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ shell-quote@1.8.3: {}
+
+ signal-exit@4.1.0: {}
+
+ sirv@3.0.2:
+ dependencies:
+ '@polka/url': 1.0.0-next.29
+ mrmime: 2.0.1
+ totalist: 3.0.1
+
+ source-map-js@1.2.1: {}
+
+ speakingurl@14.0.1: {}
+
+ strip-final-newline@4.0.0: {}
+
+ superjson@2.2.2:
+ dependencies:
+ copy-anything: 3.0.5
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ totalist@3.0.1: {}
+
+ typescript@5.8.3: {}
+
+ undici-types@6.21.0: {}
+
+ unicorn-magic@0.3.0: {}
+
+ unplugin-utils@0.3.0:
+ dependencies:
+ pathe: 2.0.3
+ picomatch: 4.0.3
+
+ update-browserslist-db@1.1.3(browserslist@4.26.0):
+ dependencies:
+ browserslist: 4.26.0
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ vite-dev-rpc@1.1.0(vite@7.1.5(@types/node@22.18.3)):
+ dependencies:
+ birpc: 2.5.0
+ vite: 7.1.5(@types/node@22.18.3)
+ vite-hot-client: 2.1.0(vite@7.1.5(@types/node@22.18.3))
+
+ vite-hot-client@2.1.0(vite@7.1.5(@types/node@22.18.3)):
+ dependencies:
+ vite: 7.1.5(@types/node@22.18.3)
+
+ vite-plugin-inspect@11.3.3(vite@7.1.5(@types/node@22.18.3)):
+ dependencies:
+ ansis: 4.1.0
+ debug: 4.4.3
+ error-stack-parser-es: 1.0.5
+ ohash: 2.0.11
+ open: 10.2.0
+ perfect-debounce: 2.0.0
+ sirv: 3.0.2
+ unplugin-utils: 0.3.0
+ vite: 7.1.5(@types/node@22.18.3)
+ vite-dev-rpc: 1.1.0(vite@7.1.5(@types/node@22.18.3))
+ transitivePeerDependencies:
+ - supports-color
+
+ vite-plugin-vue-devtools@8.0.2(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3)):
+ dependencies:
+ '@vue/devtools-core': 8.0.2(vite@7.1.5(@types/node@22.18.3))(vue@3.5.21(typescript@5.8.3))
+ '@vue/devtools-kit': 8.0.2
+ '@vue/devtools-shared': 8.0.2
+ execa: 9.6.0
+ sirv: 3.0.2
+ vite: 7.1.5(@types/node@22.18.3)
+ vite-plugin-inspect: 11.3.3(vite@7.1.5(@types/node@22.18.3))
+ vite-plugin-vue-inspector: 5.3.2(vite@7.1.5(@types/node@22.18.3))
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - supports-color
+ - vue
+
+ vite-plugin-vue-inspector@5.3.2(vite@7.1.5(@types/node@22.18.3)):
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4)
+ '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.4)
+ '@vue/compiler-dom': 3.5.21
+ kolorist: 1.8.0
+ magic-string: 0.30.19
+ vite: 7.1.5(@types/node@22.18.3)
+ transitivePeerDependencies:
+ - supports-color
+
+ vite@7.1.5(@types/node@22.18.3):
+ dependencies:
+ esbuild: 0.25.9
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.50.2
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ '@types/node': 22.18.3
+ fsevents: 2.3.3
+
+ vscode-uri@3.1.0: {}
+
+ vue-router@4.5.1(vue@3.5.21(typescript@5.8.3)):
+ dependencies:
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.21(typescript@5.8.3)
+
+ vue-tsc@3.0.7(typescript@5.8.3):
+ dependencies:
+ '@volar/typescript': 2.4.23
+ '@vue/language-core': 3.0.7(typescript@5.8.3)
+ typescript: 5.8.3
+
+ vue@3.5.21(typescript@5.8.3):
+ dependencies:
+ '@vue/compiler-dom': 3.5.21
+ '@vue/compiler-sfc': 3.5.21
+ '@vue/runtime-dom': 3.5.21
+ '@vue/server-renderer': 3.5.21(vue@3.5.21(typescript@5.8.3))
+ '@vue/shared': 3.5.21
+ optionalDependencies:
+ typescript: 5.8.3
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ which@5.0.0:
+ dependencies:
+ isexe: 3.1.1
+
+ wsl-utils@0.1.0:
+ dependencies:
+ is-wsl: 3.1.0
+
+ yallist@3.1.1: {}
+
+ yoctocolors@2.1.2: {}
diff --git a/examples/tutorials/nhost-vue-tutorial/public/favicon.ico b/examples/tutorials/nhost-vue-tutorial/public/favicon.ico
new file mode 100644
index 000000000..df36fcfb7
Binary files /dev/null and b/examples/tutorials/nhost-vue-tutorial/public/favicon.ico differ
diff --git a/examples/tutorials/nhost-vue-tutorial/src/App.vue b/examples/tutorials/nhost-vue-tutorial/src/App.vue
new file mode 100644
index 000000000..407ba5c82
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/App.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/assets/base.css b/examples/tutorials/nhost-vue-tutorial/src/assets/base.css
new file mode 100644
index 000000000..a675d59cf
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/assets/base.css
@@ -0,0 +1,76 @@
+/* color palette from */
+:root {
+ --vt-c-white: #ffffff;
+ --vt-c-white-soft: #f8f8f8;
+ --vt-c-white-mute: #f2f2f2;
+
+ --vt-c-black: #181818;
+ --vt-c-black-soft: #222222;
+ --vt-c-black-mute: #282828;
+
+ --vt-c-indigo: #2c3e50;
+
+ --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
+ --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
+ --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
+ --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
+
+ --vt-c-text-light-1: var(--vt-c-indigo);
+ --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
+ --vt-c-text-dark-1: var(--vt-c-white);
+ --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
+}
+
+/* semantic color variables for this project */
+:root {
+ --color-background: var(--vt-c-white);
+ --color-background-soft: var(--vt-c-white-soft);
+ --color-background-mute: var(--vt-c-white-mute);
+
+ --color-border: var(--vt-c-divider-light-2);
+ --color-border-hover: var(--vt-c-divider-light-1);
+
+ --color-heading: var(--vt-c-text-light-1);
+ --color-text: var(--vt-c-text-light-1);
+
+ --section-gap: 160px;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --color-background: var(--vt-c-black);
+ --color-background-soft: var(--vt-c-black-soft);
+ --color-background-mute: var(--vt-c-black-mute);
+
+ --color-border: var(--vt-c-divider-dark-2);
+ --color-border-hover: var(--vt-c-divider-dark-1);
+
+ --color-heading: var(--vt-c-text-dark-1);
+ --color-text: var(--vt-c-text-dark-2);
+ }
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ margin: 0;
+ font-weight: normal;
+}
+
+body {
+ min-height: 100vh;
+ color: var(--color-text);
+ background: var(--color-background);
+ transition:
+ color 0.5s,
+ background-color 0.5s;
+ line-height: 1.6;
+ font-family:
+ Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
+ Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
+ font-size: 15px;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
diff --git a/examples/tutorials/nhost-vue-tutorial/src/assets/logo.svg b/examples/tutorials/nhost-vue-tutorial/src/assets/logo.svg
new file mode 100644
index 000000000..756566035
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/assets/logo.svg
@@ -0,0 +1 @@
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/assets/main.css b/examples/tutorials/nhost-vue-tutorial/src/assets/main.css
new file mode 100644
index 000000000..1b327bc66
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/assets/main.css
@@ -0,0 +1,864 @@
+:root {
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+#app {
+ width: 100%;
+ min-height: 100vh;
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+
+a:hover {
+ color: #535bf2;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+
+button:hover {
+ border-color: #646cff;
+}
+
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+input,
+textarea {
+ width: 100%;
+ padding: 0.875rem 1rem;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 8px;
+ font-size: 0.875rem;
+ transition: all 0.2s ease;
+ background: rgba(255, 255, 255, 0.05);
+ color: white;
+ box-sizing: border-box;
+ font-family: inherit;
+}
+
+input:focus,
+textarea:focus {
+ outline: none;
+ border-color: #3b82f6;
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
+ background: rgba(255, 255, 255, 0.08);
+}
+
+input::placeholder,
+textarea::placeholder {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+textarea {
+ resize: vertical;
+ min-height: 4rem;
+}
+
+label {
+ display: block;
+ margin: 0 0 0.5rem 0;
+ font-weight: 500;
+ color: rgba(255, 255, 255, 0.9);
+ font-size: 0.875rem;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+/* Global Layout */
+.app-content {
+ padding: 0 2rem 2rem;
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+.page-center {
+ text-align: center;
+ padding: 2rem;
+}
+
+.page-header {
+ margin-bottom: 2rem;
+}
+
+.page-title {
+ font-weight: 700;
+ margin: 0;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.margin-bottom {
+ margin-bottom: 1rem;
+}
+
+.margin-top {
+ margin-top: 1rem;
+}
+
+.container {
+ width: 800px;
+ max-width: calc(100vw - 4rem);
+ min-width: 320px;
+ margin: 0 auto;
+ padding: 2rem;
+ box-sizing: border-box;
+ position: relative;
+}
+
+/* Status Messages */
+.success-message {
+ padding: 1rem;
+ background-color: #d4edda;
+ color: #155724;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+}
+
+.error-message {
+ background: rgba(239, 68, 68, 0.1);
+ color: #ef4444;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ border-radius: 12px;
+ padding: 1rem 1.5rem;
+ margin: 1rem 0;
+}
+
+.help-text {
+ color: #666;
+}
+
+.verification-status {
+ color: #28a745;
+ font-size: 1.2rem;
+ font-weight: bold;
+ margin-bottom: 1rem;
+}
+
+.verification-status.error {
+ color: #dc3545;
+ font-size: 1.1rem;
+}
+
+/* Email Verification Status */
+.email-verified {
+ color: #10b981;
+ font-weight: bold;
+ margin-left: 0.5rem;
+}
+
+.email-unverified {
+ color: #ef4444;
+ font-weight: bold;
+ margin-left: 0.5rem;
+}
+
+/* Debug Info */
+.debug-panel {
+ margin-bottom: 1rem;
+ padding: 1rem;
+ background-color: #f8f9fa;
+ border-radius: 8px;
+ text-align: left;
+ max-height: 200px;
+ overflow: auto;
+}
+
+.debug-title {
+ font-weight: bold;
+ margin-bottom: 0.5rem;
+}
+
+.debug-item {
+ margin-bottom: 0.25rem;
+}
+
+.debug-key {
+ font-family: monospace;
+ color: #007bff;
+}
+
+.debug-value {
+ font-family: monospace;
+}
+
+/* Session Display */
+.session-display {
+ font-size: 0.75rem;
+ overflow: auto;
+ margin: 0;
+ line-height: 1.4;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
+/* Loading Spinner */
+.spinner-verify {
+ width: 32px;
+ height: 32px;
+ border: 3px solid #f3f3f3;
+ border-top: 3px solid #007bff;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin: 0 auto;
+}
+
+/* Navigation */
+.navigation {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ margin-bottom: 2rem;
+}
+
+.nav-container {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 1rem 2rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.nav-logo {
+ font-size: 1.25rem;
+ font-weight: 700;
+ color: white;
+ text-decoration: none;
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.nav-logo:hover {
+ opacity: 0.8;
+}
+
+.nav-links {
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+}
+
+.nav-link {
+ color: rgba(255, 255, 255, 0.8);
+ text-decoration: none;
+ font-weight: 500;
+ font-size: 0.875rem;
+ padding: 0.5rem 0.75rem;
+ border-radius: 6px;
+ transition: all 0.2s ease;
+ border: none;
+ background: none;
+ cursor: pointer;
+ font-family: inherit;
+}
+
+.nav-link:hover {
+ color: white;
+ background: rgba(255, 255, 255, 0.1);
+}
+
+.nav-button {
+ color: #ef4444;
+}
+
+.nav-button:hover {
+ background: rgba(239, 68, 68, 0.2);
+}
+
+/* Buttons */
+.btn {
+ padding: 0.75rem 1.5rem;
+ border: none;
+ border-radius: 8px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ font-size: 0.875rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ min-width: 120px;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ color: white;
+}
+
+.btn-primary:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
+}
+
+.btn-secondary {
+ background: rgba(255, 255, 255, 0.1);
+ color: white;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.btn-secondary:hover {
+ background: rgba(255, 255, 255, 0.2);
+}
+
+.btn-cancel {
+ background: rgba(239, 68, 68, 0.1);
+ color: #ef4444;
+ border: 1px solid rgba(239, 68, 68, 0.3);
+}
+
+.btn-cancel:hover {
+ background: rgba(239, 68, 68, 0.2);
+}
+
+/* Loading State */
+.loading-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 4rem 2rem;
+}
+
+.loading-content {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.spinner {
+ width: 2rem;
+ height: 2rem;
+ border: 3px solid rgba(59, 130, 246, 0.3);
+ border-top: 3px solid #3b82f6;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+.loading-text {
+ color: rgba(255, 255, 255, 0.7);
+ font-size: 0.875rem;
+}
+
+/* Empty State */
+.empty-state {
+ text-align: center;
+ padding: 4rem 2rem;
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+}
+
+.empty-icon {
+ width: 4rem;
+ height: 4rem;
+ color: rgba(255, 255, 255, 0.4);
+ margin: 0 auto 1rem;
+}
+
+.empty-title {
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: white;
+ margin: 0 0 0.5rem 0;
+}
+
+.empty-description {
+ color: rgba(255, 255, 255, 0.6);
+ margin: 0;
+}
+
+/* Forms */
+.form-card {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 16px;
+ padding: 2rem;
+ margin-bottom: 2rem;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.form-title {
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: white;
+ margin: 0 0 1.5rem 0;
+}
+
+.form-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.field-group {
+ display: flex;
+ flex-direction: column;
+}
+
+.form-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1rem;
+}
+
+/* Auth Pages */
+.auth-form {
+ max-width: 400px;
+}
+
+.auth-form-field {
+ margin-bottom: 1rem;
+}
+
+.auth-input {
+ width: 100%;
+ padding: 0.5rem;
+ margin-top: 0.25rem;
+}
+
+.auth-error {
+ color: red;
+ margin-bottom: 1rem;
+ padding: 0.5rem;
+ background-color: #fee;
+ border-radius: 4px;
+}
+
+.auth-button {
+ width: 100%;
+ padding: 0.75rem;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.auth-button:disabled {
+ cursor: not-allowed;
+}
+
+.auth-button.primary {
+ background-color: #28a745;
+}
+
+.auth-button.secondary {
+ background-color: #007bff;
+}
+
+.auth-links {
+ margin-top: 1rem;
+}
+
+/* Todos */
+
+.todo-form {
+ width: 100%;
+}
+
+/* Todo List */
+.todos-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.todo-card {
+ background: rgba(255, 255, 255, 0.03);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ transition: all 0.2s ease;
+ overflow: hidden;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.todo-card:hover {
+ border-color: rgba(255, 255, 255, 0.2);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+}
+
+.todo-card.completed {
+ opacity: 0.7;
+}
+
+/* Todo Content */
+.todo-content {
+ padding: 1rem 1.5rem;
+}
+
+.todo-edit {
+ padding: 1.5rem;
+ min-height: 200px;
+}
+
+.edit-fields {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.edit-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1.5rem;
+}
+
+.todo-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+}
+
+.todo-title-btn {
+ background: none;
+ border: none;
+ padding: 0;
+ text-align: left;
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: white;
+ cursor: pointer;
+ transition: color 0.2s ease;
+ flex: 1;
+ line-height: 1.4;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ max-width: calc(100% - 140px);
+}
+
+.todo-title-btn:hover {
+ color: #3b82f6;
+}
+
+.todo-title-btn.completed {
+ text-decoration: line-through;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.todo-actions {
+ display: flex;
+ gap: 0.5rem;
+ flex-shrink: 0;
+ min-width: 132px;
+ justify-content: flex-end;
+}
+
+/* Action Buttons */
+.action-btn {
+ width: 40px;
+ height: 40px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ background: rgba(255, 255, 255, 0.05);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ transition: all 0.2s ease;
+ -webkit-text-fill-color: currentColor;
+}
+
+.action-btn-complete {
+ color: #10b981;
+ font-size: 20px;
+}
+
+.action-btn-complete:hover {
+ background: rgba(16, 185, 129, 0.2);
+ color: #34d399;
+}
+
+.action-btn-edit {
+ color: #3b82f6;
+}
+
+.action-btn-edit:hover {
+ background: rgba(59, 130, 246, 0.2);
+ color: #60a5fa;
+}
+
+.action-btn-delete {
+ color: #ef4444;
+}
+
+.action-btn-delete:hover {
+ background: rgba(239, 68, 68, 0.2);
+ color: #f87171;
+}
+
+/* Add Todo Button */
+.add-todo-btn {
+ width: 36px;
+ height: 36px;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-size: 18px;
+ font-weight: normal;
+ -webkit-text-fill-color: white;
+ transition: all 0.2s ease;
+}
+
+.add-todo-btn:hover {
+ transform: scale(1.1);
+ box-shadow: 0 4px 20px rgba(59, 130, 246, 0.4);
+}
+
+/* Todo Details */
+.todo-details {
+ margin-top: 1rem;
+ padding-top: 1rem;
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.description {
+ background: rgba(255, 255, 255, 0.02);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 8px;
+ padding: 1rem;
+ margin-bottom: 1rem;
+}
+
+.description p {
+ margin: 0;
+ color: rgba(255, 255, 255, 0.8);
+ line-height: 1.6;
+}
+
+.description.completed p {
+ text-decoration: line-through;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.todo-meta {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+}
+
+.meta-dates {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.meta-item {
+ font-size: 0.75rem;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.completion-badge {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ font-size: 0.75rem;
+ color: #10b981;
+ font-weight: 500;
+}
+
+.completion-icon {
+ width: 0.875rem;
+ height: 0.875rem;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .nav-container {
+ padding: 1rem;
+ flex-direction: column;
+ gap: 1rem;
+ }
+
+ .nav-links {
+ gap: 1rem;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+
+ .container {
+ padding: 1rem;
+ }
+
+ .form-actions {
+ flex-direction: column;
+ }
+
+ .edit-actions {
+ flex-direction: column;
+ }
+
+ .todo-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1rem;
+ }
+
+ .todo-actions {
+ align-self: stretch;
+ justify-content: center;
+ }
+
+ .meta-dates {
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+
+ .todo-meta {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ }
+}
+
+/* File Upload */
+.file-upload-btn {
+ min-height: 120px;
+ flex-direction: column;
+ gap: 0.5rem;
+ width: 100%;
+ border: 2px dashed rgba(255, 255, 255, 0.3);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.file-upload-btn:hover {
+ border-color: rgba(255, 255, 255, 0.5);
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.file-upload-info {
+ margin-top: 0.5rem;
+ font-size: 0.875rem;
+ color: rgba(255, 255, 255, 0.8);
+}
+
+/* File Table */
+.file-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.file-table th {
+ padding: 0.75rem;
+ text-align: left;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ color: rgba(255, 255, 255, 0.7);
+ font-weight: 500;
+ font-size: 0.875rem;
+}
+
+.file-table th:last-child {
+ text-align: center;
+}
+
+.file-table td {
+ padding: 0.75rem;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.file-table tr:hover {
+ background-color: rgba(255, 255, 255, 0.02);
+}
+
+.file-name {
+ color: white;
+ font-weight: 500;
+}
+
+.file-meta {
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 0.875rem;
+}
+
+.file-actions {
+ display: flex;
+ gap: 0.5rem;
+ justify-content: center;
+}
+
+/* Responsive File Table */
+@media (max-width: 768px) {
+ .file-table {
+ font-size: 0.875rem;
+ }
+
+ .file-table th,
+ .file-table td {
+ padding: 0.5rem;
+ }
+
+ .file-actions {
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+}
diff --git a/examples/tutorials/nhost-vue-tutorial/src/components/Navigation.vue b/examples/tutorials/nhost-vue-tutorial/src/components/Navigation.vue
new file mode 100644
index 000000000..f22f0486b
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/components/Navigation.vue
@@ -0,0 +1,63 @@
+
+
+
+
+ Nhost Vue Demo
+
+
+
+
+ Home
+
+
+
+
+ Todos
+
+
+ Files
+
+
+ Profile
+
+
+ Sign Out
+
+
+
+
+ Sign In
+
+
+ Sign Up
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconCommunity.vue b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconCommunity.vue
new file mode 100644
index 000000000..2dc8b0552
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconCommunity.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconDocumentation.vue b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconDocumentation.vue
new file mode 100644
index 000000000..6d4791cfb
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconDocumentation.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconEcosystem.vue b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconEcosystem.vue
new file mode 100644
index 000000000..c3a4f078c
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconEcosystem.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconSupport.vue b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconSupport.vue
new file mode 100644
index 000000000..7452834d3
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconSupport.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconTooling.vue b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconTooling.vue
new file mode 100644
index 000000000..660598d7c
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/components/icons/IconTooling.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/lib/nhost/auth.ts b/examples/tutorials/nhost-vue-tutorial/src/lib/nhost/auth.ts
new file mode 100644
index 000000000..1a35aae7d
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/lib/nhost/auth.ts
@@ -0,0 +1,131 @@
+import { createClient } from "@nhost/nhost-js";
+import type { Session } from "@nhost/nhost-js/auth";
+import { computed, reactive } from "vue";
+
+// Global reactive state
+const authState = reactive({
+ user: null as Session["user"] | null,
+ session: null as Session | null,
+ isLoading: true,
+});
+
+// Create the nhost client
+const nhost = createClient({
+ region: (import.meta.env["VITE_NHOST_REGION"] as string) || "local",
+ subdomain: (import.meta.env["VITE_NHOST_SUBDOMAIN"] as string) || "local",
+});
+
+// Subscription cleanup function
+let unsubscribe: (() => void) | null = null;
+let lastRefreshTokenIdRef: string | null = null;
+let isInitialized = false;
+
+/**
+ * Handles session reload when refresh token changes.
+ * This detects when the session has been updated from other tabs.
+ *
+ * @param currentRefreshTokenId - The current refresh token ID to compare against stored value
+ */
+const reloadSession = (currentRefreshTokenId: string | null) => {
+ if (currentRefreshTokenId !== lastRefreshTokenIdRef) {
+ lastRefreshTokenIdRef = currentRefreshTokenId;
+
+ // Update local authentication state to match current session
+ const currentSession = nhost.getUserSession();
+ authState.user = currentSession?.user || null;
+ authState.session = currentSession;
+ }
+};
+
+// Initialize auth state
+const initializeAuth = () => {
+ if (isInitialized) return;
+
+ authState.isLoading = true;
+
+ // Set initial values
+ const currentSession = nhost.getUserSession();
+ authState.user = currentSession?.user || null;
+ authState.session = currentSession;
+ lastRefreshTokenIdRef = currentSession?.refreshTokenId ?? null;
+ authState.isLoading = false;
+
+ // Subscribe to session changes from other browser tabs
+ // This enables real-time synchronization when user signs in/out in another tab
+ unsubscribe = nhost.sessionStorage.onChange((currentSession) => {
+ reloadSession(currentSession?.refreshTokenId ?? null);
+ });
+
+ // Handle session changes from page focus events (for additional session consistency)
+ const checkSessionOnFocus = () => {
+ reloadSession(nhost.getUserSession()?.refreshTokenId ?? null);
+ };
+
+ // Monitor page visibility changes (tab switching, window minimizing)
+ document.addEventListener("visibilitychange", () => {
+ if (!document.hidden) {
+ checkSessionOnFocus();
+ }
+ });
+
+ // Monitor window focus events (clicking back into the browser window)
+ window.addEventListener("focus", checkSessionOnFocus);
+
+ isInitialized = true;
+};
+
+// Cleanup function
+const cleanup = () => {
+ if (unsubscribe) {
+ unsubscribe();
+ unsubscribe = null;
+ }
+ isInitialized = false;
+};
+
+/**
+ * Vue composable for authentication state and operations.
+ *
+ * Provides reactive access to current user session, authentication state, and Nhost client.
+ * Handles cross-tab session synchronization and automatic state updates.
+ *
+ * @returns Object containing reactive authentication state and Nhost client
+ *
+ * @example
+ * ```vue
+ *
+ *
+ *
+ * Please sign in
+ * Welcome, {{ user?.displayName }}!
+ *
+ * ```
+ */
+export function useAuth() {
+ // Initialize auth if not already done
+ if (!isInitialized && typeof window !== "undefined") {
+ initializeAuth();
+ }
+
+ return {
+ user: computed(() => authState.user),
+ session: computed(() => authState.session),
+ isLoading: computed(() => authState.isLoading),
+ isAuthenticated: computed(() => !!authState.session),
+ nhost,
+ };
+}
+
+// Initialize auth immediately (for SSR compatibility)
+if (typeof window !== "undefined") {
+ initializeAuth();
+}
+
+// Cleanup on window unload
+if (typeof window !== "undefined") {
+ window.addEventListener("beforeunload", cleanup);
+}
diff --git a/examples/tutorials/nhost-vue-tutorial/src/main.ts b/examples/tutorials/nhost-vue-tutorial/src/main.ts
new file mode 100644
index 000000000..7a9c7a853
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/main.ts
@@ -0,0 +1,11 @@
+import "./assets/main.css";
+
+import { createApp } from "vue";
+import App from "./App.vue";
+import router from "./router";
+
+const app = createApp(App);
+
+app.use(router);
+
+app.mount("#app");
diff --git a/examples/tutorials/nhost-vue-tutorial/src/router/index.ts b/examples/tutorials/nhost-vue-tutorial/src/router/index.ts
new file mode 100644
index 000000000..45cb95082
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/router/index.ts
@@ -0,0 +1,77 @@
+import { createRouter, createWebHistory } from "vue-router";
+import { useAuth } from "../lib/nhost/auth";
+import Files from "../views/Files.vue";
+import HomeView from "../views/HomeView.vue";
+import ProfileView from "../views/ProfileView.vue";
+import SignIn from "../views/SignIn.vue";
+import SignUp from "../views/SignUp.vue";
+import Todos from "../views/Todos.vue";
+import Verify from "../views/Verify.vue";
+
+const router = createRouter({
+ history: createWebHistory(import.meta.env.BASE_URL),
+ routes: [
+ {
+ path: "/",
+ name: "home",
+ component: HomeView,
+ },
+ {
+ path: "/signin",
+ name: "SignIn",
+ component: SignIn,
+ },
+ {
+ path: "/signup",
+ name: "SignUp",
+ component: SignUp,
+ },
+ {
+ path: "/verify",
+ name: "Verify",
+ component: Verify,
+ },
+ {
+ path: "/profile",
+ name: "profile",
+ component: ProfileView,
+ meta: { requiresAuth: true },
+ },
+ {
+ path: "/todos",
+ name: "Todos",
+ component: Todos,
+ meta: { requiresAuth: true },
+ },
+ {
+ path: "/files",
+ name: "Files",
+ component: Files,
+ meta: { requiresAuth: true },
+ },
+ {
+ path: "/:pathMatch(.*)*",
+ redirect: "/",
+ },
+ ],
+});
+
+// Navigation guard for protected routes
+router.beforeEach((to) => {
+ if (to.meta["requiresAuth"]) {
+ const { isAuthenticated, isLoading } = useAuth();
+
+ // Show loading state while authentication is being checked
+ if (isLoading.value) {
+ // You can return a loading component path or handle loading in the component
+ return true; // Allow navigation, handle loading in component
+ }
+
+ if (!isAuthenticated.value) {
+ return "/"; // Redirect to home page
+ }
+ }
+ return true;
+});
+
+export default router;
diff --git a/examples/tutorials/nhost-vue-tutorial/src/views/AboutView.vue b/examples/tutorials/nhost-vue-tutorial/src/views/AboutView.vue
new file mode 100644
index 000000000..756ad2a17
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/views/AboutView.vue
@@ -0,0 +1,15 @@
+
+
+
This is an about page
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/views/Files.vue b/examples/tutorials/nhost-vue-tutorial/src/views/Files.vue
new file mode 100644
index 000000000..60304e8a9
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/views/Files.vue
@@ -0,0 +1,394 @@
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/views/HomeView.vue b/examples/tutorials/nhost-vue-tutorial/src/views/HomeView.vue
new file mode 100644
index 000000000..200bde705
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/views/HomeView.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
Hello, {{ user?.displayName || user?.email }}!
+
+
+
You are not signed in.
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/views/ProfileView.vue b/examples/tutorials/nhost-vue-tutorial/src/views/ProfileView.vue
new file mode 100644
index 000000000..63479d29a
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/views/ProfileView.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/views/SignIn.vue b/examples/tutorials/nhost-vue-tutorial/src/views/SignIn.vue
new file mode 100644
index 000000000..085cbea0c
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/views/SignIn.vue
@@ -0,0 +1,96 @@
+
+
+
Sign In
+
+
+
+ Email
+
+
+
+
+ Password
+
+
+
+
+ {{ error }}
+
+
+
+ {{ isLoading ? "Signing In..." : "Sign In" }}
+
+
+
+
+
+ Don't have an account? Sign Up
+
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/views/SignUp.vue b/examples/tutorials/nhost-vue-tutorial/src/views/SignUp.vue
new file mode 100644
index 000000000..18f0a82ef
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/views/SignUp.vue
@@ -0,0 +1,131 @@
+
+
+
Sign Up
+
Check Your Email
+
+
+
+ We've sent a verification link to {{ email }}
+
+
+ Please check your email and click the verification link to activate your account.
+
+
+ Back to Sign In
+
+
+
+
+
+ Display Name
+
+
+
+
+ Email
+
+
+
+
+ Password
+
+ Minimum 8 characters
+
+
+
+ {{ error }}
+
+
+
+ {{ isLoading ? "Creating Account..." : "Sign Up" }}
+
+
+
+
+
+ Already have an account? Sign In
+
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/views/Todos.vue b/examples/tutorials/nhost-vue-tutorial/src/views/Todos.vue
new file mode 100644
index 000000000..cce16e0f3
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/views/Todos.vue
@@ -0,0 +1,466 @@
+
+
+
Please sign in to view your todos.
+
+
+
+
+
+
+ Error: {{ error }}
+
+
+
+
+
+
+
+
+
+
+
+
+
No todos yet
+
+ Create your first todo to get started!
+
+
+
+
+
+
+
+ Title
+
+
+
+ Details
+
+
+
+
+ ✓ Save Changes
+
+
+ ✕ Cancel
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/src/views/Verify.vue b/examples/tutorials/nhost-vue-tutorial/src/views/Verify.vue
new file mode 100644
index 000000000..cfc4d3b3a
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/src/views/Verify.vue
@@ -0,0 +1,135 @@
+
+
+
Email Verification
+
+
+
+
Verifying your email...
+
+
+
+
+
+ ✓ Successfully verified!
+
+
You'll be redirected to your profile page shortly...
+
+
+
+
+ Verification failed
+
+
{{ error }}
+
+
+
+ URL Parameters:
+
+
+
+ {{ key }}:
+
+ {{ value }}
+
+
+
+
+ Back to Sign In
+
+
+
+
+
+
+
diff --git a/examples/tutorials/nhost-vue-tutorial/tsconfig.app.json b/examples/tutorials/nhost-vue-tutorial/tsconfig.app.json
new file mode 100644
index 000000000..913b8f279
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/tsconfig.app.json
@@ -0,0 +1,12 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
+ "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/*"],
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/examples/tutorials/nhost-vue-tutorial/tsconfig.json b/examples/tutorials/nhost-vue-tutorial/tsconfig.json
new file mode 100644
index 000000000..66b5e5703
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.node.json"
+ },
+ {
+ "path": "./tsconfig.app.json"
+ }
+ ]
+}
diff --git a/examples/tutorials/nhost-vue-tutorial/tsconfig.node.json b/examples/tutorials/nhost-vue-tutorial/tsconfig.node.json
new file mode 100644
index 000000000..a83dfc9d4
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/tsconfig.node.json
@@ -0,0 +1,19 @@
+{
+ "extends": "@tsconfig/node22/tsconfig.json",
+ "include": [
+ "vite.config.*",
+ "vitest.config.*",
+ "cypress.config.*",
+ "nightwatch.conf.*",
+ "playwright.config.*",
+ "eslint.config.*"
+ ],
+ "compilerOptions": {
+ "noEmit": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "types": ["node"]
+ }
+}
diff --git a/examples/tutorials/nhost-vue-tutorial/vite.config.ts b/examples/tutorials/nhost-vue-tutorial/vite.config.ts
new file mode 100644
index 000000000..6789b637d
--- /dev/null
+++ b/examples/tutorials/nhost-vue-tutorial/vite.config.ts
@@ -0,0 +1,14 @@
+import { fileURLToPath, URL } from "node:url";
+import vue from "@vitejs/plugin-vue";
+import { defineConfig } from "vite";
+import vueDevTools from "vite-plugin-vue-devtools";
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [vue(), vueDevTools()],
+ resolve: {
+ alias: {
+ "@": fileURLToPath(new URL("./src", import.meta.url)),
+ },
+ },
+});
diff --git a/examples/tutorials/package.json b/examples/tutorials/package.json
new file mode 100644
index 000000000..b78c5d0ad
--- /dev/null
+++ b/examples/tutorials/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "tutorials",
+ "version": "1.0.0",
+ "license": "MIT",
+ "scripts": {
+ "build": "pnpm run --sequential --filter './**' build",
+ "test": "pnpm run --sequential --filter './**' test",
+ "generate": "pnpm run --sequential --filter './**' generate"
+ },
+ "dependencies": {
+ "@nhost/nhost-js": "workspace:^"
+ }
+}
diff --git a/examples/tutorials/pnpm-lock.yaml b/examples/tutorials/pnpm-lock.yaml
new file mode 100644
index 000000000..101832f3f
--- /dev/null
+++ b/examples/tutorials/pnpm-lock.yaml
@@ -0,0 +1,13 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@nhost/nhost-js':
+ specifier: workspace:^
+ version: link:../../packages/nhost-js
diff --git a/examples/tutorials/project.nix b/examples/tutorials/project.nix
new file mode 100644
index 000000000..9aed00ee1
--- /dev/null
+++ b/examples/tutorials/project.nix
@@ -0,0 +1,111 @@
+{ self, pkgs, nix2containerPkgs, nix-filter, nixops-lib }:
+let
+ name = "tutorials";
+ version = "0.0.0-dev";
+ submodule = "examples/${name}";
+
+ node_modules = nixops-lib.js.mkNodeModules {
+ name = "node-modules-${name}";
+ version = "0.0.0-dev";
+
+ src = nix-filter.lib.filter {
+ root = ../..;
+ include = [
+ ".npmrc "
+ "package.json"
+ "pnpm-workspace.yaml "
+ "pnpm-lock.yaml"
+ "${submodule}/package.json"
+ "${submodule}/pnpm-lock.yaml"
+ "${submodule}/nhost-nextjs-tutorial/package.json"
+ "${submodule}/nhost-nextjs-tutorial/pnpm-lock.yaml"
+ "${submodule}/nhost-react-tutorial/package.json"
+ "${submodule}/nhost-react-tutorial/pnpm-lock.yaml"
+ "${submodule}/nhost-reactnative-tutorial/package.json"
+ "${submodule}/nhost-reactnative-tutorial/pnpm-lock.yaml"
+ "${submodule}/nhost-svelte-tutorial/package.json"
+ "${submodule}/nhost-svelte-tutorial/pnpm-lock.yaml"
+ "${submodule}/nhost-vue-tutorial/package.json"
+ "${submodule}/nhost-vue-tutorial/pnpm-lock.yaml"
+ ];
+ };
+
+ pnpmOpts = "--filter . --filter './${submodule}/**'";
+
+ preBuild = ''
+ mkdir packages
+ cp -r ${self.packages.${pkgs.system}.nhost-js} packages/nhost-js
+ '';
+ };
+
+ src = nix-filter.lib.filter {
+ root = ../../.;
+ include = with nix-filter.lib; [
+ isDirectory
+ ".gitignore"
+ ".npmrc"
+ "audit-ci.jsonc"
+ "biome.json"
+ "package.json"
+ "pnpm-workspace.yaml"
+ "pnpm-lock.yaml"
+ "turbo.json"
+ (inDirectory "./build")
+ (inDirectory "${submodule}")
+ ];
+ };
+
+ checkDeps = with pkgs; [ nhost-cli biome ];
+
+ buildInputs = with pkgs; [ nodejs ];
+
+ nativeBuildInputs = with pkgs; [ pnpm cacert ];
+in
+{
+ devShell = nixops-lib.js.devShell {
+ inherit node_modules;
+
+ buildInputs = [
+ ] ++ checkDeps ++ buildInputs ++ nativeBuildInputs;
+ };
+
+ check = nixops-lib.js.check {
+ inherit src node_modules submodule buildInputs nativeBuildInputs checkDeps;
+
+ preCheck = ''
+ rm -rf packages/nhost-js
+ cp -r ${self.packages.${pkgs.system}.nhost-js} packages/nhost-js
+ '';
+ };
+
+ package = pkgs.stdenv.mkDerivation {
+ inherit name version src;
+
+ nativeBuildInputs = with pkgs; [ pnpm cacert nodejs ];
+ buildInputs = with pkgs; [ nodejs ];
+
+ buildPhase = ''
+ mkdir -p $TMPDIR/home
+ export HOME=$TMPDIR/home
+
+ chmod +w -R .
+
+ for absdir in $(pnpm list --recursive --depth=-1 --parseable); do
+ dir=$(realpath --relative-to="$PWD" "$absdir")
+ echo "➜ Copying node_modules for $dir"
+ cp -r ${node_modules}/$dir/node_modules $dir/node_modules
+ done
+
+ rm -rf packages/nhost-js
+ cp -r ${self.packages.${pkgs.system}.nhost-js} packages/nhost-js
+
+ cd ${submodule}
+
+ pnpm build
+ '';
+
+ installPhase = ''
+ mkdir -p $out
+ '';
+ };
+}
diff --git a/flake.nix b/flake.nix
index 3c5fd0711..8cad63015 100644
--- a/flake.nix
+++ b/flake.nix
@@ -53,6 +53,11 @@
nixopsf = import ./nixops/project.nix {
inherit self pkgs nix-filter nixops-lib;
};
+
+ tutorialsf = import ./examples/tutorials/project.nix {
+ inherit self pkgs nix-filter nixops-lib nix2containerPkgs;
+ };
+
in
{
#nixops
@@ -68,6 +73,7 @@
mintlify-openapi = mintlify-openapif.check;
nhost-js = nhost-jsf.check;
nixops = nixopsf.check;
+ tutorials = tutorialsf.check;
};
devShells = flake-utils.lib.flattenTree {
@@ -116,6 +122,7 @@
mintlify-openapi = mintlify-openapif.devShell;
nhost-js = nhost-jsf.devShell;
nixops = nixopsf.devShell;
+ tutorials = tutorialsf.devShell;
};
packages = flake-utils.lib.flattenTree {
@@ -127,6 +134,7 @@
mintlify-openapi = mintlify-openapif.package;
nhost-js = nhost-jsf.package;
nixops = nixopsf.package;
+ tutorials = tutorialsf.package;
};
}
);
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 49e6a3f6d..cd17a1316 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -5,6 +5,7 @@ packages:
- examples/demos/**
- examples/guides/**
- examples/quickstarts/**
+ - examples/tutorials/**
- '!**/test/**'
- '!out/**'
- '!**/functions'