diff --git a/.env.example b/.env.example
index ad7ce014..fb3d6ea4 100644
--- a/.env.example
+++ b/.env.example
@@ -4,17 +4,10 @@ DATABASE_URL=postgresql://devcard:devcard@localhost:5432/devcard?schema=public
# ─── Redis ───
REDIS_URL=redis://localhost:6379
-# ─── Set The Url ───
-PUBLIC_APP_URL=
-
# ─── JWT ───
-# JWT_SECRET: any long random string, minimum 32 characters
-# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
JWT_SECRET=your-super-secret-jwt-key-change-in-production
# ─── Encryption (for OAuth tokens) ───
-# ENCRYPTION_KEY: must be exactly 32 bytes = 64 hex characters
-# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
ENCRYPTION_KEY=your-32-byte-hex-encryption-key-here
# ─── GitHub OAuth ───
@@ -32,4 +25,4 @@ MOBILE_REDIRECT_URI=devcard://oauth/callback
# ─── Server ───
PORT=3000
-NODE_ENV=development
\ No newline at end of file
+NODE_ENV=development
diff --git a/.github/scripts/ciScript.js b/.github/scripts/ciScript.js
deleted file mode 100644
index f8cb346c..00000000
--- a/.github/scripts/ciScript.js
+++ /dev/null
@@ -1,101 +0,0 @@
-module.exports = async ({ github, context, core }) => {
- const owner = context.repo.owner;
- const repo = context.repo.repo;
- const pr = context.payload.pull_request;
- const prNumber = pr.number;
- const prState = pr.state;
-
- const backendFiles = [];
- const backendTests = [];
- const mobileFiles = [];
- const webFiles = [];
-
- try {
- if (prState === 'closed') {
- console.log(`PR state is: ${prState}`);
- return {
- backendChanged: false,
- mobileChanged: false,
- webChanged: false
- };
- }
-
- const changedFiles = await github.paginate(
- github.rest.pulls.listFiles,
- {
- owner,
- repo,
- pull_number: prNumber
- }
- );
-
- changedFiles.forEach((file) => {
- const fileName = file.filename;
-
- if (fileName.startsWith('apps/backend/')) {
- backendFiles.push(fileName);
-
- const relative = fileName.replace('apps/backend/src/', '');
- const baseName = relative
- .split('/')
- .pop()
- ?.replace(/\.(ts|tsx|js|jsx)$/, '');
-
- if (baseName) {
- backendTests.push(`src/__tests__/${baseName}.test.ts`);
- }
-
- } else if (fileName.startsWith('apps/mobile/')) {
- mobileFiles.push(fileName);
- } else if (fileName.startsWith('apps/web/')) {
- webFiles.push(fileName);
- }
- });
-
- console.log({
- backendFiles,
- backendTests,
- mobileFiles,
- webFiles,
- });
-
- core.setOutput(
- "backendFiles",
- backendFiles
- .map(file => file.replace("apps/backend/", ""))
- .join(" ")
- );
-
- core.setOutput(
- "backendTests",
- [...new Set(backendTests)].join(" ")
- );
-
- core.setOutput(
- "mobileFiles",
- mobileFiles
- .map(file => file.replace("apps/mobile/", ""))
- .join(" ")
- );
-
- core.setOutput(
- "webFiles",
- webFiles
- .map(file => file.replace("apps/web/", ""))
- .join(" ")
- );
-
- core.setOutput("backendChanged", backendFiles.length > 0);
- core.setOutput("mobileChanged", mobileFiles.length > 0);
- core.setOutput("webChanged", webFiles.length > 0);
-
- } catch (error) {
- console.error(error);
-
- return {
- backendChanged: false,
- mobileChanged: false,
- webChanged: false
- };
- }
-};
\ No newline at end of file
diff --git a/.github/scripts/commentResults.js b/.github/scripts/commentResults.js
deleted file mode 100644
index 50cd1395..00000000
--- a/.github/scripts/commentResults.js
+++ /dev/null
@@ -1,101 +0,0 @@
-module.exports = async ({
- github,
- context,
- backend,
- mobile,
- web,
- backendLint,
- backendTest,
- backendTypecheck,
- mobileLint,
- mobileTest,
- webCheck,
- webBuild
-}) => {
- const owner = context.repo.owner;
- const repo = context.repo.repo;
- const prNumber = context.payload.pull_request.number;
-
- const emoji = (status) => {
- if (status === 'success') return '✅';
- if (status === 'failure') return '❌';
- if (status === 'skipped') return '⏭️';
- return '⚪';
- };
-
- const label = (status) => {
- if (!status) return '⚪ unknown';
- return `${emoji(status)} ${status}`;
- };
-
- const anyFailure = [
- backend,
- mobile,
- web
- ].includes('failure');
-
- const title = anyFailure
- ? '❌ Some checks failed'
- : '✅ CI completed';
-
- const timestamp = new Date().toUTCString();
-
- const body = `## CI Results — ${title}
-
-### 🖥️ Backend (${label(backend)})
-| Check | Status |
-|---|---|
-| Lint | ${label(backendLint)} |
-| Test | ${label(backendTest)} |
-| Typecheck | ${label(backendTypecheck)} |
-
-### 📱 Mobile (${label(mobile)})
-| Check | Status |
-|---|---|
-| Lint | ${label(mobileLint)} |
-| Test | ${label(mobileTest)} |
-
-### 🌐 Web (${label(web)})
-| Check | Status |
-|---|---|
-| Check | ${label(webCheck)} |
-| Build | ${label(webBuild)} |
-
----
-🕐 Last updated: \`${timestamp}\``;
-
- const COMMENT_MARKER = '## CI Results —';
-
- try {
- const comments = await github.paginate(
- github.rest.issues.listComments,
- {
- owner,
- repo,
- issue_number: prNumber
- }
- );
-
- const existing = comments.find(
- c => c.body && c.body.startsWith(COMMENT_MARKER)
- );
-
- if (existing) {
- await github.rest.issues.updateComment({
- owner,
- repo,
- comment_id: existing.id,
- body
- });
- } else {
- await github.rest.issues.createComment({
- owner,
- repo,
- issue_number: prNumber,
- body
- });
- }
- } catch (err) {
- console.error(err);
- }
-};
\ No newline at end of file
diff --git a/.github/scripts/discordPinReminder.js b/.github/scripts/discordPinReminder.js
deleted file mode 100644
index d5724578..00000000
--- a/.github/scripts/discordPinReminder.js
+++ /dev/null
@@ -1,37 +0,0 @@
-module.exports = async ({ github, context }) => {
- const pr = context.payload.pull_request;
- const ignoreUsers = [
- 'ShantKhatri',
- 'Harxhit',
- 'blankirigaya'
- ]
- try {
- // Only continue if merged
- if (!pr || !pr.merged) {
- console.log('PR not merged.');
- return;
- }
-
- const prNumber = pr.number;
- const contributor = pr.user.login;
-
- if(ignoreUsers.includes(contributor)){
- console.log(`Ignoring PR #${prNumber} by ${contributor}`);
- return;
- }
-
- await github.rest.issues.createComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: prNumber,
- body: `Congratulations @${contributor} on getting PR #${prNumber} merged!
-
- Thank you for your contribution. Please mention @Harxhit in our Discord server to receive the appropriate GSSoC labels and recognition.
- `
- });
-
- console.log(`Comment added to PR #${prNumber}`);
- } catch (error) {
- console.error(error)
- }
-};
diff --git a/.github/scripts/unassignIssues.js b/.github/scripts/unassignIssues.js
deleted file mode 100644
index b4886e91..00000000
--- a/.github/scripts/unassignIssues.js
+++ /dev/null
@@ -1,177 +0,0 @@
-module.exports = async ({ github, context }) => {
- const owner = context.repo.owner;
- const repo = context.repo.repo;
-
- const PROTECTED_ASSIGNEES = [
- 'ShantKhatri',
- 'Harxhit',
- 'blankirigaya'
- ];
-
- // Fetch all open issues (excluding PRs)
- let page = 1;
- let issues = [];
-
- while (true) {
- const { data } = await github.rest.issues.listForRepo({
- owner,
- repo,
- state: 'open',
- per_page: 100,
- page,
- });
-
- const onlyIssues = data.filter(
- item => !item.pull_request
- );
-
- issues = issues.concat(onlyIssues);
-
- if (data.length < 100) break;
- page++;
- }
-
- console.log(
- `Found ${issues.length} open issue(s) to check.`
- );
-
- for (const issue of issues) {
- const issueNumber = issue.number;
-
- // Skip if no assignees
- if (
- !issue.assignees ||
- issue.assignees.length === 0
- ) {
- console.log(
- `Issue #${issueNumber} has no assignees — skipping.`
- );
- continue;
- }
-
- const assigneeLogins =
- issue.assignees.map(a => a.login);
-
- // Skip protected assignees
- const hasProtectedAssignee =
- assigneeLogins.some(login =>
- PROTECTED_ASSIGNEES.includes(login)
- );
-
- if (hasProtectedAssignee) {
- console.log(
- `Issue #${issueNumber} has protected assignee(s) — skipping.`
- );
- continue;
- }
-
- let linkedPRFound = false;
- let assignedAt = null;
-
- try {
- const timeline =
- await github.rest.issues.listEventsForTimeline({
- owner,
- repo,
- issue_number: issueNumber,
- per_page: 100,
- });
-
- // Check linked PR
- linkedPRFound = timeline.data.some(event => {
- const pr = event.source?.issue;
-
- return (
- event.event === 'cross-referenced' &&
- pr?.pull_request &&
- pr.state === 'open'
- );
- });
-
- // Find latest assignment event
- const assignEvents =
- timeline.data.filter(
- event => event.event === 'assigned'
- );
-
- if (assignEvents.length > 0) {
- assignedAt =
- assignEvents[
- assignEvents.length - 1
- ].created_at;
- }
- } catch (err) {
- console.log(
- `Could not fetch timeline for issue #${issueNumber}: ${err.message}`
- );
- continue;
- }
-
- // Skip if no assignment timestamp
- if (!assignedAt) {
- console.log(
- `Issue #${issueNumber} has no assignment timestamp — skipping.`
- );
- continue;
- }
-
- const assignedDate =
- new Date(assignedAt);
- const now = new Date();
-
- const daysAssigned =
- (now - assignedDate) /
- (1000 * 60 * 60 * 24);
-
- console.log(
- `Issue #${issueNumber} assigned for ${daysAssigned.toFixed(
- 1
- )} day(s).`
- );
-
- // Skip if assigned <= 5 days
- if (daysAssigned <= 5) {
- console.log(
- `Issue #${issueNumber} assigned less than 5 days ago — skipping.`
- );
- continue;
- }
-
- // Skip if linked PR exists
- if (linkedPRFound) {
- console.log(
- `Issue #${issueNumber} has linked open/draft PR — keeping assignment.`
- );
- continue;
- }
-
- // Remove assignees
- await github.rest.issues.removeAssignees({
- owner,
- repo,
- issue_number: issueNumber,
- assignees: assigneeLogins,
- });
-
- const assigneesMention =
- assigneeLogins
- .map(user => `@${user}`)
- .join(', ');
-
- // Comment
- await github.rest.issues.createComment({
- owner,
- repo,
- issue_number: issueNumber,
- body: `Hey @ShantKhatri (Project Admin) and @Harxhit (Maintainer),
-
-This issue (previously assigned to ${assigneesMention}) has been **automatically unassigned** because no linked pull request was found within 5 days of assignment.
-
-If work is in progress, please open and link a PR to keep the assignment active.`,
- });
-
- console.log(
- `Issue #${issueNumber} unassigned successfully.`
- );
- }
-};
diff --git a/.github/scripts/welcomeScript.js b/.github/scripts/welcomeScript.js
deleted file mode 100644
index aa48ce7b..00000000
--- a/.github/scripts/welcomeScript.js
+++ /dev/null
@@ -1,72 +0,0 @@
-module.exports = async ({ github, context }) => {
- const owner = context.repo.owner;
- const repo = context.repo.repo;
- const issueNumber = context.issue.number;
- const eventName = context.eventName;
- const ghUsername = context.payload.sender.login;
-
- try {
- const issueAssociation =
- context.payload.issue?.author_association;
-
- if (
- eventName === 'issues' &&
- issueAssociation === 'NONE'
- ) {
- // Verify this is truly their first issue (listForRepo returns PRs too)
- const userIssues = await github.rest.issues.listForRepo({
- owner,
- repo,
- state: 'all',
- creator: ghUsername,
- per_page: 10
- });
-
- const actualIssues = userIssues.data.filter(issue => !issue.pull_request);
-
- if (actualIssues.length === 1) {
- return await github.rest.issues.createComment({
- owner,
- repo,
- issue_number: issueNumber,
- body: `👋 Thanks for opening your first issue, @${ghUsername}!
-
-We appreciate your contribution and are excited to have you here. Please make sure to follow the contribution guidelines and provide as much detail as possible.
-
-To stay updated, ask questions, and connect with maintainers and contributors, please join our Discord community:
-https://discord.gg/QueQN83wn
-
-Looking forward to collaborating with you!`
- });
- }
- }
-
- const prAssociation =
- context.payload.pull_request?.author_association;
-
- if (
- eventName === 'pull_request_target' &&
- (
- prAssociation === 'FIRST_TIMER' ||
- prAssociation === 'FIRST_TIME_CONTRIBUTOR'
- )
- ) {
- return await github.rest.issues.createComment({
- owner,
- repo,
- issue_number: issueNumber,
- body: `🎉 Thanks for your first contribution, @${ghUsername}!
-
-We're excited to have you here. A maintainer will review your PR soon. Please check CI results and review any feedback if needed.
-
-To stay updated, ask questions, and connect with maintainers and contributors, please join our Discord community:
-https://discord.gg/QueQN83wn
-
-Looking forward to collaborating with you!`
- });
- }
-
- } catch (error) {
- console.error(error);
- }
-};
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 45b5f7af..00000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,178 +0,0 @@
-name: CI
-
-on:
- pull_request_target:
- types: [opened, synchronize, reopened]
-
-permissions:
- pull-requests: write
-
-jobs:
- detect-changes:
- runs-on: ubuntu-latest
-
- outputs:
- backendChanged: ${{ steps.detect.outputs.backendChanged }}
- mobileChanged: ${{ steps.detect.outputs.mobileChanged }}
- webChanged: ${{ steps.detect.outputs.webChanged }}
- backendFiles: ${{ steps.detect.outputs.backendFiles }}
- mobileFiles: ${{ steps.detect.outputs.mobileFiles }}
- webFiles: ${{ steps.detect.outputs.webFiles }}
- backendTests: ${{ steps.detect.outputs.backendTests }}
-
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
-
- - name: Detect changed files
- id: detect
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const script = require('./.github/scripts/ciScript.js');
- return await script({ github, context, core });
-
- backend-ci:
- needs: detect-changes
- if: needs.detect-changes.outputs.backendChanged == 'true'
- runs-on: ubuntu-latest
-
- outputs:
- backend_lint: ${{ steps.backend_lint.outcome }}
- backend_test: ${{ steps.backend_test.outcome }}
- backend_typecheck: ${{ steps.backend_typecheck.outcome }}
-
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
-
- - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
- with:
- node-version: 22
-
- - uses: pnpm/action-setup@v6.0.8
-
- - run: pnpm install
-
- - name: Backend lint
- id: backend_lint
- continue-on-error: true
- run: cd apps/backend && pnpm eslint ${{ needs.detect-changes.outputs.backendFiles }}
-
- - name: Backend test
- id: backend_test
- continue-on-error: true
- run: cd apps/backend && pnpm test ${{ needs.detect-changes.outputs.backendTests }}
-
- - name: Backend typecheck
- id: backend_typecheck
- continue-on-error: true
- run: cd apps/backend && pnpm typecheck ${{ needs.detect-changes.outputs.backendFiles }}
-
- - name: Fail backend if checks failed
- if: steps.backend_lint.outcome == 'failure' || steps.backend_test.outcome == 'failure' || steps.backend_typecheck.outcome == 'failure'
- run: exit 1
-
- web-ci:
- needs: detect-changes
- if: needs.detect-changes.outputs.webChanged == 'true'
- runs-on: ubuntu-latest
-
- outputs:
- web_check: ${{ steps.web_check.outcome }}
- web_build: ${{ steps.web_build.outcome }}
-
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
-
- - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
- with:
- node-version: 22
-
- - uses: pnpm/action-setup@v6.0.8
-
- - run: pnpm install
-
- - name: Web check
- id: web_check
- continue-on-error: true
- run: cd apps/web && pnpm check
-
- - name: Web build
- id: web_build
- continue-on-error: true
- run: cd apps/web && pnpm build
-
- - name: Fail web if checks failed
- if: steps.web_check.outcome == 'failure' || steps.web_build.outcome == 'failure'
- run: exit 1
-
- mobile-ci:
- needs: detect-changes
- if: needs.detect-changes.outputs.mobileChanged == 'true'
- runs-on: ubuntu-latest
-
- outputs:
- mobile_lint: ${{ steps.mobile_lint.outcome }}
- mobile_test: ${{ steps.mobile_test.outcome }}
-
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
-
- - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
- with:
- node-version: 22
-
- - uses: pnpm/action-setup@v6.0.8
-
- - run: pnpm install
-
- - name: Mobile lint
- id: mobile_lint
- continue-on-error: true
- run: cd apps/mobile && pnpm eslint ${{ needs.detect-changes.outputs.mobileFiles }}
-
- - name: Mobile test
- id: mobile_test
- continue-on-error: true
- run: cd apps/mobile && pnpm test
-
- - name: Fail mobile if checks failed
- if: steps.mobile_lint.outcome == 'failure' || steps.mobile_test.outcome == 'failure'
- run: exit 1
-
- comment-results:
- needs:
- - backend-ci
- - web-ci
- - mobile-ci
- if: always()
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
-
- - name: Comment results
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const script = require('./.github/scripts/commentResults.js');
-
- await script({
- github,
- context,
-
- backend: '${{ needs.backend-ci.result }}',
- web: '${{ needs.web-ci.result }}',
- mobile: '${{ needs.mobile-ci.result }}',
-
- backendLint: '${{ needs.backend-ci.outputs.backend_lint }}',
- backendTest: '${{ needs.backend-ci.outputs.backend_test }}',
- backendTypecheck: '${{ needs.backend-ci.outputs.backend_typecheck }}',
-
- mobileLint: '${{ needs.mobile-ci.outputs.mobile_lint }}',
- mobileTest: '${{ needs.mobile-ci.outputs.mobile_test }}',
-
- webCheck: '${{ needs.web-ci.outputs.web_check }}',
- webBuild: '${{ needs.web-ci.outputs.web_build }}'
- });
\ No newline at end of file
diff --git a/.github/workflows/gssoc-discord-pin-reminder.yml b/.github/workflows/gssoc-discord-pin-reminder.yml
deleted file mode 100644
index 5c6cd7cb..00000000
--- a/.github/workflows/gssoc-discord-pin-reminder.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: GSSoC Discord Pin Reminder
-
-on:
- pull_request_target:
- types: [closed]
- workflow_dispatch:
-
-jobs:
- discord-pin-reminder:
- if: github.event.pull_request.merged == true
- runs-on: ubuntu-latest
-
- permissions:
- pull-requests: write
- issues: write
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
- - name: Notify contributor about Discord GSSoC labels
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const script = require('./.github/scripts/discordPinReminder.js');
- await script({ github, context });
diff --git a/.github/workflows/unassign-unlinked-issues.yml b/.github/workflows/unassign-unlinked-issues.yml
deleted file mode 100644
index 40bcab5f..00000000
--- a/.github/workflows/unassign-unlinked-issues.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: Unassign Issues Without Linked PR
-
-on:
- schedule:
- - cron: '0 9 */5 * *' # Runs every 5 days at 9:00 AM UTC
- workflow_dispatch: # Also allows manual triggering
-
-jobs:
- unassign-issues:
- runs-on: ubuntu-latest
- permissions:
- issues: write
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
-
- - name: Unassign issues with no linked PR and notify
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #v9.0.0
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const script = require('./.github/scripts/unassignIssues.js');
- await script({ github, context });
\ No newline at end of file
diff --git a/.github/workflows/welcome-first-time.yml b/.github/workflows/welcome-first-time.yml
deleted file mode 100644
index 2f3acc4e..00000000
--- a/.github/workflows/welcome-first-time.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: Welcome First-Time Contributors
-
-on:
- issues:
- types: [opened]
- pull_request_target:
- types: [opened]
-
-permissions:
- issues: write
- pull-requests: write
-
-jobs:
- welcome:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
-
- - name: Welcome first-time contributor
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const script = require('./.github/scripts/welcomeScript.js');
- await script({ github, context });
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0f95620b..00cb1e8b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,12 +1,6 @@
# Contributing to DevCard
-
-
-
-
-
-
-**Join the community** — ask questions, get help, discuss ideas, and meet other contributors on our [Discord server](https://discord.gg/QueQN83wn).
+Thank you for your interest in contributing to DevCard! This guide will help you get started.
## Development Setup
diff --git a/README.md b/README.md
index 26261279..cbe700ae 100644
--- a/README.md
+++ b/README.md
@@ -6,9 +6,6 @@
-
-
-
@@ -73,11 +70,6 @@ docker compose up -d
# Copy environment config
cp .env.example .env
-# ⚠️ Replace secret placeholders before starting the server:
-# JWT_SECRET → node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
-# ENCRYPTION_KEY → node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
-# Paste the generated values into your .env file. Never use placeholders in production.
-
# Run database migrations
pnpm db:migrate
@@ -278,32 +270,6 @@ New to open source? We've got you covered! Check out our [Good First Issues](htt
See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions, coding standards, and PR process.
-## Contributors
-
-Thanks to all the amazing people who contribute to **DevCard** 🚀
-
-
-
-
-
-
-
-
-
-## Project Support
-
-
-
-
-
-
-
-
-
-
-
----
-
## License
DevCard is licensed under the [Apache License 2.0](./LICENSE).
diff --git a/apps/backend/README.md b/apps/backend/README.md
deleted file mode 100644
index a807f250..00000000
--- a/apps/backend/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# DevCard Backend
-
-## Follow Engine Architecture
-
-DevCard implements a multi-layered Hybrid Follow Engine designed to connect platform professionals seamlessly while maintaining platform policy compliance.
-
-```mermaid
-graph TD
- A[User triggers Follow/Connect] --> B{Check Platform Strategy}
- B -- api (GitHub) --> C[Layer 1: Direct OAuth API integration]
- B -- webview (LinkedIn) --> D[Layer 2: In-app WebView Interaction Engine]
- B -- link (GitLab/Devfolio) --> E[Layer 3: Native deep-linking / Browser redirect]
- B -- copy (Discord) --> F[Layer 4: Clipboard Copy fallback]
-```
-
-### Layer 2: WebView Interaction Engine (LinkedIn)
-
-Due to LinkedIn's modern API restrictions preventing programmatic connection requests, direct API follow (Layer 1) is not viable. Instead, the WebView Interaction Engine routes the action through a secure, native WebView:
-
-1. **Routing Strategy**: The backend parses the connection request and returns `{ strategy: 'webview', url }` containing the resolved profile link.
-2. **Session Persistence**: The mobile WebView loads the target profile URL using system-level OAuth cookie-sharing (`sharedCookiesEnabled={true}`), ensuring the user remains authenticated.
-3. **DOM Introspection**: A lightweight JavaScript snippet is injected to continuously poll for the native LinkedIn 'Connect' button, smooth-scrolls it into view, and highlights it visually to encourage action.
-4. **Interactive Send**: Users retain full control over actual connection request submission, adhering completely to platform terms of service.
-5. **State Detection**:
- - URL State Polling: The engine inspects URL transitions containing `invite-sent` or similar sub-routes.
- - DOM Observation: The injected Javascript queries for structural indicators of successful invitation (e.g. "Pending" button state or toaster text) and posts a serialized message back to the native layer.
-6. **Robust Fallback**: If network or WebView loading times out (>10s), the engine gracefully falls back to native deep links (`linkedin://profile?id={username}`) or launches the default browser with an interactive custom in-app overlay.
-7. **Telemetry Logging**: Upon client-side success (detected via state changes or DOM indicators), the mobile app makes a `POST /api/follow/:platform/:targetUsername/log` request to the backend. This writes a record to the `FollowLog` database table for auditing and analytics tracking.
diff --git a/apps/backend/eslint.config.js b/apps/backend/eslint.config.js
deleted file mode 100644
index 3924db19..00000000
--- a/apps/backend/eslint.config.js
+++ /dev/null
@@ -1,202 +0,0 @@
-import tseslint from 'typescript-eslint';
-import pluginN from 'eslint-plugin-n';
-import pluginImportX from 'eslint-plugin-import-x';
-import pluginPromise from 'eslint-plugin-promise';
-import pluginSecurity from 'eslint-plugin-security';
-import pluginUnicorn from 'eslint-plugin-unicorn';
-
-export default tseslint.config(
-
- // ─── Global Ignores ──────────────────────────────────────────────────────────
- {
- ignores: [
- 'dist/**',
- 'build/**',
- 'node_modules/**',
- 'coverage/**',
- 'prisma/migrations/**',
- '**/*.d.ts',
- ],
- },
-
- // ─── Base: ESLint Recommended + TypeScript ──────────────────────────────────
- ...tseslint.configs.recommendedTypeChecked,
-
- // ─── Main Config ────────────────────────────────────────────────────────────
- {
- files: ['src/**/*.ts'],
-
- languageOptions: {
- parserOptions: {
- projectService: true,
- tsconfigRootDir: import.meta.dirname,
- },
- },
-
- plugins: {
- n: pluginN,
- 'import-x': pluginImportX,
- promise: pluginPromise,
- security: pluginSecurity,
- unicorn: pluginUnicorn,
- },
-
- settings: {
- 'import-x/resolver': {
- typescript: { project: './tsconfig.json' },
- node: true,
- },
- node: { version: '>=18.0.0' },
- },
-
- rules: {
-
- // ── TypeScript: Type Safety: currently off ─────────────────────────────────────────────
- '@typescript-eslint/no-explicit-any': 'off',
- '@typescript-eslint/no-unsafe-argument': 'off',
- '@typescript-eslint/no-unsafe-assignment': 'off',
- '@typescript-eslint/no-unsafe-call': 'off',
- '@typescript-eslint/no-unsafe-member-access': 'off',
- '@typescript-eslint/no-unsafe-return': 'off',
- '@typescript-eslint/no-non-null-assertion': 'off',
- '@typescript-eslint/no-unnecessary-type-assertion': 'off',
- '@typescript-eslint/prefer-nullish-coalescing': 'off',
- '@typescript-eslint/prefer-optional-chain': 'off',
- '@typescript-eslint/strict-boolean-expressions': 'off',
-
- // ── TypeScript: Async / Promises: currently off ────────────────────────────────────────
- '@typescript-eslint/no-floating-promises': 'off',
- '@typescript-eslint/no-misused-promises': 'off',
- '@typescript-eslint/await-thenable': 'off',
- '@typescript-eslint/require-await': 'off',
- '@typescript-eslint/return-await': 'off',
-
- // ── TypeScript: Imports ─────────────────────────────────────────────────
- '@typescript-eslint/consistent-type-imports': [
- 'error',
- { prefer: 'type-imports', fixStyle: 'inline-type-imports' },
- ],
- '@typescript-eslint/consistent-type-exports': 'error',
- '@typescript-eslint/no-import-type-side-effects': 'error',
-
- // ── TypeScript: Code Quality ────────────────────────────────────────────
- '@typescript-eslint/no-unused-vars': [
- 'error',
- {
- argsIgnorePattern: '^_',
- varsIgnorePattern: '^_',
- caughtErrorsIgnorePattern: '^_',
- },
- ],
- '@typescript-eslint/explicit-function-return-type': [
- 'warn',
- {
- allowExpressions: true,
- allowTypedFunctionExpressions: true,
- },
- ],
- '@typescript-eslint/prefer-as-const': 'error',
- '@typescript-eslint/no-redundant-type-constituents': 'warn',
- '@typescript-eslint/no-shadow': 'error',
- '@typescript-eslint/no-use-before-define': ['error', { functions: false }],
-
- // ── Node.js ─────────────────────────────────────────────────────────────
- 'n/no-deprecated-api': 'error',
- 'n/no-extraneous-import': 'error',
- 'n/no-process-exit': 'off',
- 'n/prefer-global/buffer': ['error', 'always'],
- 'n/prefer-global/process': ['error', 'always'],
- 'n/prefer-promises/fs': 'error',
- 'n/prefer-promises/dns': 'error',
- 'n/no-sync': 'warn',
-
- // ── Imports (import-x) ──────────────────────────────────────────────────
- 'import-x/no-duplicates': 'error',
- 'import-x/no-cycle': 'off',
- 'import-x/no-self-import': 'error',
- 'import-x/first': 'error',
- 'import-x/newline-after-import': 'error',
- 'import-x/order': [
- 'error',
- {
- groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'type'],
- 'newlines-between': 'always',
- alphabetize: { order: 'asc', caseInsensitive: true },
- },
- ],
-
- // ── Promises ────────────────────────────────────────────────────────────
- 'promise/always-return': 'off',
- 'promise/catch-or-return': 'off',
- 'promise/no-new-statics': 'error',
- 'promise/no-return-wrap': 'error',
- 'promise/param-names': 'error',
- 'promise/no-promise-in-callback': 'warn',
-
- // ── Security ────────────────────────────────────────────────────────────
- 'security/detect-object-injection': 'off',
- 'security/detect-non-literal-regexp': 'warn',
- 'security/detect-non-literal-fs-filename': 'warn',
- 'security/detect-eval-with-expression': 'error',
- 'security/detect-child-process': 'warn',
- 'security/detect-possible-timing-attacks': 'warn',
-
- // ── Unicorn ─────────────────────────────────────────────────────────────
- 'unicorn/prefer-node-protocol': 'error',
- 'unicorn/no-process-exit': 'off',
- 'unicorn/error-message': 'off',
- 'unicorn/throw-new-error': 'off',
- 'unicorn/no-useless-undefined': 'off',
- 'unicorn/prefer-string-slice': 'warn',
- 'unicorn/no-for-loop': 'off',
- 'unicorn/prefer-includes': 'warn',
- 'unicorn/no-array-for-each': 'off',
- 'unicorn/prefer-ternary': 'off',
- 'unicorn/prevent-abbreviations': 'off',
-
- // ── Core ESLint ─────────────────────────────────────────────────────────
- 'no-console': 'warn',
- 'eqeqeq': ['error', 'always'],
- 'no-var': 'error',
- 'prefer-const': 'error',
- 'no-throw-literal': 'error',
- 'curly': ['error', 'all'],
- 'object-shorthand': 'error',
- 'no-lonely-if': 'warn',
- 'no-nested-ternary': 'off',
- 'prefer-rest-params': 'error',
- 'prefer-spread': 'error',
- 'no-param-reassign': [
- 'error',
- {
- props: true,
- ignorePropertyModificationsFor: ['acc', 'request', 'reply'],
- },
- ],
- },
- },
-
- // ─── Test File Overrides ────────────────────────────────────────────────────
- {
- files: ['**/*.test.ts', '**/*.spec.ts', 'src/__tests__/**/*.ts'],
- rules: {
- '@typescript-eslint/no-explicit-any': 'off',
- '@typescript-eslint/no-unsafe-assignment': 'off',
- '@typescript-eslint/no-unsafe-member-access': 'off',
- '@typescript-eslint/no-non-null-assertion': 'off',
- '@typescript-eslint/no-floating-promises': 'off',
- 'security/detect-object-injection': 'off',
- 'no-console': 'off',
- },
- },
-
- // ─── Prisma Seed / Scripts Override ────────────────────────────────────────
- {
- files: ['prisma/**/*.ts', 'scripts/**/*.ts'],
- rules: {
- 'n/no-process-exit': 'off',
- 'unicorn/no-process-exit': 'off',
- 'no-console': 'off',
- },
- },
-);
\ No newline at end of file
diff --git a/apps/backend/package.json b/apps/backend/package.json
index 8bc19bf8..b8d11411 100644
--- a/apps/backend/package.json
+++ b/apps/backend/package.json
@@ -9,13 +9,11 @@
"start": "node dist/server.js",
"test": "vitest run",
"test:watch": "vitest",
- "lint:fix": "eslint src/ --fix",
"lint": "eslint src/",
"db:migrate": "prisma migrate dev",
"db:deploy": "prisma migrate deploy",
"db:seed": "tsx prisma/seed.ts",
- "db:studio": "prisma studio",
- "typecheck": "tsc --noEmit"
+ "db:studio": "prisma studio"
},
"dependencies": {
"@devcard/shared": "workspace:*",
@@ -24,7 +22,6 @@
"@fastify/helmet": "^12.0.0",
"@fastify/jwt": "^9.0.0",
"@fastify/multipart": "^9.0.0",
- "@fastify/rate-limit": "^10.3.0",
"@fastify/static": "^8.0.0",
"@prisma/client": "^6.0.0",
"dotenv": "^16.4.0",
@@ -37,18 +34,10 @@
"devDependencies": {
"@types/node": "^22.0.0",
"@types/qrcode": "^1.5.0",
- "eslint": "^10.4.0",
- "eslint-import-resolver-typescript": "^4.4.4",
- "eslint-plugin-import-x": "^4.16.2",
- "eslint-plugin-n": "^18.0.1",
- "eslint-plugin-promise": "^7.3.0",
- "eslint-plugin-security": "^4.0.0",
- "eslint-plugin-unicorn": "^64.0.0",
"pino-pretty": "^13.1.3",
"prisma": "^6.0.0",
"tsx": "^4.0.0",
"typescript": "^5.4.0",
- "typescript-eslint": "^8.59.3",
"vitest": "^2.0.0"
}
-}
\ No newline at end of file
+}
diff --git a/apps/backend/prisma/schema.prisma b/apps/backend/prisma/schema.prisma
index 28458021..13dec572 100644
--- a/apps/backend/prisma/schema.prisma
+++ b/apps/backend/prisma/schema.prisma
@@ -1,10 +1,10 @@
generator client {
provider = "prisma-client-js"
}
+
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
-
}
model User {
@@ -29,11 +29,6 @@ model User {
ownedViews CardView[] @relation("cardOwner")
viewedCards CardView[] @relation("cardViewer")
followLogs FollowLog[]
- organizer Event[]
- attendedEvents EventAttendee[]
-
- ownedTeams Team[] @relation("TeamOwner")
- teamMemberships TeamMember[] @relation("TeamMember")
@@unique([provider, providerId])
@@map("users")
@@ -129,69 +124,3 @@ model FollowLog {
@@map("follow_logs")
}
-
-model Event {
- id String @id @default(uuid())
- name String
- slug String @unique
- location String
- description String?
- organizerId String
- startDate DateTime
- endDate DateTime
- isPublic Boolean @default(true)
- createdAt DateTime @default(now()) @map("created_at")
- attendees EventAttendee[]
-
- organizer User @relation(fields: [organizerId], references: [id])
-}
-
-model EventAttendee {
- id String @id @default(uuid())
- userId String
- eventId String
- joinedAt DateTime
-
- event Event @relation(fields: [eventId] , references: [id])
- user User @relation(fields: [userId],references: [id])
-
- @@unique([userId, eventId])
-}
-
-enum TeamRole {
- OWNER
- ADMIN
- MEMBER
-}
-
-model Team{
- id String @id @default(uuid())
- name String
- slug String @unique
- description String?
- avatarUrl String?
- ownerId String
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
-
- owner User @relation("TeamOwner", fields: [ownerId], references: [id], onDelete: Restrict)
- members TeamMember[] @relation("TeamMember")
-
- @@map("teams")
- @@index([slug])
-}
-
-model TeamMember{
- id String @id @default(uuid())
- teamId String
- userId String
- role TeamRole
- joinedAt DateTime
-
- team Team @relation("TeamMember",fields: [teamId] , references: [id], onDelete: Cascade)
- user User @relation("TeamMember",fields: [userId] , references: [id])
-
- @@unique([userId, teamId])
- @@index([userId])
- @@map("team_members")
-}
\ No newline at end of file
diff --git a/apps/backend/prisma/seed.ts b/apps/backend/prisma/seed.ts
index f19345d8..a799a3e3 100644
--- a/apps/backend/prisma/seed.ts
+++ b/apps/backend/prisma/seed.ts
@@ -142,8 +142,8 @@ async function main() {
}
main()
- .catch((error) => {
- console.error('❌ Seed failed:', error);
+ .catch((e) => {
+ console.error('❌ Seed failed:', e);
process.exit(1);
})
.finally(async () => {
diff --git a/apps/backend/src/__tests__/analytics.test.ts b/apps/backend/src/__tests__/analytics.test.ts
deleted file mode 100644
index 4f0d07ae..00000000
--- a/apps/backend/src/__tests__/analytics.test.ts
+++ /dev/null
@@ -1,466 +0,0 @@
-import {
- describe,
- it,
- expect,
- beforeEach,
- afterEach,
- vi,
-} from 'vitest';
-
-import Fastify, {
- type FastifyInstance,
-} from 'fastify';
-
-import type { PrismaClient } from '@prisma/client';
-
-import { analyticsRoutes } from '../routes/analytics';
-
-// ─── Shared mock data ────────────────────────────────────────────────────────
-
-const MOCK_USER_ID = 'user-001';
-
-// ─── Prisma mock ─────────────────────────────────────────────────────────────
-
-const prismaMock = {
- cardView: {
- count: vi.fn(),
- findMany: vi.fn(),
- groupBy: vi.fn(),
- },
- followLog: {
- count: vi.fn(),
- },
-};
-
-// ─── App factory ─────────────────────────────────────────────────────────────
-
-let mockJwtVerify = vi.fn();
-
-async function buildApp(): Promise {
- const app = Fastify({
- logger: false,
- });
-
- app.decorate(
- 'prisma',
- prismaMock as unknown as PrismaClient
- );
-
- app.decorateRequest(
- 'jwtVerify',
- function () {
- return mockJwtVerify();
- }
- );
-
- app.decorate(
- 'authenticate',
- async function (
- request: any,
- reply: any
- ) {
- try {
- const user =
- await request.jwtVerify();
-
- request.user = user;
- } catch (_err) {
- return reply.status(401).send({
- error: 'Unauthorized',
- });
- }
- }
- );
-
- await app.register(
- analyticsRoutes,
- {
- prefix: '/api/analytics',
- }
- );
-
- await app.ready();
- return app;
-}
-
-// ─── Helpers ─────────────────────────────────────────────────────────────────
-
-function authHeader(): Record {
- return {
- Authorization:
- 'Bearer mock-token',
- };
-}
-
-// ─── Test Suite ──────────────────────────────────────────────────────────────
-
-describe(
- 'Analytics API',
- () => {
- let app: FastifyInstance;
-
- beforeEach(
- async () => {
- vi.clearAllMocks();
-
- mockJwtVerify.mockResolvedValue(
- {
- id: MOCK_USER_ID,
- }
- );
-
- app = await buildApp();
- }
- );
-
- afterEach(
- async () => {
- await app.close();
- }
- );
-
- // ── GET /overview ───────────────────────────────────────────────────────
-
- describe(
- 'GET /api/analytics/overview',
- () => {
- it(
- '200 — returns analytics overview',
- async () => {
- prismaMock.cardView.count
- .mockResolvedValueOnce(
- 100
- )
- .mockResolvedValueOnce(
- 10
- );
-
- prismaMock.followLog.count.mockResolvedValue(
- 5
- );
-
- prismaMock.cardView.findMany.mockResolvedValue(
- [
- {
- id: 'view-1',
- viewer: {
- displayName:
- 'John',
- avatarUrl:
- null,
- },
- card: {
- title:
- 'My Card',
- },
- },
- ]
- );
-
- prismaMock.cardView.groupBy.mockResolvedValue(
- [
- {
- viewerId:
- 'u1',
- viewerIp:
- null,
- },
- {
- viewerId:
- 'u2',
- viewerIp:
- null,
- },
- ]
- );
-
- const res =
- await app.inject(
- {
- method:
- 'GET',
- url:
- '/api/analytics/overview',
- headers:
- authHeader(),
- }
- );
-
- expect(
- res.statusCode
- ).toBe(200);
-
- const body =
- res.json();
-
- expect(
- body.totalViews
- ).toBe(100);
-
- expect(
- body.viewsToday
- ).toBe(10);
-
- expect(
- body.totalFollows
- ).toBe(5);
-
- expect(
- body.uniqueViewers
- ).toBe(2);
-
- expect(
- body.recentViews
- ).toHaveLength(
- 1
- );
- }
- );
-
- it(
- '401 — rejects unauthenticated request',
- async () => {
- mockJwtVerify.mockRejectedValue(
- new Error(
- 'Unauthorized'
- )
- );
-
- const res =
- await app.inject(
- {
- method:
- 'GET',
- url:
- '/api/analytics/overview',
- }
- );
-
- expect(
- res.statusCode
- ).toBe(401);
-
- expect(
- res.json()
- ).toMatchObject(
- {
- error:
- 'Unauthorized',
- }
- );
- }
- );
- }
- );
-
- // ── GET /views ──────────────────────────────────────────────────────────
-
- describe(
- 'GET /api/analytics/views',
- () => {
- it(
- '200 — returns paginated views',
- async () => {
- prismaMock.cardView.count.mockResolvedValue(
- 45
- );
-
- prismaMock.cardView.findMany.mockResolvedValue(
- [
- {
- id:
- 'view-1',
- viewer:
- {
- id:
- 'viewer-1',
- username:
- 'john',
- displayName:
- 'John',
- avatarUrl:
- null,
- },
- card:
- {
- id:
- 'card-1',
- title:
- 'Portfolio',
- },
- },
- ]
- );
-
- const res =
- await app.inject(
- {
- method:
- 'GET',
- url:
- '/api/analytics/views?page=2',
- headers:
- authHeader(),
- }
- );
-
- expect(
- res.statusCode
- ).toBe(200);
-
- const body =
- res.json();
-
- expect(
- body.data
- ).toHaveLength(
- 1
- );
-
- expect(
- body.meta
- ).toMatchObject(
- {
- total:
- 45,
- page: 2,
- limit:
- 20,
- totalPages:
- 3,
- }
- );
-
- expect(
- prismaMock.cardView.findMany.mock.calls[0][0]
- ).toMatchObject(
- {
- skip:
- 20,
- take:
- 20,
- }
- );
- }
- );
-
- it(
- '200 — filters by cardId when provided',
- async () => {
- prismaMock.cardView.count.mockResolvedValue(
- 0
- );
-
- prismaMock.cardView.findMany.mockResolvedValue(
- []
- );
-
- const res =
- await app.inject(
- {
- method:
- 'GET',
- url:
- '/api/analytics/views?cardId=card-123',
- headers:
- authHeader(),
- }
- );
-
- expect(
- res.statusCode
- ).toBe(200);
-
- expect(
- prismaMock.cardView.count.mock.calls[0][0]
- ).toMatchObject(
- {
- where:
- {
- ownerId:
- MOCK_USER_ID,
- cardId:
- 'card-123',
- },
- }
- );
- }
- );
-
- it(
- '200 — defaults to page 1',
- async () => {
- prismaMock.cardView.count.mockResolvedValue(
- 0
- );
-
- prismaMock.cardView.findMany.mockResolvedValue(
- []
- );
-
- const res =
- await app.inject(
- {
- method:
- 'GET',
- url:
- '/api/analytics/views',
- headers:
- authHeader(),
- }
- );
-
- expect(
- res.statusCode
- ).toBe(200);
-
- expect(
- prismaMock.cardView.findMany.mock.calls[0][0]
- ).toMatchObject(
- {
- skip:
- 0,
- take:
- 20,
- }
- );
- }
- );
-
- it(
- '401 — rejects unauthenticated request',
- async () => {
- mockJwtVerify.mockRejectedValue(
- new Error(
- 'Unauthorized'
- )
- );
-
- const res =
- await app.inject(
- {
- method:
- 'GET',
- url:
- '/api/analytics/views',
- }
- );
-
- expect(
- res.statusCode
- ).toBe(401);
-
- expect(
- res.json()
- ).toMatchObject(
- {
- error:
- 'Unauthorized',
- }
- );
- }
- );
- }
- );
- }
-);
\ No newline at end of file
diff --git a/apps/backend/src/__tests__/app.test.ts b/apps/backend/src/__tests__/app.test.ts
deleted file mode 100644
index 648d98a6..00000000
--- a/apps/backend/src/__tests__/app.test.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-process.env.NODE_ENV = 'test';
-
-import { describe, it, expect } from 'vitest';
-import { buildApp } from '../app';
-
-describe('GET /health', () => {
- it('should return status ok', async () => {
- const app = await buildApp();
-
- const res = await app.inject({
- method: 'GET',
- url: '/health',
- });
-
- expect(res.statusCode).toBe(200);
- expect(JSON.parse(res.body)).toEqual({ status: 'ok' });
-
- await app.close();
- });
-});
\ No newline at end of file
diff --git a/apps/backend/src/__tests__/cards.test.ts b/apps/backend/src/__tests__/cards.test.ts
deleted file mode 100644
index 813883e8..00000000
--- a/apps/backend/src/__tests__/cards.test.ts
+++ /dev/null
@@ -1,440 +0,0 @@
-import { describe, it, expect, beforeEach, vi } from 'vitest';
-import Fastify from 'fastify';
-import { cardRoutes } from '../routes/cards.js';
-
-const USER_ID = 'user-123';
-const CARD_ID = 'card-abc';
-// Must be valid UUIDs — createCardSchema and updateCardSchema use z.string().uuid()
-const OWNED_LINK_ID = '11111111-1111-1111-1111-111111111111';
-const FOREIGN_LINK_ID = '22222222-2222-2222-2222-222222222222';
-
-const mockCard = {
- id: CARD_ID,
- userId: USER_ID,
- title: 'My Card',
- isDefault: true,
- createdAt: new Date(),
- updatedAt: new Date(),
- cardLinks: [],
-};
-
-// $transaction executes the callback synchronously against the same mock client,
-// mirroring Prisma's interactive-transactions API without a real DB connection.
-const mockPrisma = {
- card: {
- count: vi.fn(),
- create: vi.fn(),
- findMany: vi.fn(),
- findFirst: vi.fn(),
- findUnique: vi.fn(),
- update: vi.fn(),
- updateMany: vi.fn(),
- delete: vi.fn(),
- },
- cardLink: {
- deleteMany: vi.fn(),
- createMany: vi.fn(),
- },
- platformLink: {
- findMany: vi.fn(),
- },
- $transaction: vi.fn(),
-};
-
-// Re-wire $transaction before every test so that it executes the callback
-// against the same mock client, preserving existing per-operation mocks.
-function wireTransaction() {
- mockPrisma.$transaction.mockImplementation(
- async (callback: (tx: typeof mockPrisma) => Promise) => callback(mockPrisma),
- );
-}
-
-async function buildApp() {
- const app = Fastify({ logger: false });
- app.decorate('prisma', mockPrisma);
- app.decorate('authenticate', async (request: any) => {
- request.user = { id: USER_ID };
- });
- app.register(cardRoutes, { prefix: '/api/cards' });
- await app.ready();
- return app;
-}
-
-// ─────────────────────────────────────────────────────────────────────────────
-// POST /api/cards
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('POST /api/cards — link ownership validation', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- wireTransaction();
- });
-
- it('returns 403 when a supplied linkId belongs to another user', async () => {
- mockPrisma.platformLink.findMany.mockResolvedValue([]);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'POST',
- url: '/api/cards',
- payload: { title: 'Test Card', linkIds: [FOREIGN_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(403);
- expect(res.json().error).toBe('One or more links do not belong to your account');
- expect(mockPrisma.card.create).not.toHaveBeenCalled();
- });
-
- it('returns 403 when a mix of owned and foreign linkIds is supplied', async () => {
- // Only 1 of 2 requested IDs is owned — count mismatch triggers 403
- mockPrisma.platformLink.findMany.mockResolvedValue([{ id: OWNED_LINK_ID }]);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'POST',
- url: '/api/cards',
- payload: { title: 'Test Card', linkIds: [OWNED_LINK_ID, FOREIGN_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(403);
- expect(res.json().error).toBe('One or more links do not belong to your account');
- expect(mockPrisma.card.create).not.toHaveBeenCalled();
- });
-
- it('creates the card when all linkIds are owned by the user', async () => {
- mockPrisma.platformLink.findMany.mockResolvedValue([{ id: OWNED_LINK_ID }]);
- mockPrisma.card.count.mockResolvedValue(0);
- mockPrisma.card.create.mockResolvedValue({ ...mockCard, cardLinks: [] });
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'POST',
- url: '/api/cards',
- payload: { title: 'Test Card', linkIds: [OWNED_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(201);
- expect(mockPrisma.platformLink.findMany).toHaveBeenCalledWith({
- where: { id: { in: [OWNED_LINK_ID] }, userId: USER_ID },
- select: { id: true },
- });
- });
-
- it('skips the ownership check and creates the card when linkIds is empty', async () => {
- mockPrisma.card.count.mockResolvedValue(1);
- mockPrisma.card.create.mockResolvedValue({ ...mockCard, isDefault: false, cardLinks: [] });
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'POST',
- url: '/api/cards',
- payload: { title: 'Empty Card', linkIds: [] },
- });
-
- expect(res.statusCode).toBe(201);
- expect(mockPrisma.platformLink.findMany).not.toHaveBeenCalled();
- });
-
- it('returns 500 when the ownership query throws unexpectedly', async () => {
- mockPrisma.platformLink.findMany.mockRejectedValue(new Error('DB connection lost'));
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'POST',
- url: '/api/cards',
- payload: { title: 'Test Card', linkIds: [OWNED_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(500);
- // No write must have been attempted after the read failure
- expect(mockPrisma.card.create).not.toHaveBeenCalled();
- });
-
- it('returns 500 when card.count throws and no partial write occurs', async () => {
- mockPrisma.platformLink.findMany.mockResolvedValue([{ id: OWNED_LINK_ID }]);
- mockPrisma.card.count.mockRejectedValue(new Error('Query timeout'));
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'POST',
- url: '/api/cards',
- payload: { title: 'Test Card', linkIds: [OWNED_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(500);
- expect(mockPrisma.card.create).not.toHaveBeenCalled();
- });
-
- it('returns 500 when card.create throws', async () => {
- mockPrisma.platformLink.findMany.mockResolvedValue([{ id: OWNED_LINK_ID }]);
- mockPrisma.card.count.mockResolvedValue(0);
- mockPrisma.card.create.mockRejectedValue(new Error('FK constraint violation'));
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'POST',
- url: '/api/cards',
- payload: { title: 'Test Card', linkIds: [OWNED_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(500);
- });
-});
-
-// ─────────────────────────────────────────────────────────────────────────────
-// PUT /api/cards/:id
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('PUT /api/cards/:id — link ownership validation', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- wireTransaction();
- });
-
- it('returns 403 when a supplied linkId belongs to another user', async () => {
- mockPrisma.card.findFirst.mockResolvedValue(mockCard);
- mockPrisma.platformLink.findMany.mockResolvedValue([]);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'PUT',
- url: `/api/cards/${CARD_ID}`,
- payload: { linkIds: [FOREIGN_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(403);
- expect(res.json().error).toBe('One or more links do not belong to your account');
- // Existing links must not have been touched
- expect(mockPrisma.$transaction).not.toHaveBeenCalled();
- expect(mockPrisma.cardLink.deleteMany).not.toHaveBeenCalled();
- expect(mockPrisma.cardLink.createMany).not.toHaveBeenCalled();
- });
-
- it('updates links atomically when all supplied linkIds are owned', async () => {
- mockPrisma.card.findFirst.mockResolvedValue(mockCard);
- mockPrisma.platformLink.findMany.mockResolvedValue([{ id: OWNED_LINK_ID }]);
- mockPrisma.cardLink.deleteMany.mockResolvedValue({ count: 0 });
- mockPrisma.cardLink.createMany.mockResolvedValue({ count: 1 });
- mockPrisma.card.findUnique.mockResolvedValue({ ...mockCard, cardLinks: [] });
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'PUT',
- url: `/api/cards/${CARD_ID}`,
- payload: { linkIds: [OWNED_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(200);
- expect(mockPrisma.platformLink.findMany).toHaveBeenCalledWith({
- where: { id: { in: [OWNED_LINK_ID] }, userId: USER_ID },
- select: { id: true },
- });
- // Both operations must run inside the transaction, not as bare queries
- expect(mockPrisma.$transaction).toHaveBeenCalledOnce();
- expect(mockPrisma.cardLink.deleteMany).toHaveBeenCalledWith({ where: { cardId: CARD_ID } });
- expect(mockPrisma.cardLink.createMany).toHaveBeenCalled();
- });
-
- it('returns 404 when the card does not belong to the user', async () => {
- mockPrisma.card.findFirst.mockResolvedValue(null);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'PUT',
- url: `/api/cards/${CARD_ID}`,
- payload: { linkIds: [OWNED_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(404);
- expect(mockPrisma.platformLink.findMany).not.toHaveBeenCalled();
- });
-
- it('returns 500 when the ownership query throws and no mutation occurs', async () => {
- mockPrisma.card.findFirst.mockResolvedValue(mockCard);
- mockPrisma.platformLink.findMany.mockRejectedValue(new Error('DB timeout'));
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'PUT',
- url: `/api/cards/${CARD_ID}`,
- payload: { linkIds: [OWNED_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(500);
- expect(mockPrisma.$transaction).not.toHaveBeenCalled();
- expect(mockPrisma.cardLink.deleteMany).not.toHaveBeenCalled();
- });
-
- it('returns 500 and preserves existing links when the transaction fails mid-flight', async () => {
- // Ownership check passes; deleteMany succeeds; createMany fails.
- // The transaction rolls back, so the card retains its original links.
- mockPrisma.card.findFirst.mockResolvedValue(mockCard);
- mockPrisma.platformLink.findMany.mockResolvedValue([{ id: OWNED_LINK_ID }]);
- mockPrisma.cardLink.deleteMany.mockResolvedValue({ count: 1 });
- mockPrisma.cardLink.createMany.mockRejectedValue(new Error('FK constraint'));
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'PUT',
- url: `/api/cards/${CARD_ID}`,
- payload: { linkIds: [OWNED_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(500);
- // Both were attempted inside the transaction (the DB rolls them back together)
- expect(mockPrisma.cardLink.deleteMany).toHaveBeenCalled();
- expect(mockPrisma.cardLink.createMany).toHaveBeenCalled();
- // The final read must not have been called -- we short-circuited on error
- expect(mockPrisma.card.findUnique).not.toHaveBeenCalled();
- });
-
- it('returns 500 when card.findFirst throws', async () => {
- mockPrisma.card.findFirst.mockRejectedValue(new Error('Connection refused'));
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'PUT',
- url: `/api/cards/${CARD_ID}`,
- payload: { linkIds: [OWNED_LINK_ID] },
- });
-
- expect(res.statusCode).toBe(500);
- });
-});
-
-// ─────────────────────────────────────────────────────────────────────────────
-// DELETE /api/cards/:id
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('DELETE /api/cards/:id', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- wireTransaction();
- });
-
- it('returns 204 on successful deletion of a non-default card', async () => {
- mockPrisma.card.findFirst.mockResolvedValue({ ...mockCard, isDefault: false });
- mockPrisma.card.count.mockResolvedValue(2);
- mockPrisma.card.delete.mockResolvedValue(mockCard);
-
- const app = await buildApp();
- const res = await app.inject({ method: 'DELETE', url: `/api/cards/${CARD_ID}` });
-
- expect(res.statusCode).toBe(204);
- expect(mockPrisma.card.delete).toHaveBeenCalledWith({ where: { id: CARD_ID } });
- // No reassignment needed for a non-default card
- expect(mockPrisma.card.update).not.toHaveBeenCalled();
- });
-
- it('returns 204 and reassigns default when deleting the current default card', async () => {
- const otherCard = { id: 'card-other', isDefault: false, userId: USER_ID };
- // First findFirst: card being deleted. Second findFirst: oldest remaining.
- mockPrisma.card.findFirst
- .mockResolvedValueOnce({ ...mockCard, isDefault: true })
- .mockResolvedValueOnce(otherCard);
- mockPrisma.card.count.mockResolvedValue(2);
- mockPrisma.card.update.mockResolvedValue({ ...otherCard, isDefault: true });
- mockPrisma.card.delete.mockResolvedValue(mockCard);
-
- const app = await buildApp();
- const res = await app.inject({ method: 'DELETE', url: `/api/cards/${CARD_ID}` });
-
- expect(res.statusCode).toBe(204);
- expect(mockPrisma.card.update).toHaveBeenCalledWith({
- where: { id: otherCard.id },
- data: { isDefault: true },
- });
- expect(mockPrisma.card.delete).toHaveBeenCalledWith({ where: { id: CARD_ID } });
- });
-
- it('returns 404 when the card is not owned by the user', async () => {
- mockPrisma.card.findFirst.mockResolvedValue(null);
-
- const app = await buildApp();
- const res = await app.inject({ method: 'DELETE', url: `/api/cards/${CARD_ID}` });
-
- expect(res.statusCode).toBe(404);
- expect(mockPrisma.card.delete).not.toHaveBeenCalled();
- });
-
- it('returns 400 when attempting to delete the last remaining card', async () => {
- mockPrisma.card.findFirst.mockResolvedValue(mockCard);
- mockPrisma.card.count.mockResolvedValue(1);
-
- const app = await buildApp();
- const res = await app.inject({ method: 'DELETE', url: `/api/cards/${CARD_ID}` });
-
- expect(res.statusCode).toBe(400);
- expect(res.json().error).toBe('Cannot delete the last remaining card. A user must have at least one card.');
- expect(mockPrisma.card.delete).not.toHaveBeenCalled();
- });
-
- it('returns 500 when card.delete throws', async () => {
- mockPrisma.card.findFirst.mockResolvedValue({ ...mockCard, isDefault: false });
- mockPrisma.card.count.mockResolvedValue(2);
- mockPrisma.card.delete.mockRejectedValue(new Error('Deadlock detected'));
-
- const app = await buildApp();
- const res = await app.inject({ method: 'DELETE', url: `/api/cards/${CARD_ID}` });
-
- expect(res.statusCode).toBe(500);
- });
-});
-
-// ─────────────────────────────────────────────────────────────────────────────
-// PUT /api/cards/:id/default
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('PUT /api/cards/:id/default', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- wireTransaction();
- });
-
- it('returns 200 and sets the card as default', async () => {
- mockPrisma.card.findFirst.mockResolvedValue(mockCard);
- mockPrisma.card.updateMany.mockResolvedValue({ count: 2 });
- mockPrisma.card.update.mockResolvedValue({ ...mockCard, isDefault: true });
-
- const app = await buildApp();
- const res = await app.inject({ method: 'PUT', url: `/api/cards/${CARD_ID}/default` });
-
- expect(res.statusCode).toBe(200);
- expect(res.json().message).toBe('Default card updated');
- expect(mockPrisma.$transaction).toHaveBeenCalledOnce();
- // Clear-all and set-one must both run inside the transaction
- expect(mockPrisma.card.updateMany).toHaveBeenCalledWith({
- where: { userId: USER_ID },
- data: { isDefault: false },
- });
- expect(mockPrisma.card.update).toHaveBeenCalledWith({
- where: { id: CARD_ID },
- data: { isDefault: true },
- });
- });
-
- it('returns 404 when the card is not owned by the user', async () => {
- mockPrisma.card.findFirst.mockResolvedValue(null);
-
- const app = await buildApp();
- const res = await app.inject({ method: 'PUT', url: `/api/cards/${CARD_ID}/default` });
-
- expect(res.statusCode).toBe(404);
- expect(mockPrisma.$transaction).not.toHaveBeenCalled();
- });
-
- it('returns 500 and rolls back when the transaction fails mid-flight', async () => {
- // updateMany clears all defaults; then update fails => transaction aborts,
- // the user retains a consistent default card rather than having none.
- mockPrisma.card.findFirst.mockResolvedValue(mockCard);
- mockPrisma.card.updateMany.mockResolvedValue({ count: 2 });
- mockPrisma.card.update.mockRejectedValue(new Error('DB write failure'));
-
- const app = await buildApp();
- const res = await app.inject({ method: 'PUT', url: `/api/cards/${CARD_ID}/default` });
-
- expect(res.statusCode).toBe(500);
- expect(mockPrisma.card.updateMany).toHaveBeenCalled();
- expect(mockPrisma.card.update).toHaveBeenCalled();
- });
-});
diff --git a/apps/backend/src/__tests__/event.test.ts b/apps/backend/src/__tests__/event.test.ts
deleted file mode 100644
index 44806af1..00000000
--- a/apps/backend/src/__tests__/event.test.ts
+++ /dev/null
@@ -1,686 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
-import Fastify, { FastifyInstance } from 'fastify';
-import { PrismaClient } from '@prisma/client';
-import { eventRoutes } from '../routes/event';
-
-// ─── Shared mock data ────────────────────────────────────────────────────────
-
-const MOCK_USER_ID = 'user-uuid-001';
-const MOCK_OTHER_USER_ID = 'user-uuid-002';
-
-const MOCK_EVENT = {
- id: 'event-uuid-001',
- name: 'DevCard Conf 2025',
- slug: 'devcard-conf-2025',
- description: 'Annual DevCard conference',
- location: 'San Francisco, CA',
- organizerId: MOCK_USER_ID,
- startDate: new Date('2025-09-01T09:00:00Z'),
- endDate: new Date('2025-09-02T18:00:00Z'),
- isPublic: true,
- createdAt: new Date('2025-01-01T00:00:00Z'),
-};
-
-const MOCK_USER_PROFILE = {
- id: MOCK_USER_ID,
- username: 'johndoe',
- displayName: 'John Doe',
- bio: 'Software engineer',
- pronouns: 'he/him',
- company: 'Acme Corp',
- avatarUrl: 'https://example.com/avatar.png',
- accentColor: '#6366f1',
-};
-
-const MOCK_OTHER_USER_PROFILE = {
- id: MOCK_OTHER_USER_ID,
- username: 'janedoe',
- displayName: 'Jane Doe',
- bio: null,
- pronouns: null,
- company: null,
- avatarUrl: null,
- accentColor: '#6366f1',
-};
-
-// ─── Prisma mock ─────────────────────────────────────────────────────────────
-
-const prismaMock = {
- event: {
- create: vi.fn(),
- findUnique: vi.fn(),
- },
- eventAttendee: {
- create: vi.fn(),
- delete: vi.fn(),
- },
-};
-
-// ─── App factory ─────────────────────────────────────────────────────────────
-//
-// Builds a minimal Fastify instance that wires up:
-// • app.prisma – the Prisma mock above
-// • request.jwtVerify() – overridden per-test via `mockJwtVerify`
-//
-// This mirrors the real app setup without touching a real DB or real JWT keys.
-
-let mockJwtVerify = vi.fn();
-
-async function buildApp(): Promise {
- const app = Fastify({ logger: false });
-
- // Decorate prisma so routes can use app.prisma.*
- app.decorate('prisma', prismaMock as unknown as PrismaClient);
-
- // Decorate jwtVerify on the request prototype so request.jwtVerify() resolves
- // to whatever the current test wants.
- app.decorateRequest('jwtVerify', function () {
- return mockJwtVerify();
- });
-
- // Register with the same prefix used in production (app.ts) so that
- // tests exercise routes at their real paths — /api/events, /api/events/:slug, etc.
- await app.register(eventRoutes, { prefix: '/api/events' });
- await app.ready();
- return app;
-}
-
-// ─── Helpers ─────────────────────────────────────────────────────────────────
-
-/** Returns a valid JWT-authenticated inject payload */
-function authHeader(): Record {
- return { Authorization: 'Bearer mock-token' };
-}
-
-/** Injects a POST /api/events request */
-async function createEvent(
- app: FastifyInstance,
- body: Record,
- authenticated = true,
-) {
- return app.inject({
- method: 'POST',
- url: '/api/events',
- headers: authenticated ? authHeader() : {},
- payload: body,
- });
-}
-
-// ─── Test suite ──────────────────────────────────────────────────────────────
-
-describe('Events API', () => {
- let app: FastifyInstance;
-
- beforeEach(async () => {
- vi.clearAllMocks();
- // Default: authenticated as MOCK_USER_ID
- mockJwtVerify.mockResolvedValue({ id: MOCK_USER_ID });
- app = await buildApp();
- });
-
- afterEach(async () => {
- await app.close();
- });
-
- // ── POST /api/events ───────────────────────────────────────────────────────
-
- describe('POST /api/events — create event', () => {
- const validBody = {
- name: 'DevCard Conf 2025',
- description: 'Annual DevCard conference',
- location: 'San Francisco, CA',
- startDate: '2025-09-01T09:00:00Z',
- endDate: '2025-09-02T18:00:00Z',
- isPublic: true,
- };
-
- it('201 — creates event and returns it for authenticated organizer', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null); // slug is free
- prismaMock.event.create.mockResolvedValue(MOCK_EVENT);
-
- const res = await createEvent(app, validBody);
-
- expect(res.statusCode).toBe(201);
- const body = res.json();
- expect(body.slug).toBe('devcard-conf-2025');
- expect(body.organizerId).toBe(MOCK_USER_ID);
- expect(body.location).toBe('San Francisco, CA');
-
- // Prisma was called with correct fields
- expect(prismaMock.event.create).toHaveBeenCalledOnce();
- const callArg = prismaMock.event.create.mock.calls[0][0].data;
- expect(callArg.name).toBe('DevCard Conf 2025');
- expect(callArg.organizerId).toBe(MOCK_USER_ID);
- expect(callArg.location).toBe('San Francisco, CA');
- });
-
- it('401 — rejects unauthenticated request', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Unauthorized'));
-
- const res = await createEvent(app, validBody, false);
-
- expect(res.statusCode).toBe(401);
- expect(res.json()).toMatchObject({ error: 'Unauthorized' });
- });
-
- it('400 — rejects missing required fields (no dates, no location)', async () => {
- const res = await createEvent(app, { name: 'Hello World' }); // missing dates + location
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects missing location', async () => {
- const { location: _omit, ...bodyWithoutLocation } = validBody;
- const res = await createEvent(app, bodyWithoutLocation);
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects location shorter than 2 characters', async () => {
- const res = await createEvent(app, { ...validBody, location: 'A' });
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects location longer than 100 characters', async () => {
- const res = await createEvent(app, { ...validBody, location: 'A'.repeat(101) });
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects event name shorter than 3 characters', async () => {
- const res = await createEvent(app, { ...validBody, name: 'Hi' });
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects event name longer than 100 characters', async () => {
- const longName = 'A'.repeat(101);
- const res = await createEvent(app, { ...validBody, name: longName });
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects invalid date format', async () => {
- const res = await createEvent(app, {
- ...validBody,
- startDate: 'not-a-date',
- });
- expect(res.statusCode).toBe(400);
- });
-
- it('201 — generates a unique slug when the first candidate is taken', async () => {
- // First findUnique returns a conflict, second returns null (slug free)
- prismaMock.event.findUnique
- .mockResolvedValueOnce(MOCK_EVENT) // slug taken
- .mockResolvedValueOnce(null); // randomised slug free
-
- prismaMock.event.create.mockResolvedValue({
- ...MOCK_EVENT,
- slug: 'devcard-conf-2025-ab12',
- });
-
- const res = await createEvent(app, validBody);
-
- expect(res.statusCode).toBe(201);
- // create was eventually called with a slug different from the base one
- const createdSlug: string = prismaMock.event.create.mock.calls[0][0].data.slug;
- expect(createdSlug).toMatch(/^devcard-conf-2025-[a-z0-9]+$/);
- });
-
- it('201 — isPublic defaults to true when omitted', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null);
- prismaMock.event.create.mockResolvedValue(MOCK_EVENT);
-
- const { isPublic: _omit, ...bodyWithoutIsPublic } = validBody;
- const res = await createEvent(app, bodyWithoutIsPublic);
-
- expect(res.statusCode).toBe(201);
- const callData = prismaMock.event.create.mock.calls[0][0].data;
- expect(callData.isPublic).toBe(true);
- });
-
- it('500 — returns 500 when database write fails', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null);
- prismaMock.event.create.mockRejectedValue(new Error('DB error'));
-
- const res = await createEvent(app, validBody);
-
- expect(res.statusCode).toBe(500);
- expect(res.json()).toMatchObject({ error: 'Failed to create event' });
- });
- });
-
- // ── GET /api/events/:slug ──────────────────────────────────────────────────
-
- describe('GET /api/events/:slug — event details', () => {
- it('200 — returns event info with attendee count', async () => {
- prismaMock.event.findUnique.mockResolvedValue({
- ...MOCK_EVENT,
- _count: { attendees: 42 },
- });
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/devcard-conf-2025',
- });
-
- expect(res.statusCode).toBe(200);
- const body = res.json();
- expect(body.slug).toBe('devcard-conf-2025');
- expect(body.attendeesCount).toBe(42);
- expect(body.location).toBe('San Francisco, CA');
- // organizerId is exposed (public info)
- expect(body.organizerId).toBe(MOCK_USER_ID);
- });
-
- it('404 — returns 404 for unknown slug', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/ghost-event',
- });
-
- expect(res.statusCode).toBe(404);
- expect(res.json()).toMatchObject({ error: 'Event not found' });
- });
-
- it('200 — works without authentication (public endpoint)', async () => {
- // Even if JWT would fail, this route should not call jwtVerify
- mockJwtVerify.mockRejectedValue(new Error('Should not be called'));
- prismaMock.event.findUnique.mockResolvedValue({
- ...MOCK_EVENT,
- _count: { attendees: 0 },
- });
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/devcard-conf-2025',
- // No Authorization header
- });
-
- expect(res.statusCode).toBe(200);
- expect(mockJwtVerify).not.toHaveBeenCalled();
- });
- });
-
- // ── POST /api/events/:slug/join ────────────────────────────────────────────
-
- describe('POST /api/events/:slug/join — join event', () => {
- it('201 — authenticated user joins an existing event', async () => {
- prismaMock.event.findUnique.mockResolvedValue(MOCK_EVENT);
- prismaMock.eventAttendee.create.mockResolvedValue({
- id: 'attendee-uuid-001',
- userId: MOCK_OTHER_USER_ID,
- eventId: MOCK_EVENT.id,
- joinedAt: new Date(),
- });
-
- mockJwtVerify.mockResolvedValue({ id: MOCK_OTHER_USER_ID });
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/events/devcard-conf-2025/join',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(201);
- expect(res.json()).toMatchObject({ message: 'User joined successfully' });
-
- const callData = prismaMock.eventAttendee.create.mock.calls[0][0].data;
- expect(callData.eventId).toBe(MOCK_EVENT.id);
- expect(callData.userId).toBe(MOCK_OTHER_USER_ID);
- });
-
- it('401 — rejects unauthenticated request', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Unauthorized'));
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/events/devcard-conf-2025/join',
- });
-
- expect(res.statusCode).toBe(401);
- expect(res.json()).toMatchObject({ error: 'Unauthorized' });
- });
-
- it('404 — returns 404 when event does not exist', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/events/ghost-event/join',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(404);
- expect(res.json()).toMatchObject({ error: 'Event not found' });
- });
-
- it('409 — returns 409 when user already joined the event', async () => {
- prismaMock.event.findUnique.mockResolvedValue(MOCK_EVENT);
- // Prisma unique constraint error
- const uniqueError = Object.assign(new Error('Unique constraint'), {
- code: 'P2002',
- });
- prismaMock.eventAttendee.create.mockRejectedValue(uniqueError);
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/events/devcard-conf-2025/join',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(409);
- expect(res.json()).toMatchObject({ error: 'Already joined' });
- });
-
- it('500 — returns 500 on unexpected database error', async () => {
- prismaMock.event.findUnique.mockResolvedValue(MOCK_EVENT);
- prismaMock.eventAttendee.create.mockRejectedValue(new Error('DB error'));
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/events/devcard-conf-2025/join',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(500);
- expect(res.json()).toMatchObject({ error: 'Failed to join' });
- });
- });
-
- // ── DELETE /api/events/:slug/leave ────────────────────────────────────────
-
- describe('DELETE /api/events/:slug/leave — leave event', () => {
- it('204 — authenticated user leaves an event they joined', async () => {
- prismaMock.event.findUnique.mockResolvedValue(MOCK_EVENT);
- prismaMock.eventAttendee.delete.mockResolvedValue({});
-
- mockJwtVerify.mockResolvedValue({ id: MOCK_OTHER_USER_ID });
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/api/events/devcard-conf-2025/leave',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(204);
-
- // Verify the compound unique key used in the delete
- const deleteArg = prismaMock.eventAttendee.delete.mock.calls[0][0].where;
- expect(deleteArg).toMatchObject({
- userId_eventId: {
- userId: MOCK_OTHER_USER_ID,
- eventId: MOCK_EVENT.id,
- },
- });
- });
-
- it('401 — rejects unauthenticated request', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Unauthorized'));
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/api/events/devcard-conf-2025/leave',
- });
-
- expect(res.statusCode).toBe(401);
- expect(res.json()).toMatchObject({ error: 'Unauthorized' });
- });
-
- it('404 — returns 404 when event does not exist', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/api/events/ghost-event/leave',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(404);
- expect(res.json()).toMatchObject({ error: 'Event not found' });
- });
-
- it('404 — returns 404 when user was never an attendee (P2025)', async () => {
- prismaMock.event.findUnique.mockResolvedValue(MOCK_EVENT);
- // Prisma record-not-found error
- const notFoundError = Object.assign(new Error('Record not found'), {
- code: 'P2025',
- });
- prismaMock.eventAttendee.delete.mockRejectedValue(notFoundError);
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/api/events/devcard-conf-2025/leave',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(404);
- expect(res.json()).toMatchObject({ error: 'User not found' });
- });
-
- it('500 — returns 500 on unexpected database error', async () => {
- prismaMock.event.findUnique.mockResolvedValue(MOCK_EVENT);
- prismaMock.eventAttendee.delete.mockRejectedValue(new Error('DB error'));
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/api/events/devcard-conf-2025/leave',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(500);
- expect(res.json()).toMatchObject({ error: 'Failed to leave' });
- });
- });
-
- // ── GET /api/events/:slug/attendees ───────────────────────────────────────
-
- describe('GET /api/events/:slug/attendees — paginated attendee list', () => {
- /** Builds a raw EventAttendee row as Prisma returns it (with nested user) */
- function makeAttendeeRow(
- profile: typeof MOCK_USER_PROFILE | typeof MOCK_OTHER_USER_PROFILE,
- ) {
- return {
- id: `attendee-${profile.id}`,
- userId: profile.id,
- eventId: MOCK_EVENT.id,
- joinedAt: new Date(),
- user: { ...profile },
- };
- }
-
- it('200 — returns paginated attendees with default page/limit', async () => {
- const attendeeRows = [
- makeAttendeeRow(MOCK_USER_PROFILE),
- makeAttendeeRow(MOCK_OTHER_USER_PROFILE),
- ];
-
- prismaMock.event.findUnique.mockResolvedValue({
- ...MOCK_EVENT,
- attendees: attendeeRows,
- });
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/devcard-conf-2025/attendees',
- });
-
- expect(res.statusCode).toBe(200);
- const body = res.json();
-
- expect(body.attendees).toHaveLength(2);
- expect(body.attendees[0]).toMatchObject({
- id: MOCK_USER_ID,
- username: 'johndoe',
- displayName: 'John Doe',
- });
-
- expect(body.pagination).toMatchObject({
- page: 1,
- limit: 10,
- total: 2,
- });
- });
-
- it('200 — respects custom page and limit query params', async () => {
- prismaMock.event.findUnique.mockResolvedValue({
- ...MOCK_EVENT,
- attendees: [makeAttendeeRow(MOCK_OTHER_USER_PROFILE)],
- });
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/devcard-conf-2025/attendees?page=2&limit=5',
- });
-
- expect(res.statusCode).toBe(200);
- const body = res.json();
- expect(body.pagination.page).toBe(2);
- expect(body.pagination.limit).toBe(5);
-
- // Verify skip/take were passed correctly to Prisma
- const includeArg = prismaMock.event.findUnique.mock.calls[0][0].include;
- expect(includeArg.attendees.skip).toBe(5); // (page-1) * limit = 1 * 5
- expect(includeArg.attendees.take).toBe(5);
- });
-
- it('200 — caps limit at 50 even if higher value is requested', async () => {
- prismaMock.event.findUnique.mockResolvedValue({
- ...MOCK_EVENT,
- attendees: [],
- });
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/devcard-conf-2025/attendees?limit=200',
- });
-
- expect(res.statusCode).toBe(200);
- const includeArg = prismaMock.event.findUnique.mock.calls[0][0].include;
- expect(includeArg.attendees.take).toBe(50);
- });
-
- it('200 — treats page < 1 as page 1', async () => {
- prismaMock.event.findUnique.mockResolvedValue({
- ...MOCK_EVENT,
- attendees: [],
- });
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/devcard-conf-2025/attendees?page=0',
- });
-
- expect(res.statusCode).toBe(200);
- const includeArg = prismaMock.event.findUnique.mock.calls[0][0].include;
- expect(includeArg.attendees.skip).toBe(0); // page forced to 1 → skip = 0
- });
-
- it('200 — returns empty attendees list for event with no attendees', async () => {
- prismaMock.event.findUnique.mockResolvedValue({
- ...MOCK_EVENT,
- attendees: [],
- });
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/devcard-conf-2025/attendees',
- });
-
- expect(res.statusCode).toBe(200);
- const body = res.json();
- expect(body.attendees).toHaveLength(0);
- expect(body.pagination.total).toBe(0);
- });
-
- it('200 — public profiles do not leak sensitive fields', async () => {
- prismaMock.event.findUnique.mockResolvedValue({
- ...MOCK_EVENT,
- attendees: [makeAttendeeRow(MOCK_USER_PROFILE)],
- });
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/devcard-conf-2025/attendees',
- });
-
- const attendee = res.json().attendees[0];
-
- // These fields MUST be present
- expect(attendee).toHaveProperty('id');
- expect(attendee).toHaveProperty('username');
- expect(attendee).toHaveProperty('displayName');
- expect(attendee).toHaveProperty('accentColor');
-
- // These fields MUST NOT be present
- expect(attendee).not.toHaveProperty('email');
- expect(attendee).not.toHaveProperty('provider');
- expect(attendee).not.toHaveProperty('providerId');
- expect(attendee).not.toHaveProperty('role');
- });
-
- it('404 — returns 404 for unknown event slug', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/events/ghost-event/attendees',
- });
-
- expect(res.statusCode).toBe(404);
- expect(res.json()).toMatchObject({ error: 'Event not found' });
- });
-
- it('200 — attendees are ordered by joinedAt desc (latest first)', async () => {
- prismaMock.event.findUnique.mockResolvedValue({
- ...MOCK_EVENT,
- attendees: [],
- });
-
- await app.inject({
- method: 'GET',
- url: '/api/events/devcard-conf-2025/attendees',
- });
-
- const includeArg = prismaMock.event.findUnique.mock.calls[0][0].include;
- expect(includeArg.attendees.orderBy).toMatchObject({ joinedAt: 'desc' });
- });
- });
-
- // ── Slug generation edge cases ────────────────────────────────────────────
-
- describe('Slug generation', () => {
- const baseBody = {
- location: 'San Francisco, CA',
- startDate: '2025-09-01T09:00:00Z',
- endDate: '2025-09-02T18:00:00Z',
- };
-
- it('converts spaces and special characters to hyphens', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null);
- prismaMock.event.create.mockResolvedValue({ ...MOCK_EVENT, slug: 'my-awesome-event' });
-
- await createEvent(app, { ...baseBody, name: 'My Awesome Event!!!' });
-
- const slug: string = prismaMock.event.create.mock.calls[0][0].data.slug;
- expect(slug).toBe('my-awesome-event');
- });
-
- it('strips leading and trailing hyphens from slug', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null);
- prismaMock.event.create.mockResolvedValue({ ...MOCK_EVENT, slug: 'event-name' });
-
- await createEvent(app, { ...baseBody, name: '---Event Name---' });
-
- const slug: string = prismaMock.event.create.mock.calls[0][0].data.slug;
- expect(slug).not.toMatch(/^-|-$/);
- });
-
- it('collapses multiple consecutive hyphens into one', async () => {
- prismaMock.event.findUnique.mockResolvedValue(null);
- prismaMock.event.create.mockResolvedValue({ ...MOCK_EVENT, slug: 'event-name' });
-
- await createEvent(app, { ...baseBody, name: 'Event Name' });
-
- const slug: string = prismaMock.event.create.mock.calls[0][0].data.slug;
- expect(slug).not.toMatch(/--/);
- });
- });
-});
\ No newline at end of file
diff --git a/apps/backend/src/__tests__/follow.test.ts b/apps/backend/src/__tests__/follow.test.ts
index 41830018..8338f606 100644
--- a/apps/backend/src/__tests__/follow.test.ts
+++ b/apps/backend/src/__tests__/follow.test.ts
@@ -1,5 +1,5 @@
-import Fastify, { FastifyInstance } from 'fastify';
-import { describe, expect, it, vi, beforeAll, beforeEach, afterAll } from 'vitest';
+import Fastify from 'fastify';
+import { describe, expect, it, vi } from 'vitest';
import { followRoutes } from '../routes/follow.js';
@@ -7,57 +7,32 @@ vi.mock('../utils/encryption.js', () => ({
decrypt: vi.fn(() => 'fake-access-token'),
}));
-// ── Shared mock data ──────────────────────────────────────────────────────────
-
-const MOCK_USER_ID = 'user-uuid-001';
-
-const MOCK_OAUTH_TOKEN = {
- id: 'token-1',
- userId: MOCK_USER_ID,
- platform: 'unknown',
- accessToken: 'encrypted-token',
-};
-
-// ── App factory ───────────────────────────────────────────────────────────────
-
-function buildApp(overrides: {
- oAuthToken?: Record;
- followLog?: Record;
-} = {}): FastifyInstance {
- const app = Fastify({ logger: false });
-
- app.decorate('prisma', {
- oAuthToken: {
- findUnique: vi.fn(),
- ...overrides.oAuthToken,
- },
- followLog: {
- create: vi.fn(),
- deleteMany: vi.fn(),
- ...overrides.followLog,
- },
- } as any);
-
- app.decorate('authenticate', async (request: any) => {
- request.user = { id: MOCK_USER_ID };
- });
+describe('POST /api/follow/:platform/:targetUsername', () => {
+ it('returns 400 when API follow is not supported for the platform', async () => {
+ const app = Fastify({ logger: false });
- return app;
-}
+ const findUnique = vi.fn().mockResolvedValue({
+ id: 'token-1',
+ userId: 'user-1',
+ platform: 'unknown',
+ accessToken: 'encrypted-token',
+ });
-async function makeApp(overrides?: Parameters[0]): Promise {
- const app = buildApp(overrides);
- await app.register(followRoutes, { prefix: '/api/follow' });
- await app.ready();
- return app;
-}
+ app.decorate('prisma', {
+ oAuthToken: {
+ findUnique,
+ },
+ followLog: {
+ create: vi.fn(),
+ },
+ }as any);
-// ─────────────────────────────────────────────────────────────────────────────
+ app.decorate('authenticate', async (request: any) => {
+ request.user = { id: 'user-1' };
+ });
-describe('POST /api/follow/:platform/:targetUsername — API follow', () => {
- it('returns 400 when API follow is not supported for the platform', async () => {
- const findUnique = vi.fn().mockResolvedValue(MOCK_OAUTH_TOKEN);
- const app = await makeApp({ oAuthToken: { findUnique } });
+ await app.register(followRoutes, { prefix: '/api/follow' });
+ await app.ready();
const response = await app.inject({
method: 'POST',
@@ -71,7 +46,7 @@ describe('POST /api/follow/:platform/:targetUsername — API follow', () => {
expect(findUnique).toHaveBeenCalledWith({
where: {
userId_platform: {
- userId: MOCK_USER_ID,
+ userId: 'user-1',
platform: 'unknown',
},
},
@@ -79,250 +54,4 @@ describe('POST /api/follow/:platform/:targetUsername — API follow', () => {
await app.close();
});
-
- it('returns webview strategy and url for webview-strategy platforms (e.g. linkedin)', async () => {
- const app = await makeApp();
-
- const response = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser',
- });
-
- const body = response.json();
-
- expect(response.statusCode).toBe(200);
- expect(body.strategy).toBe('webview');
- expect(body.url).toContain('linkedin.com/in/testuser');
-
- await app.close();
- });
-});
-
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('POST /api/follow/:platform/:targetUsername/log — follow log validation', () => {
- let app: FastifyInstance;
- let createLog: ReturnType;
-
- // One app instance shared across all log tests; mock reset between each test.
- beforeAll(async () => {
- createLog = vi.fn();
- app = await makeApp({ followLog: { create: createLog } });
- });
-
- afterAll(async () => {
- await app.close();
- });
-
- beforeEach(() => {
- createLog.mockReset();
- createLog.mockResolvedValue({ id: 'log-uuid-001' });
- });
-
- // ── Valid payloads ────────────────────────────────────────────────────────
-
- it('200 — accepts status: success, layer: foreground', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: 'success', layer: 'foreground' },
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.json()).toMatchObject({ status: 'success', logId: 'log-uuid-001' });
- expect(createLog).toHaveBeenCalledOnce();
- expect(createLog.mock.calls[0][0].data.status).toBe('success');
- });
-
- it('200 — accepts status: failed', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: 'failed', layer: 'foreground' },
- });
-
- expect(res.statusCode).toBe(200);
- expect(createLog).toHaveBeenCalledOnce();
- expect(createLog.mock.calls[0][0].data.status).toBe('failed');
- });
-
- it('200 — accepts status: pending, layer: background', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: 'pending', layer: 'background' },
- });
-
- expect(res.statusCode).toBe(200);
- expect(createLog).toHaveBeenCalledOnce();
- expect(createLog.mock.calls[0][0].data.layer).toBe('background');
- });
-
- // ── Invalid status values — analytics integrity ───────────────────────────
-
- it('400 — rejects invalid status "error" (old unvalidated internal value)', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: 'error', layer: 'foreground' },
- });
-
- expect(res.statusCode).toBe(400);
- expect(res.json()).toMatchObject({ error: 'Invalid follow log payload' });
- // DB must NOT be written — this is the analytics integrity guarantee
- expect(createLog).not.toHaveBeenCalled();
- });
-
- it('400 — rejects arbitrary status string injection', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: '"; DROP TABLE follow_logs; --', layer: 'foreground' },
- });
-
- expect(res.statusCode).toBe(400);
- expect(createLog).not.toHaveBeenCalled();
- });
-
- // ── Invalid layer values — analytics integrity ────────────────────────────
-
- // 'webview' was the old unvalidated default — it is now explicitly rejected.
- // Any existing caller sending layer: 'webview' must migrate to 'foreground'
- // (in-app WebView session) or 'background' (passive deep-link strategy).
- it('400 — rejects legacy layer "webview" (old unvalidated default)', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: 'success', layer: 'webview' },
- });
-
- expect(res.statusCode).toBe(400);
- expect(res.json()).toMatchObject({ error: 'Invalid follow log payload' });
- expect(createLog).not.toHaveBeenCalled();
- });
-
- it('400 — rejects invalid layer "api"', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: 'success', layer: 'api' },
- });
-
- expect(res.statusCode).toBe(400);
- expect(createLog).not.toHaveBeenCalled();
- });
-
- // ── Malformed / missing payloads ──────────────────────────────────────────
-
- it('400 — rejects missing status field', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { layer: 'foreground' },
- });
-
- expect(res.statusCode).toBe(400);
- expect(createLog).not.toHaveBeenCalled();
- });
-
- it('400 — rejects missing layer field', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: 'success' },
- });
-
- expect(res.statusCode).toBe(400);
- expect(createLog).not.toHaveBeenCalled();
- });
-
- it('400 — rejects empty body', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: {},
- });
-
- expect(res.statusCode).toBe(400);
- expect(createLog).not.toHaveBeenCalled();
- });
-
- // ── Correct data persisted to DB ──────────────────────────────────────────
-
- it('persists exactly the validated platform, targetUsername, status, and layer', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/twitter/janedoe/log',
- payload: { status: 'pending', layer: 'background' },
- });
-
- expect(res.statusCode).toBe(200);
- expect(createLog).toHaveBeenCalledOnce();
-
- const written = createLog.mock.calls[0][0].data;
- expect(written).toMatchObject({
- followerId: MOCK_USER_ID,
- targetUsername: 'janedoe',
- platform: 'twitter',
- status: 'pending',
- layer: 'background',
- });
- });
-
- // ── Response does not leak validation internals ───────────────────────────
-
- it('400 response only exposes { error } — no schema internals or stack traces', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: 'bad', layer: 'bad' },
- });
-
- expect(res.statusCode).toBe(400);
- const body = res.json();
- expect(body).not.toHaveProperty('issues');
- expect(body).not.toHaveProperty('stack');
- expect(Object.keys(body)).toEqual(['error']);
- });
-
- // ── DB failure after valid payload ────────────────────────────────────────
-
- it('500 — returns 500 when DB write fails after successful validation', async () => {
- createLog.mockRejectedValueOnce(new Error('DB connection lost'));
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/linkedin/testuser/log',
- payload: { status: 'success', layer: 'foreground' },
- });
-
- expect(res.statusCode).toBe(500);
- expect(res.json()).toMatchObject({ error: 'Failed to log follow event' });
- });
-});
-
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('DELETE /api/follow/:platform/:targetUsername/log — clear follow log', () => {
- it('clears follow log entries for the authenticated user', async () => {
- const deleteMany = vi.fn().mockResolvedValue({ count: 1 });
- const app = await makeApp({ followLog: { deleteMany } });
-
- const response = await app.inject({
- method: 'DELETE',
- url: '/api/follow/linkedin/testuser/log',
- });
-
- expect(response.statusCode).toBe(200);
- expect(response.json()).toMatchObject({ status: 'cleared' });
- expect(deleteMany).toHaveBeenCalledWith({
- where: {
- followerId: MOCK_USER_ID,
- platform: 'linkedin',
- targetUsername: 'testuser',
- },
- });
-
- await app.close();
- });
-});
+});
\ No newline at end of file
diff --git a/apps/backend/src/__tests__/oauth-scope.test.ts b/apps/backend/src/__tests__/oauth-scope.test.ts
deleted file mode 100644
index 0985dfa7..00000000
--- a/apps/backend/src/__tests__/oauth-scope.test.ts
+++ /dev/null
@@ -1,392 +0,0 @@
-/**
- * Regression tests for OAuth token scope isolation.
- *
- * Prior to the fix, the authentication flow (scope: read:user user:email,
- * platform key: 'github') and the GitHub connect/follow flow (scope:
- * user:follow, platform key: 'github') both wrote to the same OAuthToken
- * record. Whichever executed last silently overwrote the other's access
- * token, causing follow capability to disappear after re-authentication.
- *
- * The fix uses a dedicated platform key ('github_follow') for the connect
- * flow so the two records are independent and can never overwrite each other.
- */
-
-import { describe, it, expect, beforeEach, vi } from 'vitest';
-import Fastify from 'fastify';
-import { connectRoutes } from '../routes/connect.js';
-import { followRoutes } from '../routes/follow.js';
-import type { PrismaClient } from '@prisma/client';
-
-// ── Mocks ─────────────────────────────────────────────────────────────────────
-
-vi.mock('../utils/encryption.js', () => ({
- encrypt: vi.fn((v: string) => `enc:${v}`),
- decrypt: vi.fn((v: string) => v.replace(/^enc:/, '')),
-}));
-
-const mockFetch = vi.fn();
-beforeEach(() => {
- vi.clearAllMocks();
- (globalThis as any).fetch = mockFetch;
-
- process.env.PUBLIC_APP_URL = 'http://localhost:5173';
- process.env.BACKEND_URL = 'http://localhost:3000';
- process.env.GITHUB_CLIENT_ID = 'test-client-id';
-});
-
-const USER_ID = 'user-scope-test';
-
-// ── Connect-route test harness ────────────────────────────────────────────────
-
-function makeConnectState(userId: string): string {
- return Buffer.from(JSON.stringify({ userId, nonce: 'test-nonce' })).toString('base64');
-}
-
-function buildConnectApp(mockPrisma: Partial) {
- const app = Fastify({ logger: false });
- app.decorate('prisma', mockPrisma as PrismaClient);
- app.decorate('authenticate', async (req: any) => { req.user = { id: USER_ID }; });
- app.register(connectRoutes, { prefix: '/api/connect' });
- return app.ready().then(() => app);
-}
-
-// ── Follow-route test harness ─────────────────────────────────────────────────
-
-function buildFollowApp(mockPrisma: Partial) {
- const app = Fastify({ logger: false });
- app.decorate('prisma', mockPrisma as PrismaClient);
- app.decorate('authenticate', async (req: any) => { req.user = { id: USER_ID }; });
- app.register(followRoutes, { prefix: '/api/follow' });
- return app.ready().then(() => app);
-}
-
-// ─────────────────────────────────────────────────────────────────────────────
-// 1. Connect flow — platform key
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('GitHub connect flow — token stored under github_follow', () => {
- it('writes the follow token to platform=github_follow, not github', async () => {
- const upsert = vi.fn().mockResolvedValue({});
- const app = await buildConnectApp({ oAuthToken: { upsert } as any });
-
- mockFetch.mockResolvedValue({
- json: async () => ({ access_token: 'follow-token', scope: 'user:follow' }),
- });
-
- await app.inject({
- method: 'GET',
- url: `/api/connect/github/callback?code=code123&state=${makeConnectState(USER_ID)}`,
- });
-
- expect(upsert).toHaveBeenCalledOnce();
- const call = upsert.mock.calls[0][0];
-
- // Key must be github_follow
- expect(call.where.userId_platform.platform).toBe('github_follow');
- expect(call.create.platform).toBe('github_follow');
- expect(call.update).not.toHaveProperty('platform'); // update never changes the key
- });
-
- it('stores the scope returned by GitHub in the follow token record', async () => {
- const upsert = vi.fn().mockResolvedValue({});
- const app = await buildConnectApp({ oAuthToken: { upsert } as any });
-
- mockFetch.mockResolvedValue({
- json: async () => ({ access_token: 'follow-token', scope: 'user:follow' }),
- });
-
- await app.inject({
- method: 'GET',
- url: `/api/connect/github/callback?code=code123&state=${makeConnectState(USER_ID)}`,
- });
-
- const { create } = upsert.mock.calls[0][0];
- expect(create.scopes).toBe('user:follow');
- });
-
- it('falls back to user:follow scope when GitHub omits the scope field', async () => {
- const upsert = vi.fn().mockResolvedValue({});
- const app = await buildConnectApp({ oAuthToken: { upsert } as any });
-
- mockFetch.mockResolvedValue({
- json: async () => ({ access_token: 'follow-token' }), // no scope field
- });
-
- await app.inject({
- method: 'GET',
- url: `/api/connect/github/callback?code=code123&state=${makeConnectState(USER_ID)}`,
- });
-
- const { create } = upsert.mock.calls[0][0];
- expect(create.scopes).toBe('user:follow');
- });
-
- it('does NOT touch the github (auth) token record during the connect flow', async () => {
- const upsert = vi.fn().mockResolvedValue({});
- const app = await buildConnectApp({ oAuthToken: { upsert } as any });
-
- mockFetch.mockResolvedValue({
- json: async () => ({ access_token: 'follow-token', scope: 'user:follow' }),
- });
-
- await app.inject({
- method: 'GET',
- url: `/api/connect/github/callback?code=code123&state=${makeConnectState(USER_ID)}`,
- });
-
- // Exactly one upsert — the github_follow record; never 'github'
- expect(upsert).toHaveBeenCalledTimes(1);
- const key = upsert.mock.calls[0][0].where.userId_platform.platform;
- expect(key).not.toBe('github');
- });
-});
-
-// ─────────────────────────────────────────────────────────────────────────────
-// 2. Follow route — uses github_follow token
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('GitHub follow route — looks up github_follow token', () => {
- it('resolves the token from platform=github_follow', async () => {
- const findUnique = vi.fn().mockResolvedValue({
- id: 'tok-1',
- accessToken: 'enc:follow-token',
- });
-
- mockFetch.mockResolvedValue({ status: 204 });
-
- const app = await buildFollowApp({
- oAuthToken: { findUnique } as any,
- followLog: { create: vi.fn().mockReturnValue({ catch: vi.fn() }) } as any,
- });
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/github/targetuser',
- });
-
- expect(res.statusCode).toBe(200);
- expect(findUnique).toHaveBeenCalledWith({
- where: { userId_platform: { userId: USER_ID, platform: 'github_follow' } },
- });
- });
-
- it('returns 400 with requiresAuth when github_follow token is absent', async () => {
- const findUnique = vi.fn().mockResolvedValue(null);
-
- const app = await buildFollowApp({
- oAuthToken: { findUnique } as any,
- followLog: { create: vi.fn() } as any,
- });
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/github/targetuser',
- });
-
- expect(res.statusCode).toBe(400);
- expect(res.json().requiresAuth).toBe(true);
-
- // The lookup must be for github_follow — not the auth token
- expect(findUnique.mock.calls[0][0].where.userId_platform.platform).toBe('github_follow');
- });
-
- it('does NOT fall back to the github (auth) token if github_follow is missing', async () => {
- // Only the github_follow lookup should be attempted; never a fallback to 'github'
- const findUnique = vi.fn().mockResolvedValue(null);
-
- const app = await buildFollowApp({
- oAuthToken: { findUnique } as any,
- followLog: { create: vi.fn() } as any,
- });
-
- await app.inject({ method: 'POST', url: '/api/follow/github/targetuser' });
-
- // Exactly one DB call, and it is for github_follow
- expect(findUnique).toHaveBeenCalledTimes(1);
- expect(findUnique.mock.calls[0][0].where.userId_platform.platform).toBe('github_follow');
- });
-
- it('non-GitHub platforms still use their own name as the token key', async () => {
- const findUnique = vi.fn().mockResolvedValue({
- id: 'tok-2',
- accessToken: 'enc:some-token',
- });
-
- const app = await buildFollowApp({
- oAuthToken: { findUnique } as any,
- followLog: { create: vi.fn() } as any,
- });
-
- // 'twitter' is not GitHub — it should not be mapped to 'twitter_follow'
- await app.inject({ method: 'POST', url: '/api/follow/twitter/targetuser' });
-
- expect(findUnique.mock.calls[0][0].where.userId_platform.platform).toBe('twitter');
- });
-});
-
-// ─────────────────────────────────────────────────────────────────────────────
-// 3. Scope-overwrite lifecycle — the full story
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('Scope-overwrite lifecycle — isolation between auth and connect tokens', () => {
- it('connect flow and auth flow target independent platform keys (no shared record)', async () => {
- // Simulate: user logs in → connect → log in again.
- // The auth upsert writes 'github'; the connect upsert writes 'github_follow'.
- // They never share a key, so neither can overwrite the other.
-
- const upsertCalls: string[] = [];
- const upsert = vi.fn().mockImplementation(async (args: any) => {
- upsertCalls.push(args.where.userId_platform.platform);
- return {};
- });
-
- // ── Simulate the connect callback ──────────────────────────────────────
- const connectApp = await buildConnectApp({ oAuthToken: { upsert } as any });
-
- mockFetch.mockResolvedValue({
- json: async () => ({ access_token: 'follow-token', scope: 'user:follow' }),
- });
-
- await connectApp.inject({
- method: 'GET',
- url: `/api/connect/github/callback?code=code1&state=${makeConnectState(USER_ID)}`,
- });
-
- // Connect writes github_follow
- expect(upsertCalls).toContain('github_follow');
- expect(upsertCalls).not.toContain('github');
- });
-
- it('repeated connect cycles only ever touch github_follow', async () => {
- const upsert = vi.fn().mockResolvedValue({});
- const app = await buildConnectApp({ oAuthToken: { upsert } as any });
-
- mockFetch.mockResolvedValue({
- json: async () => ({ access_token: 'follow-token', scope: 'user:follow' }),
- });
-
- // Three consecutive reconnect attempts
- for (let i = 0; i < 3; i++) {
- await app.inject({
- method: 'GET',
- url: `/api/connect/github/callback?code=code${i}&state=${makeConnectState(USER_ID)}`,
- });
- }
-
- expect(upsert).toHaveBeenCalledTimes(3);
- for (const call of upsert.mock.calls) {
- expect(call[0].where.userId_platform.platform).toBe('github_follow');
- }
- });
-
- it('follow route succeeds immediately after a connect cycle', async () => {
- const ENCRYPTED_FOLLOW_TOKEN = 'enc:follow-token-v1';
-
- const findUnique = vi.fn().mockResolvedValue({
- id: 'tok-follow',
- accessToken: ENCRYPTED_FOLLOW_TOKEN,
- });
-
- mockFetch.mockResolvedValue({ status: 204 }); // GitHub follow API
-
- const app = await buildFollowApp({
- oAuthToken: { findUnique } as any,
- followLog: { create: vi.fn().mockReturnValue({ catch: vi.fn() }) } as any,
- });
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/github/somedev',
- });
-
- expect(res.statusCode).toBe(200);
- // Confirm we went to the GitHub API with the decrypted token
- expect(mockFetch).toHaveBeenCalledWith(
- expect.stringContaining('api.github.com/user/following/somedev'),
- expect.objectContaining({
- headers: expect.objectContaining({
- Authorization: 'Bearer follow-token-v1',
- }),
- }),
- );
- });
-
- it('follow route still works after a simulated re-login cycle', async () => {
- // Re-login would call auth.ts → upsert to 'github'.
- // The github_follow record is untouched. Follow should still resolve.
-
- const FOLLOW_TOKEN = { id: 'tok-follow', accessToken: 'enc:follow-token' };
- const findUnique = vi.fn().mockResolvedValue(FOLLOW_TOKEN);
-
- mockFetch.mockResolvedValue({ status: 204 });
-
- const app = await buildFollowApp({
- oAuthToken: { findUnique } as any,
- followLog: { create: vi.fn().mockReturnValue({ catch: vi.fn() }) } as any,
- });
-
- const res = await app.inject({
- method: 'POST',
- url: '/api/follow/github/postlogindev',
- });
-
- expect(res.statusCode).toBe(200);
- // Lookup must target github_follow, not the re-written auth token
- expect(findUnique.mock.calls[0][0].where.userId_platform.platform).toBe('github_follow');
- });
-});
-
-// ─────────────────────────────────────────────────────────────────────────────
-// 4. Encrypted token persistence
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe('Encrypted token persistence', () => {
- it('connect flow stores an encrypted token, not the raw access token', async () => {
- const RAW_TOKEN = 'ghs_raw_follow_token_abc123';
- const upsert = vi.fn().mockResolvedValue({});
-
- const app = await buildConnectApp({ oAuthToken: { upsert } as any });
-
- mockFetch.mockResolvedValue({
- json: async () => ({ access_token: RAW_TOKEN, scope: 'user:follow' }),
- });
-
- await app.inject({
- method: 'GET',
- url: `/api/connect/github/callback?code=code&state=${makeConnectState(USER_ID)}`,
- });
-
- const { create } = upsert.mock.calls[0][0];
- // encrypt mock prefixes with 'enc:' — raw token must not appear verbatim
- expect(create.accessToken).toBe(`enc:${RAW_TOKEN}`);
- expect(create.accessToken).not.toBe(RAW_TOKEN);
- });
-
- it('follow route decrypts the stored token before calling GitHub API', async () => {
- const STORED = 'enc:decrypted-follow-token';
-
- const findUnique = vi.fn().mockResolvedValue({
- id: 'tok-1',
- accessToken: STORED,
- });
-
- mockFetch.mockResolvedValue({ status: 204 });
-
- const app = await buildFollowApp({
- oAuthToken: { findUnique } as any,
- followLog: { create: vi.fn().mockReturnValue({ catch: vi.fn() }) } as any,
- });
-
- await app.inject({ method: 'POST', url: '/api/follow/github/dev' });
-
- // decrypt mock strips 'enc:' prefix
- expect(mockFetch).toHaveBeenCalledWith(
- expect.any(String),
- expect.objectContaining({
- headers: expect.objectContaining({
- Authorization: 'Bearer decrypted-follow-token',
- }),
- }),
- );
- });
-});
diff --git a/apps/backend/src/__tests__/profiles.test.ts b/apps/backend/src/__tests__/profiles.test.ts
index 07d10f98..ef1aad65 100644
--- a/apps/backend/src/__tests__/profiles.test.ts
+++ b/apps/backend/src/__tests__/profiles.test.ts
@@ -1,7 +1,6 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import Fastify from 'fastify';
import { profileRoutes } from '../routes/profiles.js';
-import type { PrismaClient } from '@prisma/client';
const mockUser = {
id: 'user-123',
@@ -20,17 +19,17 @@ const mockUser = {
providerId: 'gh-123',
};
-const mockPrisma: Pick = {
+const mockPrisma = {
user: {
findUnique: vi.fn(),
findFirst: vi.fn(),
update: vi.fn(),
- } as unknown as PrismaClient['user'],
+ },
};
async function buildApp() {
const app = Fastify();
- app.decorate('prisma', mockPrisma as unknown as PrismaClient);
+ app.decorate('prisma', mockPrisma);
app.decorate('authenticate', async (request: any) => {
request.user = { id: 'user-123' };
});
@@ -90,7 +89,7 @@ describe('PUT /api/profiles/me', () => {
expect(res.json().error).toBe('Validation failed');
});
- it('should return 409 if username is already taken (pre-check)', async () => {
+ it('should return 409 if username is already taken', async () => {
mockPrisma.user.findFirst.mockResolvedValue({ id: 'other-user' });
const app = await buildApp();
const res = await app.inject({
@@ -101,50 +100,4 @@ describe('PUT /api/profiles/me', () => {
expect(res.statusCode).toBe(409);
expect(res.json().error).toBe('Username already taken');
});
-
- it('should return 409 when a concurrent request wins the unique constraint race (P2002)', async () => {
- // Both requests pass the findFirst check; the DB unique constraint fires on
- // the losing write — Prisma raises P2002.
- mockPrisma.user.findFirst.mockResolvedValue(null);
- const p2002 = Object.assign(new Error('Unique constraint failed'), { code: 'P2002' });
- mockPrisma.user.update.mockRejectedValue(p2002);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'PUT',
- url: '/api/profiles/me',
- payload: { username: 'raced-username' },
- });
-
- expect(res.statusCode).toBe(409);
- expect(res.json().error).toBe('Username already taken');
- });
-
- it('should return 500 for unexpected database errors during update', async () => {
- mockPrisma.user.findFirst.mockResolvedValue(null);
- mockPrisma.user.update.mockRejectedValue(new Error('Connection refused'));
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'PUT',
- url: '/api/profiles/me',
- payload: { username: 'anyuser' },
- });
-
- expect(res.statusCode).toBe(500);
- expect(res.json().error).toBe('Internal server error');
- });
-
- it('should not call findFirst when no username is provided in the update', async () => {
- mockPrisma.user.update.mockResolvedValue({ ...mockUser, displayName: 'New Name' });
- const app = await buildApp();
- const res = await app.inject({
- method: 'PUT',
- url: '/api/profiles/me',
- payload: { displayName: 'New Name' },
- });
-
- expect(res.statusCode).toBe(200);
- expect(mockPrisma.user.findFirst).not.toHaveBeenCalled();
- });
});
\ No newline at end of file
diff --git a/apps/backend/src/__tests__/public.test.ts b/apps/backend/src/__tests__/public.test.ts
deleted file mode 100644
index a767b25d..00000000
--- a/apps/backend/src/__tests__/public.test.ts
+++ /dev/null
@@ -1,466 +0,0 @@
-import { describe, it, expect, beforeEach, vi } from 'vitest';
-import Fastify from 'fastify';
-import jwt from '@fastify/jwt';
-import { publicRoutes } from '../routes/public.js';
-import type { PrismaClient } from '@prisma/client';
-
-// ── Mock QR utilities ─────────────────────────────────────────────────────────
-// Prevents real QR rasterisation (and any native canvas/image deps) from running
-// during unit tests. The stubs return minimal valid values that satisfy the
-// Content-Type assertions below.
-vi.mock('../utils/qr.js', () => ({
- generateQRBuffer: vi.fn().mockResolvedValue(Buffer.from('fake-png')),
- generateQRSvg: vi.fn().mockResolvedValue('fake '),
-}));
-
-import { generateQRBuffer, generateQRSvg } from '../utils/qr.js';
-
-const mockUser = {
- id: 'user-123',
- username: 'testuser',
- displayName: 'Test User',
- bio: null,
- pronouns: null,
- role: null,
- company: null,
- avatarUrl: null,
- accentColor: '#ffffff',
- platformLinks: [],
-};
-
-const mockPrisma = {
- user: {
- findUnique: vi.fn(),
- },
- platformLink: {} as any,
- cardView: {
- create: vi.fn().mockReturnValue({ catch: vi.fn() }),
- },
- followLog: {
- findMany: vi.fn().mockResolvedValue([]),
- },
- card: {} as any,
-};
-
-// ── Redis mock ────────────────────────────────────────────────────────────────
-// Simulates ioredis behaviour: get returns null (MISS) by default.
-const mockRedis = {
- get: vi.fn().mockResolvedValue(null),
- set: vi.fn().mockResolvedValue('OK'),
- del: vi.fn().mockResolvedValue(1),
-};
-
-async function buildApp() {
- const app = Fastify();
- // Register JWT so app.jwt.sign() is available for the qr-session route.
- // @fastify/jwt also adds request.jwtVerify(), which throws when no valid
- // Authorization header is present — matching the soft-auth pattern in the routes.
- await app.register(jwt, { secret: 'test-secret-for-unit-tests-only' });
- app.decorate('prisma', mockPrisma as unknown as PrismaClient);
- // Decorate with the Redis mock so cache branches execute in tests.
- app.decorate('redis', mockRedis as any);
- app.register(publicRoutes, { prefix: '/api/public' });
- await app.ready();
- return app;
-}
-
-// ─── QR size validation ───────────────────────────────────────────────────────
-
-describe('GET /api/public/:username/qr — size validation', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- // Re-attach default mock behaviour cleared by clearAllMocks
- (generateQRBuffer as ReturnType).mockResolvedValue(Buffer.from('fake-png'));
- (generateQRSvg as ReturnType).mockResolvedValue('fake ');
- mockRedis.get.mockResolvedValue(null);
- mockRedis.set.mockResolvedValue('OK');
- });
-
- // ── Reject before DB touch ─────────────────────────────────────────────────
-
- it('rejects size=0 with 400 before any DB query', async () => {
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?size=0',
- });
- expect(res.statusCode).toBe(400);
- expect(res.json().error).toMatch(/integer between/i);
- expect(mockPrisma.user.findUnique).not.toHaveBeenCalled();
- });
-
- it('rejects size=-1 with 400 before any DB query', async () => {
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?size=-1',
- });
- expect(res.statusCode).toBe(400);
- expect(mockPrisma.user.findUnique).not.toHaveBeenCalled();
- });
-
- it('rejects size=50000 (above upper bound) with 400', async () => {
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?size=50000',
- });
- expect(res.statusCode).toBe(400);
- expect(res.json().error).toMatch(/integer between/i);
- expect(mockPrisma.user.findUnique).not.toHaveBeenCalled();
- });
-
- it('rejects size=2049 (one above upper bound) with 400', async () => {
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?size=2049',
- });
- expect(res.statusCode).toBe(400);
- expect(mockPrisma.user.findUnique).not.toHaveBeenCalled();
- });
-
- it('rejects non-numeric size (abc) with 400', async () => {
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?size=abc',
- });
- expect(res.statusCode).toBe(400);
- expect(mockPrisma.user.findUnique).not.toHaveBeenCalled();
- });
-
- it('rejects floating-point size (400.5) with 400', async () => {
- // parseInt('400.5') === 400, which IS in range — this passes.
- // Documenting the boundary: fractional strings are truncated, not rejected.
- // A string like '0.5' parseInt → 0, which is out of range.
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?size=0.5',
- });
- expect(res.statusCode).toBe(400);
- expect(mockPrisma.user.findUnique).not.toHaveBeenCalled();
- });
-
- // ── Accept valid sizes ─────────────────────────────────────────────────────
-
- it('accepts size=1 (lower bound) and returns PNG', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?size=1',
- });
- expect(res.statusCode).toBe(200);
- expect(res.headers['content-type']).toMatch(/image\/png/);
- expect(generateQRBuffer).toHaveBeenCalledWith(
- expect.any(String),
- expect.objectContaining({ width: 1 }),
- );
- });
-
- it('accepts size=2048 (upper bound) and returns PNG', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?size=2048',
- });
- expect(res.statusCode).toBe(200);
- expect(res.headers['content-type']).toMatch(/image\/png/);
- expect(generateQRBuffer).toHaveBeenCalledWith(
- expect.any(String),
- expect.objectContaining({ width: 2048 }),
- );
- });
-
- it('defaults to size=400 when no size param is provided', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr',
- });
- expect(res.statusCode).toBe(200);
- expect(generateQRBuffer).toHaveBeenCalledWith(
- expect.any(String),
- expect.objectContaining({ width: 400 }),
- );
- });
-
- // ── Format selection ───────────────────────────────────────────────────────
-
- it('returns SVG when format=svg is requested', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?format=svg&size=200',
- });
- expect(res.statusCode).toBe(200);
- expect(res.headers['content-type']).toMatch(/image\/svg\+xml/);
- expect(generateQRSvg).toHaveBeenCalledWith(
- expect.any(String),
- expect.objectContaining({ width: 200 }),
- );
- });
-
- // ── User not found ─────────────────────────────────────────────────────────
-
- it('returns 404 for an unknown username (valid size)', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(null);
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/nobody/qr?size=400',
- });
- expect(res.statusCode).toBe(404);
- expect(res.json().error).toBe('User not found');
- });
-
- // ── QR generation error ────────────────────────────────────────────────────
-
- it('returns 500 when QR generation throws', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
- (generateQRBuffer as ReturnType).mockRejectedValueOnce(
- new Error('canvas error'),
- );
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr?size=400',
- });
- expect(res.statusCode).toBe(500);
- expect(res.json().error).toBe('QR code generation failed');
- });
-});
-
-// ─── Redis cache HIT / MISS behaviour ────────────────────────────────────────
-
-describe('GET /api/public/:username — Redis cache', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- mockRedis.get.mockResolvedValue(null);
- mockRedis.set.mockResolvedValue('OK');
- mockPrisma.followLog.findMany.mockResolvedValue([]);
- mockPrisma.cardView.create.mockReturnValue({ catch: vi.fn() });
- });
-
- it('returns X-Cache: MISS and queries DB on first request', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
- const app = await buildApp();
-
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser',
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.headers['x-cache']).toBe('MISS');
- expect(res.headers['cache-control']).toBe('public, max-age=300, stale-while-revalidate=60');
- // DB was queried since Redis returned null
- expect(mockPrisma.user.findUnique).toHaveBeenCalledOnce();
- // Profile should be written to Redis after the DB fetch
- expect(mockRedis.set).toHaveBeenCalledWith(
- 'profile:testuser',
- expect.any(String),
- 'EX',
- 300,
- );
- });
-
- it('returns X-Cache: HIT and skips DB on cached request', async () => {
- // Simulate a warm cache entry
- const cached = JSON.stringify({
- _userId: 'user-123',
- username: 'testuser',
- displayName: 'Test User',
- bio: null,
- pronouns: null,
- role: null,
- company: null,
- avatarUrl: null,
- accentColor: '#ffffff',
- links: [],
- });
- mockRedis.get.mockResolvedValue(cached);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser',
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.headers['x-cache']).toBe('HIT');
- // DB must NOT be queried when cache is warm
- expect(mockPrisma.user.findUnique).not.toHaveBeenCalled();
- });
-
- it('response body on cache HIT matches the cached profile', async () => {
- const cached = JSON.stringify({
- _userId: 'user-123',
- username: 'testuser',
- displayName: 'Test User',
- bio: 'A bio',
- pronouns: null,
- role: 'Engineer',
- company: null,
- avatarUrl: null,
- accentColor: '#123456',
- links: [],
- });
- mockRedis.get.mockResolvedValue(cached);
-
- const app = await buildApp();
- const res = await app.inject({ method: 'GET', url: '/api/public/testuser' });
- const body = res.json();
-
- expect(body.username).toBe('testuser');
- expect(body.accentColor).toBe('#123456');
- // Internal _userId field must not leak into the HTTP response
- expect(body._userId).toBeUndefined();
- });
-
- it('falls through to DB when Redis.get throws', async () => {
- mockRedis.get.mockRejectedValue(new Error('Redis down'));
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
-
- const app = await buildApp();
- const res = await app.inject({ method: 'GET', url: '/api/public/testuser' });
-
- expect(res.statusCode).toBe(200);
- // DB was reached despite the Redis failure
- expect(mockPrisma.user.findUnique).toHaveBeenCalledOnce();
- });
-
- it('returns 404 when user does not exist (cache MISS)', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(null);
-
- const app = await buildApp();
- const res = await app.inject({ method: 'GET', url: '/api/public/nobody' });
-
- expect(res.statusCode).toBe(404);
- expect(res.json().error).toBe('User not found');
- });
-});
-
-// ─── QR session endpoint ──────────────────────────────────────────────────────
-
-describe('GET /api/public/:username/qr-session', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- mockRedis.get.mockResolvedValue(null);
- mockRedis.set.mockResolvedValue('OK');
- });
-
- it('returns 404 when the user does not exist', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(null);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/nobody/qr-session',
- });
-
- expect(res.statusCode).toBe(404);
- expect(res.json().error).toBe('User not found');
- });
-
- it('returns a JWT token with correct shape on DB fetch (cache MISS)', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr-session',
- });
-
- expect(res.statusCode).toBe(200);
- const body = res.json();
- expect(typeof body.token).toBe('string');
- expect(body.tokenType).toBe('JWT');
- expect(body.expiresIn).toBe(600);
- expect(typeof body.expiresAt).toBe('string');
- // expiresAt must be a valid ISO 8601 date string
- expect(new Date(body.expiresAt).getTime()).toBeGreaterThan(Date.now());
- });
-
- it('token payload encodes the public profile snapshot', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr-session',
- });
-
- const { token } = res.json();
- // Decode without verifying so we can inspect the payload in the test
- const decoded = JSON.parse(
- Buffer.from(token.split('.')[1], 'base64url').toString(),
- );
- expect(decoded.sub).toBe('testuser');
- expect(decoded.profile.username).toBe('testuser');
- expect(decoded.profile.displayName).toBe('Test User');
- });
-
- it('serves snapshot from Redis cache without querying DB', async () => {
- const cached = JSON.stringify({
- _userId: 'user-123',
- username: 'testuser',
- displayName: 'Cached User',
- bio: null,
- pronouns: null,
- role: null,
- company: null,
- avatarUrl: null,
- accentColor: '#ffffff',
- links: [],
- });
- mockRedis.get.mockResolvedValue(cached);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr-session',
- });
-
- expect(res.statusCode).toBe(200);
- // DB must not be reached when the cache is warm
- expect(mockPrisma.user.findUnique).not.toHaveBeenCalled();
-
- const { token } = res.json();
- const decoded = JSON.parse(
- Buffer.from(token.split('.')[1], 'base64url').toString(),
- );
- expect(decoded.profile.displayName).toBe('Cached User');
- });
-
- it('includes Cache-Control header in qr-session response', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
-
- const app = await buildApp();
- const res = await app.inject({
- method: 'GET',
- url: '/api/public/testuser/qr-session',
- });
-
- expect(res.headers['cache-control']).toBe('public, max-age=300, stale-while-revalidate=60');
- });
-
- it('caches the profile in Redis when served from DB', async () => {
- mockPrisma.user.findUnique.mockResolvedValue(mockUser);
-
- const app = await buildApp();
- await app.inject({ method: 'GET', url: '/api/public/testuser/qr-session' });
-
- expect(mockRedis.set).toHaveBeenCalledWith(
- 'profile:testuser',
- expect.any(String),
- 'EX',
- 300,
- );
- });
-});
diff --git a/apps/backend/src/__tests__/team.test.ts b/apps/backend/src/__tests__/team.test.ts
deleted file mode 100644
index 350298a1..00000000
--- a/apps/backend/src/__tests__/team.test.ts
+++ /dev/null
@@ -1,776 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
-import Fastify, { FastifyInstance } from 'fastify';
-import { PrismaClient, TeamRole } from '@prisma/client';
-import { teamRoutes } from '../routes/team';
-
-// ─── Shared mock data ─────────────────────────────────────────────────────────
-
-const MOCK_OWNER_ID = 'user-uuid-001';
-const MOCK_MEMBER_ID = 'user-uuid-002';
-const MOCK_OUTSIDER_ID = 'user-uuid-003';
-
-const MOCK_OWNER = {
- id: MOCK_OWNER_ID,
- username: 'johndoe',
- displayName: 'John Doe',
- bio: 'Team owner',
- pronouns: 'he/him',
- role: 'Software Engineer',
- company: 'Acme Corp',
- avatarUrl: 'https://example.com/john.png',
- accentColor: '#6366f1',
-};
-
-const MOCK_MEMBER_USER = {
- id: MOCK_MEMBER_ID,
- username: 'janedoe',
- displayName: 'Jane Doe',
- bio: null,
- pronouns: null,
- role: 'Designer',
- company: null,
- avatarUrl: null,
- accentColor: '#f43f5e',
-};
-
-const MOCK_PLATFORM_LINKS = [
- { id: 'link-uuid-001', platform: 'github', username: 'johndoe', url: 'https://github.com/johndoe', displayOrder: 0 },
- { id: 'link-uuid-002', platform: 'twitter', username: 'johndoe_', url: 'https://twitter.com/johndoe_', displayOrder: 1 },
-];
-
-const MOCK_TEAM = {
- id: 'team-uuid-001',
- name: 'DevCard Core',
- slug: 'devcard-core',
- description: 'Building the future of developer cards',
- avatarUrl: 'https://example.com/team.png',
- ownerId: MOCK_OWNER_ID,
- createdAt: new Date('2024-01-01T00:00:00Z'),
- updatedAt: new Date('2024-06-01T00:00:00Z'),
-};
-
-const MOCK_TEAM_WITH_MEMBERS = {
- ...MOCK_TEAM,
- members: [
- {
- id: 'tm-uuid-001',
- teamId: MOCK_TEAM.id,
- userId: MOCK_OWNER_ID,
- role: TeamRole.OWNER,
- joinedAt: new Date('2024-01-01T00:00:00Z'),
- user: { ...MOCK_OWNER, platformLinks: MOCK_PLATFORM_LINKS },
- },
- {
- id: 'tm-uuid-002',
- teamId: MOCK_TEAM.id,
- userId: MOCK_MEMBER_ID,
- role: TeamRole.MEMBER,
- joinedAt: new Date('2024-02-01T00:00:00Z'),
- user: { ...MOCK_MEMBER_USER, platformLinks: [] },
- },
- ],
-};
-
-// ─── Prisma mock ──────────────────────────────────────────────────────────────
-
-const prismaMock = {
- team: {
- create: vi.fn(),
- findUnique: vi.fn(),
- update: vi.fn(),
- delete: vi.fn(),
- },
- teamMember: {
- create: vi.fn(),
- delete: vi.fn(),
- },
- user: {
- findUnique: vi.fn(),
- },
- $transaction: vi.fn(),
-};
-
-// ─── App factory ──────────────────────────────────────────────────────────────
-
-let mockJwtVerify = vi.fn();
-
-async function buildApp(): Promise {
- const app = Fastify({ logger: false });
-
- app.decorate('prisma', prismaMock as unknown as PrismaClient);
-
- app.decorateRequest('jwtVerify', function () {
- return mockJwtVerify();
- });
-
- await app.register(teamRoutes);
- await app.ready();
- return app;
-}
-
-// ─── Helpers ─────────────────────────────────────────────────────────────────
-
-function authHeader(): Record {
- return { Authorization: 'Bearer mock-token' };
-}
-
-async function createTeam(
- app: FastifyInstance,
- body: Record,
- authenticated = true,
-) {
- return app.inject({
- method: 'POST',
- url: '/',
- headers: authenticated ? authHeader() : {},
- payload: body,
- });
-}
-
-// ─── Test suite ───────────────────────────────────────────────────────────────
-
-describe('Teams API', () => {
- let app: FastifyInstance;
-
- beforeEach(async () => {
- vi.clearAllMocks();
- mockJwtVerify.mockResolvedValue({ id: MOCK_OWNER_ID });
- app = await buildApp();
- });
-
- afterEach(async () => {
- await app.close();
- });
-
- // ── POST / — create team ──────────────────────────────────────────────────
-
- describe('POST / — create team', () => {
- const validBody = {
- name: 'DevCard Core',
- description: 'Building the future of developer cards',
- avatarUrl: 'https://example.com/team.png',
- };
-
- it('201 — creates team and auto-adds owner as OWNER member', async () => {
- prismaMock.team.findUnique.mockResolvedValue(null);
- prismaMock.$transaction.mockImplementation(async (cb: any) => {
- return cb({
- team: { create: vi.fn().mockResolvedValue(MOCK_TEAM) },
- teamMember: { create: vi.fn().mockResolvedValue({}) },
- });
- });
-
- const res = await createTeam(app, validBody);
-
- expect(res.statusCode).toBe(201);
- const body = res.json();
- expect(body.name).toBe('DevCard Core');
- expect(body.ownerId).toBe(MOCK_OWNER_ID);
- expect(body.slug).toBe('devcard-core');
- });
-
- it('401 — rejects unauthenticated request', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Unauthorized'));
-
- const res = await createTeam(app, validBody, false);
-
- expect(res.statusCode).toBe(401);
- expect(res.json()).toMatchObject({ error: 'Unauthorized' });
- });
-
- it('400 — rejects name shorter than 3 characters', async () => {
- const res = await createTeam(app, { ...validBody, name: 'AB' });
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects name longer than 100 characters', async () => {
- const res = await createTeam(app, { ...validBody, name: 'A'.repeat(101) });
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects invalid avatarUrl', async () => {
- const res = await createTeam(app, { ...validBody, avatarUrl: 'not-a-url' });
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects missing name', async () => {
- const { name: _omit, ...bodyWithoutName } = validBody;
- const res = await createTeam(app, bodyWithoutName);
- expect(res.statusCode).toBe(400);
- });
-
- it('201 — creates team without optional fields', async () => {
- prismaMock.team.findUnique.mockResolvedValue(null);
- prismaMock.$transaction.mockImplementation(async (cb: any) => {
- return cb({
- team: { create: vi.fn().mockResolvedValue({ ...MOCK_TEAM, description: null, avatarUrl: null }) },
- teamMember: { create: vi.fn().mockResolvedValue({}) },
- });
- });
-
- const res = await createTeam(app, { name: 'DevCard Core' });
- expect(res.statusCode).toBe(201);
- });
-
- it('500 — returns 500 on database failure', async () => {
- prismaMock.team.findUnique.mockResolvedValue(null);
- prismaMock.$transaction.mockRejectedValue(new Error('DB error'));
-
- const res = await createTeam(app, validBody);
- expect(res.statusCode).toBe(500);
- expect(res.json()).toMatchObject({ error: 'Failed to create team' });
- });
- });
-
- // ── GET /:slug — public team profile ─────────────────────────────────────
-
- describe('GET /:slug — public team profile', () => {
- it('200 — returns team with members in PublicProfile shape', async () => {
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM_WITH_MEMBERS);
-
- const res = await app.inject({ method: 'GET', url: '/devcard-core' });
-
- expect(res.statusCode).toBe(200);
- const body = res.json();
-
- expect(body.slug).toBe('devcard-core');
- expect(body.ownerId).toBe(MOCK_OWNER_ID);
- expect(body.members).toHaveLength(2);
- });
-
- it('200 — each member has PublicProfile fields and links array', async () => {
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM_WITH_MEMBERS);
-
- const res = await app.inject({ method: 'GET', url: '/devcard-core' });
- const owner = res.json().members[0];
-
- expect(owner).toHaveProperty('username', 'johndoe');
- expect(owner).toHaveProperty('displayName', 'John Doe');
- expect(owner).toHaveProperty('accentColor');
- expect(owner).toHaveProperty('links');
- expect(owner.links).toHaveLength(2);
- expect(owner.links[0]).toMatchObject({
- platform: 'github',
- username: 'johndoe',
- url: 'https://github.com/johndoe',
- displayOrder: 0,
- });
- });
-
- it('200 — member has teamRole and joinedAt fields', async () => {
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM_WITH_MEMBERS);
-
- const res = await app.inject({ method: 'GET', url: '/devcard-core' });
- const owner = res.json().members[0];
-
- expect(owner).toHaveProperty('teamRole', 'OWNER');
- expect(owner).toHaveProperty('joinedAt');
- });
-
- it('200 — does not leak sensitive user fields on members', async () => {
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM_WITH_MEMBERS);
-
- const res = await app.inject({ method: 'GET', url: '/devcard-core' });
- const member = res.json().members[0];
-
- expect(member).not.toHaveProperty('email');
- expect(member).not.toHaveProperty('provider');
- expect(member).not.toHaveProperty('providerId');
- });
-
- it('200 — works without authentication (public endpoint)', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Should not be called'));
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM_WITH_MEMBERS);
-
- const res = await app.inject({ method: 'GET', url: '/devcard-core' });
-
- expect(res.statusCode).toBe(200);
- expect(mockJwtVerify).not.toHaveBeenCalled();
- });
-
- it('404 — returns 404 for unknown slug', async () => {
- prismaMock.team.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({ method: 'GET', url: '/ghost-team' });
-
- expect(res.statusCode).toBe(404);
- expect(res.json()).toMatchObject({ error: 'Team not found' });
- });
-
- it('200 — returns empty members array for a team with no members', async () => {
- prismaMock.team.findUnique.mockResolvedValue({ ...MOCK_TEAM, members: [] });
-
- const res = await app.inject({ method: 'GET', url: '/devcard-core' });
-
- expect(res.statusCode).toBe(200);
- expect(res.json().members).toHaveLength(0);
- });
- });
-
- // ── POST /:slug/members — invite member ───────────────────────────────────
-
- describe('POST /:slug/members — invite member (owner only)', () => {
- const teamWithOwnerOnly = {
- ...MOCK_TEAM,
- owner: MOCK_OWNER,
- members: [
- {
- id: 'tm-uuid-001',
- teamId: MOCK_TEAM.id,
- userId: MOCK_OWNER_ID,
- role: TeamRole.OWNER,
- joinedAt: new Date(),
- user: MOCK_OWNER,
- },
- ],
- };
-
- it('201 — owner can invite a new member by username', async () => {
- prismaMock.team.findUnique.mockResolvedValue(teamWithOwnerOnly);
- prismaMock.user.findUnique.mockResolvedValue(MOCK_MEMBER_USER);
- prismaMock.teamMember.create.mockResolvedValue({});
-
- const res = await app.inject({
- method: 'POST',
- url: '/devcard-core/members',
- headers: authHeader(),
- payload: { username: 'janedoe' },
- });
-
- expect(res.statusCode).toBe(201);
- expect(prismaMock.teamMember.create).toHaveBeenCalledOnce();
-
- const callData = prismaMock.teamMember.create.mock.calls[0][0].data;
- expect(callData.userId).toBe(MOCK_MEMBER_ID);
- expect(callData.role).toBe(TeamRole.MEMBER);
- });
-
- it('401 — rejects unauthenticated request', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Unauthorized'));
-
- const res = await app.inject({
- method: 'POST',
- url: '/devcard-core/members',
- payload: { username: 'janedoe' },
- });
-
- expect(res.statusCode).toBe(401);
- });
-
- it('403 — non-owner cannot invite members', async () => {
- mockJwtVerify.mockResolvedValue({ id: MOCK_MEMBER_ID });
- prismaMock.team.findUnique.mockResolvedValue(teamWithOwnerOnly);
-
- const res = await app.inject({
- method: 'POST',
- url: '/devcard-core/members',
- headers: authHeader(),
- payload: { username: 'someoneelse' },
- });
-
- expect(res.statusCode).toBe(403);
- expect(prismaMock.teamMember.create).not.toHaveBeenCalled();
- });
-
- it('409 — cannot invite a user who is already a member', async () => {
- prismaMock.team.findUnique.mockResolvedValue({
- ...teamWithOwnerOnly,
- members: [
- ...teamWithOwnerOnly.members,
- {
- id: 'tm-uuid-002',
- teamId: MOCK_TEAM.id,
- userId: MOCK_MEMBER_ID,
- role: TeamRole.MEMBER,
- joinedAt: new Date(),
- user: MOCK_MEMBER_USER,
- },
- ],
- });
-
- const res = await app.inject({
- method: 'POST',
- url: '/devcard-core/members',
- headers: authHeader(),
- payload: { username: 'janedoe' },
- });
-
- expect(res.statusCode).toBe(409);
- expect(prismaMock.teamMember.create).not.toHaveBeenCalled();
- });
-
- it('409 — cannot invite the owner (they are already a member)', async () => {
- prismaMock.team.findUnique.mockResolvedValue(teamWithOwnerOnly);
-
- const res = await app.inject({
- method: 'POST',
- url: '/devcard-core/members',
- headers: authHeader(),
- payload: { username: 'johndoe' },
- });
-
- expect(res.statusCode).toBe(409);
- });
-
- it('404 — returns 404 when invited username does not exist', async () => {
- prismaMock.team.findUnique.mockResolvedValue(teamWithOwnerOnly);
- prismaMock.user.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'POST',
- url: '/devcard-core/members',
- headers: authHeader(),
- payload: { username: 'ghostuser' },
- });
-
- expect(res.statusCode).toBe(404);
- });
-
- it('404 — returns 404 when team does not exist', async () => {
- prismaMock.team.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'POST',
- url: '/ghost-team/members',
- headers: authHeader(),
- payload: { username: 'janedoe' },
- });
-
- expect(res.statusCode).toBe(404);
- });
-
- it('400 — rejects empty username', async () => {
- const res = await app.inject({
- method: 'POST',
- url: '/devcard-core/members',
- headers: authHeader(),
- payload: { username: '' },
- });
-
- expect(res.statusCode).toBe(400);
- });
- });
-
- // ── DELETE /:slug/members/:userId — remove member ─────────────────────────
-
- describe('DELETE /:slug/members/:userId — remove member', () => {
- const teamWithBothMembers = {
- ...MOCK_TEAM,
- members: [
- {
- id: 'tm-uuid-001',
- teamId: MOCK_TEAM.id,
- userId: MOCK_OWNER_ID,
- role: TeamRole.OWNER,
- joinedAt: new Date(),
- user: MOCK_OWNER,
- },
- {
- id: 'tm-uuid-002',
- teamId: MOCK_TEAM.id,
- userId: MOCK_MEMBER_ID,
- role: TeamRole.MEMBER,
- joinedAt: new Date(),
- user: MOCK_MEMBER_USER,
- },
- ],
- };
-
- it('200 — owner can remove a member', async () => {
- prismaMock.team.findUnique.mockResolvedValue(teamWithBothMembers);
- prismaMock.teamMember.delete.mockResolvedValue({});
-
- const res = await app.inject({
- method: 'DELETE',
- url: `/devcard-core/members/${MOCK_MEMBER_ID}`,
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(200);
- const deleteArg = prismaMock.teamMember.delete.mock.calls[0][0].where;
- expect(deleteArg).toMatchObject({
- userId_teamId: {
- teamId: MOCK_TEAM.id,
- userId: MOCK_MEMBER_ID,
- },
- });
- });
-
- it('200 — member can self-remove (leave team)', async () => {
- mockJwtVerify.mockResolvedValue({ id: MOCK_MEMBER_ID });
- prismaMock.team.findUnique.mockResolvedValue(teamWithBothMembers);
- prismaMock.teamMember.delete.mockResolvedValue({});
-
- const res = await app.inject({
- method: 'DELETE',
- url: `/devcard-core/members/${MOCK_MEMBER_ID}`,
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(200);
- });
-
- it('403 — owner cannot leave their own team', async () => {
- prismaMock.team.findUnique.mockResolvedValue(teamWithBothMembers);
-
- const res = await app.inject({
- method: 'DELETE',
- url: `/devcard-core/members/${MOCK_OWNER_ID}`,
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(403);
- expect(prismaMock.teamMember.delete).not.toHaveBeenCalled();
- });
-
- it('403 — outsider cannot remove another member', async () => {
- mockJwtVerify.mockResolvedValue({ id: MOCK_OUTSIDER_ID });
- prismaMock.team.findUnique.mockResolvedValue(teamWithBothMembers);
-
- const res = await app.inject({
- method: 'DELETE',
- url: `/devcard-core/members/${MOCK_MEMBER_ID}`,
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(403);
- expect(prismaMock.teamMember.delete).not.toHaveBeenCalled();
- });
-
- it('401 — rejects unauthenticated request', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Unauthorized'));
-
- const res = await app.inject({
- method: 'DELETE',
- url: `/devcard-core/members/${MOCK_MEMBER_ID}`,
- });
-
- expect(res.statusCode).toBe(401);
- });
-
- it('404 — returns 404 when team does not exist', async () => {
- prismaMock.team.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'DELETE',
- url: `/ghost-team/members/${MOCK_MEMBER_ID}`,
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(404);
- });
-
- it('404 — returns 404 when userId is not a team member', async () => {
- prismaMock.team.findUnique.mockResolvedValue(teamWithBothMembers);
-
- const res = await app.inject({
- method: 'DELETE',
- url: `/devcard-core/members/${MOCK_OUTSIDER_ID}`,
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(404);
- });
- });
-
- // ── PATCH /:slug — update team ────────────────────────────────────────────
-
- describe('PATCH /:slug — update team (owner only)', () => {
- it('200 — owner can update name, description, avatarUrl', async () => {
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM);
- prismaMock.team.update.mockResolvedValue({ ...MOCK_TEAM, name: 'New Name' });
-
- const res = await app.inject({
- method: 'PATCH',
- url: '/devcard-core',
- headers: authHeader(),
- payload: { name: 'New Name' },
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.json().name).toBe('New Name');
- });
-
- it('403 — non-owner cannot update team', async () => {
- mockJwtVerify.mockResolvedValue({ id: MOCK_MEMBER_ID });
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM);
-
- const res = await app.inject({
- method: 'PATCH',
- url: '/devcard-core',
- headers: authHeader(),
- payload: { name: 'Hijacked Name' },
- });
-
- expect(res.statusCode).toBe(403);
- expect(prismaMock.team.update).not.toHaveBeenCalled();
- });
-
- it('401 — rejects unauthenticated request', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Unauthorized'));
-
- const res = await app.inject({
- method: 'PATCH',
- url: '/devcard-core',
- payload: { name: 'New Name' },
- });
-
- expect(res.statusCode).toBe(401);
- });
-
- it('400 — rejects empty body (at least one field required)', async () => {
- const res = await app.inject({
- method: 'PATCH',
- url: '/devcard-core',
- headers: authHeader(),
- payload: {},
- });
-
- expect(res.statusCode).toBe(400);
- });
-
- it('400 — rejects invalid avatarUrl', async () => {
- const res = await app.inject({
- method: 'PATCH',
- url: '/devcard-core',
- headers: authHeader(),
- payload: { avatarUrl: 'not-a-url' },
- });
-
- expect(res.statusCode).toBe(400);
- });
-
- it('404 — returns 404 for unknown slug', async () => {
- prismaMock.team.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'PATCH',
- url: '/ghost-team',
- headers: authHeader(),
- payload: { name: 'New Name' },
- });
-
- expect(res.statusCode).toBe(404);
- });
- });
-
- // ── DELETE /:slug — delete team ───────────────────────────────────────────
-
- describe('DELETE /:slug — delete team (owner only)', () => {
- it('200 — owner can delete team', async () => {
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM);
- prismaMock.team.delete.mockResolvedValue({});
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/devcard-core',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(200);
- expect(prismaMock.team.delete).toHaveBeenCalledOnce();
- });
-
- it('403 — non-owner cannot delete team', async () => {
- mockJwtVerify.mockResolvedValue({ id: MOCK_MEMBER_ID });
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM);
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/devcard-core',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(403);
- expect(prismaMock.team.delete).not.toHaveBeenCalled();
- });
-
- it('401 — rejects unauthenticated request', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Unauthorized'));
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/devcard-core',
- });
-
- expect(res.statusCode).toBe(401);
- });
-
- it('404 — returns 404 for unknown slug', async () => {
- prismaMock.team.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/ghost-team',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(404);
- });
-
- it('500 — returns 500 on database failure', async () => {
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM);
- prismaMock.team.delete.mockRejectedValue(new Error('DB error'));
-
- const res = await app.inject({
- method: 'DELETE',
- url: '/devcard-core',
- headers: authHeader(),
- });
-
- expect(res.statusCode).toBe(500);
- });
- });
-
- // ── GET /:slug/qr — QR code ───────────────────────────────────────────────
-
- describe('GET /:slug/qr — QR code', () => {
- it('200 — returns PNG image for valid slug', async () => {
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM);
-
- const res = await app.inject({
- method: 'GET',
- url: '/devcard-core/qr',
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.headers['content-type']).toMatch('image/png');
- });
-
- it('200 — encodes correct devcard.dev URL in QR', async () => {
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM);
-
- const res = await app.inject({
- method: 'GET',
- url: '/devcard-core/qr',
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.rawPayload.length).toBeGreaterThan(0);
- });
-
- it('200 — works without authentication (public endpoint)', async () => {
- mockJwtVerify.mockRejectedValue(new Error('Should not be called'));
- prismaMock.team.findUnique.mockResolvedValue(MOCK_TEAM);
-
- const res = await app.inject({
- method: 'GET',
- url: '/devcard-core/qr',
- });
-
- expect(res.statusCode).toBe(200);
- expect(mockJwtVerify).not.toHaveBeenCalled();
- });
-
- it('404 — returns 404 for unknown slug', async () => {
- prismaMock.team.findUnique.mockResolvedValue(null);
-
- const res = await app.inject({
- method: 'GET',
- url: '/ghost-team/qr',
- });
-
- expect(res.statusCode).toBe(404);
- });
- });
-});
\ No newline at end of file
diff --git a/apps/backend/src/__tests__/validateEnv.test.ts b/apps/backend/src/__tests__/validateEnv.test.ts
deleted file mode 100644
index eb0574bd..00000000
--- a/apps/backend/src/__tests__/validateEnv.test.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import { describe, it, expect, vi, afterEach } from 'vitest';
-import { validateEnv } from '../utils/validateEnv.js';
-
-// ── helpers ──────────────────────────────────────────────────────────────────
-
-/**
- * Replaces process.exit with a throwing stub for the duration of the test so
- * that a failing validateEnv() call does not terminate the test process.
- * Returns the spy so callers can assert the exit code.
- */
-function stubExit() {
- return vi.spyOn(process, 'exit').mockImplementation((code?: number | string) => {
- throw new Error(`process.exit(${code})`);
- }) as unknown as ReturnType;
-}
-
-// ── test suite ────────────────────────────────────────────────────────────────
-
-describe('validateEnv', () => {
- afterEach(() => {
- vi.restoreAllMocks();
- vi.unstubAllEnvs();
- });
-
- // ─── JWT_SECRET ──────────────────────────────────────────────────────────
-
- it('exits with code 1 when JWT_SECRET is absent', () => {
- vi.stubEnv('JWT_SECRET', undefined as unknown as string);
- vi.stubEnv('ENCRYPTION_KEY', 'a-valid-encryption-key');
- const exit = stubExit();
-
- expect(() => validateEnv()).toThrow('process.exit(1)');
- expect(exit).toHaveBeenCalledWith(1);
- });
-
- it('exits with code 1 when JWT_SECRET is an empty string', () => {
- vi.stubEnv('JWT_SECRET', '');
- vi.stubEnv('ENCRYPTION_KEY', 'a-valid-encryption-key');
- stubExit();
-
- expect(() => validateEnv()).toThrow('process.exit(1)');
- });
-
- it('exits with code 1 when JWT_SECRET is the known insecure default in production', () => {
- vi.stubEnv('JWT_SECRET', 'dev-secret-change-me');
- vi.stubEnv('ENCRYPTION_KEY', 'a-valid-encryption-key');
- vi.stubEnv('NODE_ENV', 'production');
- stubExit();
-
- expect(() => validateEnv()).toThrow('process.exit(1)');
- });
-
- it('allows the known insecure default in non-production (development)', () => {
- // The known-insecure check is production-only so local development still
- // works with the default value without requiring a full secrets setup.
- vi.stubEnv('JWT_SECRET', 'dev-secret-change-me');
- vi.stubEnv('ENCRYPTION_KEY', 'a-valid-encryption-key');
- vi.stubEnv('NODE_ENV', 'development');
-
- // Must not throw / call process.exit
- expect(() => validateEnv()).not.toThrow();
- });
-
- it('allows the known insecure default when NODE_ENV is not set', () => {
- vi.stubEnv('JWT_SECRET', 'dev-secret-change-me');
- vi.stubEnv('ENCRYPTION_KEY', 'a-valid-encryption-key');
- vi.stubEnv('NODE_ENV', undefined as unknown as string);
-
- expect(() => validateEnv()).not.toThrow();
- });
-
- // ─── ENCRYPTION_KEY ──────────────────────────────────────────────────────
-
- it('exits with code 1 when ENCRYPTION_KEY is absent', () => {
- vi.stubEnv('JWT_SECRET', 'a-valid-jwt-secret-that-is-sufficiently-long');
- vi.stubEnv('ENCRYPTION_KEY', undefined as unknown as string);
- stubExit();
-
- expect(() => validateEnv()).toThrow('process.exit(1)');
- });
-
- it('exits with code 1 when ENCRYPTION_KEY is an empty string', () => {
- vi.stubEnv('JWT_SECRET', 'a-valid-jwt-secret-that-is-sufficiently-long');
- vi.stubEnv('ENCRYPTION_KEY', '');
- stubExit();
-
- expect(() => validateEnv()).toThrow('process.exit(1)');
- });
-
- // ─── Multiple failures ────────────────────────────────────────────────────
-
- it('reports both missing secrets in a single exit call', () => {
- vi.stubEnv('JWT_SECRET', undefined as unknown as string);
- vi.stubEnv('ENCRYPTION_KEY', undefined as unknown as string);
- const exit = stubExit();
-
- expect(() => validateEnv()).toThrow('process.exit(1)');
- // A single exit — not one per error — so operators fix everything in one deploy.
- expect(exit).toHaveBeenCalledTimes(1);
- expect(exit).toHaveBeenCalledWith(1);
- });
-
- // ─── Happy path ──────────────────────────────────────────────────────────
-
- it('passes when both secrets are valid in development', () => {
- vi.stubEnv('JWT_SECRET', 'a-valid-jwt-secret-that-is-sufficiently-long');
- vi.stubEnv('ENCRYPTION_KEY', 'a-valid-32-char-encryption-key!!');
- vi.stubEnv('NODE_ENV', 'development');
-
- expect(() => validateEnv()).not.toThrow();
- });
-
- it('passes when both secrets are valid in production', () => {
- vi.stubEnv('JWT_SECRET', 'a-long-random-production-jwt-secret-with-enough-entropy');
- vi.stubEnv('ENCRYPTION_KEY', 'a-64-char-hex-encryption-key-for-aes-256-gcm-0000000000000000');
- vi.stubEnv('NODE_ENV', 'production');
-
- expect(() => validateEnv()).not.toThrow();
- });
-
- // ─── No secret leakage ───────────────────────────────────────────────────
-
- it('does not log the value of JWT_SECRET when reporting errors', () => {
- const secretValue = 'super-secret-value-that-must-not-appear-in-logs';
- vi.stubEnv('JWT_SECRET', undefined as unknown as string);
- vi.stubEnv('ENCRYPTION_KEY', 'a-valid-encryption-key');
- stubExit();
-
- const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
-
- expect(() => validateEnv()).toThrow('process.exit(1)');
-
- const allOutput = errSpy.mock.calls.flat().join(' ');
- expect(allOutput).not.toContain(secretValue);
- });
-});
diff --git a/apps/backend/src/app.ts b/apps/backend/src/app.ts
index 06b87205..8e8cf381 100644
--- a/apps/backend/src/app.ts
+++ b/apps/backend/src/app.ts
@@ -1,37 +1,26 @@
-import path from 'node:path';
-import { fileURLToPath } from 'node:url';
-
-import cookie from '@fastify/cookie';
+import Fastify from 'fastify';
import cors from '@fastify/cors';
import helmet from '@fastify/helmet';
import jwt from '@fastify/jwt';
+import cookie from '@fastify/cookie';
import multipart from '@fastify/multipart';
-import rateLimit from '@fastify/rate-limit';
import fastifyStatic from '@fastify/static';
-import Fastify, {type FastifyInstance} from 'fastify';
+import path from 'path';
+import { fileURLToPath } from 'url';
import { prismaPlugin } from './plugins/prisma.js';
import { redisPlugin } from './plugins/redis.js';
-import { analyticsRoutes } from './routes/analytics.js';
import { authRoutes } from './routes/auth.js';
-import { cardRoutes } from './routes/cards.js';
-import { connectRoutes } from './routes/connect.js';
-import { eventRoutes } from './routes/event.js';
-import { followRoutes } from './routes/follow.js';
-import { nfcRoutes } from './routes/nfc.js';
import { profileRoutes } from './routes/profiles.js';
+import { cardRoutes } from './routes/cards.js';
import { publicRoutes } from './routes/public.js';
-import { validateEnv } from './utils/validateEnv.js';
-import { teamRoutes } from './routes/team.js';
+import { followRoutes } from './routes/follow.js';
+import { connectRoutes } from './routes/connect.js';
+import { analyticsRoutes } from './routes/analytics.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
-export async function buildApp():Promise {
- // Validate all required secrets before registering any plugin.
- // If validation fails the process exits here — no partially-initialised
- // auth state can exist because Fastify is not yet instantiated.
- validateEnv();
-
+export async function buildApp() {
const app = Fastify({
logger: {
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
@@ -66,34 +55,28 @@ export async function buildApp():Promise {
});
await app.register(jwt, {
- // validateEnv() above guarantees JWT_SECRET is present and safe.
- secret: process.env.JWT_SECRET!,
+ secret: process.env.JWT_SECRET || 'dev-secret-change-me',
});
await app.register(cookie);
await app.register(multipart, { limits: { fileSize: 5 * 1024 * 1024 } }); // 5MB
- await app.register(rateLimit, {
- max: 100,
- timeWindow: '1 minute',
- });
-// Files must be served through authenticated route handlers
-// with ownership validation.
+ // Static file serving for uploads
+ await app.register(fastifyStatic, {
+ root: path.join(__dirname, '..', 'uploads'),
+ prefix: '/uploads/',
+ decorateReply: false,
+ });
// ─── Database & Cache Plugins ───
- if (process.env.NODE_ENV !== 'test') {
- await app.register(prismaPlugin); //change
-}
- if (process.env.NODE_ENV !== 'test') {
+ await app.register(prismaPlugin);
await app.register(redisPlugin);
-}
+
// ─── Auth Decorator ───
app.decorate('authenticate', async function (request: any, reply: any) {
try {
- // Ensure the verified payload is assigned to `request.user` like the original plugin.
- const payload = await request.jwtVerify();
- if (payload) request.user = payload;
- } catch (error) {
+ await request.jwtVerify();
+ } catch (err) {
reply.status(401).send({ error: 'Unauthorized' });
}
});
@@ -102,38 +85,17 @@ export async function buildApp():Promise {
await app.register(authRoutes, { prefix: '/auth' });
await app.register(profileRoutes, { prefix: '/api/profiles' });
await app.register(cardRoutes, { prefix: '/api/cards' });
- // Public routes: standardise on `/api/u` (remove duplicate `/api/public`).
await app.register(publicRoutes, { prefix: '/api/u' });
await app.register(followRoutes, { prefix: '/api/follow' });
await app.register(connectRoutes, { prefix: '/api/connect' });
await app.register(analyticsRoutes, { prefix: '/api/analytics' });
- await app.register(nfcRoutes, { prefix: '/api/nfc' });
- await app.register(eventRoutes, {prefix: '/api/events'})
- await app.register(teamRoutes, {prefix: '/api/teams'})
-
// ─── Health Check ───
-type HealthResponse = {
- status: 'ok';
-};
-
-app.get('/health', async (): Promise => {
- return { status: 'ok' };
-});
+ app.get('/health', async () => ({
+ status: 'ok',
+ timestamp: new Date().toISOString(),
+ service: 'devcard-api',
+ }));
- // Centralized error handler: log and return a consistent 500 shape for unhandled errors.
- app.setErrorHandler((error, request, reply) => {
- app.log.error({ err: error }, 'Unhandled error');
- // Also print to console to aid test diagnostics when logger is disabled.
- // This helps surface stack traces in CI/test runs.
- // eslint-disable-next-line no-console
- console.error(error);
- // If headers were already sent, fall back to default behaviour.
- if (reply.sent) {
- return;
- }
- // Keep response shape consistent across the API.
- reply.status(500).send({ error: 'Internal server error' });
- });
return app;
}
diff --git a/apps/backend/src/env.ts b/apps/backend/src/env.ts
index 7d841d9c..5902853d 100644
--- a/apps/backend/src/env.ts
+++ b/apps/backend/src/env.ts
@@ -8,9 +8,8 @@ const envPath = path.resolve(__dirname, '../../../.env');
const result = dotenv.config({ path: envPath });
if (result.error) {
- // Keep failing fast but avoid leaking via console in production code paths.
- // This file runs before the Fastify logger is available; throw so the process exits.
- throw result.error;
+ console.error('❌ Failed to load .env from:', envPath);
+ console.error(result.error);
} else {
- // .env loaded successfully
+ console.log('✅ Loaded .env from:', envPath);
}
diff --git a/apps/backend/src/plugins/prisma.ts b/apps/backend/src/plugins/prisma.ts
index f6ebede8..98e7f798 100644
--- a/apps/backend/src/plugins/prisma.ts
+++ b/apps/backend/src/plugins/prisma.ts
@@ -1,14 +1,10 @@
import fp from 'fastify-plugin';
import { PrismaClient } from '@prisma/client';
-import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
+import type { FastifyInstance } from 'fastify';
declare module 'fastify' {
interface FastifyInstance {
prisma: PrismaClient;
- authenticate(
- request: FastifyRequest,
- reply: FastifyReply
- ): Promise;
}
}
diff --git a/apps/backend/src/plugins/redis.ts b/apps/backend/src/plugins/redis.ts
index 864b112f..c7b6f94d 100644
--- a/apps/backend/src/plugins/redis.ts
+++ b/apps/backend/src/plugins/redis.ts
@@ -17,7 +17,7 @@ export const redisPlugin = fp(async (app: FastifyInstance) => {
try {
await redis.connect();
app.log.info('🔴 Redis connected');
- } catch (error) {
+ } catch (err) {
app.log.warn('⚠️ Redis connection failed — running without cache');
}
diff --git a/apps/backend/src/routes/analytics.ts b/apps/backend/src/routes/analytics.ts
index a975424f..e9a75bb9 100644
--- a/apps/backend/src/routes/analytics.ts
+++ b/apps/backend/src/routes/analytics.ts
@@ -1,162 +1,101 @@
-import type {
- FastifyInstance,
- FastifyRequest,
- FastifyReply,
-} from 'fastify';
-
-export async function analyticsRoutes(
- app: FastifyInstance
-): Promise {
-
- app.get(
- '/overview',
- {
- // eslint-disable-next-line @typescript-eslint/unbound-method
- preHandler: [async (request, reply) => { const server = request.server as any; if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } }],
- },
- async (
- request: FastifyRequest,
- _reply: FastifyReply
- ) => {
- const userId = (request.user as any).id;
- const username = (request.user as any).username;
-
- const today = new Date();
- today.setHours(0, 0, 0, 0);
-
- const [totalViews, viewsToday, totalFollows, recentViews] = await Promise.all([
- // Total views of this user's cards/profile
- app.prisma.cardView.count({
- where: { ownerId: userId },
- }),
-
- // Views today
- app.prisma.cardView.count({
- where: {
- ownerId: userId,
- createdAt: { gte: today },
+import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
+
+export async function analyticsRoutes(app: FastifyInstance) {
+
+ app.get('/overview', {
+ preHandler: [app.authenticate],
+ }, async (request: FastifyRequest, reply: FastifyReply) => {
+ const userId = (request.user as any).id;
+
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+
+ const [totalViews, viewsToday, totalFollows, recentViews] = await Promise.all([
+ // Total views of this user's cards/profile
+ app.prisma.cardView.count({
+ where: { ownerId: userId },
+ }),
+ // Views today
+ app.prisma.cardView.count({
+ where: { ownerId: userId, createdAt: { gte: today } },
+ }),
+ // Follows performed BY this user
+ app.prisma.followLog.count({
+ where: { followerId: userId, status: 'success' },
+ }),
+ // Recent views (last 5)
+ app.prisma.cardView.findMany({
+ where: { ownerId: userId },
+ orderBy: { createdAt: 'desc' },
+ take: 5,
+ include: {
+ viewer: {
+ select: { displayName: true, avatarUrl: true },
},
- }),
-
- // Follows performed BY this user
- app.prisma.followLog.count({
- where: {
- targetUsername: username,
- status: 'success',
+ card: {
+ select: { title: true },
},
- }),
-
- // Recent views (last 5)
- app.prisma.cardView.findMany({
- where: { ownerId: userId },
- orderBy: { createdAt: 'desc' },
- take: 5,
- include: {
- viewer: {
- select: {
- displayName: true,
- avatarUrl: true,
- },
- },
- card: {
- select: {
- title: true,
- },
- },
- },
- }),
- ]);
-
- // Count unique viewers
- // In raw SQL this is `SELECT COUNT(DISTINCT viewer_id) FROM card_views WHERE owner_id = ?`
- // Prisma group-by as workaround:
- const uniqueViewersQuery =
- await app.prisma.cardView.groupBy({
- by: ['viewerId', 'viewerIp'],
- where: { ownerId: userId },
- });
-
- const uniqueViewers = uniqueViewersQuery.length;
-
- return {
- totalViews,
- viewsToday,
- totalFollows,
- uniqueViewers,
- recentViews,
- };
- }
- );
-
- app.get<{
- Querystring: {
- page?: string;
- cardId?: string;
+ },
+ }),
+ ]);
+
+ // Count unique viewers
+ // In raw SQL this is `SELECT COUNT(DISTINCT viewer_id) FROM card_views WHERE owner_id = ?`
+ // Prisma group-by as workaround:
+ const uniqueViewersQuery = await app.prisma.cardView.groupBy({
+ by: ['viewerId', 'viewerIp'],
+ where: { ownerId: userId },
+ });
+ const uniqueViewers = uniqueViewersQuery.length;
+
+ return {
+ totalViews,
+ viewsToday,
+ totalFollows,
+ uniqueViewers,
+ recentViews,
};
- }>(
- '/views',
- {
- // eslint-disable-next-line @typescript-eslint/unbound-method
- preHandler: [async (request, reply) => { const server = request.server as any; if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } }],
- },
- async (
- request: FastifyRequest<{
- Querystring: {
- page?: string;
- cardId?: string;
- };
- }>,
- _reply: FastifyReply
- ) => {
- const userId = (request.user as any).id;
- const page = parseInt(request.query.page || '1', 10);
- const limit = 20;
- const skip = (page - 1) * limit;
-
- const whereClause: any = { ownerId: userId };
-
- if (request.query.cardId) {
- whereClause.cardId = request.query.cardId;
- }
-
- const [total, views] = await Promise.all([
- app.prisma.cardView.count({
- where: whereClause,
- }),
+ });
+
+ app.get('/views', {
+ preHandler: [app.authenticate],
+ }, async (request: FastifyRequest<{ Querystring: { page?: string, cardId?: string } }>, reply: FastifyReply) => {
+ const userId = (request.user as any).id;
+ const page = parseInt(request.query.page || '1', 10);
+ const limit = 20;
+ const skip = (page - 1) * limit;
+
+ const whereClause: any = { ownerId: userId };
+ if (request.query.cardId) {
+ whereClause.cardId = request.query.cardId;
+ }
- app.prisma.cardView.findMany({
- where: whereClause,
- orderBy: { createdAt: 'desc' },
- skip,
- take: limit,
- include: {
- viewer: {
- select: {
- id: true,
- username: true,
- displayName: true,
- avatarUrl: true,
- },
- },
- card: {
- select: {
- id: true,
- title: true,
- },
- },
+ const [total, views] = await Promise.all([
+ app.prisma.cardView.count({ where: whereClause }),
+ app.prisma.cardView.findMany({
+ where: whereClause,
+ orderBy: { createdAt: 'desc' },
+ skip,
+ take: limit,
+ include: {
+ viewer: {
+ select: { id: true, username: true, displayName: true, avatarUrl: true },
+ },
+ card: {
+ select: { id: true, title: true },
},
- }),
- ]);
-
- return {
- data: views,
- meta: {
- total,
- page,
- limit,
- totalPages: Math.ceil(total / limit),
},
- };
- }
- );
-}
\ No newline at end of file
+ }),
+ ]);
+
+ return {
+ data: views,
+ meta: {
+ total,
+ page,
+ limit,
+ totalPages: Math.ceil(total / limit),
+ },
+ };
+ });
+}
diff --git a/apps/backend/src/routes/auth.ts b/apps/backend/src/routes/auth.ts
index c14949e1..e12f10af 100644
--- a/apps/backend/src/routes/auth.ts
+++ b/apps/backend/src/routes/auth.ts
@@ -1,6 +1,4 @@
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
-import { encrypt } from '../utils/encryption.js';
-import { buildOAuthState, getMobileRedirectUri } from '../services/authService.js';
const GITHUB_AUTH_URL = 'https://github.com/login/oauth/authorize';
const GITHUB_TOKEN_URL = 'https://github.com/login/oauth/access_token';
@@ -15,32 +13,12 @@ interface OAuthCallbackQuery {
}
export async function authRoutes(app: FastifyInstance) {
- // Developer login bypass (development only)
- if (process.env.NODE_ENV !== 'production') {
- app.post('/dev-login', async (request: FastifyRequest, reply: FastifyReply) => {
- const user = await app.prisma.user.findUnique({ where: { username: 'devcard-demo' } });
- if (!user) {
- return reply.status(404).send({ error: 'Demo user not seeded' });
- }
- const token = app.jwt.sign({ id: user.id, username: user.username }, { expiresIn: '30d' });
- return { token };
- });
- }
+ // ─── GitHub OAuth ───
- // GitHub OAuth start
app.get('/github', async (request: FastifyRequest, reply: FastifyReply) => {
const redirectUri = `${process.env.BACKEND_URL}/auth/github/callback`;
const clientState = (request.query as any).state || '';
- const mobileRedirectUri = (request.query as any).mobile_redirect_uri || '';
- const state = buildOAuthState(clientState, mobileRedirectUri);
-
- reply.setCookie('oauth_state', state, {
- httpOnly: true,
- secure: process.env.NODE_ENV === 'production',
- sameSite: 'lax',
- path: '/',
- maxAge: 10 * 60,
- });
+ const state = clientState ? `${clientState}_${generateState()}` : generateState();
const params = new URLSearchParams({
client_id: (process.env.GITHUB_CLIENT_ID || '').trim(),
@@ -48,29 +26,26 @@ export async function authRoutes(app: FastifyInstance) {
scope: 'read:user user:email',
state,
});
-
const authUrl = `${GITHUB_AUTH_URL}?${params}`;
- app.log.debug({ provider: 'github' }, 'OAuth redirect initiated');
+ console.log('--- GITHUB OAUTH REDIRECT ---');
+ console.log('URL:', authUrl);
return reply.redirect(authUrl);
});
- // GitHub OAuth callback
app.get('/github/callback', async (request: FastifyRequest<{ Querystring: OAuthCallbackQuery }>, reply: FastifyReply) => {
- const { code, state } = request.query;
- const storedState = request.cookies?.oauth_state;
- if (!state || !storedState || state !== storedState) {
- return reply.status(400).send({ error: 'Invalid or missing OAuth state — possible CSRF attack' });
- }
- reply.clearCookie('oauth_state', { path: '/' });
-
+ const { code } = request.query;
if (!code) {
return reply.status(400).send({ error: 'Missing authorization code' });
}
try {
+ // Exchange code for token
const tokenRes = await fetch(GITHUB_TOKEN_URL, {
method: 'POST',
- headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ },
body: JSON.stringify({
client_id: (process.env.GITHUB_CLIENT_ID || '').trim(),
client_secret: (process.env.GITHUB_CLIENT_SECRET || '').trim(),
@@ -78,16 +53,20 @@ export async function authRoutes(app: FastifyInstance) {
redirect_uri: `${process.env.BACKEND_URL}/auth/github/callback`,
}),
});
-
const tokenData = (await tokenRes.json()) as any;
+
if (tokenData.error) {
- app.log.error({ tokenData }, 'GitHub token error');
+ app.log.error('GitHub token error:', tokenData);
return reply.status(400).send({ error: 'Failed to authenticate with GitHub' });
}
- const userRes = await fetch(GITHUB_USER_URL, { headers: { Authorization: `Bearer ${tokenData.access_token}` } });
+ // Fetch user profile
+ const userRes = await fetch(GITHUB_USER_URL, {
+ headers: { Authorization: `Bearer ${tokenData.access_token}` },
+ });
const githubUser = (await userRes.json()) as any;
+ // Fetch email if not public
let email = githubUser.email;
if (!email) {
const emailsRes = await fetch('https://api.github.com/user/emails', {
@@ -98,8 +77,14 @@ export async function authRoutes(app: FastifyInstance) {
email = primary?.email || emails[0]?.email;
}
+ // Upsert user
const user = await app.prisma.user.upsert({
- where: { provider_providerId: { provider: 'github', providerId: String(githubUser.id) } },
+ where: {
+ provider_providerId: {
+ provider: 'github',
+ providerId: String(githubUser.id),
+ },
+ },
update: {
email: email || `${githubUser.login}@github.local`,
displayName: githubUser.name || githubUser.login,
@@ -117,53 +102,49 @@ export async function authRoutes(app: FastifyInstance) {
},
});
- try {
- const encryptedToken = encrypt(tokenData.access_token);
- await app.prisma.oAuthToken.upsert({
- where: { userId_platform: { userId: user.id, platform: 'github' } },
- update: { accessToken: encryptedToken, scopes: 'read:user user:email' },
- create: { userId: user.id, platform: 'github', accessToken: encryptedToken, scopes: 'read:user user:email' },
- });
- } catch (err) {
- app.log.error({ err, userId: user.id }, 'Failed to persist GitHub OAuth token — authentication proceeds');
- }
+ // Save the authentication token for 'user:email read:user' so we have a basic platform connection
+ const encryptedToken = (app as any).encryption ? (app as any).encryption.encrypt(tokenData.access_token) : tokenData.access_token;
+
+ await app.prisma.oAuthToken.upsert({
+ where: { userId_platform: { userId: user.id, platform: 'github' } },
+ update: { accessToken: encryptedToken, scopes: 'read:user user:email' },
+ create: { userId: user.id, platform: 'github', accessToken: encryptedToken, scopes: 'read:user user:email' },
+ });
- const token = app.jwt.sign({ id: user.id, username: user.username }, { expiresIn: '30d' });
+ // Generate JWT
+ const token = app.jwt.sign(
+ { id: user.id, username: user.username },
+ { expiresIn: '30d' }
+ );
+ // For mobile app: redirect with token as query param
+ const mobileRedirect = process.env.MOBILE_REDIRECT_URI;
if (request.query.state?.startsWith('mobile_')) {
- const mobileRedirect = getMobileRedirectUri(request.query.state) || process.env.MOBILE_REDIRECT_URI;
- return reply.redirect(`${mobileRedirect}#token=${token}`);
+ return reply.redirect(`${mobileRedirect}?token=${token}`);
}
+ // For web: set cookie and redirect
reply.setCookie('token', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
- maxAge: 30 * 24 * 60 * 60,
+ maxAge: 30 * 24 * 60 * 60, // 30 days
});
return reply.redirect(`${process.env.PUBLIC_APP_URL}/dashboard`);
- } catch (error) {
- app.log.error({ error }, 'GitHub auth error');
+ } catch (err) {
+ app.log.error('GitHub auth error:', err);
return reply.status(500).send({ error: 'Authentication failed' });
}
});
- // Google OAuth start
+ // ─── Google OAuth ───
+
app.get('/google', async (request: FastifyRequest, reply: FastifyReply) => {
const redirectUri = `${process.env.BACKEND_URL}/auth/google/callback`;
const clientState = (request.query as any).state || '';
- const mobileRedirectUri = (request.query as any).mobile_redirect_uri || '';
- const state = buildOAuthState(clientState, mobileRedirectUri);
-
- reply.setCookie('oauth_state', state, {
- httpOnly: true,
- secure: process.env.NODE_ENV === 'production',
- sameSite: 'lax',
- path: '/',
- maxAge: 10 * 60,
- });
+ const state = clientState ? `${clientState}_${generateState()}` : generateState();
const params = new URLSearchParams({
client_id: (process.env.GOOGLE_CLIENT_ID || '').trim(),
@@ -173,22 +154,14 @@ export async function authRoutes(app: FastifyInstance) {
state,
access_type: 'offline',
});
-
const authUrl = `${GOOGLE_AUTH_URL}?${params}`;
- app.log.debug({ provider: 'google' }, 'OAuth redirect initiated');
+ console.log('--- GOOGLE OAUTH REDIRECT ---');
+ console.log('URL:', authUrl);
return reply.redirect(authUrl);
});
- // Google callback
app.get('/google/callback', async (request: FastifyRequest<{ Querystring: OAuthCallbackQuery }>, reply: FastifyReply) => {
- const { code, state } = request.query;
-
- const storedState = request.cookies?.oauth_state;
- if (!state || !storedState || state !== storedState) {
- return reply.status(400).send({ error: 'Invalid or missing OAuth state — possible CSRF attack' });
- }
- reply.clearCookie('oauth_state', { path: '/' });
-
+ const { code } = request.query;
if (!code) {
return reply.status(400).send({ error: 'Missing authorization code' });
}
@@ -205,21 +178,33 @@ export async function authRoutes(app: FastifyInstance) {
grant_type: 'authorization_code',
}),
});
-
const tokenData = (await tokenRes.json()) as any;
+
if (tokenData.error) {
- app.log.error({ tokenData }, 'Google token error');
+ app.log.error('Google token error:', tokenData);
return reply.status(400).send({ error: 'Failed to authenticate with Google' });
}
- const userRes = await fetch(GOOGLE_USER_URL, { headers: { Authorization: `Bearer ${tokenData.access_token}` } });
+ const userRes = await fetch(GOOGLE_USER_URL, {
+ headers: { Authorization: `Bearer ${tokenData.access_token}` },
+ });
const googleUser = (await userRes.json()) as any;
+ // Generate username from email
const baseUsername = googleUser.email.split('@')[0].replace(/[^a-zA-Z0-9_-]/g, '');
const user = await app.prisma.user.upsert({
- where: { provider_providerId: { provider: 'google', providerId: googleUser.id } },
- update: { email: googleUser.email, displayName: googleUser.name || baseUsername, avatarUrl: googleUser.picture },
+ where: {
+ provider_providerId: {
+ provider: 'google',
+ providerId: googleUser.id,
+ },
+ },
+ update: {
+ email: googleUser.email,
+ displayName: googleUser.name || baseUsername,
+ avatarUrl: googleUser.picture,
+ },
create: {
email: googleUser.email,
username: `${baseUsername}_${Date.now().toString(36)}`,
@@ -230,11 +215,14 @@ export async function authRoutes(app: FastifyInstance) {
},
});
- const token = app.jwt.sign({ id: user.id, username: user.username }, { expiresIn: '30d' });
+ const token = app.jwt.sign(
+ { id: user.id, username: user.username },
+ { expiresIn: '30d' }
+ );
if (request.query.state?.startsWith('mobile_')) {
- const mobileRedirect = getMobileRedirectUri(request.query.state) || process.env.MOBILE_REDIRECT_URI;
- return reply.redirect(`${mobileRedirect}#token=${token}`);
+ const mobileRedirect = process.env.MOBILE_REDIRECT_URI;
+ return reply.redirect(`${mobileRedirect}?token=${token}`);
}
reply.setCookie('token', token, {
@@ -246,19 +234,17 @@ export async function authRoutes(app: FastifyInstance) {
});
return reply.redirect(`${process.env.PUBLIC_APP_URL}/dashboard`);
- } catch (error) {
- app.log.error({ error }, 'Google auth error');
+ } catch (err) {
+ app.log.error('Google auth error:', err);
return reply.status(500).send({ error: 'Authentication failed' });
}
});
- // Current user
- app.get('/me', { preHandler: [async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return }
- if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return }
- try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) }
- }] }, async (request: FastifyRequest, reply: FastifyReply) => {
+ // ─── Current User ───
+
+ app.get('/me', {
+ preHandler: [app.authenticate],
+ }, async (request: FastifyRequest, reply: FastifyReply) => {
const userId = (request.user as any).id;
const user = await app.prisma.user.findUnique({
where: { id: userId },
@@ -274,7 +260,9 @@ export async function authRoutes(app: FastifyInstance) {
avatarUrl: true,
accentColor: true,
createdAt: true,
- oauthTokens: { select: { platform: true, scopes: true, createdAt: true } },
+ oauthTokens: {
+ select: { platform: true, scopes: true, createdAt: true },
+ },
},
});
@@ -283,11 +271,21 @@ export async function authRoutes(app: FastifyInstance) {
}
const { oauthTokens, ...userData } = user;
- return { ...userData, connectedPlatforms: oauthTokens };
+
+ return {
+ ...userData,
+ connectedPlatforms: oauthTokens,
+ };
});
+ // ─── Logout ───
+
app.post('/logout', async (request: FastifyRequest, reply: FastifyReply) => {
reply.clearCookie('token', { path: '/' });
return { message: 'Logged out' };
});
}
+
+function generateState(): string {
+ return Math.random().toString(36).substring(2, 15);
+}
diff --git a/apps/backend/src/routes/cards.ts b/apps/backend/src/routes/cards.ts
index 32fe835c..f1af7b00 100644
--- a/apps/backend/src/routes/cards.ts
+++ b/apps/backend/src/routes/cards.ts
@@ -1,138 +1,178 @@
-import { handleDbError } from '../utils/error.util.js';
-import { createCardSchema, updateCardSchema } from '../utils/validators.js';
-import * as cardService from '../services/cardService'
-
-import type { Card } from '@devcard/shared';
-import type { Prisma } from '@prisma/client';
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
+import { createCardSchema, updateCardSchema } from '../utils/validators.js';
-
-interface CreateCardBody {
- title: string;
- linkIds: string[];
-}
-
-interface UpdateCardBody {
- title?: string;
- linkIds?: string[];
-}
-
-interface CardParams {
- id: string;
-}
-
-interface PlatformLink {
- id: string;
- userId: string;
- platform: string;
- username: string;
- url: string;
- displayOrder: number;
- createdAt: Date;
-}
-
-interface CardLinkWithPlatform {
- id: string;
- cardId: string;
- platformLinkId: string;
- displayOrder: number;
- platformLink: PlatformLink;
-}
-
-interface CardWithLinks {
- id: string;
- userId: string;
- title: string;
- isDefault: boolean;
- createdAt: Date;
- updatedAt: Date;
- cardLinks: CardLinkWithPlatform[];
-}
-
-export async function cardRoutes(app: FastifyInstance): Promise {
- app.addHook('preHandler', async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return }
- if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return }
- try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) }
- });
+export async function cardRoutes(app: FastifyInstance) {
+ app.addHook('preHandler', app.authenticate);
// ─── List Cards ───
- app.get('/', async (request: FastifyRequest, reply: FastifyReply): Promise => {
- const userId = (request.user as { id: string }).id;
- try {
- return await cardService.listCards(app, userId)
- } catch (error) {
- return handleDbError(error, request, reply)
- }
+ app.get('/', async (request: FastifyRequest, reply: FastifyReply) => {
+ const userId = (request.user as any).id;
+
+ const cards = await app.prisma.card.findMany({
+ where: { userId },
+ include: {
+ cardLinks: {
+ include: { platformLink: true },
+ orderBy: { displayOrder: 'asc' },
+ },
+ },
+ orderBy: { createdAt: 'asc' },
+ });
+
+ return cards.map((card) => ({
+ id: card.id,
+ title: card.title,
+ isDefault: card.isDefault,
+ links: card.cardLinks.map((cl) => cl.platformLink),
+ }));
});
// ─── Create Card ───
- app.post('/', async (request: FastifyRequest<{ Body: CreateCardBody }>, reply: FastifyReply): Promise => {
- const userId = (request.user as { id: string }).id;
+ app.post('/', async (request: FastifyRequest, reply: FastifyReply) => {
+ const userId = (request.user as any).id;
const parsed = createCardSchema.safeParse(request.body);
if (!parsed.success) {
return reply.status(400).send({ error: 'Validation failed', details: parsed.error.flatten() });
}
- try {
- const card = await cardService.createCard(app, userId, parsed.data)
- return reply.status(201).send(card)
- } catch (error: any) {
- if (error?.code === 'OWNERSHIP') return reply.status(403).send({ error: 'One or more links do not belong to your account' })
- return handleDbError(error, request, reply)
- }
+ // Check if user's first card → make it default
+ const cardCount = await app.prisma.card.count({ where: { userId } });
+
+ const card = await app.prisma.card.create({
+ data: {
+ userId,
+ title: parsed.data.title,
+ isDefault: cardCount === 0,
+ cardLinks: {
+ create: parsed.data.linkIds.map((linkId, index) => ({
+ platformLinkId: linkId,
+ displayOrder: index,
+ })),
+ },
+ },
+ include: {
+ cardLinks: {
+ include: { platformLink: true },
+ orderBy: { displayOrder: 'asc' },
+ },
+ },
+ });
+
+ return reply.status(201).send({
+ id: card.id,
+ title: card.title,
+ isDefault: card.isDefault,
+ links: card.cardLinks.map((cl) => cl.platformLink),
+ });
});
// ─── Update Card ───
- app.put('/:id', async (request: FastifyRequest<{ Params: CardParams; Body: UpdateCardBody }>, reply: FastifyReply): Promise => {
- const userId = (request.user as { id: string }).id;
+ app.put('/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
+ const userId = (request.user as any).id;
const { id } = request.params;
- try {
- const parsed = updateCardSchema.safeParse(request.body)
- if (!parsed.success) return reply.status(400).send({ error: 'Validation failed', details: parsed.error.flatten() })
- const updated = await cardService.updateCard(app, userId, id, parsed.data)
- if (!updated) return reply.status(404).send({ error: 'Card not found' })
- return updated
- } catch (error: any) {
- if (error?.code === 'OWNERSHIP') return reply.status(403).send({ error: 'One or more links do not belong to your account' })
- return handleDbError(error, request, reply)
+ const existing = await app.prisma.card.findFirst({
+ where: { id, userId },
+ });
+
+ if (!existing) {
+ return reply.status(404).send({ error: 'Card not found' });
}
+
+ const parsed = updateCardSchema.safeParse(request.body);
+ if (!parsed.success) {
+ return reply.status(400).send({ error: 'Validation failed', details: parsed.error.flatten() });
+ }
+
+ // Update card title
+ if (parsed.data.title) {
+ await app.prisma.card.update({
+ where: { id },
+ data: { title: parsed.data.title },
+ });
+ }
+
+ // Update card links if provided
+ if (parsed.data.linkIds) {
+ // Remove existing links
+ await app.prisma.cardLink.deleteMany({ where: { cardId: id } });
+ // Add new links
+ await app.prisma.cardLink.createMany({
+ data: parsed.data.linkIds.map((linkId, index) => ({
+ cardId: id,
+ platformLinkId: linkId,
+ displayOrder: index,
+ })),
+ });
+ }
+
+ // Fetch updated card
+ const updated = await app.prisma.card.findUnique({
+ where: { id },
+ include: {
+ cardLinks: {
+ include: { platformLink: true },
+ orderBy: { displayOrder: 'asc' },
+ },
+ },
+ });
+
+ return {
+ id: updated!.id,
+ title: updated!.title,
+ isDefault: updated!.isDefault,
+ links: updated!.cardLinks.map((cl) => cl.platformLink),
+ };
});
// ─── Delete Card ───
- app.delete('/:id', async (request: FastifyRequest<{ Params: CardParams }>, reply: FastifyReply): Promise => {
- const userId = (request.user as { id: string }).id;
+ app.delete('/:id', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
+ const userId = (request.user as any).id;
const { id } = request.params;
- try {
- const res = await cardService.deleteCard(app, userId, id)
- if (res && (res as any).code === 'NOT_FOUND') return reply.status(404).send({ error: 'Card not found' })
- if (res && (res as any).code === 'LAST_CARD') return reply.status(400).send({ error: 'Cannot delete the last remaining card. A user must have at least one card.' })
- return reply.status(204).send()
- } catch (error) {
- return handleDbError(error, request, reply)
+ const existing = await app.prisma.card.findFirst({
+ where: { id, userId },
+ });
+
+ if (!existing) {
+ return reply.status(404).send({ error: 'Card not found' });
}
+
+ await app.prisma.card.delete({ where: { id } });
+ return reply.status(204).send();
});
// ─── Set Default Card ───
- app.put('/:id/default', async (request: FastifyRequest<{ Params: CardParams }>, reply: FastifyReply): Promise => {
- const userId = (request.user as { id: string }).id;
+ app.put('/:id/default', async (request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) => {
+ const userId = (request.user as any).id;
const { id } = request.params;
- try {
- const resp = await cardService.setDefaultCard(app, userId, id)
- if (!resp) return reply.status(404).send({ error: 'Card not found' })
- return resp
- } catch (error) {
- return handleDbError(error, request, reply)
+ const existing = await app.prisma.card.findFirst({
+ where: { id, userId },
+ });
+
+ if (!existing) {
+ return reply.status(404).send({ error: 'Card not found' });
}
+
+ // Unset all other defaults
+ await app.prisma.card.updateMany({
+ where: { userId },
+ data: { isDefault: false },
+ });
+
+ // Set this one
+ await app.prisma.card.update({
+ where: { id },
+ data: { isDefault: true },
+ });
+
+ return { message: 'Default card updated' };
});
}
diff --git a/apps/backend/src/routes/connect.ts b/apps/backend/src/routes/connect.ts
index bb04194d..952e8453 100644
--- a/apps/backend/src/routes/connect.ts
+++ b/apps/backend/src/routes/connect.ts
@@ -1,17 +1,8 @@
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
-import { randomBytes } from 'crypto';
-import { encrypt } from '../utils/encryption.js';
const GITHUB_AUTH_URL = 'https://github.com/login/oauth/authorize';
const GITHUB_TOKEN_URL = 'https://github.com/login/oauth/access_token';
-// Follow-capable tokens are stored under a dedicated platform key so that
-// the authentication flow (read:user user:email scope, key = 'github') and
-// the connect flow (user:follow scope, key = 'github_follow') never share
-// the same OAuthToken record. Whichever flow runs last can no longer
-// silently overwrite the other's access token.
-const GITHUB_FOLLOW_PLATFORM = 'github_follow';
-
interface OAuthCallbackQuery {
code: string;
state?: string;
@@ -26,12 +17,7 @@ export async function connectRoutes(app: FastifyInstance) {
// ─── Status ───
app.get('/status', {
- preHandler: [async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return }
- if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return }
- try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) }
- }],
+ preHandler: [app.authenticate],
}, async (request: FastifyRequest, reply: FastifyReply) => {
const userId = (request.user as any).id;
@@ -46,32 +32,20 @@ export async function connectRoutes(app: FastifyInstance) {
// ─── GitHub Connect ───
app.get('/github', {
- preHandler: [async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return }
- if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return }
- try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) }
- }],
+ preHandler: [app.authenticate],
}, async (request: FastifyRequest, reply: FastifyReply) => {
- const userId = (request.user as any).id;
- const nonce = generateState();
-
- // Store nonce in Redis with 10-minute TTL.
- // The callback verifies this to prevent CSRF attacks.
- await app.redis.set(
- `oauth:nonce:${nonce}`,
- userId,
- 'EX',
- 600
- );
-
- const state = JSON.stringify({ userId, nonce });
+ // Generate a secure state token linking back to this user session
+ // In a real app, store this in Redis to cross-check in callback
+ const state = JSON.stringify({
+ userId: (request.user as any).id,
+ nonce: generateState(),
+ });
const redirectUri = `${process.env.BACKEND_URL}/api/connect/github/callback`;
const params = new URLSearchParams({
client_id: process.env.GITHUB_CLIENT_ID || '',
redirect_uri: redirectUri,
- scope: 'user:follow',
+ scope: 'user:follow', // ONLY asking for follow scope to avoid full profile access
state: Buffer.from(state).toString('base64'),
});
@@ -87,25 +61,17 @@ export async function connectRoutes(app: FastifyInstance) {
try {
// Decode state to find which user requested the connect
- const decodedState = parseOAuthState(state);
+ const decodedState = parseGoogleState(state);
if (!decodedState) {
return reply.redirect(`${process.env.PUBLIC_APP_URL}/settings?error=connect_failed`);
}
+ const userId = decodedState.userId;
- // Verify nonce was issued by this server -- prevents CSRF
- const storedUserId = app.redis ? await app.redis.get(`oauth:nonce:${decodedState.nonce}`) : null;
-
- if (app.redis && (!storedUserId || storedUserId !== decodedState.userId)) {
- app.log.warn({ nonce: decodedState.nonce }, 'OAuth CSRF check failed: nonce mismatch');
+ if (!userId) {
return reply.redirect(`${process.env.PUBLIC_APP_URL}/settings?error=invalid_state`);
}
- // Consume the nonce -- one-time use only (if redis configured)
- if (app.redis) await app.redis.del(`oauth:nonce:${decodedState.nonce}`);
-
- const userId = decodedState.userId;
-
// Exchange code for token
const tokenRes = await fetch(GITHUB_TOKEN_URL, {
method: 'POST',
@@ -128,16 +94,14 @@ export async function connectRoutes(app: FastifyInstance) {
return reply.redirect(`${process.env.PUBLIC_APP_URL}/settings?error=connect_failed`);
}
- // Encrypt and store the token under the dedicated follow-scope key so
- // that a subsequent login (which writes to 'github') cannot overwrite
- // this follow-capable credential.
- const encryptedToken = encrypt(tokenData.access_token);
+ // Encrypt and store the token
+ const encryptedToken = app.encryption.encrypt(tokenData.access_token);
await app.prisma.oAuthToken.upsert({
where: {
userId_platform: {
userId,
- platform: GITHUB_FOLLOW_PLATFORM,
+ platform: 'github',
},
},
update: {
@@ -146,7 +110,7 @@ export async function connectRoutes(app: FastifyInstance) {
},
create: {
userId,
- platform: GITHUB_FOLLOW_PLATFORM,
+ platform: 'github',
accessToken: encryptedToken,
scopes: tokenData.scope || 'user:follow',
},
@@ -160,9 +124,8 @@ export async function connectRoutes(app: FastifyInstance) {
return reply.redirect(`${process.env.PUBLIC_APP_URL}/settings?connected=github`);
- } catch (error) {
- const message = error instanceof Error ? error.message : String(error);
- app.log.error({ error, message }, 'GitHub connect error');
+ } catch (err) {
+ app.log.error('GitHub connect error:', err);
return reply.redirect(`${process.env.PUBLIC_APP_URL}/settings?error=server_error`);
}
});
@@ -171,21 +134,11 @@ export async function connectRoutes(app: FastifyInstance) {
// ─── Disconnect ───
app.delete('/:platform', {
- preHandler: [async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return }
- if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return }
- try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) }
- }],
+ preHandler: [app.authenticate],
}, async (request: FastifyRequest<{ Params: { platform: string } }>, reply: FastifyReply) => {
const userId = (request.user as any).id;
const { platform } = request.params;
- const SUPPORTED_PLATFORMS = ['github', 'google', 'twitter', 'linkedin'];
- if (!SUPPORTED_PLATFORMS.includes(platform)) {
- return reply.status(400).send({ error: `Unsupported platform: ${platform}` });
- }
-
try {
await app.prisma.oAuthToken.delete({
where: {
@@ -196,13 +149,13 @@ export async function connectRoutes(app: FastifyInstance) {
},
});
return { success: true };
- } catch (error) {
+ } catch (err) {
return reply.status(404).send({ error: 'Connection not found' });
}
});
}
-function parseOAuthState(state: string): ParsedOAuthState | null {
+function parseGoogleState(state: string): ParsedOAuthState | null {
try {
const decoded = JSON.parse(Buffer.from(state, 'base64').toString('utf-8'));
@@ -217,5 +170,5 @@ function parseOAuthState(state: string): ParsedOAuthState | null {
}
function generateState(): string {
- return randomBytes(32).toString('hex');
+ return Math.random().toString(36).substring(2, 15);
}
diff --git a/apps/backend/src/routes/event.ts b/apps/backend/src/routes/event.ts
deleted file mode 100644
index 4d4ee2d9..00000000
--- a/apps/backend/src/routes/event.ts
+++ /dev/null
@@ -1,285 +0,0 @@
-import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
-import { createEventSchema, joinEventSchema} from '../validations/event.validation';
-
-import {generateUniqueSlug} from '../utils/slug'
-
-
-type EventDetails = {
- id: string;
- name: string;
- slug: string;
- location: string;
- description: string | null;
- organizerUsername: string;
- organizerDisplayName: string;
- startDate: Date;
- endDate: Date;
- createdAt: Date;
- attendeesCount: number
-}
-
-type AttendeePublicProfile = {
- id: string;
- username: string;
- displayName: string;
- bio: string | null;
- pronouns: string | null;
- company: string | null;
- avatarUrl: string | null;
- accentColor: string;
-}
-
-
-type PaginatedAttendeesResponse = {
- attendees: AttendeePublicProfile[];
- pagination: {
- page: number;
- limit: number;
- total: number;
- };
-}
-
-type EventWithAttendees = {
- _count: {
- attendees: number;
- };
- attendees: {
- user: {
- id: string;
- username: string;
- displayName: string;
- bio: string | null;
- pronouns: string | null;
- company: string | null;
- avatarUrl: string | null;
- accentColor: string;
- };
- }[];
-}
-
-export async function eventRoutes(app:FastifyInstance) {
- app.post('/', { preHandler: [async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return }
- if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return }
- try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) }
- }] }, async (request: FastifyRequest<{
- Body: {
- name: string,
- description?: string,
- startDate: string,
- location: string,
- endDate: string,
- isPublic?: boolean
- }}>, reply: FastifyReply) => {
- const userId = (request.user as any).id;
- const parsed = createEventSchema.safeParse(request.body);
- if(!parsed.success){
- return reply.status(400).send({error: 'Bad request'})
- }
-
- const {name, description, startDate, endDate, isPublic ,location} = parsed.data
-
- let finalSlug = await generateUniqueSlug(name, async(slug) => {
- const existing = await app.prisma.event.findUnique({where: {slug : slug}})
-
- return !!existing
- })
-
- const startDateObj = new Date(startDate);
- const endDateObj = new Date(endDate);
-
- try {
- const newEvent = await app.prisma.event.create({
- data: {
- name,
- description,
- slug: finalSlug,
- location: location,
- startDate: startDateObj,
- endDate: endDateObj,
- isPublic: isPublic ?? true,
- organizerId: userId
- }
- })
-
- return reply.status(201).send(newEvent);
- } catch (error) {
- app.log.error('Failed to create event');
- return reply.status(500).send({error: 'Failed to create event'})
- }
-
- })
-
- //Returns event details and attendees count
- app.get('/:slug', async(request: FastifyRequest<{Params: {slug: string}}>, reply: FastifyReply) => {
- const paramsSlug = request.params.slug;
- const details = await app.prisma.event.findUnique({
- where: {
- slug: paramsSlug,
- },
- include: {
- _count: {
- select: {
- attendees: true
- }
- },
- organizer: {
- select: {
- username: true,
- displayName: true
- }
- }
- }
- })
- if(!details){
- return reply.status(404).send({error: 'Event not found'})
- }
-
- const response: EventDetails = {
- id: details.id,
- name: details.name,
- slug: details.slug,
- description: details.description,
- location: details.location,
- organizerUsername: details.organizer.username,
- organizerDisplayName: details.organizer.displayName,
- startDate: details.startDate,
- endDate: details.endDate,
- createdAt: details.createdAt,
- attendeesCount: details._count.attendees
- }
-
- return response;
- })
-
- app.post('/:slug/join', { preHandler: [async (request, reply) => { const server = request.server as any; if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } }] }, async(request: FastifyRequest<{Params: {slug: string}}>, reply: FastifyReply) => {
- const userId = (request.user as any).id;
- const paramsSlug = request.params.slug;
-
- const event = await app.prisma.event.findUnique({
- where: {
- slug: paramsSlug
- }
- })
-
- if(!event){
- return reply.status(404).send({error: 'Event not found'})
- }
-
- try {
- await app.prisma.eventAttendee.create({
- data: {
- eventId: event.id,
- userId: userId,
- joinedAt: new Date()
- }
- })
-
- return reply.status(201).send({message: 'User joined successfully'})
- } catch (error:any) {
- if(error.code === "P2002" ){
- return reply.status(409).send({error: 'Already joined'})
- }
- app.log.error((error as Error).message);
- return reply.status(500).send({error: 'Failed to join'})
- }
-
- })
-
- app.delete('/:slug/leave', { preHandler: [async (request, reply) => { const server = request.server as any; if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } }] }, async(request: FastifyRequest<{Params: {slug: string}}>, reply: FastifyReply) => {
- const userId = (request.user as any).id;
- const paramsSlug = request.params.slug;
-
- const event = await app.prisma.event.findUnique({
- where: {
- slug: paramsSlug
- }
- })
-
- if(!event){
- return reply.status(404).send({error: 'Event not found'})
- }
-
- try {
- await app.prisma.eventAttendee.delete({
- where: {
- userId_eventId: {
- userId: userId,
- eventId: event.id
- }
- }
- })
- return reply.status(204).send({message: 'User left'})
- } catch (error:any) {
- if(error.code === 'P2025'){
- return reply.status(404).send({error: 'User not found'})
- }
- app.log.error((error as Error).message)
- return reply.status(500).send({error: 'Failed to leave'})
- }
- })
-
- app.get('/:slug/attendees', async(request: FastifyRequest<{Params: {slug: string}, Querystring: {page?:string; limit?: string}}>, reply: FastifyReply) => {
- const paramsSlug = request.params.slug;
- const page = Math.max(1, Number(request.query.page) || 1);
- const limit = Math.min(50, Number(request.query.limit) || 10);
- const skip = (page - 1) * limit
- const event = await app.prisma.event.findUnique({
- where: {
- slug: paramsSlug
- },
- include: {
- _count: {
- select: { attendees: true }
- },
- attendees : {
- include: {
- user: {
- select: {
- id: true,
- username: true,
- displayName:true,
- bio: true,
- pronouns: true,
- company: true,
- avatarUrl: true,
- accentColor: true
- }
- }
- },
- skip,
- take: limit,
- orderBy: {joinedAt: 'desc'}
- }
- },
- })as EventWithAttendees | null;
-
- if(!event){
- return reply.status(404).send({error: 'Event not found'})
- }
-
-
- const attendees = event.attendees.map((attendee: EventWithAttendees['attendees'][number]) => ({
- id: attendee.user.id,
- username: attendee.user.username,
- displayName: attendee.user.displayName,
- bio: attendee.user.bio,
- pronouns: attendee.user.pronouns,
- company: attendee.user.company,
- avatarUrl: attendee.user.avatarUrl,
- accentColor: attendee.user.accentColor,
- }));
-
- const response: PaginatedAttendeesResponse = {
- attendees,
- pagination: {
- page,
- limit,
- total : event._count.attendees,
- }
- }
-
- return response;
- })
-}
\ No newline at end of file
diff --git a/apps/backend/src/routes/follow.ts b/apps/backend/src/routes/follow.ts
index a152fc55..aabc85b6 100644
--- a/apps/backend/src/routes/follow.ts
+++ b/apps/backend/src/routes/follow.ts
@@ -1,16 +1,8 @@
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { decrypt } from '../utils/encryption.js';
-import { getErrorMessage } from '../utils/error.util.js';
-import { getPlatform, getProfileUrl, getWebViewUrl } from '@devcard/shared';
-import { followLogSchema } from '../validations/follow.validation.js';
export async function followRoutes(app: FastifyInstance) {
- app.addHook('preHandler', async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return }
- if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return }
- try { const payload = await request.jwtVerify(); if (payload) (request as any).user = payload; } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) }
- });
+ app.addHook('preHandler', app.authenticate);
// ─── Follow via API (Layer 1) ───
// Currently supports: GitHub
@@ -22,29 +14,13 @@ export async function followRoutes(app: FastifyInstance) {
const userId = (request.user as any).id;
const { platform, targetUsername } = request.params;
- // GitHub follow tokens are stored under 'github_follow' to prevent the
- // authentication flow (which writes to 'github') from silently overwriting
- // the follow-capable credential. All other platforms use their plain name.
- const tokenPlatform = platform === 'github' ? 'github_follow' : platform;
-
- // Get stored OAuth token for this platform (do this up-front so tests
- // that inspect DB calls see the lookup regardless of follow strategy).
+ // Get stored OAuth token for this platform
const oauthToken = await app.prisma.oAuthToken.findUnique({
where: {
- userId_platform: { userId, platform: tokenPlatform },
+ userId_platform: { userId, platform },
},
});
- // Use WebView follow strategy if configured for the platform (e.g. LinkedIn, Twitter/X)
- const platformDef = getPlatform(platform);
- if (platformDef?.followStrategy === 'webview') {
- const url = getWebViewUrl(platform, targetUsername) || getProfileUrl(platform, targetUsername);
- return reply.send({
- strategy: 'webview',
- url,
- });
- }
-
if (!oauthToken) {
return reply.status(400).send({
error: `Not connected to ${platform}. Please connect your ${platform} account first.`,
@@ -57,12 +33,9 @@ export async function followRoutes(app: FastifyInstance) {
try {
let result;
- let succeeded = false;
-
switch (platform) {
case 'github':
result = await followGitHub(accessToken, targetUsername, reply);
- succeeded = result.success === true;
break;
default:
return reply.status(400).send({
@@ -70,8 +43,8 @@ export async function followRoutes(app: FastifyInstance) {
});
}
- // Log only genuine successes — not based on reply.statusCode default
- if (succeeded) {
+ // If follow succeeded (or was handled by the function without throwing), log it
+ if (reply.statusCode === 200 || reply.statusCode === 204) {
app.prisma.followLog.create({
data: {
followerId: userId,
@@ -80,12 +53,12 @@ export async function followRoutes(app: FastifyInstance) {
status: 'success',
layer: 'api',
},
- }).catch((err: unknown) => app.log.error(`Failed to log follow: ${getErrorMessage(err)}`));
+ }).catch(err => app.log.error('Failed to log follow:', err));
}
- return result.response;
- } catch (err: unknown) {
- app.log.error(`Follow error for ${platform}: ${getErrorMessage(err)}`);
+ return result;
+ } catch (err: any) {
+ app.log.error(`Follow error for ${platform}:`, err);
app.prisma.followLog.create({
data: {
@@ -95,72 +68,11 @@ export async function followRoutes(app: FastifyInstance) {
status: 'error',
layer: 'api',
},
- }).catch((e: unknown) => app.log.error(`Failed to log follow error: ${getErrorMessage(e)}`));
+ }).catch(e => app.log.error('Failed to log follow error:', e));
- return reply.status(500).send({
- error: 'Follow action failed',
- message: getErrorMessage(err),
- });
+ return reply.status(500).send({ error: 'Follow action failed', message: err.message });
}
});
-
- // Log follow/connect event for Layer 2/3/4 strategies (WebView, deep-link, etc.)
- //
- // status and layer are analytics-impacting fields: they drive totalFollows counters
- // and the follower-state dashboard. Both are validated against a strict allowlist
- // before any database write — arbitrary client values are rejected with 400.
- app.post('/:platform/:targetUsername/log', async (
- request: FastifyRequest<{
- Params: { platform: string; targetUsername: string };
- Body: { status?: string; layer?: string };
- }>,
- reply: FastifyReply
- ) => {
- const userId = (request.user as any).id;
- const { platform, targetUsername } = request.params;
-
- const parsed = followLogSchema.safeParse(request.body);
- if (!parsed.success) {
- return reply.status(400).send({ error: 'Invalid follow log payload' });
- }
-
- const { status, layer } = parsed.data;
-
- try {
- const log = await app.prisma.followLog.create({
- data: {
- followerId: userId,
- targetUsername,
- platform,
- status,
- layer,
- },
- });
- return reply.send({ status: 'success', logId: log.id });
- } catch (error: any) {
- app.log.error('Failed to log follow:', error);
- return reply.status(500).send({ error: 'Failed to log follow event' });
- }
- });
-
- // ─── Clear follow log (reset Done state) ───
- app.delete('/:platform/:targetUsername/log', async (
- request: FastifyRequest<{ Params: { platform: string; targetUsername: string } }>,
- reply: FastifyReply
- ) => {
- const userId = (request.user as any).id;
- const { platform, targetUsername } = request.params;
-
- await app.prisma.followLog.deleteMany({
- where: {
- followerId: userId,
- platform,
- targetUsername,
- },
- });
-
- return reply.send({ status: 'cleared' });
- });
}
// ─── GitHub Follow (Layer 1) ───
@@ -169,7 +81,7 @@ async function followGitHub(
accessToken: string,
targetUsername: string,
reply: FastifyReply
-): Promise<{ success: boolean; response: FastifyReply }> {
+) {
const response = await fetch(`https://api.github.com/user/following/${targetUsername}`, {
method: 'PUT',
headers: {
@@ -180,42 +92,30 @@ async function followGitHub(
});
if (response.status === 204) {
- return {
- success: true,
- response: reply.send({
- status: 'success',
- platform: 'github',
- targetUsername,
- message: `Now following ${targetUsername} on GitHub`,
- }),
- };
+ return reply.send({
+ status: 'success',
+ platform: 'github',
+ targetUsername,
+ message: `Now following ${targetUsername} on GitHub`,
+ });
}
if (response.status === 401 || response.status === 403) {
- return {
- success: false,
- response: reply.status(401).send({
- error: 'GitHub token expired or insufficient permissions',
- requiresAuth: true,
- }),
- };
+ return reply.status(401).send({
+ error: 'GitHub token expired or insufficient permissions',
+ requiresAuth: true,
+ });
}
if (response.status === 404) {
- return {
- success: false,
- response: reply.status(404).send({
- error: `GitHub user '${targetUsername}' not found`,
- }),
- };
+ return reply.status(404).send({
+ error: `GitHub user '${targetUsername}' not found`,
+ });
}
const errorBody = await response.text();
- return {
- success: false,
- response: reply.status(response.status).send({
- error: 'GitHub follow failed',
- details: errorBody,
- }),
- };
-}
\ No newline at end of file
+ return reply.status(response.status).send({
+ error: 'GitHub follow failed',
+ details: errorBody,
+ });
+}
diff --git a/apps/backend/src/routes/nfc.ts b/apps/backend/src/routes/nfc.ts
deleted file mode 100644
index 5cf13f0c..00000000
--- a/apps/backend/src/routes/nfc.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
-import { z } from 'zod';
-
-type NfcPayloadResponse = {
- type: 'URI';
- payload: string;
-};
-
-const nfcQuerySchema = z.object({
- card: z.string().uuid('Invalid card ID format').optional(),
-});
-
-export async function nfcRoutes(app: FastifyInstance) {
- app.addHook('preHandler', async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') {
- await server.authenticate(request, reply);
- return;
- }
- if (typeof (app as any).authenticate === 'function') {
- await (app as any).authenticate(request, reply);
- return;
- }
- try {
- await request.jwtVerify();
- } catch (e) {
- reply.status(401).send({ error: 'Unauthorized' });
- }
- });
-
- // GET /api/nfc/payload — returns NDEF URI payload for user's default DevCard URL
- // GET /api/nfc/payload?card= — returns payload for a specific card
- app.get(
- '/payload',
- async (
- request: FastifyRequest<{ Querystring: { card?: string } }>,
- reply: FastifyReply
- ) => {
- const userId = (request.user as any).id;
-
- // Validate query params with Zod
- const parseResult = nfcQuerySchema.safeParse(request.query);
- if (!parseResult.success) {
- return reply.status(400).send({
- error: 'Invalid query parameters',
- details: parseResult.error.flatten(),
- });
- }
-
- const { card: cardId } = parseResult.data;
-
- let username: string;
-
- // Fetch username
- try {
- const user = await app.prisma.user.findUnique({
- where: { id: userId },
- select: { username: true },
- });
-
- if (!user) {
- return reply.status(404).send({
- error: 'User not found',
- });
- }
-
- username = user.username;
- } catch (error) {
- request.log.error(
- { error },
- 'Failed to fetch user for NFC payload'
- );
- return reply.status(500).send({
- error: 'Failed to fetch user profile',
- });
- }
-
- // If a specific card is requested, verify ownership
- if (cardId) {
- try {
- const card = await app.prisma.card.findUnique({
- where: { id: cardId },
- select: { userId: true },
- });
-
- if (!card || card.userId !== userId) {
- return reply.status(404).send({
- error: 'Card not found',
- });
- }
- } catch (error) {
- request.log.error(
- { error },
- 'Failed to fetch card for NFC payload'
- );
- return reply.status(500).send({
- error: 'Failed to fetch card',
- });
- }
- }
-
-const safeUsername = encodeURIComponent(username);
-const payloadUrl = `${process.env.PUBLIC_APP_URL}/${safeUsername}${
- cardId ? `?card=${encodeURIComponent(cardId)}` : ''
-}`;
- const response: NfcPayloadResponse = {
- type: 'URI',
- payload: payloadUrl,
- };
-
- return reply.send(response);
- }
- );
-}
\ No newline at end of file
diff --git a/apps/backend/src/routes/profiles.ts b/apps/backend/src/routes/profiles.ts
index 81026c74..99aacb8e 100644
--- a/apps/backend/src/routes/profiles.ts
+++ b/apps/backend/src/routes/profiles.ts
@@ -1,52 +1,43 @@
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { getProfileUrl } from '@devcard/shared';
-import { updateProfileSchema, createLinkSchema, reorderLinksSchema } from '../utils/validators.js';
-import { getErrorMessage } from '../utils/error.util.js';
-import * as profileService from '../services/profileService'
-
-// ── Response types ────────────────────────────────────────────────────────────
-// Declared explicitly so the API contract is visible without tracing through
-// Prisma's generic return types. Follows the convention in public.ts.
-
-type ProfileUpdateResponse = {
- id: string;
- email: string;
- username: string;
- displayName: string;
- bio: string | null;
- pronouns: string | null;
- role: string | null;
- company: string | null;
- avatarUrl: string | null;
- accentColor: string;
-};
+import {
+ updateProfileSchema,
+ createLinkSchema,
+ reorderLinksSchema,
+} from '../utils/validators.js';
export async function profileRoutes(app: FastifyInstance) {
// All profile routes require auth
- app.addHook('preHandler', async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') {
- await server.authenticate(request, reply);
- return;
- }
- if (typeof (app as any).authenticate === 'function') {
- await (app as any).authenticate(request, reply);
- return;
- }
- try {
- await request.jwtVerify();
- } catch (e) {
- reply.status(401).send({ error: 'Unauthorized' });
- }
- });
+ app.addHook('preHandler', app.authenticate);
// ─── Get Own Profile ───
app.get('/me', async (request: FastifyRequest, reply: FastifyReply) => {
const userId = (request.user as any).id;
- const user = await profileService.getOwnProfile(app, userId)
- if (!user) return reply.status(404).send({ error: 'User not found' })
- return user
+
+ const user = await app.prisma.user.findUnique({
+ where: { id: userId },
+ include: {
+ platformLinks: {
+ orderBy: { displayOrder: 'asc' },
+ },
+ cards: {
+ where: { isDefault: true },
+ select: { id: true },
+ take: 1,
+ },
+ },
+ });
+
+ if (!user) {
+ return reply.status(404).send({ error: 'User not found' });
+ }
+
+ const { provider, providerId, ...profileData } = user;
+ return {
+ ...profileData,
+ defaultCardId: user.cards[0]?.id || null,
+ };
});
// ─── Update Profile ───
@@ -59,11 +50,9 @@ export async function profileRoutes(app: FastifyInstance) {
return reply.status(400).send({ error: 'Validation failed', details: parsed.error.flatten() });
}
- // Fast-path uniqueness check. This read-before-write eliminates the common
- // case (clearly taken username) without touching the write path, but it
- // cannot prevent the race window between two concurrent requests that both
- // pass this check simultaneously. The unique constraint on the DB is the
- // authoritative guard — P2002 below is the definitive conflict signal.
+ // Check username uniqueness if changing
+ // Note: For production, consider adding a timestamp/version field to handle
+ // race conditions where two users might try to claim the same username simultaneously.
if (parsed.data.username) {
const existing = await app.prisma.user.findFirst({
where: {
@@ -76,14 +65,24 @@ export async function profileRoutes(app: FastifyInstance) {
}
}
- try {
- const response = await profileService.updateProfile(app, userId, parsed.data)
- return response
- } catch (err: any) {
- if (err?.code === 'P2002') return reply.status(409).send({ error: 'Username already taken' })
- app.log.error({ err }, 'DB error in PUT /profiles/me')
- return reply.status(500).send({ error: 'Internal server error' })
- }
+ const updated = await app.prisma.user.update({
+ where: { id: userId },
+ data: parsed.data,
+ select: {
+ id: true,
+ email: true,
+ username: true,
+ displayName: true,
+ bio: true,
+ pronouns: true,
+ role: true,
+ company: true,
+ avatarUrl: true,
+ accentColor: true,
+ },
+ });
+
+ return updated;
});
// ─── Add Platform Link ───
@@ -96,13 +95,26 @@ export async function profileRoutes(app: FastifyInstance) {
return reply.status(400).send({ error: 'Validation failed', details: parsed.error.flatten() });
}
- try {
- const link = await profileService.createPlatformLink(app, userId, parsed.data)
- return reply.status(201).send(link)
- } catch (err: any) {
- app.log.error({ err }, 'Failed to create platform link')
- return reply.status(500).send({ error: 'Internal server error' })
- }
+ // Auto-generate URL from platform registry if not provided
+ const url = parsed.data.url || getProfileUrl(parsed.data.platform, parsed.data.username);
+
+ // Get next display order
+ const maxOrder = await app.prisma.platformLink.aggregate({
+ where: { userId },
+ _max: { displayOrder: true },
+ });
+
+ const link = await app.prisma.platformLink.create({
+ data: {
+ userId,
+ platform: parsed.data.platform,
+ username: parsed.data.username,
+ url,
+ displayOrder: (maxOrder._max.displayOrder ?? -1) + 1,
+ },
+ });
+
+ return reply.status(201).send(link);
});
// ─── Update Platform Link ───
@@ -111,16 +123,31 @@ export async function profileRoutes(app: FastifyInstance) {
const userId = (request.user as any).id;
const { id } = request.params;
- const parsedReq = createLinkSchema.safeParse(request.body)
- if (!parsedReq.success) return reply.status(400).send({ error: 'Validation failed', details: parsedReq.error.flatten() })
- try {
- const updated = await profileService.updatePlatformLink(app, userId, id, parsedReq.data)
- if (!updated) return reply.status(404).send({ error: 'Link not found' })
- return updated
- } catch (err: any) {
- app.log.error({ err }, 'Failed to update platform link')
- return reply.status(500).send({ error: 'Internal server error' })
+ const existing = await app.prisma.platformLink.findFirst({
+ where: { id, userId },
+ });
+
+ if (!existing) {
+ return reply.status(404).send({ error: 'Link not found' });
+ }
+
+ const parsed = createLinkSchema.safeParse(request.body);
+ if (!parsed.success) {
+ return reply.status(400).send({ error: 'Validation failed', details: parsed.error.flatten() });
}
+
+ const url = parsed.data.url || getProfileUrl(parsed.data.platform, parsed.data.username);
+
+ const updated = await app.prisma.platformLink.update({
+ where: { id },
+ data: {
+ platform: parsed.data.platform,
+ username: parsed.data.username,
+ url,
+ },
+ });
+
+ return updated;
});
// ─── Delete Platform Link ───
@@ -129,28 +156,37 @@ export async function profileRoutes(app: FastifyInstance) {
const userId = (request.user as any).id;
const { id } = request.params;
- try {
- const deleted = await profileService.deletePlatformLink(app, userId, id)
- if (!deleted) return reply.status(404).send({ error: 'Link not found' })
- return reply.status(204).send()
- } catch (err: any) {
- app.log.error({ err }, 'Failed to delete platform link')
- return reply.status(500).send({ error: 'Internal server error' })
+ const existing = await app.prisma.platformLink.findFirst({
+ where: { id, userId },
+ });
+
+ if (!existing) {
+ return reply.status(404).send({ error: 'Link not found' });
}
+
+ await app.prisma.platformLink.delete({ where: { id } });
+ return reply.status(204).send();
});
// ─── Reorder Links ───
app.put('/me/links/reorder', async (request: FastifyRequest, reply: FastifyReply) => {
const userId = (request.user as any).id;
- const parsedReq = reorderLinksSchema.safeParse(request.body)
- if (!parsedReq.success) return reply.status(400).send({ error: 'Validation failed', details: parsedReq.error.flatten() })
- try {
- const resp = await profileService.reorderLinks(app, userId, parsedReq.data.links)
- return resp
- } catch (err: any) {
- app.log.error({ err }, 'Failed to reorder links')
- return reply.status(500).send({ error: 'Internal server error' })
+ const parsed = reorderLinksSchema.safeParse(request.body);
+
+ if (!parsed.success) {
+ return reply.status(400).send({ error: 'Validation failed', details: parsed.error.flatten() });
}
+
+ await app.prisma.$transaction(
+ parsed.data.links.map((link) =>
+ app.prisma.platformLink.updateMany({
+ where: { id: link.id, userId },
+ data: { displayOrder: link.displayOrder },
+ })
+ )
+ );
+
+ return { message: 'Links reordered' };
});
}
diff --git a/apps/backend/src/routes/public.ts b/apps/backend/src/routes/public.ts
index 27f544d8..f60e6133 100644
--- a/apps/backend/src/routes/public.ts
+++ b/apps/backend/src/routes/public.ts
@@ -1,134 +1,135 @@
-import type { FastifyContextConfig, FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
+import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { generateQRBuffer, generateQRSvg } from '../utils/qr.js';
-import type { PlatformLink } from '@devcard/shared';
-import { getErrorMessage } from '../utils/error.util.js';
-import * as publicService from '../services/publicService'
-
-
-// ── QR size bounds ────────────────────────────────────────────────────────────
-// Enforced before any DB query or image allocation. Values outside this range
-// are rejected with 400 so a single unauthenticated request cannot trigger an
-// unbounded memory allocation in the QR rasteriser.
-const MIN_QR_SIZE = 1;
-const MAX_QR_SIZE = 2048;
-
-// ── Cache constants ───────────────────────────────────────────────────────────
-// Public profile cache TTL matches the Cache-Control max-age (5 minutes).
-// The QR session JWT TTL is 10 minutes so an offline scan remains valid well
-// beyond the HTTP cache window.
-const PROFILE_CACHE_TTL = 300; // seconds (5 minutes)
-const CACHE_CONTROL_HEADER = 'public, max-age=300, stale-while-revalidate=60';
type PublicProfileLink = {
id: string;
platform: string;
- username: string;
- url: string;
- displayOrder: number;
- followed?: boolean;
+ username: string;
+ url: string;
+ displayOrder: number;
}
-type UsernamePublicProfileResponse = {
- username: string;
+type UsernamePublicProfileResponse = {
+ username: string;
displayName: string;
- bio: string | null;
- pronouns: string | null;
- role: string | null;
+ bio: string | null;
+ pronouns: string | null;
+ role: string | null;
company: string | null;
- avatarUrl: string | null;
+ avatarUrl: string | null;
accentColor: string;
links: PublicProfileLink[]
-}
+}
type PublicProfileCardLink = {
id: string;
platform: string;
- username: string;
- url: string;
- followed?: boolean;
+ username: string;
+ url: string;
}
type CardPublicProfileResponse = {
- id: string;
- title: string;
+ id: string;
+ title: string;
owner: {
- username: string;
- displayName: string;
+ username: string;
+ displayName: string;
bio: string | null;
avatarUrl: string | null;
- accentColor: string;
- };
+ accentColor: string;
+ };
links: PublicProfileCardLink[]
}
type UsernameCardPublicProfileResponse = {
- title: string;
+ title: string;
owner: {
- username: string;
+ username: string;
displayName: string;
- bio: string | null;
- pronouns: string | null;
- role: string | null;
+ bio: string | null;
+ pronouns: string | null;
+ role: string | null;
company: string | null;
- avatarUrl: string | null;
+ avatarUrl: string | null;
accentColor: string;
- };
+ };
links: PublicProfileCardLink[]
}
-// Represents a CardLink record with the joined PlatformLink relation
-interface CardLinkWithPlatform {
- id: string;
- displayOrder: number;
- platformLink: PlatformLink;
-}
-
-// ── Internal Redis cache shape ────────────────────────────────────────────────
-// Extends the public response with the owner's DB id so that background view
-// tracking can still fire on cache-HIT requests without an extra DB read.
-type CachedProfileEntry = UsernamePublicProfileResponse & { _userId: string };
export async function publicRoutes(app: FastifyInstance) {
- // ─── Public Profile ───────────────────────────────────────────────────────
// ─── Public Profile ───
- /**
- * GET /api/u/:username
+ /**
+ * GET /api/public/:username
* Returns the public profile information for a user.
- */
- app.get('/:username', {
- config: {
- rateLimit: {
- max: 100,
- timeWindow: '1 minute',
- },
- },
- }, async (request: FastifyRequest<{ Params: { username: string } }>, reply: FastifyReply) => {
+ */
+ app.get('/:username', async (request: FastifyRequest<{ Params: { username: string } }>, reply: FastifyReply) => {
const { username } = request.params;
- const cacheKey = `profile:${username}`;
- // Try to extract viewer from Authorization header (soft auth).
- let viewerId: string | null = null
+ const user = await app.prisma.user.findUnique({
+ where: { username },
+ include: {
+ platformLinks: {
+ orderBy: { displayOrder: 'asc' },
+ },
+ },
+ });
+
+ if (!user) {
+ return reply.status(404).send({ error: 'User not found' });
+ }
+
+ // Try to extract viewer from Authorization header (soft auth)
+ let viewerId = null;
try {
if (request.headers.authorization) {
- const decoded = (await request.jwtVerify()) as { id?: string }
- viewerId = decoded?.id ?? null
+ const decoded = await request.jwtVerify() as any;
+ if (decoded?.id !== user.id) {
+ viewerId = decoded.id; // Only log if they aren't the owner
+ }
} else {
- viewerId = null
+ viewerId = null; // Unauthenticated viewer
}
- } catch {
- // ignored
+ } catch (e) {
+ // Ignored if invalid token
}
- try {
- const result = await publicService.getPublicProfile(app, username, viewerId, request)
- if (!result) return reply.status(404).send({ error: 'User not found' })
- reply.header('X-Cache', result.cached ? 'HIT' : 'MISS').header('Cache-Control', CACHE_CONTROL_HEADER)
- return result.data
- } catch (err: any) {
- app.log.error({ err }, 'Failed to fetch public profile')
- return reply.status(500).send({ error: 'Internal server error' })
+ // Don't track if the owner is viewing their own profile
+ if (viewerId !== user.id) {
+ // Background view tracking
+ app.prisma.cardView.create({
+ data: {
+ ownerId: user.id,
+ cardId: null, // this is a profile view, not a card view
+ viewerId,
+ viewerIp: request.ip || null,
+ viewerAgent: request.headers['user-agent'] || null,
+ source: (request.query as any)?.source || 'link',
+ },
+ }).catch(err => app.log.error('Failed to log view:', err));
}
+
+ const response: UsernamePublicProfileResponse = {
+ username: user.username,
+ displayName: user.displayName,
+ bio: user.bio,
+ pronouns: user.pronouns,
+ role: user.role,
+ company: user.company,
+ avatarUrl: user.avatarUrl,
+ accentColor: user.accentColor,
+ links: user.platformLinks.map((link) => ({
+ id: link.id,
+ platform: link.platform,
+ username: link.username,
+ url: link.url,
+ displayOrder: link.displayOrder,
+ })),
+ }
+
+ return response;
+
});
/**
@@ -138,121 +139,133 @@ export async function publicRoutes(app: FastifyInstance) {
*/
// ─── Shared Card View (Direct) ───
- app.get('/card/:cardId', {
- config: {
- rateLimit: {
- max: 100,
- timeWindow: '1 minute'
- }
- } as FastifyContextConfig
- }, async (request: FastifyRequest<{ Params: { cardId: string } }>, reply: FastifyReply) => {
+ app.get('/card/:cardId', async (request: FastifyRequest<{ Params: { cardId: string } }>, reply: FastifyReply) => {
const { cardId } = request.params;
- try {
- const card = await publicService.getCardById(app, cardId)
- if (!card) return reply.status(404).send({ error: 'Card not found' })
- const response = { id: card.id, title: card.title, owner: { username: card.user.username, displayName: card.user.displayName, bio: card.user.bio, avatarUrl: card.user.avatarUrl, accentColor: card.user.accentColor }, links: card.cardLinks.map((cl: any) => ({ id: cl.platformLink.id, platform: cl.platformLink.platform, username: cl.platformLink.username, url: cl.platformLink.url })) }
- return response
- } catch (err: any) {
- app.log.error({ err }, 'Failed to fetch shared card')
- return reply.status(500).send({ error: 'Internal server error' })
+ const card = await app.prisma.card.findUnique({
+ where: { id: cardId },
+ include: {
+ user: true,
+ cardLinks: {
+ include: { platformLink: true },
+ orderBy: { displayOrder: 'asc' },
+ },
+ },
+ });
+
+ if (!card) {
+ return reply.status(404).send({ error: 'Card not found' });
}
+
+ const response: CardPublicProfileResponse = {
+ id: card.id,
+ title: card.title,
+ owner: {
+ username: card.user.username,
+ displayName: card.user.displayName,
+ bio: card.user.bio,
+ avatarUrl: card.user.avatarUrl,
+ accentColor: card.user.accentColor,
+ },
+ links: card.cardLinks.map((cl) => ({
+ id: cl.platformLink.id,
+ platform: cl.platformLink.platform,
+ username: cl.platformLink.username,
+ url: cl.platformLink.url,
+ })),
+ }
+
+ return response;
+
});
- // ─── Public Card View ─────────────────────────────────────────────────────
// ─── Public Card View ───
/**
- * GET /api/u/:username/card/:cardId
+ * GET /api/public/:username/card/:cardId
* Returns full owner profile + specific card data.
* Used when viewing a card through username + cardId (e.g. QR code scans).
- */
- app.get('/:username/card/:cardId', {
- config: {
- rateLimit: {
- max: 100,
- timeWindow: '1 minute',
- },
- },
- }, async (request: FastifyRequest<{ Params: { username: string; cardId: string } }>, reply: FastifyReply) => {
+ */
+ app.get('/:username/card/:cardId', async (request: FastifyRequest<{ Params: { username: string; cardId: string } }>, reply: FastifyReply) => {
const { username, cardId } = request.params;
- let viewerId: string | null = null
+ const user = await app.prisma.user.findUnique({
+ where: { username },
+ });
+
+ if (!user) {
+ return reply.status(404).send({ error: 'User not found' });
+ }
+
+ const card = await app.prisma.card.findFirst({
+ where: { id: cardId, userId: user.id },
+ include: {
+ cardLinks: {
+ include: { platformLink: true },
+ orderBy: { displayOrder: 'asc' },
+ },
+ },
+ });
+
+ if (!card) {
+ return reply.status(404).send({ error: 'Card not found' });
+ }
+
+ let viewerId = null;
try {
if (request.headers.authorization) {
- const decoded = (await request.jwtVerify()) as { id?: string }
- viewerId = decoded?.id ?? null
+ const decoded = await request.jwtVerify() as any;
+ if (decoded?.id !== user.id) {
+ viewerId = decoded.id;
+ }
}
- } catch {
- // ignored
- }
+ } catch (e) {}
- try {
- const result = await publicService.getUserCard(app, username, cardId, viewerId, request)
- if (result.notFound) return reply.status(404).send({ error: 'User or card not found' })
- return result.data
- } catch (err: any) {
- app.log.error({ err }, 'Failed to fetch user card')
- return reply.status(500).send({ error: 'Internal server error' })
+ if (viewerId !== user.id) {
+ app.prisma.cardView.create({
+ data: {
+ ownerId: user.id,
+ cardId: card.id,
+ viewerId,
+ viewerIp: request.ip || null,
+ viewerAgent: request.headers['user-agent'] || null,
+ source: (request.query as any)?.source || 'qr',
+ },
+ }).catch(err => app.log.error('Failed to log card view:', err));
}
- });
- // ─── QR Session ──────────────────────────────────────────────────────────
- // Returns a short-lived signed JWT encoding the public profile snapshot.
- // Intended for native apps to generate QR codes that remain scannable when
- // the device has no live network connectivity (offline QR mode, spec §5.9).
- app.get('/:username/qr-session', {
- config: {
- rateLimit: {
- max: 30,
- timeWindow: '1 minute'
- }
- } as FastifyContextConfig
- }, async (request: FastifyRequest<{ Params: { username: string } }>, reply: FastifyReply) => {
- const { username } = request.params;
- const cacheKey = `profile:${username}`;
- try {
- const result = await publicService.getPublicProfile(app, username, null, request)
- if (!result) return reply.status(404).send({ error: 'User not found' })
- const snapshot = result.data
- const expiresIn = 600
- const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString()
- const token = app.jwt.sign({ profile: snapshot, sub: username }, { expiresIn: '10m' })
- reply.header('Cache-Control', CACHE_CONTROL_HEADER)
- return { token, tokenType: 'JWT', expiresIn, expiresAt }
- } catch (err: any) {
- app.log.error({ err }, 'Failed to create qr-session')
- return reply.status(500).send({ error: 'Internal server error' })
+ const response: UsernameCardPublicProfileResponse = {
+ title: card.title,
+ owner: {
+ username: user.username,
+ displayName: user.displayName,
+ bio: user.bio,
+ pronouns: user.pronouns,
+ role: user.role,
+ company: user.company,
+ avatarUrl: user.avatarUrl,
+ accentColor: user.accentColor,
+ },
+ links: card.cardLinks.map((cl) => ({
+ id: cl.platformLink.id,
+ platform: cl.platformLink.platform,
+ username: cl.platformLink.username,
+ url: cl.platformLink.url,
+ displayOrder: cl.displayOrder,
+ })),
}
+ return response;
});
- // ─── QR Code Generation ───────────────────────────────────────────────────
+ // ─── QR Code Generation ───
- app.get('/:username/qr', {
- config: {
- rateLimit: {
- max: 50, // Lower limit for QR generation as it's more resource intensive
- timeWindow: '1 minute'
- }
- } as FastifyContextConfig
- }, async (request: FastifyRequest<{
+ app.get('/:username/qr', async (request: FastifyRequest<{
Params: { username: string };
Querystring: { format?: string; size?: string };
}>, reply: FastifyReply) => {
const { username } = request.params;
const format = (request.query as any).format || 'png';
-
- // Parse and validate size before touching the DB or allocating any buffers.
- // parseInt safely handles non-numeric strings (returns NaN) and ignores any
- // trailing fractional part, so '400.9' → 400 which is within bounds.
- const rawSize = (request.query as any).size;
- const size = rawSize !== undefined ? parseInt(rawSize, 10) : 400;
-
- if (!Number.isInteger(size) || size < MIN_QR_SIZE || size > MAX_QR_SIZE) {
- return reply.status(400).send({
- error: `QR size must be an integer between ${MIN_QR_SIZE} and ${MAX_QR_SIZE}`,
- });
- }
+ const size = parseInt((request.query as any).size || '400', 10);
// Verify user exists
const user = await app.prisma.user.findUnique({
@@ -265,16 +278,18 @@ export async function publicRoutes(app: FastifyInstance) {
const profileUrl = `${process.env.PUBLIC_APP_URL}/u/${username}`;
- try {
- if (format === 'svg') {
- const svg = await generateQRSvg(profileUrl, { width: size })
- return reply.header('Content-Type', 'image/svg+xml').header('Content-Disposition', `inline; filename="devcard-${username}.svg"`).send(svg)
- }
- const png = await generateQRBuffer(profileUrl, { width: size })
- return reply.header('Content-Type', 'image/png').header('Content-Disposition', `inline; filename="devcard-${username}.png"`).send(png)
- } catch (error) {
- app.log.error({ error, username, size, format }, 'QR generation failed')
- return reply.status(500).send({ error: 'QR code generation failed' })
+ if (format === 'svg') {
+ const svg = await generateQRSvg(profileUrl, { width: size });
+ return reply
+ .header('Content-Type', 'image/svg+xml')
+ .header('Content-Disposition', `inline; filename="devcard-${username}.svg"`)
+ .send(svg);
}
+
+ const png = await generateQRBuffer(profileUrl, { width: size });
+ return reply
+ .header('Content-Type', 'image/png')
+ .header('Content-Disposition', `inline; filename="devcard-${username}.png"`)
+ .send(png);
});
}
diff --git a/apps/backend/src/routes/team.ts b/apps/backend/src/routes/team.ts
deleted file mode 100644
index af177e52..00000000
--- a/apps/backend/src/routes/team.ts
+++ /dev/null
@@ -1,389 +0,0 @@
-import {Prisma, TeamRole } from '@prisma/client';
-import QRCode from 'qrcode'
-
-import {generateUniqueSlug} from '../utils/slug'
-import { createTeamScehma,inviteMembers,updateTeam } from '../validations/team.validation';
-
-import type {PlatformLink, PublicProfile} from '@devcard/shared'
-import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
-
-type TeamMember = PublicProfile & {
- teamRole: TeamRole
- joinedAt: Date;
-}
-
-type TeamProfile = {
- id: string;
- name: string;
- slug: string;
- description: string | null;
- ownerId: string;
- avatarUrl: string | null;
- createdAt: Date;
- updatedAt: Date | null;
- members: TeamMember[];
-}
-
-export async function teamRoutes(app:FastifyInstance){
- app.post('/', { preHandler: [async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return }
- if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return }
- try { const payload = await request.jwtVerify(); if (payload) (request as any).user = payload; } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) }
- }] }, async(request:FastifyRequest<{
- Body: {name: string, description? : string, avatarUrl?: string }
- }>, reply: FastifyReply) => {
- const userId = (request.user as any).id;
- const parsed = createTeamScehma.safeParse(request.body);
- if(!parsed.success){
- return reply.status(400).send({error: 'Bad request'})
- };
- const {name , description , avatarUrl} = parsed.data;
-
- const finalSlug = await generateUniqueSlug(name, async(slug) => {
- const existing = await app.prisma.team.findUnique({where: {slug }})
-
- return !!existing
- })
-
- try {
- const team = await app.prisma.$transaction(async (tx) => {
- const team = await tx.team.create({
- data: {
- name,
- slug: finalSlug,
- description,
- avatarUrl,
- ownerId: userId,
- }
- })
-
- await tx.teamMember.create({
- data: {
- teamId : team.id,
- userId,
- role: TeamRole.OWNER,
- joinedAt: new Date(),
- }
- })
- return team
- })
- return reply.status(201).send(team)
-
- }catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError) {
- switch (error.code) {
- case 'P2002':
- return reply.status(409).send({
- error: 'Team slug already exists'
- });
-
- case 'P2003':
- return reply.status(400).send({
- error: 'Invalid organizer'
- });
- }
- }
- app.log.error('Failed to create a team');
- return reply.status(500).send({
- error: 'Failed to create team'
- });
- }
- })
-
- app.get('/:slug', async(request: FastifyRequest<{Params: {slug: string}}>, reply: FastifyReply) => {
- const paramsSlug = request.params.slug;
-
- try {
- const details = await app.prisma.team.findUnique(
- {
- where: {slug: paramsSlug},
- include: {
- members: {
- include: {
- user: {
- include: {
- platformLinks: true
- }
- }
- }
- }
- }
- }
- )
-
- if(!details){
- return reply.status(404).send({error: 'Team not found'})
- }
-
- const members = details.members.map((tm): TeamMember => ({
- username: tm.user.username,
- displayName: tm.user.displayName,
- bio: tm.user.bio,
- pronouns: tm.user.pronouns,
- role: tm.user.role,
- company: tm.user.company,
- avatarUrl: tm.user.avatarUrl,
- accentColor: tm.user.accentColor,
- links: tm.user.platformLinks.map((pl: PlatformLink) => ({
- id: pl.id,
- platform: pl.platform,
- username: pl.username,
- url: pl.url,
- displayOrder: pl.displayOrder,
- })),
- teamRole: tm.role,
- joinedAt: tm.joinedAt,
-
- }))
-
- const response: TeamProfile = {
- id: details.id,
- name: details.name,
- slug: details.slug,
- description: details.description,
- avatarUrl: details.avatarUrl,
- ownerId: details.ownerId,
- createdAt: details?.createdAt,
- updatedAt: details.updatedAt,
- members
- }
-
- return response;
- } catch (error) {
- app.log.error(error);
- return reply.status(500).send('Database query failed')
- }
-
- })
-
- app.post('/:slug/members', { preHandler: [async (request, reply) => {
- const server = request.server as any;
- if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return }
- if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return }
- try { const payload = await request.jwtVerify(); if (payload) (request as any).user = payload; } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) }
- }] }, async(request: FastifyRequest<{Params: {slug:string}, Body:{username:string}}>, reply: FastifyReply) => {
- const paramsSlug = request.params.slug;
- const userId = (request.user as any).id;
- const parsed = inviteMembers.safeParse(request.body);
- if(!parsed.success){
- return reply.status(400).send({error: 'Bad request'})
- };
- const {username} = parsed.data;
- try {
- const teamDetails = await app.prisma.team.findUnique(
- {where: {slug: paramsSlug },
- include:{
- owner: true,
- members: {
- include: {
- user: true
- }
- }
- }
- }
- )
- if(!teamDetails){
- return reply.status(404).send('Team not found');
- }
- //Check request user is owner
- if(teamDetails?.ownerId !== userId){
- return reply.status(403).send('Forbidden')
- }
-
- const alreadyMember = teamDetails.members.find((u) => u.user.username === username)
-
- //Check invited username is not a member and owner;
- if(alreadyMember || teamDetails.owner.username === username){
- return reply.status(409).send('Conflict')
- }
-
- const invitedUserDetails = await app.prisma.user.findUnique((
- {where: {
- username
- }}))
-
- if(!invitedUserDetails){
- return reply.status(404).send('User not found')
- }
-
- await app.prisma.teamMember.create({
- data: {
- teamId: teamDetails.id,
- userId: invitedUserDetails.id,
- role: TeamRole.MEMBER,
- joinedAt: new Date()
- }
- })
-
- return reply.status(201).send('User invited')
-
- } catch (error) {
- app.log.error(error);
- return reply.status(500).send('Database query failed')
- }
- })
-
- app.delete('/:slug/members/:userId', { preHandler: [async (request, reply) => { const server = request.server as any; if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } }] }, async(request: FastifyRequest<{Params: {slug: string, userId: string}}>, reply: FastifyReply) => {
- const paramsSlug = request.params.slug
- const paramsUserId = request.params.userId
- const userID = (request.user as any).id;
- const teamDetails = await app.prisma.team.findUnique(
- {where: {slug: paramsSlug},
- include: {
- members: {
- include:{
- user: true
- }
- }
- }
- })
-
- if(!teamDetails){
- return reply.status(404).send({error: 'Team not found'})
- }
-
- const isMember = teamDetails.members.find((m) => paramsUserId === m.user.id)
-
- if(!isMember){
- return reply.status(404).send({
- error: 'Member not found',
- });
- }
-
- const isOwner = teamDetails.ownerId === userID;
- const isSelfRemove = paramsUserId === userID;
-
- if (!isOwner && !isSelfRemove) {
- return reply.status(403).send({
- error: 'Forbidden',
- });
- }
-
- //TODO: Assign owner role to next person
- if(paramsUserId === teamDetails.ownerId){
- return reply.status(403).send({
- error: 'Owner cannot leave team',
- });
- }
-
- if(isOwner || isSelfRemove){
- try {
- await app.prisma.teamMember.delete({
- where: {
- userId_teamId: {
- teamId: teamDetails.id,
- userId: paramsUserId
- }
- }
- })
- reply.status(200).send('Member removed')
- } catch (error) {
- app.log.error(error);
-
- return reply.status(500).send('DB query failed')
- }
- }
- })
-
- app.patch('/:slug',{ preHandler: [async (request, reply) => { const server = request.server as any; if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } }] }, async(request: FastifyRequest<{Params: {slug: string},Body: {description?:string, name?:string, avatarUrl?:string}}>, reply: FastifyReply) => {
- const userId = (request.user as any).id;
- const paramsSlug = request.params.slug;
- const parsed = updateTeam.safeParse(request.body);
- if(!parsed.success){
- return reply.status(400).send({error: 'Bad request'})
- };
-
- const {name, description,avatarUrl} = parsed.data;
-
-
- const teamDetails = await app.prisma.team.findUnique({where:{slug: paramsSlug}})
-
- if(!teamDetails){
- return reply.status(404).send('Team not found');
- }
-
- if(teamDetails.ownerId !== userId){
- return reply.status(403).send({
- error: 'Forbidden',
- });
- }
-
- try {
- const updatedTeam = await app.prisma.team.update({
- where: {
- slug: paramsSlug
- },
- data: {
- name,
- description,
- avatarUrl,
- }
- })
- return reply.status(200).send(updatedTeam)
- } catch (error) {
- app.log.error(error);
- return reply.status(500).send('DB query failed')
- }
-
- })
-
- app.delete('/:slug',{ preHandler: [async (request, reply) => { const server = request.server as any; if (typeof server?.authenticate === 'function') { await server.authenticate(request, reply); return } if (typeof (app as any).authenticate === 'function') { await (app as any).authenticate(request, reply); return } try { await request.jwtVerify() } catch (e) { reply.status(401).send({ error: 'Unauthorized' }) } }] }, async(request:FastifyRequest<{Params:{slug: string}}>, reply:FastifyReply) => {
- const userId = (request.user as any).id;
- const paramsSlug = request.params.slug;
-
-
- const teamDetails = await app.prisma.team.findUnique({
- where:{
- slug: paramsSlug
- }
- })
-
- if(!teamDetails){
- return reply.status(404).send('Team not found');
- }
-
- if(teamDetails.ownerId !== userId){
- return reply.status(403).send({
- error: 'Forbidden',
- });
- }
-
- try {
- await app.prisma.team.delete({
- where: {
- slug: paramsSlug,
- }
- })
-
- return reply.status(200).send('Team deleted')
- } catch (error) {
- app.log.error(error)
-
- return reply.status(500).send('DB query failed')
- }
- })
-
- app.get('/:slug/qr',async(request:FastifyRequest<{Params:{slug:string}}>, reply: FastifyReply) => {
- const paramsSlug = request.params.slug;
- try {
- const teamDetails = await app.prisma.team.findUnique({
- where: {
- slug: paramsSlug
- }
- })
-
- if(!teamDetails){
- return reply.status(404).send('Team not found');
- }
-
- const url = `https://devcard.dev/team/${teamDetails.slug}`
- const qrImage = await QRCode.toBuffer(url)
- return reply.type('image/png').send(qrImage)
- } catch (error) {
- app.log.error(error);
- return reply.status(500).send("QR generation failed")
- }
-
- })
-}
\ No newline at end of file
diff --git a/apps/backend/src/server.ts b/apps/backend/src/server.ts
index d494cf94..aea785d9 100644
--- a/apps/backend/src/server.ts
+++ b/apps/backend/src/server.ts
@@ -10,8 +10,8 @@ async function start() {
try {
await app.listen({ port: PORT, host: HOST });
app.log.info(`🚀 DevCard API running at http://${HOST}:${PORT}`);
- } catch (error) {
- app.log.error(error);
+ } catch (err) {
+ app.log.error(err);
process.exit(1);
}
}
diff --git a/apps/backend/src/services/authService.ts b/apps/backend/src/services/authService.ts
deleted file mode 100644
index 9af718c5..00000000
--- a/apps/backend/src/services/authService.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { randomBytes } from 'crypto';
-
-export function generateState(): string {
- return randomBytes(32).toString('hex');
-}
-
-export function buildOAuthState(clientState: string, mobileRedirectUri: string): string {
- if (!clientState) {
- return generateState();
- }
-
- if (clientState.startsWith('mobile_') && mobileRedirectUri) {
- const encodedRedirect = Buffer.from(mobileRedirectUri, 'utf8').toString('base64url');
- return `${clientState}.${encodedRedirect}.${generateState()}`;
- }
-
- return `${clientState}.${generateState()}`;
-}
-
-export function getMobileRedirectUri(state?: string): string | null {
- if (!state?.startsWith('mobile_')) {
- return null;
- }
-
- const encodedRedirect = state.split('.')[1];
- if (!encodedRedirect) {
- return null;
- }
-
- try {
- return Buffer.from(encodedRedirect, 'base64url').toString('utf8');
- } catch {
- return null;
- }
-}
diff --git a/apps/backend/src/services/cardService.ts b/apps/backend/src/services/cardService.ts
deleted file mode 100644
index a9721783..00000000
--- a/apps/backend/src/services/cardService.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import type { FastifyInstance } from 'fastify'
-import type { Prisma } from '@prisma/client'
-
-export async function listCards(app: FastifyInstance, userId: string) {
- const cards = await app.prisma.card.findMany({
- where: { userId },
- take: 50,
- include: { cardLinks: { include: { platformLink: true }, orderBy: { displayOrder: 'asc' } } },
- orderBy: { createdAt: 'asc' },
- })
-
- return cards.map((card: any) => ({ id: card.id, title: card.title, isDefault: card.isDefault, links: card.cardLinks.map((cl: any) => cl.platformLink) }))
-}
-
-export async function createCard(app: FastifyInstance, userId: string, body: { title: string; linkIds: string[] }) {
- if (body.linkIds.length > 0) {
- const ownedLinks = await app.prisma.platformLink.findMany({ where: { id: { in: body.linkIds }, userId }, select: { id: true } })
- if (ownedLinks.length !== body.linkIds.length) throw Object.assign(new Error('Link ownership mismatch'), { code: 'OWNERSHIP' })
- }
-
- const cardCount = await app.prisma.card.count({ where: { userId } })
-
- const card = await app.prisma.card.create({
- data: {
- userId,
- title: body.title,
- isDefault: cardCount === 0,
- cardLinks: { create: body.linkIds.map((linkId, index) => ({ platformLinkId: linkId, displayOrder: index })) },
- },
- include: { cardLinks: { include: { platformLink: true }, orderBy: { displayOrder: 'asc' } } },
- })
-
- return { id: card.id, title: card.title, isDefault: card.isDefault, links: card.cardLinks.map((cl: any) => cl.platformLink) }
-}
-
-export async function updateCard(app: FastifyInstance, userId: string, id: string, body: { title?: string; linkIds?: string[] }) {
- const existing = await app.prisma.card.findFirst({ where: { id, userId } })
- if (!existing) return null
-
- if (body.title) {
- await app.prisma.card.update({ where: { id }, data: { title: body.title } })
- }
-
- if (body.linkIds) {
- if (body.linkIds.length > 0) {
- const ownedLinks = await app.prisma.platformLink.findMany({ where: { id: { in: body.linkIds }, userId }, select: { id: true } })
- if (ownedLinks.length !== body.linkIds.length) throw Object.assign(new Error('Link ownership mismatch'), { code: 'OWNERSHIP' })
- }
-
- const linkIds = body.linkIds
- await app.prisma.$transaction(async (tx: Prisma.TransactionClient) => {
- await tx.cardLink.deleteMany({ where: { cardId: id } })
- if (linkIds.length > 0) {
- await tx.cardLink.createMany({ data: linkIds.map((linkId, index) => ({ cardId: id, platformLinkId: linkId, displayOrder: index })) })
- }
- })
- }
-
- const updated = await app.prisma.card.findUnique({ where: { id }, include: { cardLinks: { include: { platformLink: true }, orderBy: { displayOrder: 'asc' } } } })
- return { id: updated!.id, title: updated!.title, isDefault: updated!.isDefault, links: updated!.cardLinks.map((cl: any) => cl.platformLink) }
-}
-
-export async function deleteCard(app: FastifyInstance, userId: string, id: string) {
- return await app.prisma.$transaction(async (tx: Prisma.TransactionClient) => {
- const existing = await tx.card.findFirst({ where: { id, userId } })
- if (!existing) return Object.assign(new Error('NotFound'), { code: 'NOT_FOUND' })
-
- const userCardCount = await tx.card.count({ where: { userId } })
- if (userCardCount <= 1) return Object.assign(new Error('Cannot delete last card'), { code: 'LAST_CARD' })
-
- if (existing.isDefault) {
- const oldestRemainingCard = await tx.card.findFirst({ where: { userId, id: { not: id } }, orderBy: { createdAt: 'asc' } })
- if (oldestRemainingCard) {
- await tx.card.update({ where: { id: oldestRemainingCard.id }, data: { isDefault: true } })
- }
- }
-
- await tx.card.delete({ where: { id } })
- return null
- })
-}
-
-export async function setDefaultCard(app: FastifyInstance, userId: string, id: string) {
- const existing = await app.prisma.card.findFirst({ where: { id, userId } })
- if (!existing) return null
-
- await app.prisma.$transaction(async (tx: Prisma.TransactionClient) => {
- await tx.card.updateMany({ where: { userId }, data: { isDefault: false } })
- await tx.card.update({ where: { id }, data: { isDefault: true } })
- })
-
- return { message: 'Default card updated' }
-}
diff --git a/apps/backend/src/services/profileService.ts b/apps/backend/src/services/profileService.ts
deleted file mode 100644
index dc97b2a4..00000000
--- a/apps/backend/src/services/profileService.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import type { FastifyInstance } from 'fastify'
-import { getProfileUrl } from '@devcard/shared'
-import type { PlatformLink } from '@devcard/shared'
-import { getErrorMessage } from '../utils/error.util.js'
-
-export async function getOwnProfile(app: FastifyInstance, userId: string) {
- const user = await app.prisma.user.findUnique({
- where: { id: userId },
- include: {
- platformLinks: { orderBy: { displayOrder: 'asc' } },
- cards: { where: { isDefault: true }, select: { id: true }, take: 1 },
- },
- })
-
- if (!user) return null
-
- const { provider, providerId, ...profileData } = user as any
- return { ...profileData, defaultCardId: user.cards[0]?.id || null }
-}
-
-export async function updateProfile(app: FastifyInstance, userId: string, data: any) {
- // Fast-path uniqueness check
- if (data.username) {
- const existing = await app.prisma.user.findFirst({
- where: { username: data.username, NOT: { id: userId } },
- })
- if (existing) throw Object.assign(new Error('Username taken'), { code: 'P2002' })
- }
-
- const currentUser = await app.prisma.user.findUnique({ where: { id: userId }, select: { username: true } })
-
- try {
- const response = await app.prisma.user.update({ where: { id: userId }, data, select: {
- id: true, email: true, username: true, displayName: true, bio: true, pronouns: true, role: true, company: true, avatarUrl: true, accentColor: true
- } })
-
- if (app.redis && currentUser) {
- app.redis.del(`profile:${currentUser.username}`).catch((err: unknown) =>
- app.log.warn(`Failed to invalidate profile cache: ${getErrorMessage(err)}`)
- )
- }
-
- return response
- } catch (err: any) {
- if (err?.code === 'P2002') throw err
- app.log.error({ err }, 'DB error in updateProfile')
- throw err
- }
-}
-
-export async function createPlatformLink(app: FastifyInstance, userId: string, linkData: any) {
- const url = linkData.url || getProfileUrl(linkData.platform, linkData.username)
- const maxOrder = await app.prisma.platformLink.aggregate({ where: { userId }, _max: { displayOrder: true } })
- return app.prisma.platformLink.create({ data: { userId, platform: linkData.platform, username: linkData.username, url, displayOrder: (maxOrder._max.displayOrder ?? -1) + 1 } })
-}
-
-export async function updatePlatformLink(app: FastifyInstance, userId: string, id: string, linkData: any) {
- const existing = await app.prisma.platformLink.findFirst({ where: { id, userId } })
- if (!existing) return null
- const url = linkData.url || getProfileUrl(linkData.platform, linkData.username)
- return app.prisma.platformLink.update({ where: { id }, data: { platform: linkData.platform, username: linkData.username, url } })
-}
-
-export async function deletePlatformLink(app: FastifyInstance, userId: string, id: string) {
- const existing = await app.prisma.platformLink.findFirst({ where: { id, userId } })
- if (!existing) return false
- await app.prisma.platformLink.delete({ where: { id } })
- return true
-}
-
-export async function reorderLinks(app: FastifyInstance, userId: string, links: Array<{ id: string; displayOrder: number }>) {
- await app.prisma.$transaction(links.map((link) => app.prisma.platformLink.updateMany({ where: { id: link.id, userId }, data: { displayOrder: link.displayOrder } })))
- return { message: 'Links reordered' }
-}
diff --git a/apps/backend/src/services/publicService.ts b/apps/backend/src/services/publicService.ts
deleted file mode 100644
index 758ab78f..00000000
--- a/apps/backend/src/services/publicService.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import type { FastifyInstance } from 'fastify'
-import { getErrorMessage } from '../utils/error.util.js'
-
-const PROFILE_CACHE_TTL = 300
-const CACHE_CONTROL_HEADER = 'public, max-age=300, stale-while-revalidate=60'
-
-export async function getPublicProfile(app: FastifyInstance, username: string, viewerId: string | null, request: any) {
- const cacheKey = `profile:${username}`
-
- if (app.redis) {
- try {
- const cached = await app.redis.get(cacheKey)
- if (cached) {
- const { _userId, ...profileData } = JSON.parse(cached)
- if (viewerId && viewerId !== _userId) {
- app.prisma.cardView.create({ data: { ownerId: _userId, cardId: null, viewerId, viewerIp: request.ip || null, viewerAgent: request.headers['user-agent'] || null, source: request.query?.source || 'link' } }).catch((err: unknown) => app.log.error(`Failed to log view: ${getErrorMessage(err)}`))
- }
- return { cached: true, data: profileData, cacheKey }
- }
- } catch (err) {
- app.log.warn(`Redis cache read failed for ${cacheKey}: ${getErrorMessage(err)}`)
- }
- }
-
- const user = await app.prisma.user.findUnique({ where: { username }, include: { platformLinks: { orderBy: { displayOrder: 'asc' } } } })
- if (!user) return null
-
- if (viewerId && viewerId !== user.id) {
- app.prisma.cardView.create({ data: { ownerId: user.id, cardId: null, viewerId, viewerIp: request.ip || null, viewerAgent: request.headers['user-agent'] || null, source: request.query?.source || 'link' } }).catch((error: unknown) => app.log.error(`Failed to log view: ${getErrorMessage(error)}`))
- }
-
- let followedLinkIds: string[] = []
- if (viewerId && user.platformLinks.length > 0) {
- const successfulFollows = await app.prisma.followLog.findMany({ where: { followerId: viewerId, status: 'success', OR: user.platformLinks.map((link: any) => ({ platform: link.platform, targetUsername: link.username })) }, select: { platform: true, targetUsername: true } })
- followedLinkIds = user.platformLinks.filter((link: any) => successfulFollows.some((f: any) => f.platform === link.platform && f.targetUsername.toLowerCase() === link.username.toLowerCase())).map((l: any) => l.id)
- }
-
- const baseLinks = user.platformLinks.map((link: any) => ({ id: link.id, platform: link.platform, username: link.username, url: link.url, displayOrder: link.displayOrder, followed: false }))
-
- if (app.redis) {
- const entry = { _userId: user.id, username: user.username, displayName: user.displayName, bio: user.bio, pronouns: user.pronouns, role: user.role, company: user.company, avatarUrl: user.avatarUrl, accentColor: user.accentColor, links: baseLinks }
- app.redis.set(cacheKey, JSON.stringify(entry), 'EX', PROFILE_CACHE_TTL).catch((err: unknown) => app.log.warn(`Redis cache write failed for ${cacheKey}: ${getErrorMessage(err)}`))
- }
-
- const response = { username: user.username, displayName: user.displayName, bio: user.bio, pronouns: user.pronouns, role: user.role, company: user.company, avatarUrl: user.avatarUrl, accentColor: user.accentColor, links: baseLinks.map((link) => ({ ...link, followed: followedLinkIds.includes(link.id) })) }
-
- return { cached: false, data: response, cacheKey }
-}
-
-export async function getCardById(app: FastifyInstance, cardId: string) {
- const card = await app.prisma.card.findUnique({ where: { id: cardId }, include: { user: true, cardLinks: { include: { platformLink: true }, orderBy: { displayOrder: 'asc' } } } })
- return card
-}
-
-export async function getUserCard(app: FastifyInstance, username: string, cardId: string, viewerId: string | null, request: any) {
- const user = await app.prisma.user.findUnique({ where: { username } })
- if (!user) return { notFound: true }
- const card = await app.prisma.card.findFirst({ where: { id: cardId, userId: user.id }, include: { cardLinks: { include: { platformLink: true }, orderBy: { displayOrder: 'asc' } } } })
- if (!card) return { notFound: true }
-
- if (viewerId && viewerId !== user.id) {
- app.prisma.cardView.create({ data: { ownerId: user.id, cardId: card.id, viewerId, viewerIp: request.ip || null, viewerAgent: request.headers['user-agent'] || null, source: request.query?.source || 'qr' } }).catch((error: unknown) => app.log.error(`Failed to log view: ${getErrorMessage(error)}`))
- }
-
- const response = { title: card.title, owner: { username: user.username, displayName: user.displayName, bio: user.bio, pronouns: user.pronouns, role: user.role, company: user.company, avatarUrl: user.avatarUrl, accentColor: user.accentColor }, links: card.cardLinks.map((cl: any) => ({ id: cl.platformLink.id, platform: cl.platformLink.platform, username: cl.platformLink.username, url: cl.platformLink.url, displayOrder: cl.displayOrder })) }
- return { notFound: false, data: response }
-}
diff --git a/apps/backend/src/types/fastify.d.ts b/apps/backend/src/types/fastify.d.ts
deleted file mode 100644
index 8e7aee95..00000000
--- a/apps/backend/src/types/fastify.d.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import '@fastify/cookie';
-import { FastifyRequest } from 'fastify';
-
-declare module 'fastify' {
- interface FastifyRequest {
- cookies: Record;
- }
-}
diff --git a/apps/backend/src/utils/error.util.ts b/apps/backend/src/utils/error.util.ts
deleted file mode 100644
index fef1b98b..00000000
--- a/apps/backend/src/utils/error.util.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { FastifyReply, FastifyRequest } from 'fastify';
-import { Prisma } from '@prisma/client';
-
-export function getErrorMessage(err: unknown): string {
- return err instanceof Error ? err.message : String(err);
-}
-
-export function handleDbError(error: unknown, request: FastifyRequest, reply: FastifyReply) {
- request.log.error(error);
-
- if (error instanceof Prisma.PrismaClientKnownRequestError) {
- // P2002: Unique constraint failed
- if (error.code === 'P2002') {
- return reply.status(409).send({ error: 'Conflict: Record already exists or violates unique constraint' });
- }
- // P2025: Record to update not found
- if (error.code === 'P2025') {
- return reply.status(404).send({ error: 'Not Found: Record does not exist' });
- }
- // P2003: Foreign key constraint failed
- if (error.code === 'P2003') {
- return reply.status(400).send({ error: 'Constraint failed: Related record not found or invalid' });
- }
- return reply.status(400).send({ error: `Database error: ${error.message}` });
- }
-
- if (error instanceof Prisma.PrismaClientValidationError) {
- return reply.status(400).send({ error: 'Database validation failed' });
- }
-
- return reply.status(500).send({ error: 'Internal Server Error' });
-}
\ No newline at end of file
diff --git a/apps/backend/src/utils/slug.ts b/apps/backend/src/utils/slug.ts
deleted file mode 100644
index 24b772f3..00000000
--- a/apps/backend/src/utils/slug.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-export function createSlug(name:string){
- return name.toLowerCase().trim().replace(/\s+/g, '-').replace(/[^a-z0-9-]+/g, '').replace(/-+/g, '-').replace(/^-+|-+$/g, '')
-}
-
-export async function generateUniqueSlug(name: string,
- slugExists: (slug: string) => Promise
-){
- const cleanSlug = createSlug(name)
- let finalSlug = cleanSlug;
- while(true){
- const exists = await slugExists(finalSlug)
-
- if(!exists) break;
-
- const randomSuffix = Math.random().toString(36).substring(2,6);
- finalSlug = `${cleanSlug}-${randomSuffix}`
- }
- return finalSlug;
-}
diff --git a/apps/backend/src/utils/validateEnv.ts b/apps/backend/src/utils/validateEnv.ts
deleted file mode 100644
index cd361fc8..00000000
--- a/apps/backend/src/utils/validateEnv.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * Startup environment validation.
- *
- * Validates all required secrets before the application registers any plugins.
- * Missing or insecure values cause an immediate, deterministic process exit so
- * the server never reaches a partially-initialised auth state.
- *
- * Call this at the very top of buildApp(), before any Fastify plugin registration.
- */
-
-/**
- * Secrets that are committed to the public repository and must not be used in
- * production. Any match triggers an immediate startup failure.
- */
-const KNOWN_INSECURE_DEFAULTS: ReadonlySet = new Set([
- 'dev-secret-change-me',
-]);
-
-/**
- * Validates that all required secrets are present and safe.
- * Exits the process with code 1 on any violation, logging all failures at once
- * so operators can fix everything in a single deploy cycle.
- *
- * Secrets are never logged — only their presence and safety are reported.
- */
-export function validateEnv(): void {
- const errors: string[] = [];
- const isProduction = process.env.NODE_ENV === 'production';
-
- // ── JWT_SECRET ──────────────────────────────────────────────────────────────
- const jwtSecret = process.env.JWT_SECRET;
-
- if (!jwtSecret) {
- errors.push(
- 'JWT_SECRET is not set. Generate a secure value with:\n' +
- ' node -e "console.log(require(\'crypto\').randomBytes(64).toString(\'hex\'))"',
- );
- } else if (isProduction && KNOWN_INSECURE_DEFAULTS.has(jwtSecret)) {
- errors.push(
- 'JWT_SECRET is set to a known insecure default and cannot be used in production.\n' +
- ' Generate a secure value with:\n' +
- ' node -e "console.log(require(\'crypto\').randomBytes(64).toString(\'hex\'))"',
- );
- }
-
- // ── ENCRYPTION_KEY ──────────────────────────────────────────────────────────
- // getEncryptionKey() in utils/encryption.ts already throws at call-time when
- // this is missing, but catching it at startup is safer — the error surfaces
- // before any request is served rather than mid-flight on the first encrypt/
- // decrypt call.
- const encryptionKey = process.env.ENCRYPTION_KEY;
-
- if (!encryptionKey) {
- errors.push(
- 'ENCRYPTION_KEY is not set. Generate a secure value with:\n' +
- ' node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"',
- );
- }
-
- // ── Fail fast ───────────────────────────────────────────────────────────────
- if (errors.length === 0) {
- return;
- }
-
- console.error('');
- console.error('╔══════════════════════════════════════════════════════════╗');
- console.error('║ STARTUP FAILED — missing or insecure required secrets ║');
- console.error('╚══════════════════════════════════════════════════════════╝');
- console.error('');
- for (const msg of errors) {
- console.error(` ✖ ${msg}`);
- console.error('');
- }
-
- process.exit(1);
-}
diff --git a/apps/backend/src/utils/validators.ts b/apps/backend/src/utils/validators.ts
index bd41bef2..80d2caa1 100644
--- a/apps/backend/src/utils/validators.ts
+++ b/apps/backend/src/utils/validators.ts
@@ -1,5 +1,4 @@
import { z } from 'zod';
-import { getPlatform } from '@devcard/shared';
export const updateProfileSchema = z.object({
displayName: z.string().min(1).max(100).optional(),
@@ -23,17 +22,6 @@ export const createLinkSchema = z.object({
platform: z.string().min(1),
username: z.string().min(1).max(200),
url: z.string().url().optional(),
-}).superRefine((data, ctx) => {
- const platformDef = getPlatform(data.platform);
- if (platformDef?.validationRegex) {
- if (!platformDef.validationRegex.test(data.username)) {
- ctx.addIssue({
- code: z.ZodIssueCode.custom,
- message: `Invalid format for ${platformDef.name} handle`,
- path: ['username'],
- });
- }
- }
});
export const reorderLinksSchema = z.object({
diff --git a/apps/backend/src/validations/event.validation.ts b/apps/backend/src/validations/event.validation.ts
deleted file mode 100644
index 0fc4044f..00000000
--- a/apps/backend/src/validations/event.validation.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import {z} from 'zod'
-
-export const createEventSchema = z.object({
- name: z.string().min(3, 'Event name must be at least 3 characters long').max(100,'Event name cannot be longer than 100 characters'),
- description: z.string().min(1).optional(),
- location: z.string().min(2, 'Location should be atleast 2 characters long').max(100,'Location cannot be longer than 100 characters'),
- startDate: z.string().pipe(z.coerce.date()),
- endDate: z.string().pipe(z.coerce.date()),
- isPublic: z.boolean().default(true)
-})
-
-export const joinEventSchema = z.object({})
\ No newline at end of file
diff --git a/apps/backend/src/validations/follow.validation.ts b/apps/backend/src/validations/follow.validation.ts
deleted file mode 100644
index 319f1de1..00000000
--- a/apps/backend/src/validations/follow.validation.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { z } from 'zod';
-
-/**
- * Strict allowlist schema for analytics-impacting follow log fields.
- *
- * Both `status` and `layer` feed directly into analytics counters and the
- * follower-state dashboard. Only the values enumerated below may be
- * persisted — all other values are rejected before any database write.
- *
- * status:
- * 'success' — the follow action completed and was accepted by the platform
- * 'failed' — the action completed but was rejected (e.g. rate-limit, block)
- * 'pending' — the action was initiated; outcome not yet confirmed by client
- *
- * layer (hybrid follow engine interaction surface):
- * 'foreground' — user interacted directly with an in-app WebView session
- * 'background' — follow triggered through a passive deep-link / redirect strategy
- */
-export const followLogSchema = z.object({
- status: z.enum(['success', 'failed', 'pending'], {
- errorMap: () => ({
- message: "status must be one of: 'success', 'failed', 'pending'",
- }),
- }),
- layer: z.enum(['foreground', 'background'], {
- errorMap: () => ({
- message: "layer must be one of: 'foreground', 'background'",
- }),
- }),
-});
-
-export type FollowLogBody = z.infer;
diff --git a/apps/backend/src/validations/team.validation.ts b/apps/backend/src/validations/team.validation.ts
deleted file mode 100644
index 153333c0..00000000
--- a/apps/backend/src/validations/team.validation.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import {z} from 'zod';
-
-export const createTeamScehma = z.object({
- name: z.string().min(3, 'Event name must be at least 3 characters long').max(100,'Event name cannot be longer than 100 characters'),
- description: z.string().min(1).optional(),
- avatarUrl : z.string().url().optional(),
-})
-
-
-export const inviteMembers = z.object({
- username: z.string().min(1,'Username must be atleast 1 character')
-})
-
-export const updateTeam = z.object({
- name: z.string().min(1, 'Name must be at least 1 character').optional(),
- description: z.string().min(1,'Description must be at least 1 character').optional(),
- avatarUrl: z.string().url('Invalid avatar URL').optional(),
-}).refine(
- (data) =>
- data.name !== undefined ||
- data.description !== undefined ||
- data.avatarUrl !== undefined,
- {
- message: 'At least one field is required',
- }
-)
\ No newline at end of file
diff --git a/apps/mobile/App.tsx b/apps/mobile/App.tsx
index d577bd7a..811892f4 100644
--- a/apps/mobile/App.tsx
+++ b/apps/mobile/App.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { NavigationContainer, LinkingOptions } from '@react-navigation/native';
+import { NavigationContainer } from '@react-navigation/native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
@@ -7,44 +7,21 @@ import { AuthProvider, useAuth } from './src/context/AuthContext';
import { ThemeProvider } from './src/context/ThemeContext';
import AuthStack from './src/navigation/AuthStack';
import MainTabs from './src/navigation/MainTabs';
-import SplashScreen from './src/screens/SplashScreen';
-import { DEEP_LINK_SCHEME } from './src/config';
import { Linking, StyleSheet } from 'react-native';
-// ── Deep Link Configuration ───────────────────────────────────────────────────
-
-const linking: LinkingOptions<{}> = {
- prefixes: [`${DEEP_LINK_SCHEME}://`],
- config: {
- screens: {
- MainTabs: {
- screens: {
- Home: 'home',
- Scan: 'scan',
- },
- },
- DevCardView: 'u/:username',
- },
- },
-};
-
-// ── App Content ───────────────────────────────────────────────────────────────
-
function AppContent() {
const { isAuthenticated, isLoading, login } = useAuth();
React.useEffect(() => {
const handleDeepLink = (event: { url: string }) => {
- try {
- const url = new URL(event.url);
- const hashParams = new URLSearchParams(url.hash.replace(/^#/, ''));
- const token = url.searchParams.get('token') || hashParams.get('token');
- if (token) {
- login(token);
- }
- } catch (error) {
- console.error('Deep link parse error:', error);
+ console.log('--- DEEP LINK RECEIVED ---');
+ console.log('URL:', event.url);
+ const url = new URL(event.url);
+ const token = url.searchParams.get('token');
+ if (token) {
+ console.log('Token found, logging in...');
+ login(token);
}
};
@@ -60,18 +37,16 @@ function AppContent() {
}, [login]);
if (isLoading) {
- return ;
+ return null; // Splash screen could go here
}
return (
-
+
{isAuthenticated ? : }
);
}
-// ── Root ───────────────────────────────────────────────────────────────────────
-
export default function App() {
return (
diff --git a/apps/mobile/app.json b/apps/mobile/app.json
index 2917e473..20c74dff 100644
--- a/apps/mobile/app.json
+++ b/apps/mobile/app.json
@@ -1,5 +1,4 @@
{
"name": "DevCard",
- "displayName": "DevCard",
- "scheme": "devcard"
+ "displayName": "DevCard"
}
diff --git a/apps/mobile/babel.config.js b/apps/mobile/babel.config.js
index 8ba8eb65..02c7d135 100644
--- a/apps/mobile/babel.config.js
+++ b/apps/mobile/babel.config.js
@@ -1,4 +1,4 @@
module.exports = {
presets: ['module:@react-native/babel-preset'],
- plugins: ['react-native-worklets/plugin'],
+ plugins: ['react-native-reanimated/plugin'],
};
diff --git a/apps/mobile/index.js b/apps/mobile/index.js
index d5ce57df..fd5fb918 100644
--- a/apps/mobile/index.js
+++ b/apps/mobile/index.js
@@ -1,3 +1,7 @@
+/**
+ * @format
+ */
+
import 'react-native-gesture-handler';
import { AppRegistry } from 'react-native';
import App from './App';
diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js
index 0d21ee3a..ca00dd01 100644
--- a/apps/mobile/metro.config.js
+++ b/apps/mobile/metro.config.js
@@ -1,4 +1,4 @@
-const { getDefaultConfig } = require('@react-native/metro-config');
+const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const path = require('path');
// Monorepo root
@@ -6,47 +6,21 @@ const projectRoot = __dirname;
const monorepoRoot = path.resolve(projectRoot, '../..');
/**
- * Metro configuration for React Native monorepo
+ * Metro configuration for monorepo
+ * https://reactnative.dev/docs/metro
+ *
+ * @type {import('@react-native/metro-config').MetroConfig}
*/
-module.exports = (async () => {
- const config = await getDefaultConfig(projectRoot);
+const config = {
+ watchFolders: [monorepoRoot],
+ resolver: {
+ nodeModulesPaths: [
+ path.resolve(projectRoot, 'node_modules'),
+ path.resolve(monorepoRoot, 'node_modules'),
+ ],
+ // Ensure shared package is resolved
+ disableHierarchicalLookup: false,
+ },
+};
- config.watchFolders = [monorepoRoot];
- config.resolver = config.resolver || {};
- config.resolver.nodeModulesPaths = [
- path.resolve(projectRoot, 'node_modules'),
- path.resolve(monorepoRoot, 'node_modules'),
- ];
- config.resolver.disableHierarchicalLookup = false;
-
- const pinnedModules = {
- react: path.resolve(projectRoot, 'node_modules/react'),
- 'react-native': path.resolve(projectRoot, 'node_modules/react-native'),
- 'react-native-reanimated': path.resolve(
- projectRoot,
- 'node_modules/react-native-reanimated'
- ),
- 'react-native-worklets': path.resolve(
- projectRoot,
- 'node_modules/react-native-worklets'
- ),
- 'react-native-gesture-handler': path.resolve(
- projectRoot,
- 'node_modules/react-native-gesture-handler'
- ),
- };
-
- config.resolver.extraNodeModules = pinnedModules;
- config.resolver.resolveRequest = (context, moduleName, platform) => {
- for (const [name, modulePath] of Object.entries(pinnedModules)) {
- if (moduleName === name || moduleName.startsWith(`${name}/`)) {
- const target = path.join(modulePath, moduleName.slice(name.length));
- return context.resolveRequest(context, target, platform);
- }
- }
-
- return context.resolveRequest(context, moduleName, platform);
- };
-
- return config;
-})();
+module.exports = mergeConfig(getDefaultConfig(__dirname), config);
diff --git a/apps/mobile/package.json b/apps/mobile/package.json
index 4cae19e2..92fcba44 100644
--- a/apps/mobile/package.json
+++ b/apps/mobile/package.json
@@ -2,7 +2,6 @@
"name": "@devcard/mobile",
"version": "0.0.1",
"private": true,
- "main": "index.js",
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
@@ -19,20 +18,17 @@
"@react-navigation/native": "^7.0.0",
"@react-navigation/native-stack": "^7.0.0",
"react": "19.2.3",
- "react-dom": "^19.2.3",
+ "react-dom": "^19.2.4",
"react-native": "0.84.1",
- "react-native-camera-kit": "^14.0.0",
- "react-native-gesture-handler": "^2.28.0",
+ "react-native-gesture-handler": "^2.20.2",
"react-native-qrcode-svg": "^6.3.0",
- "react-native-reanimated": "^3.16.7",
+ "react-native-reanimated": "^3.15.0",
"react-native-safe-area-context": "^5.5.2",
"react-native-screens": "^4.0.0",
"react-native-svg": "^15.0.0",
"react-native-vector-icons": "^10.0.0",
- "react-native-view-shot": "^5.1.0",
"react-native-web": "^0.21.2",
- "react-native-webview": "^13.0.0",
- "react-native-worklets": "0.5.1"
+ "react-native-webview": "^13.0.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
diff --git a/apps/mobile/src/components/Avatar.tsx b/apps/mobile/src/components/Avatar.tsx
deleted file mode 100644
index 8a0ee0c8..00000000
--- a/apps/mobile/src/components/Avatar.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import { View, Text, Image, ViewStyle, ImageStyle, StyleSheet } from 'react-native';
-import { COLORS } from '../theme/tokens';
-
-type Props = {
- uri?: string | null;
- name?: string;
- size?: number;
- style?: ViewStyle | ImageStyle;
-};
-
-export const Avatar: React.FC = ({ uri, name = 'D', size = 56, style }) => {
- const initials = name.charAt(0).toUpperCase();
- const imageStyle = [{ width: size, height: size, borderRadius: size / 2 } as ImageStyle, style as ImageStyle];
- const placeholderStyle = [{ width: size, height: size, borderRadius: size / 2, backgroundColor: COLORS.primary }, style as ViewStyle];
-
- return uri ? (
-
- ) : (
-
- {initials}
-
- );
-};
-
-export default Avatar;
-
-const styles = StyleSheet.create({
- placeholder: {
- alignItems: 'center',
- justifyContent: 'center',
- },
- placeholderText: {
- color: COLORS.white,
- fontWeight: '800',
- },
-});
diff --git a/apps/mobile/src/components/CardPickerSheet.tsx b/apps/mobile/src/components/CardPickerSheet.tsx
index 7cbb12d3..44af9d93 100644
--- a/apps/mobile/src/components/CardPickerSheet.tsx
+++ b/apps/mobile/src/components/CardPickerSheet.tsx
@@ -8,7 +8,6 @@ import {
BottomSheetScrollView,
} from '@gorhom/bottom-sheet';
import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS } from '../theme/tokens';
-import { EmptyState } from './EmptyState';
type Props = {
cards: Card[];
@@ -51,10 +50,7 @@ const CardPickerSheet = React.forwardRef(
{cards.length === 0 ? (
-
+ No cards yet
) : (
cards.map(card => {
@@ -148,10 +144,12 @@ const styles = StyleSheet.create({
textAlign: 'center',
},
noCards: {
- backgroundColor: COLORS.bgCard,
- borderRadius: BORDER_RADIUS.md,
- borderWidth: 1,
- borderColor: COLORS.border,
+ alignItems: 'center',
+ paddingVertical: SPACING.lg,
+ },
+ noCardsText: {
+ fontSize: FONT_SIZE.sm,
+ color: COLORS.textMuted,
},
cardRow: {
flexDirection: 'row',
diff --git a/apps/mobile/src/components/ColorPicker.tsx b/apps/mobile/src/components/ColorPicker.tsx
deleted file mode 100644
index 83eecd8f..00000000
--- a/apps/mobile/src/components/ColorPicker.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import React from 'react';
-import { View, TouchableOpacity, StyleSheet } from 'react-native';
-import { COLORS, SPACING, BORDER_RADIUS } from '../theme/tokens';
-
-// ── Predefined Accent Color Palette ───────────────────────────────────────────
-// 8 curated colors that work well as card accent on the dark DevCard theme.
-
-export const ACCENT_COLORS = [
- '#6366F1', // Indigo (default)
- '#8B5CF6', // Violet
- '#EC4899', // Pink
- '#EF4444', // Red
- '#F59E0B', // Amber
- '#22C55E', // Green
- '#06B6D4', // Cyan
- '#3B82F6', // Blue
-] as const;
-
-export type AccentColor = (typeof ACCENT_COLORS)[number];
-
-interface ColorPickerProps {
- selected: string;
- onSelect: (color: string) => void;
-}
-
-export default function ColorPicker({ selected, onSelect }: ColorPickerProps) {
- return (
-
- {ACCENT_COLORS.map((color) => {
- const isActive = selected === color;
- return (
- onSelect(color)}
- activeOpacity={0.7}
- accessibilityLabel={`Select accent color ${color}`}
- accessibilityRole="radio"
- accessibilityState={{ selected: isActive }}
- />
- );
- })}
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- flexDirection: 'row',
- flexWrap: 'wrap',
- gap: SPACING.sm,
- justifyContent: 'center',
- },
- swatch: {
- width: 40,
- height: 40,
- borderRadius: BORDER_RADIUS.full,
- borderWidth: 2,
- borderColor: COLORS.transparent,
- },
- swatchActive: {
- borderColor: COLORS.white,
- transform: [{ scale: 1.15 }],
- shadowColor: '#000',
- shadowOffset: { width: 0, height: 2 },
- shadowOpacity: 0.4,
- shadowRadius: 4,
- elevation: 6,
- },
-});
diff --git a/apps/mobile/src/components/EmptyState.tsx b/apps/mobile/src/components/EmptyState.tsx
deleted file mode 100644
index 2ad886db..00000000
--- a/apps/mobile/src/components/EmptyState.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import { View, Text, StyleSheet } from 'react-native';
-import { COLORS, SPACING, FONT_SIZE } from '../theme/tokens';
-
-interface EmptyStateProps {
- emoji?: string;
- title: string;
- description?: string;
-}
-
-export const EmptyState: React.FC = ({ emoji, title, description }) => (
-
- {emoji ? {emoji} : null}
- {title}
- {description ? {description} : null}
-
-);
-
-const styles = StyleSheet.create({
- container: {
- alignItems: 'center',
- paddingVertical: SPACING.xxl,
- paddingHorizontal: SPACING.lg,
- },
- emoji: {
- fontSize: 48,
- marginBottom: SPACING.md,
- },
- title: {
- fontSize: FONT_SIZE.lg,
- fontWeight: '700',
- color: COLORS.textPrimary,
- textAlign: 'center',
- },
- description: {
- marginTop: SPACING.xs,
- fontSize: FONT_SIZE.sm,
- color: COLORS.textMuted,
- textAlign: 'center',
- lineHeight: 20,
- },
-});
diff --git a/apps/mobile/src/components/LoadingPlaceholder.tsx b/apps/mobile/src/components/LoadingPlaceholder.tsx
deleted file mode 100644
index 22f5b211..00000000
--- a/apps/mobile/src/components/LoadingPlaceholder.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-import { View, StyleSheet } from 'react-native';
-import { Skeleton } from './Skeleton';
-import { SPACING, BORDER_RADIUS, COLORS } from '../theme/tokens';
-
-interface LoadingPlaceholderProps {
- rows?: number;
-}
-
-export const LoadingPlaceholder: React.FC = ({ rows = 3 }) => (
-
- {Array.from({ length: rows }).map((_, index) => (
-
-
-
-
-
-
-
- ))}
-
-);
-
-const styles = StyleSheet.create({
- container: {
- padding: SPACING.lg,
- },
- item: {
- flexDirection: 'row',
- alignItems: 'center',
- gap: SPACING.md,
- marginBottom: SPACING.md,
- backgroundColor: COLORS.bgCard,
- borderRadius: BORDER_RADIUS.lg,
- padding: SPACING.md,
- },
- textColumn: {
- flex: 1,
- justifyContent: 'center',
- },
- secondLine: {
- marginTop: SPACING.xs,
- },
-});
diff --git a/apps/mobile/src/components/Skeleton.tsx b/apps/mobile/src/components/Skeleton.tsx
index 4c65e855..23f52d27 100644
--- a/apps/mobile/src/components/Skeleton.tsx
+++ b/apps/mobile/src/components/Skeleton.tsx
@@ -1,10 +1,10 @@
import React, { useEffect, useRef } from 'react';
-import { Animated, StyleSheet, ViewStyle, DimensionValue } from 'react-native';
+import { View, Animated, StyleSheet, ViewStyle } from 'react-native';
import { COLORS } from '../theme/tokens';
interface SkeletonProps {
- width?: DimensionValue;
- height?: DimensionValue;
+ width?: number | string;
+ height?: number | string;
borderRadius?: number;
style?: ViewStyle;
}
diff --git a/apps/mobile/src/config.ts b/apps/mobile/src/config.ts
index 3ef038e2..7d3e7dda 100644
--- a/apps/mobile/src/config.ts
+++ b/apps/mobile/src/config.ts
@@ -1,22 +1,12 @@
-import { Platform } from 'react-native';
+// DevCard API Configuration
-// ── DevCard API Configuration ─────────────────────────────────────────────────
-// Environment-aware URLs with no Expo dependency. On Android emulators the
-// loopback address is 10.0.2.2; on iOS simulators localhost works directly.
-
-const ANDROID_LOCALHOST = '10.0.2.2';
-const IOS_LOCALHOST = 'localhost';
-const DEV_HOST = Platform.OS === 'android' ? ANDROID_LOCALHOST : IOS_LOCALHOST;
-
-export const API_BASE_URL: string = __DEV__
- ? `http://${DEV_HOST}:3000`
+// For Android emulator, use localhost with 'adb reverse tcp:3000 tcp:3000'
+export const API_BASE_URL = __DEV__
+ ? 'http://localhost:3000'
: 'https://api.devcard.dev';
-export const APP_URL: string = __DEV__
+export const APP_URL = __DEV__
? 'http://localhost:5173'
: 'https://devcard.dev';
-// Deep link scheme — must match android/app/build.gradle and ios/Info.plist
-export const DEEP_LINK_SCHEME = 'devcard';
-
-export const OAUTH_REDIRECT_URI = `${DEEP_LINK_SCHEME}://oauth/callback`;
+export const OAUTH_REDIRECT_URI = 'devcard://oauth/callback';
diff --git a/apps/mobile/src/context/AuthContext.tsx b/apps/mobile/src/context/AuthContext.tsx
index 343e103c..77559e4c 100644
--- a/apps/mobile/src/context/AuthContext.tsx
+++ b/apps/mobile/src/context/AuthContext.tsx
@@ -1,13 +1,5 @@
-import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
-import AsyncStorage from '@react-native-async-storage/async-storage';
-import { get } from '../services/api';
-
-// ── Storage Keys ──────────────────────────────────────────────────────────────
-
-const TOKEN_KEY = 'devcard.auth.token';
-const FIRST_LAUNCH_KEY = 'devcard.firstLaunch';
-
-// ── Types ─────────────────────────────────────────────────────────────────────
+import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
+import { API_BASE_URL } from '../config';
interface User {
id: string;
@@ -28,99 +20,59 @@ interface AuthContextType {
token: string | null;
isAuthenticated: boolean;
isLoading: boolean;
- isFirstLaunch: boolean;
login: (token: string) => Promise;
- logout: () => Promise;
+ logout: () => void;
refreshUser: () => Promise;
}
-// ── Context ───────────────────────────────────────────────────────────────────
-
const AuthContext = createContext(undefined);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState(null);
const [token, setToken] = useState(null);
const [isLoading, setIsLoading] = useState(true);
- const [isFirstLaunch, setIsFirstLaunch] = useState(false);
-
- // ── Hydrate token from AsyncStorage on mount ──
useEffect(() => {
- const hydrate = async () => {
- try {
- const [storedToken, launchFlag] = await Promise.all([
- AsyncStorage.getItem(TOKEN_KEY),
- AsyncStorage.getItem(FIRST_LAUNCH_KEY),
- ]);
-
- if (launchFlag === null) {
- setIsFirstLaunch(true);
- await AsyncStorage.setItem(FIRST_LAUNCH_KEY, 'false');
- }
-
- if (storedToken) {
- setToken(storedToken);
- // Validate token by fetching profile
- const userData = await get('/api/profiles/me', storedToken).catch(() => null);
- if (userData) {
- setUser(userData);
- } else {
- // Token expired or invalid — clear it
- await AsyncStorage.removeItem(TOKEN_KEY);
- setToken(null);
- }
- }
- } catch (error) {
- console.error('Auth hydration failed:', error);
- } finally {
- setIsLoading(false);
- }
- };
-
- hydrate();
+ // TODO: Load token from secure storage on app start
+ setIsLoading(false);
}, []);
- // ── Login ──
-
- const login = useCallback(async (newToken: string) => {
+ const login = async (newToken: string) => {
setToken(newToken);
+ // TODO: Save token to secure storage
try {
- await AsyncStorage.setItem(TOKEN_KEY, newToken);
- const userData = await get('/api/profiles/me', newToken).catch(() => null);
- if (userData) {
+ const res = await fetch(`${API_BASE_URL}/api/profiles/me`, {
+ headers: { Authorization: `Bearer ${newToken}` },
+ });
+ if (res.ok) {
+ const userData = await res.json();
setUser(userData);
}
- } catch (error) {
- console.error('Failed to persist token or fetch user:', error);
+ } catch (err) {
+ console.error('Failed to fetch user:', err);
}
- }, []);
-
- // ── Logout ──
+ };
- const logout = useCallback(async () => {
+ const logout = () => {
setToken(null);
setUser(null);
- try {
- await AsyncStorage.removeItem(TOKEN_KEY);
- } catch (error) {
- console.error('Failed to clear stored token:', error);
- }
- }, []);
-
- // ── Refresh User ──
+ // TODO: Clear token from secure storage
+ };
- const refreshUser = useCallback(async () => {
+ const refreshUser = async () => {
if (!token) return;
try {
- const userData = await get('/api/profiles/me', token).catch(() => null);
- if (userData) {
+ const res = await fetch(`${API_BASE_URL}/api/profiles/me`, {
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ if (res.ok) {
+ const userData = await res.json();
setUser(userData);
}
- } catch (error) {
- console.error('Failed to refresh user:', error);
+ } catch (err) {
+ console.error('Failed to refresh user:', err);
}
- }, [token]);
+ };
return (
{
- data: T | null;
- loading: boolean;
- error: string | null;
- refetch: () => Promise;
-}
-
-interface UseApiQueryOptions {
- /** Skip the initial fetch (useful for conditional queries) */
- skip?: boolean;
-}
-
-export function useApiQuery(
- path: string,
- options: UseApiQueryOptions = {},
-): UseApiQueryResult {
- const { token, logout } = useAuth();
- const [data, setData] = useState(null);
- const [loading, setLoading] = useState(!options.skip);
- const [error, setError] = useState(null);
-
- const fetchData = useCallback(async () => {
- if (!token) {
- setLoading(false);
- return;
- }
- setLoading(true);
- setError(null);
- try {
- const result = await apiRequest(path, {
- method: 'GET',
- token,
- onUnauthorized: logout,
- });
- setData(result);
- } catch (err: unknown) {
- const message = err instanceof Error ? err.message : 'Request failed';
- setError(message);
- console.error(`useApiQuery(${path}):`, message);
- } finally {
- setLoading(false);
- }
- }, [path, token, logout]);
-
- useEffect(() => {
- if (!options.skip) {
- fetchData();
- }
- }, [fetchData, options.skip]);
-
- return { data, loading, error, refetch: fetchData };
-}
diff --git a/apps/mobile/src/hooks/useContacts.ts b/apps/mobile/src/hooks/useContacts.ts
deleted file mode 100644
index 99b59b2a..00000000
--- a/apps/mobile/src/hooks/useContacts.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-import { useState, useEffect, useCallback } from 'react';
-import AsyncStorage from '@react-native-async-storage/async-storage';
-import type { SavedContact } from '../types';
-
-// ── Storage Key ───────────────────────────────────────────────────────────────
-
-const CONTACTS_KEY = 'devcard.contacts';
-
-// ── Hook ──────────────────────────────────────────────────────────────────────
-
-interface UseContactsResult {
- contacts: SavedContact[];
- loading: boolean;
- saveContact: (contact: Omit) => Promise;
- removeContact: (username: string) => Promise;
- isContactSaved: (username: string) => boolean;
- refetch: () => Promise;
-}
-
-export function useContacts(): UseContactsResult {
- const [contacts, setContacts] = useState([]);
- const [loading, setLoading] = useState(true);
-
- const loadContacts = useCallback(async () => {
- try {
- const raw = await AsyncStorage.getItem(CONTACTS_KEY);
- if (raw) {
- const parsed: SavedContact[] = JSON.parse(raw);
- // Sort by most recently saved first
- parsed.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime());
- setContacts(parsed);
- } else {
- setContacts([]);
- }
- } catch (error) {
- console.error('Failed to load contacts:', error);
- setContacts([]);
- } finally {
- setLoading(false);
- }
- }, []);
-
- useEffect(() => {
- loadContacts();
- }, [loadContacts]);
-
- const persistContacts = async (updated: SavedContact[]) => {
- try {
- await AsyncStorage.setItem(CONTACTS_KEY, JSON.stringify(updated));
- setContacts(updated);
- } catch (error) {
- console.error('Failed to persist contacts:', error);
- }
- };
-
- const saveContact = useCallback(
- async (contact: Omit) => {
- const existing = contacts.filter((c) => c.username !== contact.username);
- const newContact: SavedContact = {
- ...contact,
- savedAt: new Date().toISOString(),
- };
- const updated = [newContact, ...existing];
- await persistContacts(updated);
- },
- [contacts],
- );
-
- const removeContact = useCallback(
- async (username: string) => {
- const updated = contacts.filter((c) => c.username !== username);
- await persistContacts(updated);
- },
- [contacts],
- );
-
- const isContactSaved = useCallback(
- (username: string) => contacts.some((c) => c.username === username),
- [contacts],
- );
-
- return {
- contacts,
- loading,
- saveContact,
- removeContact,
- isContactSaved,
- refetch: loadContacts,
- };
-}
diff --git a/apps/mobile/src/navigation/MainTabs.tsx b/apps/mobile/src/navigation/MainTabs.tsx
index 74cb88af..11e4e9a4 100644
--- a/apps/mobile/src/navigation/MainTabs.tsx
+++ b/apps/mobile/src/navigation/MainTabs.tsx
@@ -11,49 +11,26 @@ import SettingsScreen from '../screens/SettingsScreen';
import ScanScreen from '../screens/ScanScreen';
import DevCardViewScreen from '../screens/DevCardViewScreen';
import WebViewScreen from '../screens/WebViewScreen';
-import ContactsScreen from '../screens/ContactsScreen';
-import EventsScreen from '../screens/EventsScreen';
-import EventDetailScreen from '../screens/EventDetailScreen';
-import TeamsScreen from '../screens/TeamsScreen';
-import TeamDetailScreen from '../screens/TeamDetailScreen';
-import NfcScreen from '../screens/NfcScreen';
-import { ConnectPlatformsScreen } from '../screens/ConnectPlatformsScreen';
-import { ViewsScreen } from '../screens/ViewsScreen';
+import ConnectPlatformsScreen from '../screens/ConnectPlatformsScreen';
+import ViewsScreen from '../screens/ViewsScreen';
// ─── Types ───
export type MainTabsParamList = {
Home: undefined;
- Contacts: undefined;
+ Links: undefined;
Scan: undefined;
Cards: undefined;
Settings: undefined;
};
-// Standalone type for WebViewConnect route params — exported for reuse in
-// WebViewScreen, DevCardViewScreen, or any future screen that navigates here.
-export type WebViewConnectParams = {
- platform: string;
- url: string;
- platformName: string;
- username?: string;
- linkId?: string;
- cardOwnerUsername: string;
-};
-
export type RootStackParamList = {
MainTabs: undefined;
- DevCardView: { username: string; followSuccessLinkId?: string };
- WebViewConnect: WebViewConnectParams;
+ DevCardView: { username: string };
+ WebViewConnect: { platform: string; profileUrl: string; displayName: string };
ConnectPlatforms: undefined;
Views: undefined;
- Links: undefined;
- Events: undefined;
- EventDetail: { slug: string; name: string };
- Teams: undefined;
- TeamDetail: { slug: string; name: string };
- Nfc: undefined;
};
// ─── Tab Bar Icon ───
@@ -61,7 +38,7 @@ export type RootStackParamList = {
function TabIcon({ name, focused }: { name: string; focused: boolean }) {
const icons: Record = {
Home: '🏠',
- Contacts: '📇',
+ Links: '🔗',
Scan: '📷',
Cards: '💳',
Settings: '⚙️',
@@ -75,14 +52,6 @@ function TabIcon({ name, focused }: { name: string; focused: boolean }) {
);
}
-function ScanButton() {
- return (
-
- 📷
-
- );
-}
-
// ─── Tab Navigator ───
const Tab = createBottomTabNavigator();
@@ -101,13 +70,17 @@ function TabNavigator() {
),
})}>
-
+
,
+ tabBarIcon: ({ focused }) => (
+
+ 📷
+
+ ),
}}
/>
@@ -144,12 +117,6 @@ export default function MainTabs() {
component={ViewsScreen}
options={{ title: 'Card Views Analytics', headerShown: true, headerStyle: { backgroundColor: COLORS.bgPrimary }, headerTintColor: COLORS.textPrimary }}
/>
-
-
-
-
-
-
);
}
diff --git a/apps/mobile/src/screens/CardsScreen.tsx b/apps/mobile/src/screens/CardsScreen.tsx
index 6953ffd2..023ceb42 100644
--- a/apps/mobile/src/screens/CardsScreen.tsx
+++ b/apps/mobile/src/screens/CardsScreen.tsx
@@ -16,9 +16,7 @@ import { useFocusEffect } from '@react-navigation/native';
import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS, SHADOWS } from '../theme/tokens';
import { useAuth } from '../context/AuthContext';
import { PLATFORMS } from '@devcard/shared';
-import { get, post, del, put } from '../services/api';
-import { EmptyState } from '../components/EmptyState';
-import { Skeleton } from '../components/Skeleton';
+import { API_BASE_URL } from '../config';
interface PlatformLink {
id: string;
@@ -41,22 +39,26 @@ export default function CardsScreen() {
const [newTitle, setNewTitle] = useState('');
const [selectedLinkIds, setSelectedLinkIds] = useState([]);
const [refreshing, setRefreshing] = useState(false);
- const [loading, setLoading] = useState(true);
- const fetchData = useCallback(async (showLoading = true) => {
- if (showLoading) setLoading(true);
+ const fetchData = useCallback(async () => {
try {
- const [cardsData, profileData] = await Promise.all([
- get('/api/cards', token).catch(() => []),
- get('/api/profiles/me', token).catch(() => null),
+ const [cardsRes, profileRes] = await Promise.all([
+ fetch(`${API_BASE_URL}/api/cards`, {
+ headers: { Authorization: `Bearer ${token}` },
+ }),
+ fetch(`${API_BASE_URL}/api/profiles/me`, {
+ headers: { Authorization: `Bearer ${token}` },
+ }),
]);
- setCards(cardsData || []);
- setAllLinks(profileData?.platformLinks || []);
- } catch (error) {
- console.error('Failed to fetch:', error);
+ if (cardsRes.ok) setCards(await cardsRes.json());
+ if (profileRes.ok) {
+ const data = await profileRes.json();
+ setAllLinks(data.platformLinks || []);
+ }
+ } catch (err) {
+ console.error('Failed to fetch:', err);
} finally {
setRefreshing(false);
- if (showLoading) setLoading(false);
}
}, [token]);
@@ -68,7 +70,7 @@ export default function CardsScreen() {
const onRefresh = () => {
setRefreshing(true);
- fetchData(false);
+ fetchData();
};
const createCard = async () => {
@@ -77,12 +79,21 @@ export default function CardsScreen() {
return;
}
try {
- await post('/api/cards', { title: newTitle.trim(), linkIds: selectedLinkIds }, token);
- setShowCreate(false);
- setNewTitle('');
- setSelectedLinkIds([]);
- fetchData();
- } catch {
+ const res = await fetch(`${API_BASE_URL}/api/cards`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${token}`,
+ },
+ body: JSON.stringify({ title: newTitle.trim(), linkIds: selectedLinkIds }),
+ });
+ if (res.ok) {
+ setShowCreate(false);
+ setNewTitle('');
+ setSelectedLinkIds([]);
+ fetchData();
+ }
+ } catch (err) {
Alert.alert('Error', 'Failed to create card');
}
};
@@ -94,11 +105,10 @@ export default function CardsScreen() {
text: 'Delete',
style: 'destructive',
onPress: async () => {
- try {
- await del(`/api/cards/${id}`, undefined, token);
- } catch {
- // ignore
- }
+ await fetch(`${API_BASE_URL}/api/cards/${id}`, {
+ method: 'DELETE',
+ headers: { Authorization: `Bearer ${token}` },
+ });
fetchData();
},
},
@@ -106,11 +116,10 @@ export default function CardsScreen() {
};
const setDefault = async (id: string) => {
- try {
- await put(`/api/cards/${id}/default`, undefined, token);
- } catch {
- // ignore
- }
+ await fetch(`${API_BASE_URL}/api/cards/${id}/default`, {
+ method: 'PUT',
+ headers: { Authorization: `Bearer ${token}` },
+ });
fetchData();
};
@@ -122,29 +131,6 @@ export default function CardsScreen() {
);
};
- if (loading) {
- return (
-
-
-
-
-
-
-
- {[1, 2].map((item) => (
-
-
-
-
-
-
-
- ))}
-
-
- );
- }
-
return (
@@ -225,11 +211,11 @@ export default function CardsScreen() {
)}
ListEmptyComponent={
-
+
+ 💳
+ No cards yet
+ Create context cards for different situations
+
}
/>
@@ -297,19 +283,6 @@ const styles = StyleSheet.create({
emptyEmoji: { fontSize: 48, marginBottom: SPACING.md },
emptyText: { fontSize: FONT_SIZE.lg, fontWeight: '600', color: COLORS.textPrimary },
emptySubtext: { fontSize: FONT_SIZE.sm, color: COLORS.textMuted, marginTop: SPACING.xs },
- loadingList: { paddingHorizontal: SPACING.lg },
- loadingCard: {
- borderRadius: BORDER_RADIUS.lg,
- backgroundColor: COLORS.bgCard,
- padding: SPACING.md,
- marginBottom: SPACING.lg,
- },
- loadingActionRow: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- marginTop: SPACING.md,
- },
modalOverlay: { flex: 1, backgroundColor: COLORS.overlay, justifyContent: 'flex-end' },
modalContent: {
backgroundColor: COLORS.bgSecondary, borderTopLeftRadius: BORDER_RADIUS.xl,
diff --git a/apps/mobile/src/screens/ConnectPlatformsScreen.tsx b/apps/mobile/src/screens/ConnectPlatformsScreen.tsx
index 2e59ed11..8b359ca7 100644
--- a/apps/mobile/src/screens/ConnectPlatformsScreen.tsx
+++ b/apps/mobile/src/screens/ConnectPlatformsScreen.tsx
@@ -1,12 +1,10 @@
import React, { useState, useEffect, useCallback } from 'react';
-import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Alert, Linking } from 'react-native';
+import { View, Text, StyleSheet, ScrollView, ActivityIndicator, TouchableOpacity, Alert, Linking } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS } from '../theme/tokens';
import { useAuth } from '../context/AuthContext';
import { API_BASE_URL } from '../config';
-import { get, del } from '../services/api';
-import { LoadingPlaceholder } from '../components/LoadingPlaceholder';
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import type { RootStackParamList } from '../navigation/MainTabs';
@@ -29,10 +27,15 @@ export const ConnectPlatformsScreen: React.FC = ({ navigation: _navigatio
return;
}
try {
- const data = await get('/api/connect/status', token).catch(() => null);
- setConnectedPlatforms(data?.connectedPlatforms || []);
- } catch (error) {
- console.error('Failed to fetch connected platforms', error);
+ const response = await fetch(`${API_BASE_URL}/api/connect/status`, {
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ if (response.ok) {
+ const data = await response.json();
+ setConnectedPlatforms(data.connectedPlatforms || []);
+ }
+ } catch (err) {
+ console.error('Failed to fetch connected platforms', err);
} finally {
setLoading(false);
}
@@ -75,8 +78,15 @@ export const ConnectPlatformsScreen: React.FC = ({ navigation: _navigatio
onPress: async () => {
try {
if (!token) return;
- await del(`/api/connect/${platform}`, undefined, token);
- fetchConnections();
+ const response = await fetch(`${API_BASE_URL}/api/connect/${platform}`, {
+ method: 'DELETE',
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ if (response.ok) {
+ fetchConnections();
+ } else {
+ Alert.alert('Error', 'Failed to disconnect');
+ }
} catch {
Alert.alert('Error', 'Failed to disconnect');
}
@@ -126,9 +136,9 @@ export const ConnectPlatformsScreen: React.FC = ({ navigation: _navigatio
if (loading) {
return (
-
-
-
+
+
+
);
}
diff --git a/apps/mobile/src/screens/ContactsScreen.tsx b/apps/mobile/src/screens/ContactsScreen.tsx
deleted file mode 100644
index a657592a..00000000
--- a/apps/mobile/src/screens/ContactsScreen.tsx
+++ /dev/null
@@ -1,169 +0,0 @@
-import React, { useCallback } from 'react';
-import {
- View,
- Text,
- StyleSheet,
- FlatList,
- TouchableOpacity,
- Alert,
- StatusBar,
-} from 'react-native';
-import { SafeAreaView } from 'react-native-safe-area-context';
-import { useFocusEffect } from '@react-navigation/native';
-import Avatar from '../components/Avatar';
-import { EmptyState } from '../components/EmptyState';
-import { LoadingPlaceholder } from '../components/LoadingPlaceholder';
-import { useContacts } from '../hooks/useContacts';
-import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS } from '../theme/tokens';
-import type { SavedContact } from '../types';
-import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
-import type { RootStackParamList } from '../navigation/MainTabs';
-
-type Props = {
- navigation: NativeStackNavigationProp;
-};
-
-export default function ContactsScreen({ navigation }: Props) {
- const { contacts, loading, removeContact, refetch } = useContacts();
-
- useFocusEffect(
- useCallback(() => {
- refetch();
- }, [refetch]),
- );
-
- const handlePress = (contact: SavedContact) => {
- navigation.navigate('DevCardView', { username: contact.username });
- };
-
- const handleRemove = (contact: SavedContact) => {
- Alert.alert(
- 'Remove Contact',
- `Remove ${contact.displayName} from saved contacts?`,
- [
- { text: 'Cancel', style: 'cancel' },
- {
- text: 'Remove',
- style: 'destructive',
- onPress: () => removeContact(contact.username),
- },
- ],
- );
- };
-
- const formatDate = (dateString: string) => {
- const d = new Date(dateString);
- return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
- };
-
- if (loading) {
- return (
-
-
-
-
- );
- }
-
- return (
-
-
-
-
- Saved Contacts
- {contacts.length}
-
-
- item.username}
- contentContainerStyle={styles.list}
- renderItem={({ item }) => (
- handlePress(item)}
- onLongPress={() => handleRemove(item)}
- activeOpacity={0.7}>
-
-
-
- {item.displayName}
-
- {item.role || item.company ? (
-
- {[item.role, item.company].filter(Boolean).join(' · ')}
-
- ) : null}
- {item.metAt ? (
-
- Met at {item.metAt}
-
- ) : null}
-
-
-
- {formatDate(item.savedAt)}
-
-
- )}
- ListEmptyComponent={
-
- }
- />
-
- );
-}
-
-const styles = StyleSheet.create({
- container: { flex: 1, backgroundColor: COLORS.bgPrimary },
- header: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- padding: SPACING.lg,
- paddingBottom: SPACING.md,
- },
- title: { fontSize: FONT_SIZE.xl, fontWeight: '800', color: COLORS.textPrimary },
- count: {
- fontSize: FONT_SIZE.sm,
- fontWeight: '700',
- color: COLORS.textMuted,
- backgroundColor: COLORS.bgElevated,
- borderRadius: BORDER_RADIUS.full,
- paddingHorizontal: SPACING.sm,
- paddingVertical: 2,
- overflow: 'hidden',
- },
- list: { padding: SPACING.lg, gap: SPACING.sm, paddingTop: 0 },
- contactItem: {
- flexDirection: 'row',
- alignItems: 'center',
- backgroundColor: COLORS.bgCard,
- borderRadius: BORDER_RADIUS.md,
- padding: SPACING.md,
- borderWidth: 1,
- borderColor: COLORS.border,
- },
- avatar: {
- width: 48,
- height: 48,
- borderRadius: 24,
- marginRight: SPACING.md,
- },
- info: { flex: 1 },
- name: { fontSize: FONT_SIZE.md, fontWeight: '600', color: COLORS.textPrimary },
- detail: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, marginTop: 2 },
- metAt: { fontSize: FONT_SIZE.xs, color: COLORS.primary, marginTop: 2 },
- meta: { alignItems: 'flex-end', gap: 4 },
- accentDot: { width: 10, height: 10, borderRadius: 5 },
- date: { fontSize: FONT_SIZE.xs, color: COLORS.textMuted },
-});
diff --git a/apps/mobile/src/screens/DevCardViewScreen.tsx b/apps/mobile/src/screens/DevCardViewScreen.tsx
index 7d6de992..46cf9519 100644
--- a/apps/mobile/src/screens/DevCardViewScreen.tsx
+++ b/apps/mobile/src/screens/DevCardViewScreen.tsx
@@ -1,10 +1,11 @@
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
+ Image,
Linking,
Clipboard,
StatusBar,
@@ -14,12 +15,9 @@ import {
import { SafeAreaView } from 'react-native-safe-area-context';
import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS, SHADOWS } from '../theme/tokens';
import { Skeleton } from '../components/Skeleton';
-import { EmptyState } from '../components/EmptyState';
-import Avatar from '../components/Avatar';
import { PLATFORMS, getProfileUrl, getWebViewUrl } from '@devcard/shared';
-import { get, post, del } from '../services/api';
+import { API_BASE_URL } from '../config';
import { useAuth } from '../context/AuthContext';
-import { useContacts } from '../hooks/useContacts';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RouteProp } from '@react-navigation/native';
import type { RootStackParamList } from '../navigation/MainTabs';
@@ -52,106 +50,29 @@ interface ProfileData {
type FollowState = Record;
-// ─── Platform Emoji Icon Map ───
-const PLATFORM_EMOJI: Record = {
- github: '🐙',
- linkedin: 'in',
- twitter: '𝕏',
- gitlab: '🦊',
- devfolio: '🏗️',
- npm: '📦',
- devto: '👩💻',
- hashnode: '📝',
- medium: 'M',
- leetcode: '🏆',
- hackerrank: '⚔️',
- stackoverflow: '💬',
- discord: '🎮',
- telegram: '✈️',
- email: '✉️',
- portfolio: '🌐',
- custom: '🔗',
-};
-
-// ─── Brand-colored action buttons ───
-const PLATFORM_BTN_COLOR: Record = {
- github: '#238636',
- linkedin: '#0A66C2',
- twitter: '#1D9BF0',
- gitlab: '#FC6D26',
- devfolio: '#3770FF',
- npm: '#CB3837',
- devto: '#3B49DF',
- leetcode: '#FFA116',
- hackerrank: '#00B86B',
- stackoverflow: '#F58025',
- discord: '#5865F2',
- telegram: '#26A5E4',
- email: '#EA4335',
- portfolio: '#6366F1',
-};
-
export default function DevCardViewScreen({ navigation, route }: Props) {
const { username } = route.params;
const { token } = useAuth();
- const { isContactSaved, saveContact, removeContact } = useContacts();
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(true);
const [followStates, setFollowStates] = useState({});
- const isSaved = isContactSaved(username);
-
- const handleSaveContact = async () => {
- if (!profile) return;
- if (isSaved) {
- await removeContact(username);
- } else {
- await saveContact({
- username: profile.username,
- displayName: profile.displayName,
- avatarUrl: profile.avatarUrl,
- accentColor: profile.accentColor || COLORS.primary,
- bio: profile.bio,
- role: profile.role,
- company: profile.company,
- metAt: 'DevCard App',
- note: null,
- });
- Alert.alert('Saved!', `${profile.displayName} has been added to your contacts.`);
- }
- };
+ useEffect(() => {
+ fetchProfile();
+ }, [username]);
- const fetchProfile = useCallback(async () => {
+ const fetchProfile = async () => {
try {
- const data = await get(`/api/u/${username}`, token);
- if (data) {
- setProfile(data);
- const initialFollowStates: FollowState = {};
- if (data.links) {
- data.links.forEach((link: any) => {
- if (link.followed) initialFollowStates[link.id] = 'success';
- });
- }
- setFollowStates(initialFollowStates);
+ const res = await fetch(`${API_BASE_URL}/api/u/${username}`);
+ if (res.ok) {
+ setProfile(await res.json());
}
- } catch (error) {
- console.error('Failed to fetch profile:', error);
+ } catch (err) {
+ console.error('Failed to fetch profile:', err);
} finally {
setLoading(false);
}
- }, [token, username]);
-
- useEffect(() => {
- fetchProfile();
- }, [fetchProfile]);
-
- const successLinkId = route.params?.followSuccessLinkId;
- useEffect(() => {
- if (successLinkId) {
- setFollowStates(prev => ({ ...prev, [successLinkId]: 'success' }));
- navigation.setParams({ followSuccessLinkId: undefined } as any);
- }
- }, [navigation, successLinkId]);
+ };
// ─── Hybrid Follow Engine ───
@@ -163,26 +84,17 @@ export default function DevCardViewScreen({ navigation, route }: Props) {
switch (strategy) {
case 'api':
+ // Layer 1: Silent API follow
await handleApiFollow(link);
break;
case 'webview':
- setFollowStates(prev => ({ ...prev, [link.id]: 'loading' }));
- try {
- const data = await post(`/api/follow/${link.platform}/${link.username}`, undefined, token);
- setFollowStates(prev => ({ ...prev, [link.id]: 'idle' }));
- if (data?.strategy === 'webview') {
- handleWebViewConnect(link, data.url);
- } else {
- setFollowStates(prev => ({ ...prev, [link.id]: 'success' }));
- }
- } catch {
- setFollowStates(prev => ({ ...prev, [link.id]: 'idle' }));
- handleWebViewConnect(link);
- }
+ // Layer 2: WebView connect
+ handleWebViewConnect(link);
break;
case 'copy':
+ // Copy to clipboard (Discord)
Clipboard.setString(link.username);
Alert.alert('Copied!', `${link.username} copied to clipboard`);
setFollowStates(prev => ({ ...prev, [link.id]: 'success' }));
@@ -190,6 +102,7 @@ export default function DevCardViewScreen({ navigation, route }: Props) {
case 'link':
default:
+ // Layer 3: Open in browser/app
const url = link.url || getProfileUrl(link.platform, link.username);
if (url) {
Linking.openURL(url).catch(() =>
@@ -204,49 +117,40 @@ export default function DevCardViewScreen({ navigation, route }: Props) {
const handleApiFollow = async (link: PlatformLink) => {
setFollowStates(prev => ({ ...prev, [link.id]: 'loading' }));
try {
- await post(`/api/follow/${link.platform}/${link.username}`, undefined, token);
- setFollowStates(prev => ({ ...prev, [link.id]: 'success' }));
- } catch (err: any) {
- const msg = (err && err.message) || '';
- if (msg.includes('requiresAuth')) {
- setFollowStates(prev => ({ ...prev, [link.id]: 'idle' }));
- const webViewUrl = getWebViewUrl(link.platform, link.username);
- if (webViewUrl) {
+ const res = await fetch(
+ `${API_BASE_URL}/api/follow/${link.platform}/${link.username}`,
+ {
+ method: 'POST',
+ headers: { Authorization: `Bearer ${token}` },
+ }
+ );
+ if (res.ok) {
+ setFollowStates(prev => ({ ...prev, [link.id]: 'success' }));
+ } else {
+ const data = await res.json();
+ if (data.requiresAuth) {
+ // Fall back to WebView if token missing
handleWebViewConnect(link);
} else {
- const profileUrl = link.url || getProfileUrl(link.platform, link.username);
- if (profileUrl) Linking.openURL(profileUrl).catch(() => Alert.alert('Error', `Could not open ${link.platform} profile`));
+ setFollowStates(prev => ({ ...prev, [link.id]: 'error' }));
}
- } else {
- setFollowStates(prev => ({ ...prev, [link.id]: 'error' }));
}
- }
- };
-
- // Reset a "Done" tile — clears follow log from backend and resets local state
- const handleResetFollowState = async (link: PlatformLink) => {
- try {
- await del(`/api/follow/${link.platform}/${link.username}/log`, undefined, token);
} catch {
- // ignore
+ setFollowStates(prev => ({ ...prev, [link.id]: 'error' }));
}
- setFollowStates(prev => ({ ...prev, [link.id]: 'idle' }));
};
// Layer 2: WebView-based connect
- const handleWebViewConnect = (link: PlatformLink, resolvedUrl?: string) => {
+ const handleWebViewConnect = (link: PlatformLink) => {
const webViewUrl = getWebViewUrl(link.platform, link.username);
const profileUrl = link.url || getProfileUrl(link.platform, link.username);
- const url = resolvedUrl || webViewUrl || profileUrl;
+ const url = webViewUrl || profileUrl;
if (url) {
navigation.navigate('WebViewConnect', {
platform: link.platform,
- url,
- platformName: PLATFORMS[link.platform]?.name || link.platform,
- username: link.username,
- linkId: link.id,
- cardOwnerUsername: username,
+ profileUrl: url,
+ displayName: PLATFORMS[link.platform]?.name || link.platform,
});
}
};
@@ -269,27 +173,20 @@ export default function DevCardViewScreen({ navigation, route }: Props) {
}
};
- const getButtonColor = (link: PlatformLink, state: string): string => {
- if (state === 'success') return COLORS.success;
- if (state === 'loading') return COLORS.primaryDark;
- if (state === 'error') return '#DC2626';
- return PLATFORM_BTN_COLOR[link.platform] || COLORS.primary;
- };
-
if (loading) {
return (
{/* Header Skeleton */}
-
+
-
+
@@ -301,15 +198,15 @@ export default function DevCardViewScreen({ navigation, route }: Props) {
{/* Tiles Skeleton */}
-
+
{[1, 2, 3].map(i => (
-
-
-
+
+
+
-
+
))}
@@ -341,138 +238,93 @@ export default function DevCardViewScreen({ navigation, route }: Props) {
✕
- {/* Save Contact Button */}
- {profile && (
-
-
- {isSaved ? 'Saved' : 'Save'}
-
-
- )}
-
- {/* Profile Card */}
-
- {/* Gradient layers */}
-
+ {/* Profile Card — PREMIUM REDESIGN */}
+
-
- {/* Top row: brand + contactless */}
+
-
+
DevCard PRO
-
- PLATINUM
-
+ 📶
- {/* Middle: avatar + name/role */}
-
-
+
+ {profile.avatarUrl ? (
+
+ ) : (
+
+
+ {profile.displayName.charAt(0).toUpperCase()}
+
+
+ )}
- {profile.displayName}
- {(profile.role || profile.company) && (
-
- {profile.role}{profile.company ? ` @ ${profile.company}` : ''}
-
- )}
+ {profile.displayName}
+
+ {profile.role}{profile.company ? ` @ ${profile.company}` : ''}
+
{profile.pronouns && (
{profile.pronouns}
)}
- {/* Bottom: bio + divider */}
- {profile.bio ? (
-
-
- {profile.bio}
+
+
+ {profile.bio && {profile.bio} }
- ) : null}
+
+ PLATINUM
+
+
{/* Platform Tiles Section */}
-
- Digital Touchpoints
-
- {profile.links.length}
-
-
-
- {profile.links.length === 0 ? (
-
-
-
- ) : profile.links.map(link => {
+ Digital Touchpoints
+ {profile.links.map(link => {
const platform = PLATFORMS[link.platform];
const state = followStates[link.id] || 'idle';
- const btnColor = getButtonColor(link, state);
- const isDone = state === 'success';
- const tileIconDynamic = isDone
- ? { backgroundColor: 'rgba(34,197,94,0.12)', borderColor: COLORS.success }
- : { backgroundColor: (platform?.color || COLORS.primary) + '22', borderColor: (platform?.color || COLORS.primary) + '66' };
return (
handlePlatformAction(link)}
- onLongPress={() => {
- if (isDone) {
- Alert.alert(
- 'Reset connection?',
- `This will clear the "Done" status for ${platform?.name || link.platform}.`,
- [
- { text: 'Cancel', style: 'cancel' },
- {
- text: 'Reset',
- style: 'destructive',
- onPress: () => handleResetFollowState(link),
- },
- ]
- );
- }
- }}
- activeOpacity={isDone ? 0.9 : 0.8}
+ activeOpacity={0.8}
disabled={state === 'loading'}>
-
- {/* Icon */}
-
- {isDone ? (
- ✓
- ) : (
-
- {PLATFORM_EMOJI[link.platform] || platform?.name.charAt(0) || '?'}
-
- )}
+
+
+ {platform?.name.charAt(0) || '?'}
+
-
- {/* Info */}
{platform?.name || link.platform}
- {link.username}
+ {link.username}
-
- {/* Action Button */}
-
+
{state === 'loading' ? (
) : (
- {getButtonLabel(link)}
+
+ {getButtonLabel(link)}
+
)}
-
);
})}
@@ -480,7 +332,6 @@ export default function DevCardViewScreen({ navigation, route }: Props) {
{/* Footer */}
-
Powered by DevCard ⚡
@@ -493,139 +344,159 @@ const styles = StyleSheet.create({
closeBtn: {
position: 'absolute', top: 50, right: 20, zIndex: 10,
width: 36, height: 36, borderRadius: 18,
- backgroundColor: 'rgba(255,255,255,0.08)',
- borderWidth: 1, borderColor: 'rgba(255,255,255,0.12)',
- alignItems: 'center', justifyContent: 'center',
+ backgroundColor: COLORS.bgElevated, alignItems: 'center', justifyContent: 'center',
},
closeBtnText: { color: COLORS.textSecondary, fontSize: FONT_SIZE.md },
- saveContactBtn: {
- position: 'absolute', top: 50, left: 20, zIndex: 10,
- paddingHorizontal: SPACING.md, paddingVertical: 8, borderRadius: 18,
- backgroundColor: COLORS.primary,
- alignItems: 'center', justifyContent: 'center',
- },
- saveContactBtnText: { color: COLORS.white, fontSize: FONT_SIZE.sm, fontWeight: '700' },
scrollContent: { padding: SPACING.lg, paddingTop: SPACING.xxl },
premiumHeaderCard: {
- backgroundColor: '#0B1120',
- borderRadius: 20,
- padding: SPACING.lg,
+ backgroundColor: '#0F172A',
+ borderRadius: 24,
+ padding: SPACING.xl,
borderWidth: 1,
...SHADOWS.card,
marginBottom: SPACING.xl,
position: 'relative',
overflow: 'hidden',
- gap: SPACING.md,
- },
- cardGlowTop: {
- position: 'absolute',
- top: -40,
- left: -40,
- width: 160,
- height: 160,
- borderRadius: 80,
- backgroundColor: 'rgba(99,102,241,0.12)',
+ aspectRatio: 1.58,
+ justifyContent: 'space-between',
},
cardGlass: {
...StyleSheet.absoluteFillObject,
- backgroundColor: 'rgba(255,255,255,0.015)',
+ backgroundColor: 'rgba(255, 255, 255, 0.03)',
},
cardTop: {
- flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ brandRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 8,
+ },
+ miniChip: {
+ width: 30,
+ height: 20,
+ borderRadius: 4,
+ backgroundColor: '#94A3B8',
+ opacity: 0.5,
+ },
+ brandText: {
+ color: 'rgba(255,255,255,0.5)',
+ fontSize: 10,
+ fontWeight: '800',
+ letterSpacing: 2,
+ },
+ contactless: {
+ fontSize: 20,
+ opacity: 0.4,
+ },
+ cardMid: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: SPACING.lg,
},
- brandRow: { flexDirection: 'row', alignItems: 'center', gap: 7 },
- miniChip: { width: 28, height: 18, borderRadius: 4, opacity: 0.7 },
- brandText: { color: 'rgba(255,255,255,0.45)', fontSize: 9, fontWeight: '800', letterSpacing: 2.5 },
- cardMid: { flexDirection: 'row', alignItems: 'center', gap: SPACING.md },
- avatarRing: {
- borderRadius: 38,
+ avatarContainer: {
+ ...SHADOWS.card,
+ shadowOpacity: 0.3,
+ },
+ avatar: {
+ width: 70,
+ height: 70,
+ borderRadius: 35,
borderWidth: 2,
- padding: 2,
+ borderColor: 'rgba(255,255,255,0.1)',
+ },
+ avatarPlaceholder: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ avatarText: {
+ fontSize: 32,
+ fontWeight: '800',
+ color: COLORS.white,
+ },
+ mainInfo: {
+ flex: 1,
},
- avatar: { width: 64, height: 64, borderRadius: 32 },
- avatarPlaceholder: { alignItems: 'center', justifyContent: 'center' },
- avatarText: { fontSize: 28, fontWeight: '800', color: COLORS.white },
- mainInfo: { flex: 1, gap: 3 },
profileName: {
- fontSize: 20, fontWeight: '800', color: COLORS.white, letterSpacing: 0.2,
+ fontSize: 24,
+ fontWeight: '800',
+ color: COLORS.white,
+ letterSpacing: 0.5,
},
profileRole: {
- fontSize: 11, color: 'rgba(255,255,255,0.55)', fontWeight: '500', lineHeight: 15,
+ fontSize: 12,
+ color: COLORS.textSecondary,
+ fontWeight: '600',
+ marginTop: 2,
},
- pronouns: { fontSize: 10, color: COLORS.textMuted, fontStyle: 'italic' },
- cardBottom: { gap: SPACING.xs },
- cardDivider: {
- height: 1, backgroundColor: 'rgba(255,255,255,0.06)', marginBottom: 2,
+ pronouns: {
+ fontSize: 10,
+ color: COLORS.textMuted,
+ marginTop: 4,
+ fontStyle: 'italic',
+ },
+ cardBottom: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ bioContainer: {
+ flex: 1,
+ marginRight: SPACING.md,
+ },
+ bioText: {
+ fontSize: 10,
+ color: 'rgba(255,255,255,0.4)',
+ lineHeight: 14,
},
- bioText: { fontSize: 10.5, color: 'rgba(255,255,255,0.38)', lineHeight: 15 },
cardBadge: {
- alignSelf: 'flex-start',
- paddingHorizontal: 8, paddingVertical: 3, borderRadius: 4,
- borderWidth: 1,
+ paddingHorizontal: 8,
+ paddingVertical: 4,
+ borderRadius: 4,
+ backgroundColor: 'rgba(255,255,255,0.05)',
+ borderWidth: 0.5,
+ borderColor: 'rgba(255,255,255,0.1)',
},
- badgeText: { fontSize: 8, fontWeight: '900', letterSpacing: 1.5 },
-
- // ─── Tiles ───
- tilesSection: { gap: SPACING.sm },
- tilesHeader: {
- flexDirection: 'row', alignItems: 'center',
- justifyContent: 'space-between', marginBottom: SPACING.xs,
+ badgeText: {
+ fontSize: 8,
+ fontWeight: '900',
+ color: 'rgba(255,255,255,0.6)',
+ letterSpacing: 1.5,
},
+ tilesSection: { gap: SPACING.sm },
tilesLabel: {
- fontSize: FONT_SIZE.xs, color: COLORS.textMuted, fontWeight: '700',
- textTransform: 'uppercase', letterSpacing: 1.5,
+ fontSize: FONT_SIZE.sm, color: COLORS.textMuted, fontWeight: '600',
+ textTransform: 'uppercase', letterSpacing: 1, marginBottom: SPACING.xs,
},
- tilesCount: {
- backgroundColor: 'rgba(255,255,255,0.08)',
- borderRadius: 10, paddingHorizontal: 8, paddingVertical: 2,
- borderWidth: 1, borderColor: 'rgba(255,255,255,0.1)',
- },
- tilesCountText: { fontSize: 11, fontWeight: '700', color: COLORS.textMuted },
platformTile: {
flexDirection: 'row', alignItems: 'center',
backgroundColor: COLORS.bgCard, borderRadius: BORDER_RADIUS.md,
padding: SPACING.md, borderWidth: 1, borderColor: COLORS.border,
- gap: SPACING.sm,
- },
- tileDone: {
- borderColor: COLORS.success + '55',
- backgroundColor: 'rgba(34, 197, 94, 0.06)',
},
+ tileDone: { borderColor: COLORS.success, backgroundColor: 'rgba(34, 197, 94, 0.05)' },
tileIcon: {
- width: 44, height: 44, borderRadius: 12,
+ width: 40, height: 40, borderRadius: 10,
alignItems: 'center', justifyContent: 'center',
},
- tileIconBorder: { borderWidth: 1 },
- tileIconText: { fontWeight: '800', fontSize: 16, letterSpacing: -0.5 },
- tileIconDoneText: { fontWeight: '800', fontSize: 18, color: COLORS.success },
- tileInfo: { flex: 1 },
+ tileIconText: { color: COLORS.white, fontWeight: '700', fontSize: FONT_SIZE.md },
+ tileInfo: { flex: 1, marginLeft: SPACING.md },
tilePlatform: { fontSize: FONT_SIZE.md, fontWeight: '600', color: COLORS.textPrimary },
tileUsername: { fontSize: FONT_SIZE.sm, color: COLORS.textMuted, marginTop: 1 },
tileAction: {
- borderRadius: BORDER_RADIUS.sm,
- paddingHorizontal: SPACING.md, paddingVertical: 7,
- minWidth: 72, alignItems: 'center', justifyContent: 'center',
+ backgroundColor: COLORS.primary, borderRadius: BORDER_RADIUS.sm,
+ paddingHorizontal: SPACING.md, paddingVertical: SPACING.xs,
+ minWidth: 72, alignItems: 'center',
},
- tileActionText: { color: COLORS.white, fontWeight: '700', fontSize: 13 },
- emptyLinksCard: {
- backgroundColor: COLORS.bgCard,
- borderRadius: BORDER_RADIUS.md,
- borderWidth: 1,
- borderColor: COLORS.border,
- },
- skelMb8: { marginBottom: 8 },
- skelMb12: { marginBottom: 12 },
- skelMb6: { marginBottom: 6 },
- tileInfoMl16: { marginLeft: 16 },
-
- // ─── Error / Footer ───
+ tileActionDone: { backgroundColor: COLORS.success },
+ tileActionLoading: { backgroundColor: COLORS.primaryDark },
+ tileActionText: { color: COLORS.white, fontWeight: '700', fontSize: FONT_SIZE.sm },
+ tileActionTextDone: {},
errorState: { flex: 1, alignItems: 'center', justifyContent: 'center' },
errorEmoji: { fontSize: 48, marginBottom: SPACING.md },
errorText: { fontSize: FONT_SIZE.lg, color: COLORS.textPrimary, fontWeight: '600' },
backLink: { color: COLORS.primary, fontSize: FONT_SIZE.md, marginTop: SPACING.md },
footer: { alignItems: 'center', paddingVertical: SPACING.xl },
- footerDivider: {
- width: 40, height: 1, backgroundColor: 'rgba(255,255,255,0.08)', marginBottom: SPACING.md,
- },
- footerText: { fontSize: FONT_SIZE.xs, color: COLORS.textMuted, letterSpacing: 0.5 },
+ footerText: { fontSize: FONT_SIZE.xs, color: COLORS.textMuted },
});
diff --git a/apps/mobile/src/screens/EventDetailScreen.tsx b/apps/mobile/src/screens/EventDetailScreen.tsx
deleted file mode 100644
index 3b5e2428..00000000
--- a/apps/mobile/src/screens/EventDetailScreen.tsx
+++ /dev/null
@@ -1,184 +0,0 @@
-import React, { useState, useEffect, useCallback } from 'react';
-import {
- View, Text, StyleSheet, FlatList, TouchableOpacity,
- StatusBar, Alert,
-} from 'react-native';
-import { SafeAreaView } from 'react-native-safe-area-context';
-import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
-import Avatar from '../components/Avatar';
-import { LoadingPlaceholder } from '../components/LoadingPlaceholder';
-import { EmptyState } from '../components/EmptyState';
-import { useAuth } from '../context/AuthContext';
-import { get, post, del } from '../services/api';
-import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS, SHADOWS } from '../theme/tokens';
-import type { NativeStackScreenProps } from '@react-navigation/native-stack';
-import type { RootStackParamList } from '../navigation/MainTabs';
-
-type Props = NativeStackScreenProps;
-
-interface Attendee {
- id: string; username: string; displayName: string;
- bio: string | null; avatarUrl: string | null; accentColor: string;
-}
-
-export default function EventDetailScreen({ route, navigation }: Props) {
- const { slug, name } = route.params;
- const { token } = useAuth();
- const [event, setEvent] = useState(null);
- const [attendees, setAttendees] = useState([]);
- const [loading, setLoading] = useState(true);
- const [joining, setJoining] = useState(false);
-
- const fetchEvent = useCallback(async () => {
- setLoading(true);
- try {
- const [detail, atts] = await Promise.all([
- get(`/api/events/${slug}`, token),
- get(`/api/events/${slug}/attendees`, token),
- ]);
- setEvent(detail);
- setAttendees(atts?.attendees || []);
- } catch { Alert.alert('Error', 'Failed to load event'); }
- finally { setLoading(false); }
- }, [slug, token]);
-
- useEffect(() => { fetchEvent(); }, [fetchEvent]);
-
- const handleJoin = async () => {
- setJoining(true);
- try {
- await post(`/api/events/${slug}/join`, undefined, token);
- Alert.alert('Joined!', 'You are now part of this event.');
- fetchEvent();
- } catch (err: unknown) {
- const msg = err instanceof Error ? err.message : '';
- Alert.alert(msg.includes('409') ? 'Already Joined' : 'Error',
- msg.includes('409') ? 'You are already part of this event.' : 'Failed to join.');
- } finally { setJoining(false); }
- };
-
- const handleLeave = () => {
- Alert.alert('Leave Event', 'Are you sure?', [
- { text: 'Cancel', style: 'cancel' },
- { text: 'Leave', style: 'destructive', onPress: async () => {
- try { await del(`/api/events/${slug}/leave`, undefined, token); fetchEvent(); }
- catch { Alert.alert('Error', 'Failed to leave event'); }
- }},
- ]);
- };
-
- const fmtDate = (s: string) => new Date(s).toLocaleDateString(undefined, {
- weekday: 'short', month: 'short', day: 'numeric',
- });
-
- if (loading) return (
-
-
-
-
- );
-
- return (
-
-
- item.id}
- contentContainerStyle={styles.list}
- ListHeaderComponent={
-
-
- {event?.name || name}
- {event?.location && (
-
-
- {event.location}
-
- )}
-
-
-
- {event ? `${fmtDate(event.startDate)} – ${fmtDate(event.endDate)}` : ''}
-
-
- {event?.description && (
- {event.description}
- )}
-
-
-
- {joining ? 'Joining…' : 'Join Event'}
-
-
-
- Leave
-
-
-
-
- Attendees ({event?.attendeesCount || attendees.length})
-
-
- }
- renderItem={({ item }) => (
- navigation.navigate('DevCardView', { username: item.username })}
- activeOpacity={0.7}>
-
-
- {item.displayName}
- @{item.username}
-
-
-
- )}
- ListEmptyComponent={
-
- }
- />
-
- );
-}
-
-const styles = StyleSheet.create({
- container: { flex: 1, backgroundColor: COLORS.bgPrimary },
- list: { padding: SPACING.lg },
- infoCard: {
- backgroundColor: COLORS.bgCard, borderRadius: BORDER_RADIUS.lg,
- padding: SPACING.lg, borderWidth: 1, borderColor: COLORS.border,
- marginBottom: SPACING.lg, ...SHADOWS.card,
- },
- eventName: { fontSize: FONT_SIZE.xl, fontWeight: '800', color: COLORS.textPrimary, marginBottom: SPACING.sm },
- metaRow: { flexDirection: 'row', alignItems: 'center', gap: SPACING.xs, marginBottom: 4 },
- metaText: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary },
- description: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, marginTop: SPACING.sm, lineHeight: 20 },
- actions: { flexDirection: 'row', gap: SPACING.sm, marginTop: SPACING.lg },
- joinBtn: {
- flex: 1, backgroundColor: COLORS.primary, borderRadius: BORDER_RADIUS.md,
- padding: SPACING.md, alignItems: 'center', ...SHADOWS.button,
- },
- joinBtnText: { color: COLORS.white, fontWeight: '700', fontSize: FONT_SIZE.md },
- leaveBtn: {
- backgroundColor: COLORS.bgElevated, borderRadius: BORDER_RADIUS.md,
- padding: SPACING.md, paddingHorizontal: SPACING.lg,
- borderWidth: 1, borderColor: COLORS.border,
- },
- leaveBtnText: { color: COLORS.error, fontWeight: '600', fontSize: FONT_SIZE.md },
- sectionTitle: {
- fontSize: FONT_SIZE.lg, fontWeight: '700', color: COLORS.textPrimary,
- marginBottom: SPACING.md,
- },
- attendeeRow: {
- flexDirection: 'row', alignItems: 'center', backgroundColor: COLORS.bgCard,
- borderRadius: BORDER_RADIUS.md, padding: SPACING.md, marginBottom: SPACING.sm,
- borderWidth: 1, borderColor: COLORS.border,
- },
- avatar: { width: 40, height: 40, borderRadius: 20, marginRight: SPACING.md },
- attendeeInfo: { flex: 1 },
- attendeeName: { fontSize: FONT_SIZE.md, fontWeight: '600', color: COLORS.textPrimary },
- attendeeUser: { fontSize: FONT_SIZE.sm, color: COLORS.textMuted, marginTop: 1 },
-});
diff --git a/apps/mobile/src/screens/EventsScreen.tsx b/apps/mobile/src/screens/EventsScreen.tsx
deleted file mode 100644
index c4dbf7bf..00000000
--- a/apps/mobile/src/screens/EventsScreen.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import React, { useState, useCallback } from 'react';
-import {
- View, Text, StyleSheet, TextInput, TouchableOpacity,
- StatusBar, Alert,
-} from 'react-native';
-import { SafeAreaView } from 'react-native-safe-area-context';
-import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
-import { EmptyState } from '../components/EmptyState';
-import { useAuth } from '../context/AuthContext';
-import { get } from '../services/api';
-import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS, SHADOWS } from '../theme/tokens';
-import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
-import type { RootStackParamList } from '../navigation/MainTabs';
-
-type Props = { navigation: NativeStackNavigationProp };
-
-export default function EventsScreen({ navigation }: Props) {
- const { token } = useAuth();
- const [slugInput, setSlugInput] = useState('');
- const [loading, setLoading] = useState(false);
-
- const handleLookup = async () => {
- const slug = slugInput.trim().toLowerCase();
- if (!slug) { Alert.alert('Enter a slug', 'Please enter the event slug or code.'); return; }
- setLoading(true);
- try {
- const event = await get(`/api/events/${slug}`, token);
- if (event) navigation.navigate('EventDetail', { slug: event.slug, name: event.name });
- } catch { Alert.alert('Not Found', 'No event found with that code.'); }
- finally { setLoading(false); setSlugInput(''); }
- };
-
- return (
-
-
-
- Events
- Join an event to network with attendees
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: { flex: 1, backgroundColor: COLORS.bgPrimary },
- header: { padding: SPACING.lg, paddingBottom: SPACING.sm },
- title: { fontSize: FONT_SIZE.xl, fontWeight: '800', color: COLORS.textPrimary },
- subtitle: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, marginTop: SPACING.xs },
- joinSection: { paddingHorizontal: SPACING.lg, paddingBottom: SPACING.lg },
- inputRow: { flexDirection: 'row', gap: SPACING.sm },
- input: {
- flex: 1, backgroundColor: COLORS.bgCard, borderRadius: BORDER_RADIUS.md,
- padding: SPACING.md, color: COLORS.textPrimary, fontSize: FONT_SIZE.md,
- borderWidth: 1, borderColor: COLORS.border,
- },
- searchBtn: {
- backgroundColor: COLORS.primary, borderRadius: BORDER_RADIUS.md,
- width: 48, alignItems: 'center', justifyContent: 'center', ...SHADOWS.button,
- },
- disabled: { opacity: 0.5 },
-});
diff --git a/apps/mobile/src/screens/HomeScreen.tsx b/apps/mobile/src/screens/HomeScreen.tsx
index b4d504b2..80de203c 100644
--- a/apps/mobile/src/screens/HomeScreen.tsx
+++ b/apps/mobile/src/screens/HomeScreen.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useState, useEffect } from 'react';
import {
View,
Text,
@@ -7,18 +7,15 @@ import {
TouchableOpacity,
Share,
StatusBar,
+ Image,
RefreshControl,
- TextInput,
} from 'react-native';
-import { Skeleton } from '../components/Skeleton';
-import Avatar from '../components/Avatar';
import { SafeAreaView } from 'react-native-safe-area-context';
import QRCode from 'react-native-qrcode-svg';
import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS, SHADOWS } from '../theme/tokens';
import { useAuth } from '../context/AuthContext';
import { PLATFORMS } from '@devcard/shared';
-import { APP_URL } from '../config';
-import { get } from '../services/api';
+import { APP_URL, API_BASE_URL } from '../config';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RootStackParamList } from '../navigation/MainTabs';
@@ -40,37 +37,37 @@ export default function HomeScreen({ navigation }: Props) {
const [analytics, setAnalytics] = useState(null);
const [showQR, setShowQR] = useState(false);
const [refreshing, setRefreshing] = useState(false);
- const [loading, setLoading] = useState(true);
- const [searchUsername, setSearchUsername] = useState('');
const profileUrl = user?.defaultCardId
? `${APP_URL}/devcard/${user.defaultCardId}`
: `${APP_URL}/u/${user?.username}`;
- const fetchData = useCallback(async () => {
- setLoading(true);
+ useEffect(() => {
+ fetchData();
+ }, []);
+
+ const fetchData = async () => {
try {
- const [profileData, analyticsData] = await Promise.all([
- get('/api/profiles/me', token).catch(() => null),
- get('/api/analytics/overview', token).catch(() => null),
+ const [profileRes, analyticsRes] = await Promise.all([
+ fetch(`${API_BASE_URL}/api/profiles/me`, {
+ headers: { Authorization: `Bearer ${token}` },
+ }),
+ fetch(`${API_BASE_URL}/api/analytics/overview`, {
+ headers: { Authorization: `Bearer ${token}` },
+ })
]);
- if (profileData) {
- setLinks(profileData.platformLinks || []);
+ if (profileRes.ok) {
+ const data = await profileRes.json();
+ setLinks(data.platformLinks || []);
}
- if (analyticsData) {
- setAnalytics(analyticsData);
+ if (analyticsRes.ok) {
+ setAnalytics(await analyticsRes.json());
}
- } catch (error) {
- console.error('Failed to fetch dashboard data:', error);
- } finally {
- setLoading(false);
+ } catch (err) {
+ console.error('Failed to fetch dashboard data:', err);
}
- }, [token]);
-
- useEffect(() => {
- fetchData();
- }, [fetchData]);
+ };
const onRefresh = async () => {
setRefreshing(true);
@@ -84,26 +81,11 @@ export default function HomeScreen({ navigation }: Props) {
message: `Check out my DevCard: ${profileUrl}`,
url: profileUrl,
});
- } catch (error) {
- console.error('Share failed:', error);
+ } catch (err) {
+ console.error('Share failed:', err);
}
};
- if (loading) {
- return (
-
-
-
-
-
-
-
-
-
-
- );
- }
-
return (
@@ -126,7 +108,15 @@ export default function HomeScreen({ navigation }: Props) {
{/* Profile Card Preview */}
-
+ {user?.avatarUrl ? (
+
+ ) : (
+
+
+ {(user?.displayName || 'D').charAt(0).toUpperCase()}
+
+
+ )}
{user?.displayName}
{user?.pronouns && (
@@ -145,26 +135,20 @@ export default function HomeScreen({ navigation }: Props) {
{/* Platform Links Summary */}
- {links.length > 0 ? (
- <>
- {links.slice(0, 4).map(link => {
- const platform = PLATFORMS[link.platform];
- return (
-
-
- {platform?.name || link.platform}
-
-
- );
- })}
- {links.length > 4 && (
-
- +{links.length - 4}
-
- )}
- >
- ) : (
- No platform links added yet. Add links in the Links tab to populate your preview.
+ {links.slice(0, 4).map(link => {
+ const platform = PLATFORMS[link.platform];
+ return (
+
+
+ {platform?.name || link.platform}
+
+
+ );
+ })}
+ {links.length > 4 && (
+
+ +{links.length - 4}
+
)}
@@ -193,13 +177,13 @@ export default function HomeScreen({ navigation }: Props) {
{/* Action Buttons */}
-
+
📤
- Share
+ Share Card
(navigation as any).navigate('Views')}
activeOpacity={0.85}>
📈
- Stats
+ Analytics
👁️
Preview
-
- (navigation as any).navigate('Links')}
- activeOpacity={0.85}>
- 🔗
- Links
-
-
-
-
- (navigation as any).navigate('Events')}
- activeOpacity={0.85}>
- 🎪
- Events
-
-
- (navigation as any).navigate('Teams')}
- activeOpacity={0.85}>
- 👥
- Teams
-
-
- (navigation as any).navigate('Nfc')}
- activeOpacity={0.85}>
- 📳
- NFC
-
-
-
-
- {/* Search / Lookup */}
-
- 🔍 View a DevCard
-
- {
- const u = searchUsername.trim();
- if (u) (navigation as any).navigate('DevCardView', { username: u });
- }}
- />
- {
- const u = searchUsername.trim();
- if (u) (navigation as any).navigate('DevCardView', { username: u });
- }}
- >
- Go →
-
-
{/* Stats */}
@@ -356,13 +275,12 @@ const styles = StyleSheet.create({
qrToggle: { flexDirection: 'row', alignItems: 'center', gap: SPACING.sm },
qrToggleEmoji: { fontSize: 24 },
qrToggleText: { fontSize: FONT_SIZE.md, color: COLORS.textSecondary, fontWeight: '500' },
- actionsGrid: { flexDirection: 'row', gap: SPACING.sm, marginBottom: SPACING.sm },
+ actions: { flexDirection: 'row', gap: SPACING.md, marginBottom: SPACING.lg },
actionButton: {
flex: 1,
backgroundColor: COLORS.bgCard,
borderRadius: BORDER_RADIUS.md,
- padding: SPACING.sm,
- paddingVertical: SPACING.md,
+ padding: SPACING.md,
alignItems: 'center',
borderWidth: 1,
borderColor: COLORS.border,
@@ -381,48 +299,4 @@ const styles = StyleSheet.create({
statNumber: { fontSize: FONT_SIZE.xl, fontWeight: '800', color: COLORS.primary },
statLabel: { fontSize: FONT_SIZE.xs, color: COLORS.textMuted, marginTop: 4 },
statDivider: { width: 1, backgroundColor: COLORS.border },
- loadingRoot: {
- flex: 1,
- padding: SPACING.lg,
- backgroundColor: COLORS.bgPrimary,
- },
- loadingSpacer: {
- marginTop: SPACING.sm,
- },
- loadingSection: {
- marginTop: SPACING.lg,
- },
- emptyHint: {
- color: COLORS.textMuted,
- fontSize: FONT_SIZE.sm,
- lineHeight: 20,
- marginTop: SPACING.sm,
- maxWidth: '70%',
- },
- // Search
- searchSection: {
- marginBottom: SPACING.lg,
- },
- searchLabel: {
- fontSize: FONT_SIZE.sm, fontWeight: '700', color: COLORS.textSecondary,
- marginBottom: SPACING.sm, letterSpacing: 0.3,
- },
- searchRow: {
- flexDirection: 'row', gap: SPACING.sm,
- },
- searchInput: {
- flex: 1,
- backgroundColor: COLORS.bgCard,
- borderRadius: BORDER_RADIUS.md,
- paddingHorizontal: SPACING.md, paddingVertical: 12,
- color: COLORS.textPrimary, fontSize: FONT_SIZE.md,
- borderWidth: 1, borderColor: COLORS.border,
- },
- searchBtn: {
- backgroundColor: COLORS.primary,
- borderRadius: BORDER_RADIUS.md,
- paddingHorizontal: SPACING.lg,
- justifyContent: 'center', alignItems: 'center',
- },
- searchBtnText: { color: COLORS.white, fontWeight: '700', fontSize: FONT_SIZE.md },
});
diff --git a/apps/mobile/src/screens/LinksScreen.tsx b/apps/mobile/src/screens/LinksScreen.tsx
index fd420275..daded55f 100644
--- a/apps/mobile/src/screens/LinksScreen.tsx
+++ b/apps/mobile/src/screens/LinksScreen.tsx
@@ -14,11 +14,8 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS } from '../theme/tokens';
import { useAuth } from '../context/AuthContext';
import { PLATFORMS, getAllPlatforms } from '@devcard/shared';
-import { get, post, del } from '../services/api';
-import { EmptyState } from '../components/EmptyState';
-import { LoadingPlaceholder } from '../components/LoadingPlaceholder';
+import { API_BASE_URL } from '../config';
import type { PlatformDef } from '@devcard/shared';
-import DraggableFlatList, { ScaleDecorator, RenderItemParams } from 'react-native-draggable-flatlist';
interface PlatformLink {
id: string;
@@ -34,17 +31,18 @@ export default function LinksScreen() {
const [showAddModal, setShowAddModal] = useState(false);
const [selectedPlatform, setSelectedPlatform] = useState(null);
const [usernameInput, setUsernameInput] = useState('');
- const [loading, setLoading] = useState(true);
const fetchLinks = useCallback(async () => {
- setLoading(true);
try {
- const data = await get('/api/profiles/me', token).catch(() => null);
- setLinks(data?.platformLinks || []);
- } catch (error) {
- console.error('Failed to fetch links:', error);
- } finally {
- setLoading(false);
+ const res = await fetch(`${API_BASE_URL}/api/profiles/me`, {
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ if (res.ok) {
+ const data = await res.json();
+ setLinks(data.platformLinks || []);
+ }
+ } catch (err) {
+ console.error('Failed to fetch links:', err);
}
}, [token]);
@@ -55,12 +53,24 @@ export default function LinksScreen() {
const addLink = async () => {
if (!selectedPlatform || !usernameInput.trim()) return;
try {
- await post('/api/profiles/me/links', { platform: selectedPlatform.id, username: usernameInput.trim() }, token);
- setShowAddModal(false);
- setSelectedPlatform(null);
- setUsernameInput('');
- fetchLinks();
- } catch {
+ const res = await fetch(`${API_BASE_URL}/api/profiles/me/links`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${token}`,
+ },
+ body: JSON.stringify({
+ platform: selectedPlatform.id,
+ username: usernameInput.trim(),
+ }),
+ });
+ if (res.ok) {
+ setShowAddModal(false);
+ setSelectedPlatform(null);
+ setUsernameInput('');
+ fetchLinks();
+ }
+ } catch (err) {
Alert.alert('Error', 'Failed to add link');
}
};
@@ -72,71 +82,20 @@ export default function LinksScreen() {
text: 'Remove',
style: 'destructive',
onPress: async () => {
- try {
- await del(`/api/profiles/me/links/${id}`, undefined, token);
- fetchLinks();
- } catch {
- Alert.alert('Error', 'Failed to remove link');
- }
+ try {
+ await fetch(`${API_BASE_URL}/api/profiles/me/links/${id}`, {
+ method: 'DELETE',
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ fetchLinks();
+ } catch (err) {
+ Alert.alert('Error', 'Failed to remove link');
+ }
},
},
]);
};
- const handleReorder = async (data: PlatformLink[]) => {
- setLinks(data);
- try {
- const payload = {
- links: data.map((link, index) => ({ id: link.id, displayOrder: index })),
- };
- await put('/api/profiles/me/links/reorder', payload, token);
- } catch {
- Alert.alert('Error', 'Failed to save new order');
- fetchLinks(); // Revert on failure
- }
- };
-
- const renderItem = ({ item, drag, isActive }: RenderItemParams) => {
- const platform = PLATFORMS[item.platform];
- return (
-
-
-
- ⋮⋮
-
-
-
- {platform?.name || item.platform}
- {item.username}
-
- deleteLink(item.id)}
- style={styles.deleteBtn}>
- ✕
-
-
-
- );
- };
-
- if (loading) {
- return (
-
-
-
-
- );
- }
-
return (
@@ -150,18 +109,33 @@ export default function LinksScreen() {
- handleReorder(data)}
keyExtractor={item => item.id}
contentContainerStyle={styles.list}
- renderItem={renderItem}
+ renderItem={({ item }) => {
+ const platform = PLATFORMS[item.platform];
+ return (
+
+
+
+ {platform?.name || item.platform}
+ {item.username}
+
+ deleteLink(item.id)}
+ style={styles.deleteBtn}>
+ ✕
+
+
+ );
+ }}
ListEmptyComponent={
-
+
+ 🔗
+ No links yet
+ Add your first platform link
+
}
/>
@@ -238,30 +212,16 @@ const styles = StyleSheet.create({
backgroundColor: COLORS.bgCard, borderRadius: BORDER_RADIUS.md,
padding: SPACING.md, borderWidth: 1, borderColor: COLORS.border,
},
- linkItemActive: {
- backgroundColor: COLORS.bgElevated,
- borderColor: COLORS.primary,
- elevation: 8,
- shadowColor: COLORS.black,
- shadowOffset: { width: 0, height: 4 },
- shadowOpacity: 0.3,
- shadowRadius: 8,
- },
- dragHandle: {
- paddingRight: SPACING.sm,
- justifyContent: 'center',
- },
- dragHandleText: {
- color: COLORS.textMuted,
- fontSize: 20,
- fontWeight: 'bold',
- },
platformDot: { width: 12, height: 12, borderRadius: 6, marginRight: SPACING.md },
linkInfo: { flex: 1 },
platformName: { fontSize: FONT_SIZE.md, fontWeight: '600', color: COLORS.textPrimary },
username: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, marginTop: 2 },
deleteBtn: { padding: SPACING.sm },
deleteBtnText: { color: COLORS.error, fontSize: FONT_SIZE.md, fontWeight: '700' },
+ empty: { alignItems: 'center', paddingVertical: SPACING.xxl },
+ emptyEmoji: { fontSize: 48, marginBottom: SPACING.md },
+ emptyText: { fontSize: FONT_SIZE.lg, fontWeight: '600', color: COLORS.textPrimary },
+ emptySubtext: { fontSize: FONT_SIZE.sm, color: COLORS.textMuted, marginTop: SPACING.xs },
modalOverlay: {
flex: 1, backgroundColor: COLORS.overlay,
justifyContent: 'flex-end',
diff --git a/apps/mobile/src/screens/NfcScreen.tsx b/apps/mobile/src/screens/NfcScreen.tsx
deleted file mode 100644
index d14c317d..00000000
--- a/apps/mobile/src/screens/NfcScreen.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-import React, { useState, useCallback } from 'react';
-import {
- View, Text, StyleSheet, TouchableOpacity, StatusBar, Alert,
-} from 'react-native';
-import { SafeAreaView } from 'react-native-safe-area-context';
-import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
-import { useAuth } from '../context/AuthContext';
-import { get } from '../services/api';
-import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS, SHADOWS } from '../theme/tokens';
-import type { NfcPayload } from '../types';
-
-/**
- * NfcScreen — NFC tag write/read UI.
- *
- * NOTE: Actual NFC hardware interaction requires `react-native-nfc-manager`
- * which needs a dev build (not Expo Go). This screen provides the UI and
- * fetches the NDEF payload from the backend. The NFC write call is stubbed
- * with a TODO for native module integration.
- */
-export default function NfcScreen() {
- const { token } = useAuth();
- const [payload, setPayload] = useState(null);
- const [loading, setLoading] = useState(false);
- const [written, setWritten] = useState(false);
-
- const fetchPayload = useCallback(async () => {
- setLoading(true);
- try {
- const data = await get('/api/nfc/payload', token);
- setPayload(data);
- } catch {
- Alert.alert('Error', 'Failed to fetch NFC payload from server.');
- } finally {
- setLoading(false);
- }
- }, [token]);
-
- const handleWriteTag = async () => {
- if (!payload) {
- await fetchPayload();
- return;
- }
-
- // TODO: Integrate react-native-nfc-manager here
- // import NfcManager, { NfcTech, Ndef } from 'react-native-nfc-manager';
- // await NfcManager.requestTechnology(NfcTech.Ndef);
- // const bytes = Ndef.encodeMessage([Ndef.uriRecord(payload.payload)]);
- // await NfcManager.ndefHandler.writeNdefMessage(bytes);
- // await NfcManager.cancelTechnologyRequest();
-
- Alert.alert(
- 'NFC Not Available',
- 'NFC write requires a dev build with react-native-nfc-manager. The payload URL has been prepared.',
- [{ text: 'OK' }],
- );
- setWritten(false);
- };
-
- return (
-
-
-
-
-
-
-
-
- NFC Tag Writer
-
- Write your DevCard URL to an NFC tag so anyone can tap to view your profile.
-
-
-
-
-
- Payload URL
-
-
- {payload?.payload || 'Tap "Prepare" to generate'}
-
-
-
-
-
-
- {loading ? 'Loading…' : 'Prepare Payload'}
-
-
-
-
-
-
- Write to NFC Tag
-
-
-
- {written && (
-
-
- Tag written successfully!
-
- )}
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: { flex: 1, backgroundColor: COLORS.bgPrimary },
- content: { flex: 1, padding: SPACING.lg, alignItems: 'center', justifyContent: 'center' },
- iconContainer: {
- width: 120, height: 120, borderRadius: 60,
- backgroundColor: COLORS.bgCard, alignItems: 'center', justifyContent: 'center',
- borderWidth: 2, borderColor: COLORS.primary + '44', marginBottom: SPACING.lg,
- },
- title: { fontSize: FONT_SIZE.xl, fontWeight: '800', color: COLORS.textPrimary },
- subtitle: {
- fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, textAlign: 'center',
- marginTop: SPACING.xs, marginBottom: SPACING.xl, lineHeight: 20, maxWidth: 300,
- },
- card: {
- width: '100%', backgroundColor: COLORS.bgCard, borderRadius: BORDER_RADIUS.lg,
- padding: SPACING.lg, borderWidth: 1, borderColor: COLORS.border, marginBottom: SPACING.lg,
- },
- cardRow: { flexDirection: 'row', alignItems: 'center', gap: SPACING.xs, marginBottom: SPACING.sm },
- cardLabel: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, fontWeight: '500' },
- payloadUrl: { fontSize: FONT_SIZE.sm, color: COLORS.primary, fontFamily: 'monospace' },
- prepareBtn: {
- flexDirection: 'row', alignItems: 'center', gap: SPACING.sm,
- backgroundColor: COLORS.bgElevated, borderRadius: BORDER_RADIUS.md,
- padding: SPACING.md, paddingHorizontal: SPACING.lg, marginBottom: SPACING.md,
- borderWidth: 1, borderColor: COLORS.border,
- },
- prepareBtnText: { color: COLORS.textPrimary, fontWeight: '600', fontSize: FONT_SIZE.md },
- writeBtn: {
- flexDirection: 'row', alignItems: 'center', gap: SPACING.sm,
- backgroundColor: COLORS.primary, borderRadius: BORDER_RADIUS.md,
- padding: SPACING.md, paddingHorizontal: SPACING.xl, ...SHADOWS.button,
- },
- writeBtnDisabled: { backgroundColor: COLORS.bgElevated },
- writeBtnText: { color: COLORS.white, fontWeight: '700', fontSize: FONT_SIZE.md },
- writeBtnTextDisabled: { color: COLORS.textMuted },
- successBanner: {
- flexDirection: 'row', alignItems: 'center', gap: SPACING.sm,
- marginTop: SPACING.lg, backgroundColor: 'rgba(34,197,94,0.1)',
- borderRadius: BORDER_RADIUS.md, padding: SPACING.md,
- },
- successText: { color: COLORS.success, fontWeight: '600', fontSize: FONT_SIZE.sm },
-});
diff --git a/apps/mobile/src/screens/ScanScreen.tsx b/apps/mobile/src/screens/ScanScreen.tsx
index 1f300351..b864cdd6 100644
--- a/apps/mobile/src/screens/ScanScreen.tsx
+++ b/apps/mobile/src/screens/ScanScreen.tsx
@@ -7,26 +7,19 @@ import {
TextInput,
StatusBar,
Alert,
- Share,
- Platform,
- PermissionsAndroid,
+ ActivityIndicator,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useFocusEffect } from '@react-navigation/native';
import QRCode from 'react-native-qrcode-svg';
-import ViewShot from 'react-native-view-shot';
-import { Camera } from 'react-native-camera-kit';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS } from '../theme/tokens';
-import { EmptyState } from '../components/EmptyState';
-import { Skeleton } from '../components/Skeleton';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RootStackParamList } from '../navigation/MainTabs';
import type { BottomSheetModal } from '@gorhom/bottom-sheet';
import type { Card } from '@devcard/shared';
import { useAuth } from '../context/AuthContext';
-import { APP_URL } from '../config';
-import { get } from '../services/api';
+import { API_BASE_URL, APP_URL } from '../config';
import CardPickerSheet from '../components/CardPickerSheet';
type Props = {
@@ -46,9 +39,6 @@ export default function ScanScreen({ navigation }: Props) {
const [loadingCards, setLoadingCards] = useState(false);
const sheetRef = useRef(null);
- const qrRef = useRef(null);
- const [hasPermission, setHasPermission] = useState(false);
-
// Extract username from DevCard URL
const parseDevCardUrl = (url: string): string | null => {
const match = url.match(/\/u\/([a-zA-Z0-9_-]+)/);
@@ -65,59 +55,22 @@ export default function ScanScreen({ navigation }: Props) {
}
};
- const requestCameraPermission = async () => {
- if (Platform.OS === 'android') {
- try {
- const granted = await PermissionsAndroid.request(
- PermissionsAndroid.PERMISSIONS.CAMERA,
- {
- title: 'Camera Permission',
- message: 'DevCard needs camera access to scan QR codes.',
- buttonNeutral: 'Ask Me Later',
- buttonNegative: 'Cancel',
- buttonPositive: 'OK',
- },
- );
- setHasPermission(granted === PermissionsAndroid.RESULTS.GRANTED);
- } catch (err) {
- console.warn(err);
- }
- } else {
- // iOS permissions would typically be handled via react-native-permissions
- // For this demo, assume true if not Android
- setHasPermission(true);
- }
- };
-
- const handleCameraRead = (url: string) => {
- const username = parseDevCardUrl(url);
- if (username) {
- navigation.navigate('DevCardView', { username });
- }
- };
-
- const handleSaveQR = async () => {
- if (qrRef.current && qrRef.current.capture) {
- try {
- const uri = await qrRef.current.capture();
- await Share.share({
- title: 'My DevCard QR',
- url: uri,
- });
- } catch (err) {
- Alert.alert('Error', 'Failed to save QR code');
- }
- }
- };
+ // NOTE: Camera QR scanning requires react-native-camera-kit
+ // which needs native setup. For now, we provide manual entry.
+ // Camera integration will be added when building on device.
const fetchCards = useCallback(async () => {
if (!token) return;
setLoadingCards(true);
try {
- const data = await get('/api/cards', token).catch(() => []);
- setCards(data || []);
- } catch (error) {
- console.error('Failed to fetch cards:', error);
+ const res = await fetch(`${API_BASE_URL}/api/cards`, {
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ if (res.ok) {
+ setCards(await res.json());
+ }
+ } catch (err) {
+ console.error('Failed to fetch cards:', err);
} finally {
setLoadingCards(false);
}
@@ -178,8 +131,8 @@ export default function ScanScreen({ navigation }: Props) {
setSelectedCardId(cardId);
try {
await AsyncStorage.setItem(LAST_SELECTED_CARD_KEY, cardId);
- } catch (error) {
- console.error('Failed to persist selected card:', error);
+ } catch (err) {
+ console.error('Failed to persist selected card:', err);
} finally {
sheetRef.current?.dismiss();
}
@@ -227,12 +180,9 @@ export default function ScanScreen({ navigation }: Props) {
-
+
{loadingCards ? (
-
-
-
-
+
) : qrUrl ? (
) : (
-
+ Create a card to generate a QR
)}
-
-
+
{!!qrUrl && (
-
- Scan to open your DevCard
-
- Share QR Image
-
-
+ Scan to open your DevCard
)}
- {/* Camera Scanner */}
+ {/* Camera Placeholder */}
- {hasPermission ? (
- handleCameraRead(event.nativeEvent.codeStringValue)}
- showFrame={false}
- />
- ) : (
-
- 📷
- Camera Permission Required
-
- Grant Permission
-
-
- )}
+
+ 📷
+ Camera QR Scanner
+
+ Point your camera at a DevCard QR code
+
+
{/* Corner markers */}
@@ -358,22 +290,7 @@ const styles = StyleSheet.create({
minHeight: 220,
},
qrHint: { textAlign: 'center', color: COLORS.textMuted, fontSize: FONT_SIZE.sm },
- saveQrBtn: {
- backgroundColor: COLORS.bgElevated,
- borderRadius: BORDER_RADIUS.sm,
- paddingHorizontal: SPACING.md,
- paddingVertical: SPACING.xs,
- borderWidth: 1,
- borderColor: COLORS.border,
- },
- saveQrBtnText: { color: COLORS.primary, fontSize: FONT_SIZE.xs, fontWeight: '600' },
- qrFooter: { alignItems: 'center', marginTop: SPACING.sm, gap: SPACING.xs },
- qrSkeleton: {
- alignItems: 'center',
- },
- qrSkeletonText: {
- marginTop: SPACING.md,
- },
+ qrPlaceholder: { color: COLORS.textMuted, fontSize: FONT_SIZE.sm },
cameraArea: {
flex: 1, maxHeight: 350,
backgroundColor: COLORS.bgCard, borderRadius: BORDER_RADIUS.lg,
@@ -385,12 +302,6 @@ const styles = StyleSheet.create({
cameraEmoji: { fontSize: 48, marginBottom: SPACING.md },
cameraText: { fontSize: FONT_SIZE.md, fontWeight: '600', color: COLORS.textPrimary },
cameraSubtext: { fontSize: FONT_SIZE.sm, color: COLORS.textMuted, marginTop: SPACING.xs },
- reqPermBtn: {
- backgroundColor: COLORS.primary, borderRadius: BORDER_RADIUS.sm,
- paddingHorizontal: SPACING.md, paddingVertical: SPACING.sm,
- marginTop: SPACING.md,
- },
- reqPermBtnText: { color: COLORS.white, fontSize: FONT_SIZE.sm, fontWeight: '600' },
corner: {
position: 'absolute', width: 30, height: 30,
borderColor: COLORS.primary, borderWidth: 3,
diff --git a/apps/mobile/src/screens/SettingsScreen.tsx b/apps/mobile/src/screens/SettingsScreen.tsx
index 933d08d9..7d282a63 100644
--- a/apps/mobile/src/screens/SettingsScreen.tsx
+++ b/apps/mobile/src/screens/SettingsScreen.tsx
@@ -8,53 +8,46 @@ import {
TextInput,
Alert,
StatusBar,
+ Image,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
-import { useNavigation } from '@react-navigation/native';
import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS } from '../theme/tokens';
-import Avatar from '../components/Avatar';
-import ColorPicker from '../components/ColorPicker';
import { useAuth } from '../context/AuthContext';
-import { put } from '../services/api';
+import { API_BASE_URL } from '../config';
export default function SettingsScreen() {
- const navigation = useNavigation();
const { user, token, refreshUser, logout } = useAuth();
const [displayName, setDisplayName] = useState(user?.displayName || '');
const [bio, setBio] = useState(user?.bio || '');
const [pronouns, setPronouns] = useState(user?.pronouns || '');
const [role, setRole] = useState(user?.role || '');
const [company, setCompany] = useState(user?.company || '');
- const [accentColor, setAccentColor] = useState(user?.accentColor || '#6366F1');
const [saving, setSaving] = useState(false);
- const handleAvatarTap = () => {
- // TODO: Integrate react-native-image-picker when building on device
- // import { launchImageLibrary } from 'react-native-image-picker';
- // const result = await launchImageLibrary({ mediaType: 'photo', quality: 0.8 });
- // Upload via multipart/form-data to PUT /api/profiles/me/avatar
- Alert.alert(
- 'Change Avatar',
- 'Avatar upload requires react-native-image-picker in a dev build. Coming soon!',
- );
- };
-
const handleSave = async () => {
setSaving(true);
try {
- const payload = {
- displayName: displayName.trim() || undefined,
- bio: bio.trim() || null,
- pronouns: pronouns.trim() || null,
- role: role.trim() || null,
- company: company.trim() || null,
- accentColor,
- };
-
- await put('/api/profiles/me', payload, token);
- await refreshUser();
- Alert.alert('Success', 'Profile updated!');
- } catch {
+ const res = await fetch(`${API_BASE_URL}/api/profiles/me`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${token}`,
+ },
+ body: JSON.stringify({
+ displayName: displayName.trim(),
+ bio: bio.trim() || null,
+ pronouns: pronouns.trim() || null,
+ role: role.trim() || null,
+ company: company.trim() || null,
+ }),
+ });
+ if (res.ok) {
+ await refreshUser();
+ Alert.alert('Success', 'Profile updated!');
+ } else {
+ Alert.alert('Error', 'Failed to update profile');
+ }
+ } catch (err) {
Alert.alert('Error', 'Something went wrong');
} finally {
setSaving(false);
@@ -76,16 +69,17 @@ export default function SettingsScreen() {
Profile Settings
{/* Avatar */}
-
-
- Tap to change
+
+ {user?.avatarUrl ? (
+
+ ) : (
+
+
+ {(user?.displayName || 'D').charAt(0).toUpperCase()}
+
+
+ )}
@{user?.username}
-
-
- {/* Accent Color */}
-
- Card Accent Color
-
{/* Form */}
@@ -169,13 +163,11 @@ const styles = StyleSheet.create({
title: { fontSize: FONT_SIZE.xl, fontWeight: '800', color: COLORS.textPrimary, marginBottom: SPACING.lg },
avatarSection: { alignItems: 'center', marginBottom: SPACING.xl },
avatar: { width: 80, height: 80, borderRadius: 40 },
- avatarHint: { fontSize: FONT_SIZE.xs, color: COLORS.primary, marginTop: SPACING.xs, fontWeight: '500' },
avatarPlaceholder: {
backgroundColor: COLORS.primary, alignItems: 'center', justifyContent: 'center',
},
avatarText: { fontSize: FONT_SIZE.xxl, fontWeight: '700', color: COLORS.white },
usernameDisplay: { fontSize: FONT_SIZE.md, color: COLORS.textSecondary, marginTop: SPACING.sm },
- colorSection: { marginBottom: SPACING.lg },
form: { gap: SPACING.md, marginBottom: SPACING.lg },
field: {},
fieldLabel: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, marginBottom: SPACING.xs, fontWeight: '500' },
diff --git a/apps/mobile/src/screens/SplashScreen.tsx b/apps/mobile/src/screens/SplashScreen.tsx
deleted file mode 100644
index 2e6c4991..00000000
--- a/apps/mobile/src/screens/SplashScreen.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import React, { useEffect, useRef } from 'react';
-import { View, Text, StyleSheet, Animated } from 'react-native';
-import { COLORS, FONT_SIZE, SPACING } from '../theme/tokens';
-
-/**
- * SplashScreen — Branded loading screen shown during auth token hydration.
- *
- * Uses a pulsing opacity animation on the logo to indicate loading activity
- * without requiring any external dependencies.
- */
-export default function SplashScreen() {
- const opacity = useRef(new Animated.Value(0.4)).current;
- const scale = useRef(new Animated.Value(0.9)).current;
-
- useEffect(() => {
- Animated.loop(
- Animated.sequence([
- Animated.parallel([
- Animated.timing(opacity, {
- toValue: 1,
- duration: 800,
- useNativeDriver: true,
- }),
- Animated.timing(scale, {
- toValue: 1.05,
- duration: 800,
- useNativeDriver: true,
- }),
- ]),
- Animated.parallel([
- Animated.timing(opacity, {
- toValue: 0.4,
- duration: 800,
- useNativeDriver: true,
- }),
- Animated.timing(scale, {
- toValue: 0.9,
- duration: 800,
- useNativeDriver: true,
- }),
- ]),
- ]),
- ).start();
- }, [opacity, scale]);
-
- return (
-
-
- ⚡
-
- DevCard
- Loading your profile…
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: COLORS.bgPrimary,
- alignItems: 'center',
- justifyContent: 'center',
- gap: SPACING.md,
- },
- logoContainer: {
- width: 100,
- height: 100,
- borderRadius: 50,
- backgroundColor: COLORS.bgCard,
- alignItems: 'center',
- justifyContent: 'center',
- borderWidth: 2,
- borderColor: COLORS.primary + '44',
- },
- logo: {
- fontSize: 48,
- },
- title: {
- fontSize: FONT_SIZE.xxl,
- fontWeight: '800',
- color: COLORS.textPrimary,
- letterSpacing: -0.5,
- },
- subtitle: {
- fontSize: FONT_SIZE.sm,
- color: COLORS.textMuted,
- },
-});
diff --git a/apps/mobile/src/screens/TeamDetailScreen.tsx b/apps/mobile/src/screens/TeamDetailScreen.tsx
deleted file mode 100644
index 9503bb72..00000000
--- a/apps/mobile/src/screens/TeamDetailScreen.tsx
+++ /dev/null
@@ -1,127 +0,0 @@
-import React, { useState, useEffect, useCallback } from 'react';
-import {
- View, Text, StyleSheet, FlatList, TouchableOpacity,
- StatusBar, Alert,
-} from 'react-native';
-import { SafeAreaView } from 'react-native-safe-area-context';
-import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
-import Avatar from '../components/Avatar';
-import { LoadingPlaceholder } from '../components/LoadingPlaceholder';
-import { EmptyState } from '../components/EmptyState';
-import { useAuth } from '../context/AuthContext';
-import { get } from '../services/api';
-import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS, SHADOWS } from '../theme/tokens';
-import type { NativeStackScreenProps } from '@react-navigation/native-stack';
-import type { RootStackParamList } from '../navigation/MainTabs';
-import type { TeamMember } from '../types';
-
-type Props = NativeStackScreenProps;
-
-export default function TeamDetailScreen({ route, navigation }: Props) {
- const { slug, name } = route.params;
- const { token } = useAuth();
- const [team, setTeam] = useState(null);
- const [loading, setLoading] = useState(true);
-
- const fetchTeam = useCallback(async () => {
- setLoading(true);
- try {
- const data = await get(`/api/teams/${slug}`, token);
- setTeam(data);
- } catch { Alert.alert('Error', 'Failed to load team'); }
- finally { setLoading(false); }
- }, [slug, token]);
-
- useEffect(() => { fetchTeam(); }, [fetchTeam]);
-
- const getRoleBadge = (role: string) => {
- switch (role) {
- case 'OWNER': return { label: 'Owner', color: COLORS.warning };
- case 'ADMIN': return { label: 'Admin', color: COLORS.info };
- default: return { label: 'Member', color: COLORS.textMuted };
- }
- };
-
- if (loading) return (
-
-
-
-
- );
-
- const members: TeamMember[] = team?.members || [];
-
- return (
-
-
- item.username}
- contentContainerStyle={styles.list}
- ListHeaderComponent={
-
-
- {team?.name || name}
- {team?.description && (
- {team.description}
- )}
-
- {members.length} member{members.length !== 1 ? 's' : ''}
-
-
- Members
-
- }
- renderItem={({ item }) => {
- const badge = getRoleBadge(item.teamRole);
- return (
- navigation.navigate('DevCardView', { username: item.username })}
- activeOpacity={0.7}>
-
-
- {item.displayName}
- {item.role && {item.role} }
-
-
- {badge.label}
-
-
- );
- }}
- ListEmptyComponent={
-
- }
- />
-
- );
-}
-
-const styles = StyleSheet.create({
- container: { flex: 1, backgroundColor: COLORS.bgPrimary },
- list: { padding: SPACING.lg },
- infoCard: {
- backgroundColor: COLORS.bgCard, borderRadius: BORDER_RADIUS.lg,
- padding: SPACING.lg, borderWidth: 1, borderColor: COLORS.border,
- marginBottom: SPACING.lg, ...SHADOWS.card,
- },
- teamName: { fontSize: FONT_SIZE.xl, fontWeight: '800', color: COLORS.textPrimary, marginBottom: SPACING.xs },
- description: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, lineHeight: 20, marginBottom: SPACING.sm },
- memberCount: { fontSize: FONT_SIZE.sm, color: COLORS.primary, fontWeight: '600' },
- sectionTitle: { fontSize: FONT_SIZE.lg, fontWeight: '700', color: COLORS.textPrimary, marginBottom: SPACING.md },
- memberRow: {
- flexDirection: 'row', alignItems: 'center', backgroundColor: COLORS.bgCard,
- borderRadius: BORDER_RADIUS.md, padding: SPACING.md, marginBottom: SPACING.sm,
- borderWidth: 1, borderColor: COLORS.border,
- },
- avatar: { width: 40, height: 40, borderRadius: 20, marginRight: SPACING.md },
- memberInfo: { flex: 1 },
- memberName: { fontSize: FONT_SIZE.md, fontWeight: '600', color: COLORS.textPrimary },
- memberRole: { fontSize: FONT_SIZE.sm, color: COLORS.textMuted, marginTop: 1 },
- roleBadge: {
- borderWidth: 1, borderRadius: BORDER_RADIUS.full,
- paddingHorizontal: SPACING.sm, paddingVertical: 2,
- },
- roleBadgeText: { fontSize: FONT_SIZE.xs, fontWeight: '600' },
-});
diff --git a/apps/mobile/src/screens/TeamsScreen.tsx b/apps/mobile/src/screens/TeamsScreen.tsx
deleted file mode 100644
index c64e047e..00000000
--- a/apps/mobile/src/screens/TeamsScreen.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import React, { useState, useCallback } from 'react';
-import {
- View, Text, StyleSheet, FlatList, TouchableOpacity,
- TextInput, StatusBar, Alert,
-} from 'react-native';
-import { SafeAreaView } from 'react-native-safe-area-context';
-import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
-import { EmptyState } from '../components/EmptyState';
-import { useAuth } from '../context/AuthContext';
-import { get } from '../services/api';
-import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS, SHADOWS } from '../theme/tokens';
-import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
-import type { RootStackParamList } from '../navigation/MainTabs';
-
-type Props = { navigation: NativeStackNavigationProp };
-
-export default function TeamsScreen({ navigation }: Props) {
- const { token } = useAuth();
- const [slugInput, setSlugInput] = useState('');
- const [loading, setLoading] = useState(false);
-
- const handleLookup = async () => {
- const slug = slugInput.trim().toLowerCase();
- if (!slug) { Alert.alert('Enter a slug', 'Enter the team slug.'); return; }
- setLoading(true);
- try {
- const team = await get(`/api/teams/${slug}`, token);
- if (team) navigation.navigate('TeamDetail', { slug: team.slug, name: team.name });
- } catch { Alert.alert('Not Found', 'No team found with that slug.'); }
- finally { setLoading(false); setSlugInput(''); }
- };
-
- return (
-
-
-
- Teams
- Look up a team to view their group DevCard
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: { flex: 1, backgroundColor: COLORS.bgPrimary },
- header: { padding: SPACING.lg, paddingBottom: SPACING.sm },
- title: { fontSize: FONT_SIZE.xl, fontWeight: '800', color: COLORS.textPrimary },
- subtitle: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, marginTop: SPACING.xs },
- joinSection: { paddingHorizontal: SPACING.lg, paddingBottom: SPACING.lg },
- inputRow: { flexDirection: 'row', gap: SPACING.sm },
- input: {
- flex: 1, backgroundColor: COLORS.bgCard, borderRadius: BORDER_RADIUS.md,
- padding: SPACING.md, color: COLORS.textPrimary, fontSize: FONT_SIZE.md,
- borderWidth: 1, borderColor: COLORS.border,
- },
- searchBtn: {
- backgroundColor: COLORS.primary, borderRadius: BORDER_RADIUS.md,
- width: 48, alignItems: 'center', justifyContent: 'center', ...SHADOWS.button,
- },
- disabled: { opacity: 0.5 },
-});
diff --git a/apps/mobile/src/screens/ViewsScreen.tsx b/apps/mobile/src/screens/ViewsScreen.tsx
index cd0654ea..24dc79ee 100644
--- a/apps/mobile/src/screens/ViewsScreen.tsx
+++ b/apps/mobile/src/screens/ViewsScreen.tsx
@@ -1,13 +1,10 @@
-import React, { useState, useEffect, useCallback, useMemo } from 'react';
-import { View, Text, StyleSheet, FlatList } from 'react-native';
+import React, { useState, useEffect, useCallback } from 'react';
+import { View, Text, StyleSheet, FlatList, ActivityIndicator, Image } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS } from '../theme/tokens';
import { useAuth } from '../context/AuthContext';
-import { get } from '../services/api';
-import { EmptyState } from '../components/EmptyState';
-import Avatar from '../components/Avatar';
-import { LoadingPlaceholder } from '../components/LoadingPlaceholder';
+import { API_BASE_URL } from '../config';
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import type { RootStackParamList } from '../navigation/MainTabs';
@@ -17,30 +14,30 @@ export const ViewsScreen: React.FC = () => {
const { token } = useAuth();
const [loading, setLoading] = useState(true);
const [views, setViews] = useState([]);
- const [overview, setOverview] = useState(null);
- const fetchData = useCallback(async () => {
+ const fetchViews = useCallback(async () => {
if (!token) {
setLoading(false);
return;
}
try {
- const [viewsData, overviewData] = await Promise.all([
- get('/api/analytics/views', token).catch(() => null),
- get('/api/analytics/overview', token).catch(() => null),
- ]);
- setViews(viewsData?.data || []);
- setOverview(overviewData);
- } catch (error) {
- console.error('Failed to fetch analytics', error);
+ const response = await fetch(`${API_BASE_URL}/api/analytics/views`, {
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ if (response.ok) {
+ const data = await response.json();
+ setViews(data.data || []);
+ }
+ } catch (err) {
+ console.error('Failed to fetch views analytics', err);
} finally {
setLoading(false);
}
}, [token]);
useEffect(() => {
- fetchData();
- }, [fetchData]);
+ fetchViews();
+ }, [fetchViews]);
const formatDate = (dateString: string) => {
const d = new Date(dateString);
@@ -56,59 +53,6 @@ export const ViewsScreen: React.FC = () => {
}
};
- // Generate simple bar chart data for last 7 days
- const chartData = useMemo(() => {
- const last7Days = Array.from({ length: 7 }, (_, i) => {
- const d = new Date();
- d.setDate(d.getDate() - (6 - i));
- return { date: d.toLocaleDateString('en-US', { weekday: 'short' }), count: 0 };
- });
-
- views.forEach(v => {
- const d = new Date(v.createdAt).toLocaleDateString('en-US', { weekday: 'short' });
- const day = last7Days.find(x => x.date === d);
- if (day) day.count++;
- });
-
- const max = Math.max(...last7Days.map(d => d.count), 1); // prevent division by zero
- return { data: last7Days, max };
- }, [views]);
-
- const renderHeader = () => (
-
-
-
- {overview?.totalViews || 0}
- Total Views
-
-
- {overview?.followsCount || 0}
- Connections
-
-
-
-
- Views (Last 7 Days)
-
- {chartData.data.map((item, idx) => {
- const heightPerc = (item.count / chartData.max) * 100;
- return (
-
- {item.count > 0 ? item.count : ''}
-
-
-
- {item.date}
-
- );
- })}
-
-
-
- Recent Activity
-
- );
-
const renderItem = ({ item }: { item: any }) => {
const isAnonymous = !item.viewer;
@@ -120,9 +64,11 @@ export const ViewsScreen: React.FC = () => {
) : item.viewer.avatarUrl ? (
-
+
) : (
-
+
+ {item.viewer.displayName.charAt(0)}
+
)}
@@ -146,26 +92,25 @@ export const ViewsScreen: React.FC = () => {
if (loading) {
return (
-
-
-
+
+
+
);
}
return (
{views.length === 0 ? (
-
+
+
+ No Views Yet
+ Share your card or QR code to start collecting analytics.
+
) : (
item.id}
renderItem={renderItem}
- ListHeaderComponent={renderHeader}
contentContainerStyle={styles.listContainer}
/>
)}
@@ -269,87 +214,4 @@ const styles = StyleSheet.create({
textAlign: 'center',
marginTop: SPACING.sm,
},
- headerContainer: {
- paddingBottom: SPACING.lg,
- },
- statsRow: {
- flexDirection: 'row',
- gap: SPACING.md,
- marginBottom: SPACING.lg,
- },
- statCard: {
- flex: 1,
- backgroundColor: COLORS.bgCard,
- borderRadius: BORDER_RADIUS.lg,
- padding: SPACING.lg,
- alignItems: 'center',
- borderWidth: 1,
- borderColor: COLORS.borderLight,
- },
- statValue: {
- fontSize: 28,
- fontWeight: '800',
- color: COLORS.primary,
- },
- statLabel: {
- fontSize: FONT_SIZE.sm,
- color: COLORS.textMuted,
- marginTop: 4,
- fontWeight: '600',
- },
- chartCard: {
- backgroundColor: COLORS.bgCard,
- borderRadius: BORDER_RADIUS.lg,
- padding: SPACING.lg,
- borderWidth: 1,
- borderColor: COLORS.borderLight,
- marginBottom: SPACING.xl,
- },
- chartTitle: {
- fontSize: FONT_SIZE.md,
- fontWeight: '700',
- color: COLORS.textPrimary,
- marginBottom: SPACING.lg,
- },
- chartContainer: {
- flexDirection: 'row',
- alignItems: 'flex-end',
- justifyContent: 'space-between',
- height: 140,
- paddingTop: 20,
- },
- barWrapper: {
- alignItems: 'center',
- flex: 1,
- },
- barTrack: {
- width: 24,
- height: 100,
- backgroundColor: COLORS.bgElevated,
- borderRadius: 4,
- justifyContent: 'flex-end',
- overflow: 'hidden',
- },
- barFill: {
- width: '100%',
- backgroundColor: COLORS.primary,
- borderRadius: 4,
- },
- barLabel: {
- fontSize: 10,
- color: COLORS.textMuted,
- marginTop: SPACING.sm,
- },
- barLabelTop: {
- fontSize: 10,
- color: COLORS.primary,
- marginBottom: 4,
- fontWeight: 'bold',
- },
- sectionTitle: {
- fontSize: FONT_SIZE.lg,
- fontWeight: '700',
- color: COLORS.textPrimary,
- marginBottom: SPACING.md,
- },
});
diff --git a/apps/mobile/src/screens/WebViewScreen.tsx b/apps/mobile/src/screens/WebViewScreen.tsx
index 844c248a..03806d8f 100644
--- a/apps/mobile/src/screens/WebViewScreen.tsx
+++ b/apps/mobile/src/screens/WebViewScreen.tsx
@@ -1,19 +1,14 @@
-import React, { useRef, useState, useEffect } from 'react';
+import React, { useRef } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
StatusBar,
- Linking,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { WebView } from 'react-native-webview';
-import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS, SHADOWS } from '../theme/tokens';
-import { Skeleton } from '../components/Skeleton';
-import { getDeepLinkUrl } from '@devcard/shared';
-import { post } from '../services/api';
-import { useAuth } from '../context/AuthContext';
+import { COLORS, SPACING, FONT_SIZE, BORDER_RADIUS } from '../theme/tokens';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RouteProp } from '@react-navigation/native';
import type { RootStackParamList } from '../navigation/MainTabs';
@@ -28,486 +23,64 @@ type Props = {
*
* Opens the platform profile in an in-app WebView so the user can
* tap the native Follow/Connect button without leaving DevCard.
+ *
+ * Key features:
+ * - sharedCookiesEnabled: shares auth cookies from system browser OAuth
+ * - Auto-detects when user navigates away (they tapped Connect)
+ * - Clean close button to dismiss
*/
export default function WebViewScreen({ navigation, route }: Props) {
- const {
- platform,
- url,
- platformName,
- username,
- linkId,
- cardOwnerUsername,
- } = route.params;
-
- const { token } = useAuth();
- const platformDisplayName = platformName || platform;
+ const { platform, profileUrl, displayName } = route.params;
const webViewRef = useRef(null);
- const [hasLoaded, setHasLoaded] = useState(false);
- const [fallbackTriggered, setFallbackTriggered] = useState(false);
- const [showFallbackOverlay, setShowFallbackOverlay] = useState(false);
- const [successToast, setSuccessToast] = useState(null);
- const [progress, setProgress] = useState(0);
-
- const isSuccessHandled = useRef(false);
- const successTimerRef = useRef | null>(null);
- // Track whether the injected JS ever detected success during this session
- const successDetectedInSession = useRef(false);
-
- // Safety Timeout Fallback: 10 seconds
- useEffect(() => {
- if (hasLoaded || fallbackTriggered) return;
-
- const timer = setTimeout(() => {
- setFallbackTriggered(true);
- setShowFallbackOverlay(true);
- }, 10000);
-
- return () => clearTimeout(timer);
- }, [hasLoaded, fallbackTriggered]);
-
- useEffect(() => {
- return () => {
- if (successTimerRef.current) {
- clearTimeout(successTimerRef.current);
- }
- };
- }, []);
-
- const handleOpenDeepLink = () => {
- let targetUsername = username;
- if (!targetUsername && url) {
- const parts = url.split('/');
- const lastPart = parts[parts.length - 1] || parts[parts.length - 2];
- targetUsername = lastPart.split('?')[0];
- }
-
- const deepLink = targetUsername ? getDeepLinkUrl(platform, targetUsername) : null;
- if (deepLink) {
- Linking.canOpenURL(deepLink)
- .then((supported) => {
- Linking.openURL(supported ? deepLink : url);
- navigation.goBack();
- })
- .catch(() => {
- Linking.openURL(url);
- navigation.goBack();
- });
- } else {
- Linking.openURL(url);
- navigation.goBack();
- }
- };
-
- const handleOpenBrowser = () => {
- Linking.openURL(url);
- navigation.goBack();
- };
-
- const handleRetryWebView = () => {
- setHasLoaded(false);
- setFallbackTriggered(false);
- setShowFallbackOverlay(false);
- setProgress(0);
- webViewRef.current?.reload();
- };
-
- const handleSuccess = async () => {
- if (isSuccessHandled.current) return;
- isSuccessHandled.current = true;
- successDetectedInSession.current = true;
- setSuccessToast(`Connection request sent on ${platformDisplayName}`);
-
- // Asynchronously log follow to the backend
- if (token && username) {
- try {
- await post(`/api/follow/${platform}/${username}/log`, { status: 'success', layer: 'webview' }, token);
- } catch (error) {
- console.warn('Failed to log WebView follow success:', error);
- }
- }
-
- // Auto-dismiss after 2 seconds with success param back to parent
- successTimerRef.current = setTimeout(() => {
- navigateBackWithSuccess();
- }, 2000);
- };
-
- const navigateBackWithSuccess = () => {
- if (linkId) {
- navigation.navigate({
- name: 'DevCardView',
- params: { username: cardOwnerUsername, followSuccessLinkId: linkId },
- merge: true,
- });
- } else {
- navigation.goBack();
- }
- };
-
- // Done button: check current page state live before going back
- const handleDonePress = () => {
- // If success was already handled, navigate with success immediately
- if (successDetectedInSession.current) {
- if (successTimerRef.current) clearTimeout(successTimerRef.current);
- navigateBackWithSuccess();
- return;
- }
-
- // Inject a one-shot check script to see if LinkedIn currently shows success
- const checkScript = `
- (function() {
- var bodyText = document.body ? document.body.innerText.toLowerCase() : '';
- var successKeywords = ['invite sent', 'invitation sent', 'request sent', 'pending'];
- var found = successKeywords.some(function(k) { return bodyText.includes(k); });
- if (!found) {
- var els = document.querySelectorAll('button, a, span, [role="button"]');
- for (var i = 0; i < els.length; i++) {
- var t = (els[i].textContent || '').toLowerCase();
- var lbl = (els[i].getAttribute('aria-label') || '').toLowerCase();
- if (successKeywords.some(function(k) { return t.includes(k) || lbl.includes(k); })) {
- found = true;
- break;
- }
- }
- }
- window.ReactNativeWebView.postMessage(JSON.stringify({ status: found ? 'done_with_success' : 'done_without_success' }));
- })();
- `;
- if (webViewRef.current) {
- webViewRef.current.injectJavaScript(checkScript);
- } else {
- navigation.goBack();
- }
- };
-
- const handleHttpError = (syntheticEvent: any) => {
- const { nativeEvent } = syntheticEvent;
- console.warn('WebView HTTP error: ', nativeEvent?.statusCode, nativeEvent?.description);
- };
-
- const handleError = (syntheticEvent: any) => {
- const { nativeEvent } = syntheticEvent;
- console.warn('WebView general loading error:', nativeEvent?.description);
- if (!fallbackTriggered) {
- setFallbackTriggered(true);
- setShowFallbackOverlay(true);
- }
- };
-
- // JS Injection: LinkedIn-specific Connect button highlighting & event detection
- // injectedJavaScriptBeforeContentLoaded runs BEFORE any page content — sets up listeners early
- const injectedJSBeforeLoad = platform === 'linkedin' ? `
- (function() {
- // Set up the SUCCESS_KEYWORDS and postMessage bridge as early as possible
- window.__devcardSuccessKeywords = [
- 'invite sent', 'invitation sent', 'request sent',
- 'connection request sent', 'pending', 'withdraw'
- ];
- window.__devcardSuccessReported = false;
- window.__devcardHighlighted = false;
-
- window.__devcardCheck = function() {
- if (window.__devcardSuccessReported) return;
- var kws = window.__devcardSuccessKeywords;
- var bodyText = document.body ? document.body.innerText.toLowerCase() : '';
- for (var k = 0; k < kws.length; k++) {
- if (bodyText.includes(kws[k])) {
- window.__devcardSuccessReported = true;
- try { window.ReactNativeWebView.postMessage(JSON.stringify({ status: 'success' })); } catch(error){}
- return;
- }
- }
- var els = document.querySelectorAll('button, span, a, [role="button"]');
- for (var i = 0; i < els.length; i++) {
- var t = (els[i].textContent || '').toLowerCase();
- var l = (els[i].getAttribute('aria-label') || '').toLowerCase();
- for (var j = 0; j < kws.length; j++) {
- if (t.includes(kws[j]) || l.includes(kws[j])) {
- window.__devcardSuccessReported = true;
- try { window.ReactNativeWebView.postMessage(JSON.stringify({ status: 'success' })); } catch(error){}
- return;
- }
- }
- }
- };
-
- // Check when page becomes visible (fires after dialogs close)
- document.addEventListener('visibilitychange', function() {
- if (document.visibilityState === 'visible') {
- setTimeout(window.__devcardCheck, 200);
- setTimeout(window.__devcardCheck, 600);
- }
- });
-
- // Check on focus events (modal dismissal, back navigation)
- window.addEventListener('focus', function() {
- setTimeout(window.__devcardCheck, 300);
- });
- })();
- ` : undefined;
-
- const injectedJS = platform === 'linkedin' ? `
- (function() {
- function log(msg) {
- try {
- window.ReactNativeWebView.postMessage(JSON.stringify({ status: 'debug', message: msg }));
- } catch(error){}
- }
-
- log('LinkedIn JS Engine Started');
-
- // Inject pulsating highlight CSS for the Connect button
- var styleEl = document.createElement('style');
- styleEl.innerHTML = [
- '@keyframes pulse-highlight {',
- ' 0% { box-shadow: 0 0 0 0px rgba(10,102,194,0.7); border-color: #0A66C2; }',
- ' 70% { box-shadow: 0 0 0 10px rgba(10,102,194,0); border-color: #0084FF; }',
- ' 100% { box-shadow: 0 0 0 0px rgba(10,102,194,0); border-color: #0A66C2; }',
- '}',
- '.devcard-highlight {',
- ' animation: pulse-highlight 2s infinite !important;',
- ' border: 3px solid #0A66C2 !important;',
- ' transform: scale(1.02) !important;',
- '}'
- ].join('');
- if (document.head) document.head.appendChild(styleEl);
-
- // Reuse globals set by injectedJavaScriptBeforeContentLoaded if available
- var SUCCESS_KEYWORDS = (window.__devcardSuccessKeywords) || [
- 'invite sent', 'invitation sent', 'request sent',
- 'connection request sent', 'pending', 'withdraw'
- ];
- var successReported = (window.__devcardSuccessReported) || false;
- var highlighted = (window.__devcardHighlighted) || false;
-
- function reportSuccess(reason) {
- if (successReported) return;
- successReported = true;
- if (window.__devcardSuccessReported !== undefined) window.__devcardSuccessReported = true;
- try { window.ReactNativeWebView.postMessage(JSON.stringify({ status: 'success' })); } catch(error){}
- log('Success: ' + reason);
- }
-
- function checkPage() {
- if (successReported) return;
-
- // 1. Body text scan
- var bodyText = document.body ? document.body.innerText.toLowerCase() : '';
- for (var k = 0; k < SUCCESS_KEYWORDS.length; k++) {
- if (bodyText.includes(SUCCESS_KEYWORDS[k])) {
- reportSuccess('body:' + SUCCESS_KEYWORDS[k]);
- return;
- }
- }
-
- // 2. Element scan
- var allEls = document.querySelectorAll('button, a, span, [role="button"], li');
- for (var i = 0; i < allEls.length; i++) {
- var el = allEls[i];
- var text = (el.textContent || '').replace(new RegExp('\\s+', 'g'), ' ').trim().toLowerCase();
- var aria = (el.getAttribute('aria-label') || '').toLowerCase();
- var combined = text + ' ' + aria;
- for (var j = 0; j < SUCCESS_KEYWORDS.length; j++) {
- if (combined.includes(SUCCESS_KEYWORDS[j])) {
- reportSuccess('element:' + combined.substring(0, 40));
- return;
- }
- }
- // Highlight the Connect button
- if (!highlighted) {
- var isConnect = (text === 'connect' || aria === 'connect' || aria.includes('connect to'))
- && !text.includes('connections') && !text.includes('connected') && !el.disabled;
- if (isConnect) {
- el.scrollIntoView({ behavior: 'smooth', block: 'center' });
- el.classList.add('devcard-highlight');
- highlighted = true;
- log('Connect button highlighted');
- }
- }
- }
- }
-
- checkPage();
-
- // MutationObserver — watches childList, subtree AND characterData
- function startObserver() {
- var obs = new MutationObserver(function(mutations) { checkPage(); });
- obs.observe(document.body, {
- childList: true, subtree: true, characterData: true, attributes: true,
- attributeFilter: ['aria-label', 'class', 'disabled']
- });
- log('MutationObserver active');
- }
-
- if (document.body) {
- startObserver();
- } else {
- document.addEventListener('DOMContentLoaded', startObserver);
- }
-
- // Polling every 700ms (runs for up to 90 seconds)
- var pollCount = 0;
- var pollTimer = setInterval(function() {
- pollCount++;
- checkPage();
- if (successReported || pollCount > 128) clearInterval(pollTimer);
- }, 700);
-
- // Also run check on popstate (LinkedIn SPA navigation)
- window.addEventListener('popstate', function() {
- setTimeout(checkPage, 300);
- setTimeout(checkPage, 800);
- });
-
- log('Engine ready, polling + observer active');
- })();
- ` : undefined;
-
return (
- {/* Header Container */}
-
-
- navigation.goBack()} activeOpacity={0.7}>
- ✕ Close
-
- {platformDisplayName}
-
-
- {/* Loading Progress Bar */}
- {progress > 0 && progress < 1 && (
-
- )}
+ {/* Header Bar */}
+
+ navigation.goBack()}>
+ ✕ Close
+
+ {displayName}
+
{/* Info Banner */}
- You are viewing this profile in DevCard — tap Connect on {platformDisplayName} to send your request
+ Tap the Follow or{' '}
+ Connect button below to complete the action
- {successToast && (
-
- {successToast}
-
- )}
-
{/* WebView */}
- {url ? (
-
- setProgress(nativeEvent.progress)}
- onLoadEnd={() => setHasLoaded(true)}
- onError={handleError}
- onHttpError={handleHttpError}
- onMessage={(event) => {
- try {
- const data = JSON.parse(event.nativeEvent.data);
- if (data.status === 'success') {
- handleSuccess();
- } else if (data.status === 'done_with_success') {
- // Done button pressed: success found on current page
- handleSuccess();
- } else if (data.status === 'done_without_success') {
- // Done button pressed: no success found, just go back
- navigation.goBack();
- } else if (data.status === 'debug') {
- console.log('[WebView JS] ' + data.message);
- }
- } catch {}
- }}
- onNavigationStateChange={(navState) => {
- // Detect final invite-sent/shared subroutes (exclude early pages like send-invite)
- if (
- navState.url.includes('invite-sent') ||
- navState.url.includes('inviteShared') ||
- navState.url.includes('invitation-sent')
- ) {
- handleSuccess();
- }
- }}
- renderLoading={() => (
-
-
-
-
-
- Loading {platformDisplayName}...
-
- )}
- />
-
- {/* Premium Fallback Overlay for slow load / timeouts */}
- {showFallbackOverlay && (
-
-
- ⏳
- Profile loading is slow
-
- {platformDisplayName} is taking longer than usual to load inside the app. Would you like to open it directly in the native app?
-
-
-
- Open in {platformDisplayName} App
-
-
-
- Open in Default Browser
-
-
-
-
- Retry Loading
-
- navigation.goBack()}
- activeOpacity={0.7}>
- Cancel
-
-
-
-
- )}
-
- ) : (
-
- Invalid profile URL
-
- )}
-
- {/* Done Button Footer */}
+ (
+
+ Loading {displayName}...
+
+ )}
+ onNavigationStateChange={(navState) => {
+ // If user navigates away from the profile page,
+ // they likely completed the action
+ // We could auto-close here in the future
+ }}
+ />
+
+ {/* Done Button */}
+ onPress={() => navigation.goBack()}>
Done
@@ -520,142 +93,25 @@ const styles = StyleSheet.create({
header: {
flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between',
padding: SPACING.md, borderBottomWidth: 1, borderBottomColor: COLORS.border,
- backgroundColor: COLORS.bgSecondary,
},
- closeText: { color: COLORS.textSecondary, fontSize: FONT_SIZE.md, fontWeight: '600' },
+ closeText: { color: COLORS.textSecondary, fontSize: FONT_SIZE.md },
headerTitle: { fontSize: FONT_SIZE.md, fontWeight: '700', color: COLORS.textPrimary },
headerSpacer: { width: 60 },
- progressBar: {
- height: 3,
- position: 'absolute',
- bottom: 0,
- left: 0,
- zIndex: 10,
- },
banner: {
backgroundColor: COLORS.bgCard, padding: SPACING.md,
borderBottomWidth: 1, borderBottomColor: COLORS.border,
},
- bannerText: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, textAlign: 'center', lineHeight: 20 },
+ bannerText: { fontSize: FONT_SIZE.sm, color: COLORS.textSecondary, textAlign: 'center' },
bannerBold: { fontWeight: '700', color: COLORS.primary },
- toast: {
- position: 'absolute',
- top: 118,
- left: SPACING.md,
- right: SPACING.md,
- zIndex: 20,
- backgroundColor: COLORS.success,
- borderRadius: BORDER_RADIUS.md,
- padding: SPACING.md,
- alignItems: 'center',
- ...SHADOWS.button,
- },
- toastText: { color: COLORS.white, fontSize: FONT_SIZE.sm, fontWeight: '700' },
- webContainer: { flex: 1, position: 'relative' },
webview: { flex: 1 },
- loading: {
- ...StyleSheet.absoluteFillObject,
- alignItems: 'center',
- justifyContent: 'center',
- backgroundColor: COLORS.bgPrimary,
- padding: SPACING.lg,
- zIndex: 5,
- },
- loadingBlock: { marginTop: SPACING.lg },
- loadingLine: { marginTop: SPACING.md },
- loadingText: { color: COLORS.textMuted, fontSize: FONT_SIZE.md, marginTop: SPACING.lg },
+ loading: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: COLORS.bgPrimary },
+ loadingText: { color: COLORS.textMuted, fontSize: FONT_SIZE.md },
footer: {
padding: SPACING.md, borderTopWidth: 1, borderTopColor: COLORS.border,
- backgroundColor: COLORS.bgSecondary,
},
doneButton: {
- backgroundColor: COLORS.bgElevated, borderRadius: BORDER_RADIUS.md,
+ backgroundColor: COLORS.success, borderRadius: BORDER_RADIUS.md,
padding: SPACING.md, alignItems: 'center',
- borderWidth: 1,
- borderColor: COLORS.border,
- },
- doneButtonText: { color: COLORS.textPrimary, fontWeight: '700', fontSize: FONT_SIZE.md },
-
- // Custom Fallback Overlay Styling
- overlayContainer: {
- ...StyleSheet.absoluteFillObject,
- backgroundColor: 'rgba(15, 15, 26, 0.95)',
- justifyContent: 'center',
- alignItems: 'center',
- padding: SPACING.lg,
- zIndex: 50,
- },
- overlayCard: {
- backgroundColor: COLORS.bgSecondary,
- borderRadius: BORDER_RADIUS.lg,
- padding: SPACING.xl,
- width: '100%',
- maxWidth: 340,
- alignItems: 'center',
- borderWidth: 1,
- borderColor: COLORS.border,
- ...SHADOWS.card,
- },
- overlayIcon: {
- fontSize: 48,
- marginBottom: SPACING.md,
- },
- overlayTitle: {
- fontSize: FONT_SIZE.lg,
- fontWeight: '700',
- color: COLORS.textPrimary,
- marginBottom: SPACING.sm,
- textAlign: 'center',
- },
- overlayDescription: {
- fontSize: FONT_SIZE.sm,
- color: COLORS.textSecondary,
- textAlign: 'center',
- marginBottom: SPACING.lg,
- lineHeight: 20,
- },
- overlayPrimaryButton: {
- backgroundColor: COLORS.primary,
- borderRadius: BORDER_RADIUS.md,
- paddingVertical: SPACING.md,
- width: '100%',
- alignItems: 'center',
- marginBottom: SPACING.sm,
- ...SHADOWS.button,
- },
- overlayPrimaryButtonText: {
- color: COLORS.white,
- fontWeight: '700',
- fontSize: FONT_SIZE.md,
- },
- overlaySecondaryButton: {
- backgroundColor: COLORS.bgElevated,
- borderRadius: BORDER_RADIUS.md,
- paddingVertical: SPACING.md,
- width: '100%',
- alignItems: 'center',
- marginBottom: SPACING.lg,
- borderWidth: 1,
- borderColor: COLORS.border,
- },
- overlaySecondaryButtonText: {
- color: COLORS.textPrimary,
- fontWeight: '600',
- fontSize: FONT_SIZE.md,
- },
- overlayRowButtons: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- width: '100%',
- paddingHorizontal: SPACING.sm,
- },
- overlayTextButton: {
- paddingVertical: SPACING.sm,
- paddingHorizontal: SPACING.md,
- },
- overlayTextButtonText: {
- color: COLORS.textMuted,
- fontSize: FONT_SIZE.sm,
- fontWeight: '600',
},
+ doneButtonText: { color: COLORS.white, fontWeight: '700', fontSize: FONT_SIZE.md },
});
diff --git a/apps/mobile/src/services/api.ts b/apps/mobile/src/services/api.ts
deleted file mode 100644
index 70daf195..00000000
--- a/apps/mobile/src/services/api.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { API_BASE_URL } from '../config';
-
-type RequestOptions = {
- method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
- body?: unknown;
- token?: string | null;
- onUnauthorized?: () => void;
-};
-
-export async function apiRequest(
- path: string,
- { method = 'GET', body, token, onUnauthorized }: RequestOptions = {}
-): Promise {
- const headers: Record = {
- 'Content-Type': 'application/json',
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
- };
-
- const res = await fetch(`${API_BASE_URL}${path}`, {
- method,
- headers,
- ...(body ? { body: JSON.stringify(body) } : {}),
- });
-
- if (res.status === 401 || res.status === 403) {
- onUnauthorized?.();
- throw new Error('Unauthorized');
- }
-
- if (!res.ok) {
- const err = await res.json().catch(() => ({}));
- throw new Error((err as any)?.message ?? `Request failed: ${res.status}`);
- }
-
- // Some endpoints may return empty responses
- const text = await res.text();
- if (!text) return (null as unknown) as T;
- return JSON.parse(text) as T;
-}
-
-export const get = (path: string, token?: string | null) => apiRequest(path, { method: 'GET', token });
-export const post = (path: string, body?: unknown, token?: string | null) => apiRequest(path, { method: 'POST', body, token });
-export const put = (path: string, body?: unknown, token?: string | null) => apiRequest(path, { method: 'PUT', body, token });
-export const del = (path: string, body?: unknown, token?: string | null) => apiRequest(path, { method: 'DELETE', body, token });
-
-export default { apiRequest, get, post, put, del };
diff --git a/apps/mobile/src/types/index.ts b/apps/mobile/src/types/index.ts
deleted file mode 100644
index c815a5d0..00000000
--- a/apps/mobile/src/types/index.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-// ── Centralized Mobile Type Definitions ───────────────────────────────────────
-// Re-exports shared types and defines mobile-only types to eliminate duplicate
-// interface declarations across screens (was duplicated in 4+ files).
-
-export type {
- User,
- PlatformLink,
- Card,
- PublicProfile,
- PublicCard,
- FollowStatus,
- FollowResult,
- AuthResponse,
- CardView,
- AnalyticsOverview,
- ConnectedPlatform,
- FollowLog,
- OAuthTokenInfo,
- CreateLinkPayload,
- UpdateProfilePayload,
- CreateCardPayload,
- UpdateCardPayload,
- ReorderLinksPayload,
-} from '@devcard/shared';
-
-export type { PlatformDef, FollowStrategy } from '@devcard/shared';
-
-// ── Mobile-only Types ─────────────────────────────────────────────────────────
-
-export interface SavedContact {
- username: string;
- displayName: string;
- avatarUrl: string | null;
- accentColor: string;
- bio: string | null;
- role: string | null;
- company: string | null;
- metAt: string | null;
- note: string | null;
- savedAt: string;
-}
-
-export interface EventSummary {
- id: string;
- name: string;
- slug: string;
- location: string;
- description: string | null;
- startDate: string;
- endDate: string;
- attendeesCount: number;
-}
-
-export interface EventDetail extends EventSummary {
- organizerId: string;
- createdAt: string;
-}
-
-export interface EventAttendee {
- id: string;
- username: string;
- displayName: string;
- bio: string | null;
- pronouns: string | null;
- company: string | null;
- avatarUrl: string | null;
- accentColor: string;
-}
-
-export interface TeamSummary {
- id: string;
- name: string;
- slug: string;
- description: string | null;
- avatarUrl: string | null;
- ownerId: string;
- createdAt: string;
- updatedAt: string | null;
- members: TeamMember[];
-}
-
-export interface TeamMember {
- username: string;
- displayName: string;
- bio: string | null;
- pronouns: string | null;
- role: string | null;
- company: string | null;
- avatarUrl: string | null;
- accentColor: string;
- teamRole: 'OWNER' | 'ADMIN' | 'MEMBER';
- joinedAt: string;
-}
-
-export type FollowState = Record;
-
-export interface NfcPayload {
- type: 'URI';
- payload: string;
-}
diff --git a/apps/web/src/app.css b/apps/web/src/app.css
index 0f9e8bb0..bb09fef9 100644
--- a/apps/web/src/app.css
+++ b/apps/web/src/app.css
@@ -3,51 +3,48 @@
:root {
/* Primary Palette */
--primary: #6366f1;
- --primary-glow: rgba(99, 102, 241, 0.4);
+ --primary-glow: rgba(99, 102, 241, 0.5);
--accent: #a855f7;
- --accent-glow: rgba(168, 85, 247, 0.35);
-
+ --accent-glow: rgba(168, 85, 247, 0.4);
+
/* Backgrounds */
--bg-primary: #ffffff;
--bg-secondary: #f8fafc;
- --bg-page: #eef2ff;
- --bg-glass: rgba(255, 255, 255, 0.38);
+ --bg-glass: rgba(255, 255, 255, 0.7);
--bg-card: #ffffff;
-
+
/* Text */
--text-primary: #0f172a;
--text-secondary: #475569;
- --text-muted: #64748b;
-
+ --text-muted: #94a3b8;
+
/* Effects */
- --border: rgba(226, 232, 240, 0.9);
- --border-glass: rgba(99, 102, 241, 0.25);
+ --border: rgba(226, 232, 240, 0.8);
+ --border-glass: rgba(255, 255, 255, 0.3);
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
- --shadow-md: 0 6px 18px -8px rgb(0 0 0 / 0.12), 0 4px 14px -12px rgb(0 0 0 / 0.08);
- --shadow-lg: 0 12px 24px -10px rgb(0 0 0 / 0.15), 0 6px 12px -14px rgb(0 0 0 / 0.08);
- --shadow-nav: 0 8px 32px -8px rgba(99, 102, 241, 0.18), 0 2px 8px 0 rgba(99, 102, 241, 0.08);
-
- --radius: 14px;
- --radius-lg: 26px;
- --radius-xl: 34px;
-
- --theme-transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
+
+ --radius: 12px;
+ --radius-lg: 20px;
+ --radius-xl: 32px;
+
+ --theme-transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
html.dark {
--bg-primary: #020617;
--bg-secondary: #0f172a;
- --bg-page: #050b18;
- --bg-glass: rgba(15, 23, 42, 0.72);
+ --bg-glass: rgba(15, 23, 42, 0.6);
--bg-card: #0f172a;
-
+
--text-primary: #f8fafc;
--text-secondary: #cbd5e1;
--text-muted: #64748b;
-
- --border: rgba(30, 41, 59, 0.85);
- --border-glass: rgba(255, 255, 255, 0.12);
- --shadow-nav: 0 4px 24px -6px rgba(0, 0, 0, 0.45), 0 1px 4px 0 rgba(0, 0, 0, 0.25);
+
+ --border: rgba(30, 41, 59, 0.8);
+ --border-glass: rgba(255, 255, 255, 0.1);
}
* {
@@ -58,11 +55,7 @@ html.dark {
body {
font-family: 'Inter', sans-serif;
- background:
- radial-gradient(ellipse at 60% -10%, rgba(99, 102, 241, 0.28) 0%, transparent 55%),
- radial-gradient(ellipse at -10% 80%, rgba(168, 85, 247, 0.18) 0%, transparent 45%),
- radial-gradient(ellipse at 100% 60%, rgba(99, 102, 241, 0.12) 0%, transparent 40%),
- var(--bg-page);
+ background-color: var(--bg-primary);
color: var(--text-primary);
transition: var(--theme-transition);
-webkit-font-smoothing: antialiased;
@@ -73,7 +66,7 @@ body {
h1, h2, h3, h4, h5, h6 {
font-family: 'Outfit', sans-serif;
font-weight: 700;
- line-height: 1.15;
+ line-height: 1.1;
}
a {
@@ -82,22 +75,11 @@ a {
transition: var(--theme-transition);
}
-button,
-.btn-primary,
-.btn-secondary {
- transition: transform 0.24s ease, box-shadow 0.24s ease, background-color 0.24s ease, border-color 0.24s ease, color 0.24s ease;
-}
-
-button {
- font: inherit;
-}
-
.glass {
background: var(--bg-glass);
- backdrop-filter: blur(18px);
- -webkit-backdrop-filter: blur(18px);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
border: 1px solid var(--border-glass);
- box-shadow: var(--shadow-nav);
}
.gradient-text {
@@ -108,99 +90,17 @@ button {
}
.btn-primary {
- display: inline-flex;
- align-items: center;
- justify-content: center;
background: linear-gradient(135deg, var(--primary), var(--accent));
color: white;
- padding: 0.95rem 1.85rem;
- border-radius: calc(var(--radius) * 1.2);
- font-weight: 700;
- box-shadow: 0 18px 35px -18px rgba(99, 102, 241, 0.9);
+ padding: 0.8rem 1.6rem;
+ border-radius: var(--radius);
+ font-weight: 600;
+ box-shadow: 0 4px 15px var(--primary-glow);
border: none;
cursor: pointer;
}
.btn-primary:hover {
transform: translateY(-2px);
- box-shadow: 0 22px 40px -16px rgba(99, 102, 241, 0.9);
-}
-
-.btn-primary:focus-visible {
- outline: 3px solid rgba(99, 102, 241, 0.35);
- outline-offset: 3px;
-}
-
-.btn-secondary {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 0.85rem 1.75rem;
- border-radius: calc(var(--radius) * 1.2);
- font-weight: 700;
- border: 1px solid rgba(255, 255, 255, 0.14);
- background: rgba(255, 255, 255, 0.08);
- color: var(--text-primary);
- cursor: pointer;
+ box-shadow: 0 6px 20px var(--primary-glow);
}
-
-.btn-secondary:hover {
- background: rgba(255, 255, 255, 0.14);
- border-color: rgba(99, 102, 241, 0.45);
-}
-
-.btn-secondary:focus-visible {
- outline: 3px solid rgba(99, 102, 241, 0.18);
- outline-offset: 3px;
-}
-
-/* ---------- Custom themed scrollbar (issue #151) ---------- */
-
-/* Firefox */
-html {
- scrollbar-width: thin;
- scrollbar-color: var(--primary) var(--bg-secondary);
-}
-
-/* WebKit (Chromium, Safari, Edge) */
-::-webkit-scrollbar {
- width: 10px;
- height: 10px;
-}
-
-::-webkit-scrollbar-track {
- background: var(--bg-secondary);
- border-radius: 999px;
-}
-
-::-webkit-scrollbar-thumb {
- background: linear-gradient(135deg, var(--primary), var(--accent));
- border-radius: 999px;
- border: 2px solid var(--bg-secondary);
- background-clip: padding-box;
- transition: background 0.2s ease, box-shadow 0.2s ease;
-}
-
-::-webkit-scrollbar-thumb:hover {
- background: linear-gradient(135deg, var(--accent), var(--primary));
- box-shadow: 0 0 8px var(--primary-glow);
-}
-
-::-webkit-scrollbar-corner {
- background: var(--bg-secondary);
-}
-
-@media (prefers-reduced-motion: reduce) {
- * {
- animation-duration: 0.01ms !important;
- animation-iteration-count: 1 !important;
- transition-duration: 0.01ms !important;
- scroll-behavior: auto !important;
- }
-}
-
-/* Light mode btn-secondary fix */
-:root:not(.dark) .btn-secondary {
- border-color: var(--border);
- background: rgba(0, 0, 0, 0.04);
-}
\ No newline at end of file
diff --git a/apps/web/src/app.html b/apps/web/src/app.html
index 666257e4..f273cc58 100644
--- a/apps/web/src/app.html
+++ b/apps/web/src/app.html
@@ -3,11 +3,6 @@
-
-
-
-
-
%sveltekit.head%
diff --git a/apps/web/src/lib/apiClient.ts b/apps/web/src/lib/apiClient.ts
deleted file mode 100644
index dbaad43f..00000000
--- a/apps/web/src/lib/apiClient.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-const API_BASE_URL = import.meta.env.PUBLIC_API_URL ?? 'http://localhost:3000';
-
-type RequestOptions = {
- method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
- body?: unknown;
- token?: string | null;
- onUnauthorized?: () => void;
-};
-
-export async function apiRequest(
- endpoint: string,
- { method = 'GET', body, token, onUnauthorized }: RequestOptions = {}
-): Promise {
- const headers: Record = {
- 'Content-Type': 'application/json',
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
- };
-
- const response = await fetch(`${API_BASE_URL}${endpoint}`, {
- method,
- headers,
- ...(body ? { body: JSON.stringify(body) } : {}),
- });
-
- if (response.status === 401 || response.status === 403) {
- onUnauthorized?.();
- throw new Error('Unauthorized');
- }
-
- if (!response.ok) {
- const error = await response.json().catch(() => ({}));
- throw new Error((error as any)?.message ?? `Request failed: ${response.status}`);
- }
-
- return response.json() as Promise;
-}
\ No newline at end of file
diff --git a/apps/web/src/routes/+page.svelte b/apps/web/src/routes/+page.svelte
index efaa65e5..512f9053 100644
--- a/apps/web/src/routes/+page.svelte
+++ b/apps/web/src/routes/+page.svelte
@@ -1,3 +1,92 @@
+
+
+
+ DevCard — One Tap. Every Profile. Every Platform.
+
+
+
+
+
+
+
+
+
⚡ DevCard
+
+ {theme === 'light' ? '🌙' : '☀️'}
+
+
+
+
+
+ GSSoC'26 Edition
+ One Tap. Every Profile. Every Platform.
+
+ The open-source standard for developer networking. Put all your profiles—GitHub, LinkedIn, LeetCode, and more—into a single, high-impact digital card.
+
+
+
+
+
+
+
🔗
+
Unified Identity
+
Combine your fragmented online presence into a cohesive professional identity.
+
+
+
⚡
+
Instant Follow
+
Integrated APIs allow followers to connect with you instantly across platforms.
+
+
+
🔒
+
Private by Design
+
No tracking, no data selling. Your information stays where it belongs: with you.
+
+
+
+
+
+
\ No newline at end of file
+
diff --git a/apps/web/src/routes/devcard/[id]/+page.server.ts b/apps/web/src/routes/devcard/[id]/+page.server.ts
index a93fbc75..adc98179 100644
--- a/apps/web/src/routes/devcard/[id]/+page.server.ts
+++ b/apps/web/src/routes/devcard/[id]/+page.server.ts
@@ -1,28 +1,17 @@
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
-const API_BASE = process.env.BACKEND_URL || 'http://localhost:3000';
-
export const load: PageServerLoad = async ({ params, fetch }) => {
const { id } = params;
- try {
- const res = await fetch(`${API_BASE}/api/u/card/${id}`);
-
- if (res.status === 404) {
- throw error(404, 'Card not found');
- }
+ // Use internal fetch to reach the backend
+ // In production, this would be the actual API URL
+ const res = await fetch(`http://localhost:3000/api/u/card/${id}`);
- if (!res.ok) {
- throw error(500, 'Failed to load card');
- }
-
- const card = await res.json();
- return { card };
- } catch (error) {
- if (error && typeof error === 'object' && 'status' in error) {
- throw error;
- }
- throw error(500, 'Failed to connect to backend');
+ if (!res.ok) {
+ throw error(404, 'Card not found');
}
+
+ const card = await res.json();
+ return { card };
};
diff --git a/apps/web/src/routes/devcard/[id]/+page.svelte b/apps/web/src/routes/devcard/[id]/+page.svelte
index 7423f7ba..a38073fe 100644
--- a/apps/web/src/routes/devcard/[id]/+page.svelte
+++ b/apps/web/src/routes/devcard/[id]/+page.svelte
@@ -104,7 +104,7 @@
diff --git a/apps/web/src/routes/u/[username]/+page.svelte b/apps/web/src/routes/u/[username]/+page.svelte
index 50cb4226..d75e485c 100644
--- a/apps/web/src/routes/u/[username]/+page.svelte
+++ b/apps/web/src/routes/u/[username]/+page.svelte
@@ -15,48 +15,9 @@
};
let mounted = $state(false);
- let copyMessage = $state('');
- let copyStatus = $state<'success' | 'error'>('success');
- let copyMessageTimeout: ReturnType;
-
onMount(() => {
mounted = true;
-
- return () => {
- if (copyMessageTimeout) {
- clearTimeout(copyMessageTimeout);
- }
- };
});
-
- function showCopyMessage(message: string, status: 'success' | 'error') {
- copyMessage = message;
- copyStatus = status;
-
- if (copyMessageTimeout) {
- clearTimeout(copyMessageTimeout);
- }
-
- clearTimeout(copyTimeout);
-
- copyTimeout = setTimeout(() => {
- copyMessage = '';
- }, 3000);
- }
-
- async function copyProfileUrl() {
- if (!navigator.clipboard?.writeText) {
- showCopyMessage('Clipboard API unavailable. Copy the URL from your address bar.', 'error');
- return;
- }
-
- try {
- await navigator.clipboard.writeText(window.location.href);
- showCopyMessage('Profile link copied.', 'success');
- } catch {
- showCopyMessage('Could not copy link. Copy the URL from your address bar.', 'error');
- }
- }
@@ -135,17 +96,7 @@
{/if}
@@ -159,7 +110,7 @@
bottom: 0;
background: radial-gradient(circle at 50% 0%, var(--accent), transparent 50%),
#020617;
- opacity: 0.18;
+ opacity: 0.15;
z-index: -1;
}
@@ -168,10 +119,10 @@
display: flex;
flex-direction: column;
align-items: center;
- padding: clamp(2rem, 6vw, 5rem) 1.25rem 3rem;
+ padding: 4rem 1.5rem;
opacity: 0;
- transform: translateY(22px);
- transition: opacity 0.65s ease, transform 0.65s ease;
+ transform: translateY(20px);
+ transition: all 0.8s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.profile-container.loaded {
@@ -181,67 +132,65 @@
.profile-card {
width: 100%;
- max-width: 540px;
+ max-width: 480px;
border-radius: var(--radius-xl);
- padding: 2.5rem 2rem;
- box-shadow: 0 26px 60px -20px rgba(0, 0, 0, 0.55);
+ padding: 3rem 2rem;
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
position: relative;
overflow: hidden;
- border: 1px solid rgba(255, 255, 255, 0.08);
- background: rgba(15, 23, 42, 0.96);
}
.profile-header {
text-align: center;
- margin-bottom: 2.5rem;
+ margin-bottom: 3rem;
}
.avatar-wrapper {
position: relative;
- width: 120px;
- height: 120px;
- margin: 0 auto 1.75rem;
+ width: 110px;
+ height: 110px;
+ margin: 0 auto 1.5rem;
}
.avatar {
width: 100%;
height: 100%;
- border-radius: 32% 68% 63% 37% / 34% 36% 64% 66%;
+ border-radius: 35% 65% 70% 30% / 30% 30% 70% 70%;
object-fit: cover;
- border: 3px solid rgba(255, 255, 255, 0.18);
+ border: 3px solid white;
position: relative;
z-index: 2;
+ animation: morph 8s ease-in-out infinite;
}
- .avatar-placeholder {
- width: 100%;
- height: 100%;
- border-radius: 32% 68% 63% 37% / 34% 36% 64% 66%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 3rem;
- font-weight: 800;
- color: white;
+ @keyframes morph {
+ 0%, 100% { border-radius: 35% 65% 70% 30% / 30% 30% 70% 70%; }
+ 50% { border-radius: 65% 35% 30% 70% / 70% 70% 30% 30%; }
+ }
+
+ .avatar-glow {
+ position: absolute;
+ top: 0; left: 0; right: 0; bottom: 0;
+ filter: blur(20px);
+ opacity: 0.4;
+ z-index: 1;
+ border-radius: 50%;
}
.display-name {
- font-size: clamp(2rem, 4vw, 2.5rem);
+ font-size: 2.25rem;
font-weight: 800;
- letter-spacing: -0.5px;
- margin-bottom: 0.75rem;
+ letter-spacing: -1px;
+ margin-bottom: 0.5rem;
}
.role-badge {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 0.45rem 1rem;
- background: rgba(255, 255, 255, 0.08);
- border: 1px solid rgba(255, 255, 255, 0.12);
- border-radius: 999px;
- font-size: 0.9rem;
- font-weight: 700;
+ display: inline-block;
+ padding: 0.4rem 1rem;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 100px;
+ font-size: 0.85rem;
+ font-weight: 600;
color: var(--text-secondary);
margin-bottom: 1rem;
}
@@ -249,83 +198,72 @@
.bio {
color: var(--text-secondary);
font-size: 1rem;
- line-height: 1.85;
- max-width: 640px;
+ line-height: 1.6;
+ max-width: 320px;
margin: 0 auto;
}
.links-grid {
display: flex;
flex-direction: column;
- gap: 1rem;
+ gap: 0.75rem;
}
.link-tile {
display: flex;
align-items: center;
padding: 1rem;
- border-radius: calc(var(--radius) * 1.1);
- border: 1px solid rgba(255, 255, 255, 0.1);
- background: rgba(255, 255, 255, 0.06);
- box-shadow: 0 12px 30px -18px rgba(0, 0, 0, 0.35);
- transition: transform 0.25s ease, background 0.25s ease, border-color 0.25s ease;
+ border-radius: var(--radius-lg);
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
animation: slideIn 0.5s ease-out forwards;
animation-delay: var(--delay);
opacity: 0;
}
- .link-tile:hover,
- .link-tile:focus-visible {
- background: rgba(255, 255, 255, 0.13);
- transform: translateY(-2px);
- border-color: rgba(99, 102, 241, 0.35);
- }
-
- .link-tile:focus-visible {
- outline: 3px solid rgba(99, 102, 241, 0.2);
- outline-offset: 3px;
- }
-
@keyframes slideIn {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
+ .link-tile:hover {
+ background: rgba(255, 255, 255, 0.15);
+ transform: scale(1.02) translateX(5px);
+ }
+
.tile-icon {
- width: 46px;
- height: 46px;
- border-radius: 15px;
+ width: 44px;
+ height: 44px;
+ border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 800;
- font-size: 1.1rem;
- box-shadow: 0 8px 18px -10px rgba(0,0,0,0.4);
+ font-size: 1.2rem;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
.tile-content {
flex: 1;
- margin-left: 1.1rem;
+ margin-left: 1.25rem;
}
.platform-name {
display: block;
font-weight: 700;
- font-size: 1rem;
+ font-size: 1.05rem;
}
.username {
display: block;
- font-size: 0.9rem;
+ font-size: 0.85rem;
color: var(--text-muted);
- margin-top: 0.1rem;
}
.arrow {
- opacity: 0.45;
+ opacity: 0.3;
font-size: 1.2rem;
- transition: transform 0.25s ease, opacity 0.25s ease;
+ transition: all 0.3s;
}
.link-tile:hover .arrow {
@@ -334,104 +272,48 @@
}
.card-footer {
- margin-top: 2.5rem;
- padding-top: 1.75rem;
- border-top: 1px solid rgba(255,255,255,0.08);
+ margin-top: 3rem;
+ padding-top: 2rem;
+ border-top: 1px solid rgba(255,255,255,0.05);
display: flex;
justify-content: space-between;
align-items: center;
color: var(--text-muted);
- font-size: 0.82rem;
- gap: 1rem;
- flex-wrap: wrap;
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 1px;
}
.logo-sm {
color: var(--text-secondary);
font-family: 'Outfit', sans-serif;
- font-weight: 700;
}
.get-your-own {
- margin-top: 2rem;
+ margin-top: 3rem;
text-align: center;
}
.get-your-own p {
- margin-bottom: 0.5rem;
- font-size: 0.95rem;
+ font-size: 0.9rem;
color: var(--text-muted);
+ margin-bottom: 0.5rem;
}
- .profile-actions {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: center;
- gap: 0.75rem;
- }
-
- .get-devcard-link {
- font-weight: 700;
- font-size: 1.05rem;
- }
-
- .copy-link-button {
- border: 1px solid var(--border-glass);
- border-radius: var(--radius);
- background: rgba(255, 255, 255, 0.08);
- color: var(--text-primary);
- cursor: pointer;
- font: inherit;
+ .get-your-own a {
font-weight: 700;
- padding: 0.65rem 1rem;
- transition: all 0.2s ease;
- }
-
- .copy-link-button:hover {
- background: rgba(255, 255, 255, 0.15);
- transform: translateY(-1px);
- }
-
- .copy-link-button:focus-visible {
- outline: 2px solid var(--accent);
- outline-offset: 3px;
- }
-
- .copy-message {
- min-height: 1.2rem;
- margin-top: 0.75rem;
- margin-bottom: 0;
- font-size: 0.85rem;
- }
-
- .copy-message.success {
- color: var(--text-secondary);
- }
-
- .copy-message.error {
- color: #ef4444;
+ font-size: 1.1rem;
}
.error-glass {
text-align: center;
- padding: 3rem;
+ padding: 4rem;
border-radius: var(--radius-xl);
- width: min(100%, 520px);
}
- @media (max-width: 720px) {
+ @media (max-width: 480px) {
.profile-card { padding: 2rem 1.5rem; }
- .profile-header { margin-bottom: 2rem; }
- .avatar-wrapper { width: 108px; height: 108px; margin-bottom: 1.5rem; }
- .card-footer { flex-direction: column; align-items: flex-start; }
- }
-
- @media (max-width: 520px) {
- .profile-container { padding: 2rem 1rem 2.5rem; }
- .display-name { font-size: 2rem; }
- .link-tile { padding: 0.95rem; }
- .tile-content { margin-left: 0.9rem; }
- .card-footer { text-align: left; }
+ .display-name { font-size: 1.75rem; }
}
diff --git a/docker-compose.yml b/docker-compose.yml
index cfa524ca..0786787a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -4,7 +4,7 @@ services:
container_name: devcard-postgres
restart: unless-stopped
ports:
- - '5433:5432'
+ - '5432:5432'
environment:
POSTGRES_USER: devcard
POSTGRES_PASSWORD: devcard
diff --git a/packages/shared/src/__tests__/cards.test.ts b/packages/shared/src/__tests__/cards.test.ts
deleted file mode 100644
index 0c1a6d1e..00000000
--- a/packages/shared/src/__tests__/cards.test.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { describe, it, expect } from 'vitest';
-import { validateCardPlatforms, diffCardPlatforms } from '../cards';
-
-describe('validateCardPlatforms', () => {
- it('passes with valid platforms', () => {
- const result = validateCardPlatforms(['github', 'linkedin']);
- expect(result.valid).toBe(true);
- expect(result.errors).toHaveLength(0);
- });
-
- it('fails with empty array', () => {
- const result = validateCardPlatforms([]);
- expect(result.valid).toBe(false);
- expect(result.errors).toContain('At least one platform is required.');
- });
-
- it('fails with unknown platform', () => {
- const result = validateCardPlatforms(['github', 'myspace']);
- expect(result.valid).toBe(false);
- expect(result.errors.some(e => e.includes('myspace'))).toBe(true);
- });
-
- it('fails with duplicate platforms', () => {
- const result = validateCardPlatforms(['github', 'github']);
- expect(result.valid).toBe(false);
- expect(result.errors.some(e => e.includes('Duplicate'))).toBe(true);
- });
-
- it('passes with exactly 10 platforms', () => {
- const platforms = ['github','linkedin','twitter','youtube','twitch',
- 'discord','devto','medium','dribbble','leetcode'];
- const result = validateCardPlatforms(platforms);
- expect(result.valid).toBe(true);
- });
-
- it('fails with more than 10 platforms', () => {
- const platforms = ['github','linkedin','twitter','youtube','twitch',
- 'discord','devto','medium','dribbble','leetcode','npm'];
- const result = validateCardPlatforms(platforms);
- expect(result.valid).toBe(false);
- expect(result.errors.some(e => e.includes('Maximum 10'))).toBe(true);
- });
-
- it('fails with all invalid platforms', () => {
- const result = validateCardPlatforms(['myspace', 'bebo']);
- expect(result.valid).toBe(false);
- expect(result.errors.length).toBeGreaterThanOrEqual(2);
- });
-});
-
-describe('diffCardPlatforms', () => {
- it('correctly identifies added, removed, unchanged', () => {
- const diff = diffCardPlatforms(['github', 'linkedin'], ['github', 'twitter']);
- expect(diff.added).toEqual(['twitter']);
- expect(diff.removed).toEqual(['linkedin']);
- expect(diff.unchanged).toEqual(['github']);
- });
-
- it('handles empty old card', () => {
- const diff = diffCardPlatforms([], ['github']);
- expect(diff.added).toEqual(['github']);
- expect(diff.removed).toEqual([]);
- expect(diff.unchanged).toEqual([]);
- });
-
- it('handles identical cards', () => {
- const diff = diffCardPlatforms(['github'], ['github']);
- expect(diff.added).toEqual([]);
- expect(diff.removed).toEqual([]);
- expect(diff.unchanged).toEqual(['github']);
- });
-});
\ No newline at end of file
diff --git a/packages/shared/src/__tests__/platforms-url.test.ts b/packages/shared/src/__tests__/platforms-url.test.ts
deleted file mode 100644
index cbfac373..00000000
--- a/packages/shared/src/__tests__/platforms-url.test.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { describe, it, expect } from 'vitest';
-import { getProfileUrl, getWebViewUrl, getDeepLinkUrl } from '../platforms';
-
-// ─── getProfileUrl Tests ───
-
-describe('getProfileUrl', () => {
- it('should return the correct GitHub profile URL', () => {
- expect(getProfileUrl('github', 'octocat')).toBe('https://github.com/octocat');
- });
-
- it('should return the correct LinkedIn profile URL', () => {
- expect(getProfileUrl('linkedin', 'john')).toBe('https://www.linkedin.com/in/john');
- });
-
- it('should return the correct Twitter profile URL', () => {
- expect(getProfileUrl('twitter', 'john')).toBe('https://x.com/john');
- });
-
- it('should return empty string for an unknown platform', () => {
- expect(getProfileUrl('nonexistent', 'user')).toBe('');
- });
-});
-
-// ─── getWebViewUrl Tests ───
-
-describe('getWebViewUrl', () => {
- it('should return the correct LinkedIn webview URL', () => {
- expect(getWebViewUrl('linkedin', 'john')).toBe('https://www.linkedin.com/in/john');
- });
-
- it('should return the correct Twitter webview URL', () => {
- expect(getWebViewUrl('twitter', 'john')).toBe('https://x.com/john');
- });
-
- it('should return null for platforms without a webview URL (github)', () => {
- expect(getWebViewUrl('github', 'octocat')).toBeNull();
- });
-
- it('should return null for an unknown platform', () => {
- expect(getWebViewUrl('nonexistent', 'user')).toBeNull();
- });
-});
-
-// ─── getDeepLinkUrl Tests ───
-
-describe('getDeepLinkUrl', () => {
- it('should return the correct Twitter deep link URL', () => {
- expect(getDeepLinkUrl('twitter', 'john')).toBe('twitter://user?screen_name=john');
- });
-
- it('should return the correct LinkedIn deep link URL', () => {
- expect(getDeepLinkUrl('linkedin', 'john')).toBe('linkedin://profile?id=john');
- });
-
- it('should return null for platforms without a deep link (github)', () => {
- expect(getDeepLinkUrl('github', 'octocat')).toBeNull();
- });
-
- it('should return null for an unknown platform', () => {
- expect(getDeepLinkUrl('nonexistent', 'user')).toBeNull();
- });
-});
diff --git a/packages/shared/src/cards.ts b/packages/shared/src/cards.ts
deleted file mode 100644
index d9fa5130..00000000
--- a/packages/shared/src/cards.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-export type CardValidationResult = {
- valid: boolean;
- errors: string[];
-};
-
-const PLATFORMS = new Set([
- 'github', 'linkedin', 'twitter', 'instagram', 'youtube',
- 'twitch', 'discord', 'devto', 'hashnode', 'medium',
- 'dribbble', 'behance', 'figma', 'stackoverflow', 'leetcode',
- 'codepen', 'replit', 'npm', 'producthunt', 'website',
-]);
-
-export function validateCardPlatforms(platforms: string[]): CardValidationResult {
- const errors: string[] = [];
-
- if (platforms.length === 0) {
- errors.push('At least one platform is required.');
- }
-
- if (platforms.length > 10) {
- errors.push(`Maximum 10 platforms allowed, got ${platforms.length}.`);
- }
-
- const seen = new Set();
- for (const p of platforms) {
- if (!PLATFORMS.has(p)) {
- errors.push(`Unknown platform: "${p}".`);
- }
- if (seen.has(p)) {
- errors.push(`Duplicate platform: "${p}".`);
- }
- seen.add(p);
- }
-
- return { valid: errors.length === 0, errors };
-}
-
-export function diffCardPlatforms(
- oldCard: string[],
- newCard: string[]
-): { added: string[]; removed: string[]; unchanged: string[] } {
- const oldSet = new Set(oldCard);
- const newSet = new Set(newCard);
-
- return {
- added: newCard.filter(p => !oldSet.has(p)),
- removed: oldCard.filter(p => !newSet.has(p)),
- unchanged: oldCard.filter(p => newSet.has(p)),
- };
-}
\ No newline at end of file
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 409d3e76..a57e7e77 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -1,3 +1,2 @@
export * from './platforms';
export * from './types';
-export * from './cards';
\ No newline at end of file
diff --git a/packages/shared/src/platforms.test.ts b/packages/shared/src/platforms.test.ts
index 6ce07a0b..d1ff86b7 100644
--- a/packages/shared/src/platforms.test.ts
+++ b/packages/shared/src/platforms.test.ts
@@ -74,38 +74,3 @@ describe('getWebViewUrl / getDeepLinkUrl – stackoverflow', () => {
expect(getDeepLinkUrl('stackoverflow', '1234/user')).toBeNull();
});
});
-
-// ─── validationRegex Tests ───
-
-describe('validationRegex logic', () => {
- it('should correctly validate github usernames', () => {
- const regex = PLATFORMS.github.validationRegex!;
- expect(regex.test('valid-user')).toBe(true);
- expect(regex.test('a')).toBe(true);
- expect(regex.test('user123')).toBe(true);
- // Invalid
- expect(regex.test('-invalid')).toBe(false);
- expect(regex.test('invalid-')).toBe(false);
- expect(regex.test('in--valid')).toBe(false);
- expect(regex.test('user name')).toBe(false);
- });
-
- it('should correctly validate linkedin usernames', () => {
- const regex = PLATFORMS.linkedin.validationRegex!;
- expect(regex.test('valid-user')).toBe(true);
- expect(regex.test('user123')).toBe(true);
- // Invalid
- expect(regex.test('ab')).toBe(false); // Too short
- expect(regex.test('user name')).toBe(false);
- });
-
- it('should correctly validate twitter usernames', () => {
- const regex = PLATFORMS.twitter.validationRegex!;
- expect(regex.test('valid_user')).toBe(true);
- expect(regex.test('user123')).toBe(true);
- // Invalid
- expect(regex.test('user-name')).toBe(false); // Hyphens not allowed
- expect(regex.test('this_is_a_very_long_name_indeed')).toBe(false); // Too long
- expect(regex.test('user name')).toBe(false);
- });
-});
diff --git a/packages/shared/src/platforms.ts b/packages/shared/src/platforms.ts
index 81c81ab4..a218957c 100644
--- a/packages/shared/src/platforms.ts
+++ b/packages/shared/src/platforms.ts
@@ -27,8 +27,6 @@ export interface PlatformDef {
usernamePlaceholder: string;
/** Whether the platform uses full URL instead of username */
usesFullUrl: boolean;
- /** Regex pattern to validate usernames */
- validationRegex?: RegExp;
}
// ─── Platform Registry ───
@@ -46,7 +44,6 @@ export const PLATFORMS: Record = {
oauthScopes: ['user:follow', 'read:user'],
usernamePlaceholder: 'e.g. octocat',
usesFullUrl: false,
- validationRegex: /^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$/,
},
linkedin: {
id: 'linkedin',
@@ -60,7 +57,6 @@ export const PLATFORMS: Record = {
oauthScopes: ['r_liteprofile'],
usernamePlaceholder: 'e.g. johndoe',
usesFullUrl: false,
- validationRegex: /^[a-zA-Z0-9-]{3,100}$/,
},
twitter: {
id: 'twitter',
@@ -74,7 +70,6 @@ export const PLATFORMS: Record = {
oauthScopes: [],
usernamePlaceholder: 'e.g. elonmusk',
usesFullUrl: false,
- validationRegex: /^[A-Za-z0-9_]{1,15}$/,
},
gitlab: {
id: 'gitlab',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b08a8f46..68186049 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -32,27 +32,24 @@ importers:
'@fastify/multipart':
specifier: ^9.0.0
version: 9.4.0
- '@fastify/rate-limit':
- specifier: ^10.3.0
- version: 10.3.0
'@fastify/static':
specifier: ^8.0.0
version: 8.3.0
'@prisma/client':
specifier: ^6.0.0
- version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)
+ version: 6.19.2(prisma@6.19.2(typescript@5.9.3))(typescript@5.9.3)
dotenv:
specifier: ^16.4.0
version: 16.6.1
fastify:
specifier: ^5.0.0
- version: 5.8.5
+ version: 5.8.2
fastify-plugin:
specifier: ^5.0.0
version: 5.1.0
ioredis:
specifier: ^5.4.0
- version: 5.11.0
+ version: 5.10.0
qrcode:
specifier: ^1.5.0
version: 1.5.4
@@ -62,49 +59,25 @@ importers:
devDependencies:
'@types/node':
specifier: ^22.0.0
- version: 22.19.19
+ version: 22.19.15
'@types/qrcode':
specifier: ^1.5.0
version: 1.5.6
- eslint:
- specifier: ^10.4.0
- version: 10.4.1(jiti@2.7.0)
- eslint-import-resolver-typescript:
- specifier: ^4.4.4
- version: 4.4.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3))(eslint@10.4.1(jiti@2.7.0)))(eslint@10.4.1(jiti@2.7.0))
- eslint-plugin-import-x:
- specifier: ^4.16.2
- version: 4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3))(eslint@10.4.1(jiti@2.7.0))
- eslint-plugin-n:
- specifier: ^18.0.1
- version: 18.0.1(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
- eslint-plugin-promise:
- specifier: ^7.3.0
- version: 7.3.0(eslint@10.4.1(jiti@2.7.0))
- eslint-plugin-security:
- specifier: ^4.0.0
- version: 4.0.0
- eslint-plugin-unicorn:
- specifier: ^64.0.0
- version: 64.0.0(eslint@10.4.1(jiti@2.7.0))
pino-pretty:
specifier: ^13.1.3
version: 13.1.3
prisma:
specifier: ^6.0.0
- version: 6.19.3(typescript@5.9.3)
+ version: 6.19.2(typescript@5.9.3)
tsx:
specifier: ^4.0.0
- version: 4.22.3
+ version: 4.21.0
typescript:
specifier: ^5.4.0
version: 5.9.3
- typescript-eslint:
- specifier: ^8.59.3
- version: 8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
vitest:
specifier: ^2.0.0
- version: 2.1.9(@types/node@22.19.19)(terser@5.48.0)
+ version: 2.1.9(@types/node@22.19.15)(terser@5.46.0)
apps/mobile:
dependencies:
@@ -113,77 +86,68 @@ importers:
version: link:../../packages/shared
'@gorhom/bottom-sheet':
specifier: ^5.0.5
- version: 5.2.14(@types/react-native@0.70.19)(@types/react@19.2.15)(react-native-gesture-handler@2.31.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-reanimated@3.19.5(@babel/core@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 5.2.14(@types/react-native@0.70.19)(@types/react@19.2.14)(react-native-gesture-handler@2.31.2(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-reanimated@3.19.5(@babel/core@7.29.0)(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
'@react-native-async-storage/async-storage':
specifier: ^2.1.0
- version: 2.2.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))
+ version: 2.2.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))
'@react-native/new-app-screen':
specifier: 0.84.1
- version: 0.84.1(@types/react@19.2.15)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 0.84.1(@types/react@19.2.14)(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
'@react-navigation/bottom-tabs':
specifier: ^7.0.0
- version: 7.16.2(@react-navigation/native@7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-screens@4.25.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 7.15.5(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
'@react-navigation/native':
specifier: ^7.0.0
- version: 7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
'@react-navigation/native-stack':
specifier: ^7.0.0
- version: 7.16.0(@react-navigation/native@7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-screens@4.25.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 7.14.4(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
react:
specifier: 19.2.3
version: 19.2.3
react-dom:
- specifier: ^19.2.3
- version: 19.2.6(react@19.2.3)
+ specifier: ^19.2.4
+ version: 19.2.4(react@19.2.3)
react-native:
specifier: 0.84.1
- version: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
- react-native-camera-kit:
- specifier: ^14.0.0
- version: 14.2.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
react-native-gesture-handler:
- specifier: ^2.28.0
- version: 2.31.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ specifier: ^2.20.2
+ version: 2.31.2(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
react-native-qrcode-svg:
specifier: ^6.3.0
- version: 6.3.21(react-native-svg@15.15.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 6.3.21(react-native-svg@15.15.3(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
react-native-reanimated:
- specifier: ^3.16.7
- version: 3.19.5(@babel/core@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ specifier: ^3.15.0
+ version: 3.19.5(@babel/core@7.29.0)(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
react-native-safe-area-context:
specifier: ^5.5.2
- version: 5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
react-native-screens:
specifier: ^4.0.0
- version: 4.25.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 4.24.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
react-native-svg:
specifier: ^15.0.0
- version: 15.15.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 15.15.3(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
react-native-vector-icons:
specifier: ^10.0.0
version: 10.3.0
- react-native-view-shot:
- specifier: ^5.1.0
- version: 5.1.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
react-native-web:
specifier: ^0.21.2
- version: 0.21.2(react-dom@19.2.6(react@19.2.3))(react@19.2.3)
+ version: 0.21.2(react-dom@19.2.4(react@19.2.3))(react@19.2.3)
react-native-webview:
specifier: ^13.0.0
- version: 13.16.1(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
- react-native-worklets:
- specifier: 0.5.1
- version: 0.5.1(@babel/core@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ version: 13.16.1(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
devDependencies:
'@babel/core':
specifier: ^7.25.2
- version: 7.29.7
+ version: 7.29.0
'@babel/preset-env':
specifier: ^7.25.3
- version: 7.29.7(@babel/core@7.29.7)
+ version: 7.29.0(@babel/core@7.29.0)
'@babel/runtime':
specifier: ^7.25.0
- version: 7.29.7
+ version: 7.28.6
'@react-native-community/cli':
specifier: 20.1.0
version: 20.1.0(typescript@5.9.3)
@@ -195,19 +159,19 @@ importers:
version: 20.1.0
'@react-native/babel-preset':
specifier: 0.84.1
- version: 0.84.1(@babel/core@7.29.7)
+ version: 0.84.1(@babel/core@7.29.0)
'@react-native/codegen':
specifier: 0.84.1
- version: 0.84.1(@babel/core@7.29.7)
+ version: 0.84.1(@babel/core@7.29.0)
'@react-native/eslint-config':
specifier: 0.84.1
- version: 0.84.1(eslint@8.57.1)(jest@29.7.0(@types/node@22.19.19))(prettier@2.8.8)(typescript@5.9.3)
+ version: 0.84.1(eslint@8.57.1)(jest@29.7.0(@types/node@22.19.15))(prettier@2.8.8)(typescript@5.9.3)
'@react-native/gradle-plugin':
specifier: 0.84.1
version: 0.84.1
'@react-native/metro-config':
specifier: 0.84.1
- version: 0.84.1(@babel/core@7.29.7)
+ version: 0.84.1(@babel/core@7.29.0)
'@react-native/typescript-config':
specifier: 0.84.1
version: 0.84.1
@@ -216,7 +180,7 @@ importers:
version: 29.5.14
'@types/react':
specifier: ^19.2.0
- version: 19.2.15
+ version: 19.2.14
'@types/react-native-vector-icons':
specifier: ^6.4.18
version: 6.4.18
@@ -228,7 +192,7 @@ importers:
version: 8.57.1
jest:
specifier: ^29.6.3
- version: 29.7.0(@types/node@22.19.19)
+ version: 29.7.0(@types/node@22.19.15)
prettier:
specifier: 2.8.8
version: 2.8.8
@@ -247,25 +211,25 @@ importers:
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^7.0.0
- version: 7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.56.0(@typescript-eslint/types@8.60.0))(typescript@5.9.3)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)))
+ version: 7.0.1(@sveltejs/kit@2.54.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.10)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))
'@sveltejs/kit':
specifier: ^2.50.2
- version: 2.61.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.56.0(@typescript-eslint/types@8.60.0))(typescript@5.9.3)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))
+ version: 2.54.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.10)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
'@sveltejs/vite-plugin-svelte':
specifier: ^6.2.4
- version: 6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))
+ version: 6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
svelte:
specifier: ^5.51.0
- version: 5.56.0(@typescript-eslint/types@8.60.0)
+ version: 5.53.10
svelte-check:
specifier: ^4.4.2
- version: 4.4.8(picomatch@4.0.4)(svelte@5.56.0(@typescript-eslint/types@8.60.0))(typescript@5.9.3)
+ version: 4.4.5(picomatch@4.0.3)(svelte@5.53.10)(typescript@5.9.3)
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^7.3.1
- version: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)
+ version: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
packages/shared:
devDependencies:
@@ -274,163 +238,157 @@ importers:
version: 5.9.3
vitest:
specifier: ^2.0.0
- version: 2.1.9(@types/node@22.19.19)(terser@5.48.0)
+ version: 2.1.9(@types/node@22.19.15)(terser@5.46.0)
packages:
- '@babel/code-frame@7.29.7':
- resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
+ '@babel/code-frame@7.29.0':
+ resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
engines: {node: '>=6.9.0'}
- '@babel/compat-data@7.29.7':
- resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==}
+ '@babel/compat-data@7.29.0':
+ resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==}
engines: {node: '>=6.9.0'}
- '@babel/core@7.29.7':
- resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==}
+ '@babel/core@7.29.0':
+ resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==}
engines: {node: '>=6.9.0'}
- '@babel/eslint-parser@7.29.7':
- resolution: {integrity: sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw==}
+ '@babel/eslint-parser@7.28.6':
+ resolution: {integrity: sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==}
engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
peerDependencies:
'@babel/core': ^7.11.0
eslint: ^7.5.0 || ^8.0.0 || ^9.0.0
- '@babel/generator@7.29.7':
- resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==}
+ '@babel/generator@7.29.1':
+ resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==}
engines: {node: '>=6.9.0'}
- '@babel/helper-annotate-as-pure@7.29.7':
- resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==}
+ '@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.29.7':
- resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==}
+ '@babel/helper-compilation-targets@7.28.6':
+ resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
engines: {node: '>=6.9.0'}
- '@babel/helper-create-class-features-plugin@7.29.7':
- resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==}
+ '@babel/helper-create-class-features-plugin@7.28.6':
+ resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/helper-create-regexp-features-plugin@7.29.7':
- resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==}
+ '@babel/helper-create-regexp-features-plugin@7.28.5':
+ resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/helper-define-polyfill-provider@0.6.8':
- resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==}
+ '@babel/helper-define-polyfill-provider@0.6.7':
+ resolution: {integrity: sha512-6Fqi8MtQ/PweQ9xvux65emkLQ83uB+qAVtfHkC9UodyHMIZdxNI01HjLCLUtybElp2KY2XNE0nOgyP1E1vXw9w==}
peerDependencies:
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
- '@babel/helper-globals@7.29.7':
- resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==}
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
engines: {node: '>=6.9.0'}
- '@babel/helper-member-expression-to-functions@7.29.7':
- resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==}
+ '@babel/helper-member-expression-to-functions@7.28.5':
+ resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==}
engines: {node: '>=6.9.0'}
- '@babel/helper-module-imports@7.29.7':
- resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==}
+ '@babel/helper-module-imports@7.28.6':
+ resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
engines: {node: '>=6.9.0'}
- '@babel/helper-module-transforms@7.29.7':
- resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==}
+ '@babel/helper-module-transforms@7.28.6':
+ resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/helper-optimise-call-expression@7.29.7':
- resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==}
+ '@babel/helper-optimise-call-expression@7.27.1':
+ resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==}
engines: {node: '>=6.9.0'}
- '@babel/helper-plugin-utils@7.29.7':
- resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==}
+ '@babel/helper-plugin-utils@7.28.6':
+ resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
engines: {node: '>=6.9.0'}
- '@babel/helper-remap-async-to-generator@7.29.7':
- resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==}
+ '@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.29.7':
- resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==}
+ '@babel/helper-replace-supers@7.28.6':
+ resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/helper-skip-transparent-expression-wrappers@7.29.7':
- resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==}
+ '@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.29.7':
- resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-identifier@7.29.7':
- resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-option@7.29.7':
- resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==}
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
engines: {node: '>=6.9.0'}
- '@babel/helper-wrap-function@7.29.7':
- resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==}
+ '@babel/helper-wrap-function@7.28.6':
+ resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==}
engines: {node: '>=6.9.0'}
- '@babel/helpers@7.29.7':
- resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==}
+ '@babel/helpers@7.28.6':
+ resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==}
engines: {node: '>=6.9.0'}
- '@babel/parser@7.29.7':
- resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==}
+ '@babel/parser@7.29.0':
+ resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==}
engines: {node: '>=6.0.0'}
hasBin: true
- '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7':
- resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7':
- resolution: {integrity: sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==}
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5':
+ resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7':
- resolution: {integrity: sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==}
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1':
+ resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7':
- resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==}
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1':
+ resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7':
- resolution: {integrity: sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==}
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1':
+ resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.13.0
- '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7':
- resolution: {integrity: sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==}
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6':
+ resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/plugin-proposal-export-default-from@7.29.7':
- resolution: {integrity: sha512-p+G5BNXDcy3bOXplhY4HybQ1GxH3i2Tppmdm/3epyRu2VgJJZuUlZ61MqRTg582Q7ZLBdP7fePYvsumSEkMxcQ==}
+ '@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
@@ -467,26 +425,26 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-syntax-export-default-from@7.29.7':
- resolution: {integrity: sha512-foag0BB37ROhdeIX9O8G0jX7hw0UekJc04cHMrYLOnrErsnBKqJGHJ8eDRpoCFZBvEPPygmmtw4qyU97qa4oOw==}
+ '@babel/plugin-syntax-export-default-from@7.28.6':
+ resolution: {integrity: sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-syntax-flow@7.29.7':
- resolution: {integrity: sha512-ajMX6QPcyomotqwpzhkYGxcK2i/us0rs1Qo9QvUpa+Fca0FTmqrzKrctoIYLMxcOhGZldGT/BAVkRGTWBiR8gQ==}
+ '@babel/plugin-syntax-flow@7.28.6':
+ resolution: {integrity: sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-syntax-import-assertions@7.29.7':
- resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==}
+ '@babel/plugin-syntax-import-assertions@7.28.6':
+ resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-syntax-import-attributes@7.29.7':
- resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==}
+ '@babel/plugin-syntax-import-attributes@7.28.6':
+ resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
@@ -501,8 +459,8 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-syntax-jsx@7.29.7':
- resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==}
+ '@babel/plugin-syntax-jsx@7.28.6':
+ resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
@@ -549,8 +507,8 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-syntax-typescript@7.29.7':
- resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==}
+ '@babel/plugin-syntax-typescript@7.28.6':
+ resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
@@ -561,356 +519,356 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/plugin-transform-arrow-functions@7.29.7':
- resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==}
+ '@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.29.7':
- resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==}
+ '@babel/plugin-transform-async-generator-functions@7.29.0':
+ resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-async-to-generator@7.29.7':
- resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==}
+ '@babel/plugin-transform-async-to-generator@7.28.6':
+ resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-block-scoped-functions@7.29.7':
- resolution: {integrity: sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==}
+ '@babel/plugin-transform-block-scoped-functions@7.27.1':
+ resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-block-scoping@7.29.7':
- resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==}
+ '@babel/plugin-transform-block-scoping@7.28.6':
+ resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-class-properties@7.29.7':
- resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==}
+ '@babel/plugin-transform-class-properties@7.28.6':
+ resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-class-static-block@7.29.7':
- resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==}
+ '@babel/plugin-transform-class-static-block@7.28.6':
+ resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.12.0
- '@babel/plugin-transform-classes@7.29.7':
- resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==}
+ '@babel/plugin-transform-classes@7.28.6':
+ resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-computed-properties@7.29.7':
- resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==}
+ '@babel/plugin-transform-computed-properties@7.28.6':
+ resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-destructuring@7.29.7':
- resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==}
+ '@babel/plugin-transform-destructuring@7.28.5':
+ resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-dotall-regex@7.29.7':
- resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==}
+ '@babel/plugin-transform-dotall-regex@7.28.6':
+ resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-duplicate-keys@7.29.7':
- resolution: {integrity: sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==}
+ '@babel/plugin-transform-duplicate-keys@7.27.1':
+ resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7':
- resolution: {integrity: sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==}
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0':
+ resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/plugin-transform-dynamic-import@7.29.7':
- resolution: {integrity: sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==}
+ '@babel/plugin-transform-dynamic-import@7.27.1':
+ resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-explicit-resource-management@7.29.7':
- resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==}
+ '@babel/plugin-transform-explicit-resource-management@7.28.6':
+ resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-exponentiation-operator@7.29.7':
- resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==}
+ '@babel/plugin-transform-exponentiation-operator@7.28.6':
+ resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-export-namespace-from@7.29.7':
- resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==}
+ '@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.29.7':
- resolution: {integrity: sha512-wRHeUjUjCZnMHmiO5bRgjFLcoEh7JyTdByOW11ahhwNa4V0bmeGEaIvt51yq0zQp2yWIpqfxXXPyUP6GFJZHOQ==}
+ '@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.29.7':
- resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==}
+ '@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.29.7':
- resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==}
+ '@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-json-strings@7.29.7':
- resolution: {integrity: sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==}
+ '@babel/plugin-transform-json-strings@7.28.6':
+ resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-literals@7.29.7':
- resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==}
+ '@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.29.7':
- resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==}
+ '@babel/plugin-transform-logical-assignment-operators@7.28.6':
+ resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-member-expression-literals@7.29.7':
- resolution: {integrity: sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==}
+ '@babel/plugin-transform-member-expression-literals@7.27.1':
+ resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-modules-amd@7.29.7':
- resolution: {integrity: sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==}
+ '@babel/plugin-transform-modules-amd@7.27.1':
+ resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-modules-commonjs@7.29.7':
- resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==}
+ '@babel/plugin-transform-modules-commonjs@7.28.6':
+ resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-modules-systemjs@7.29.7':
- resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==}
+ '@babel/plugin-transform-modules-systemjs@7.29.0':
+ resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-modules-umd@7.29.7':
- resolution: {integrity: sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==}
+ '@babel/plugin-transform-modules-umd@7.27.1':
+ resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-named-capturing-groups-regex@7.29.7':
- resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==}
+ '@babel/plugin-transform-named-capturing-groups-regex@7.29.0':
+ resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/plugin-transform-new-target@7.29.7':
- resolution: {integrity: sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==}
+ '@babel/plugin-transform-new-target@7.27.1':
+ resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-nullish-coalescing-operator@7.29.7':
- resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==}
+ '@babel/plugin-transform-nullish-coalescing-operator@7.28.6':
+ resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-numeric-separator@7.29.7':
- resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==}
+ '@babel/plugin-transform-numeric-separator@7.28.6':
+ resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-object-rest-spread@7.29.7':
- resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==}
+ '@babel/plugin-transform-object-rest-spread@7.28.6':
+ resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-object-super@7.29.7':
- resolution: {integrity: sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==}
+ '@babel/plugin-transform-object-super@7.27.1':
+ resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-optional-catch-binding@7.29.7':
- resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==}
+ '@babel/plugin-transform-optional-catch-binding@7.28.6':
+ resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-optional-chaining@7.29.7':
- resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==}
+ '@babel/plugin-transform-optional-chaining@7.28.6':
+ resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-parameters@7.29.7':
- resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==}
+ '@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.29.7':
- resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==}
+ '@babel/plugin-transform-private-methods@7.28.6':
+ resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-private-property-in-object@7.29.7':
- resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==}
+ '@babel/plugin-transform-private-property-in-object@7.28.6':
+ resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-property-literals@7.29.7':
- resolution: {integrity: sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==}
+ '@babel/plugin-transform-property-literals@7.27.1':
+ resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-react-display-name@7.29.7':
- resolution: {integrity: sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==}
+ '@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-self@7.29.7':
- resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==}
+ '@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.29.7':
- resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==}
+ '@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.29.7':
- resolution: {integrity: sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==}
+ '@babel/plugin-transform-react-jsx@7.28.6':
+ resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-regenerator@7.29.7':
- resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==}
+ '@babel/plugin-transform-regenerator@7.29.0':
+ resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-regexp-modifiers@7.29.7':
- resolution: {integrity: sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==}
+ '@babel/plugin-transform-regexp-modifiers@7.28.6':
+ resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/plugin-transform-reserved-words@7.29.7':
- resolution: {integrity: sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==}
+ '@babel/plugin-transform-reserved-words@7.27.1':
+ resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-runtime@7.29.7':
- resolution: {integrity: sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==}
+ '@babel/plugin-transform-runtime@7.29.0':
+ resolution: {integrity: sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-shorthand-properties@7.29.7':
- resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==}
+ '@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.29.7':
- resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==}
+ '@babel/plugin-transform-spread@7.28.6':
+ resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-sticky-regex@7.29.7':
- resolution: {integrity: sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==}
+ '@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.29.7':
- resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==}
+ '@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-typeof-symbol@7.29.7':
- resolution: {integrity: sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==}
+ '@babel/plugin-transform-typeof-symbol@7.27.1':
+ resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-typescript@7.29.7':
- resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==}
+ '@babel/plugin-transform-typescript@7.28.6':
+ resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-unicode-escapes@7.29.7':
- resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==}
+ '@babel/plugin-transform-unicode-escapes@7.27.1':
+ resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-unicode-property-regex@7.29.7':
- resolution: {integrity: sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==}
+ '@babel/plugin-transform-unicode-property-regex@7.28.6':
+ resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-unicode-regex@7.29.7':
- resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==}
+ '@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/plugin-transform-unicode-sets-regex@7.29.7':
- resolution: {integrity: sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==}
+ '@babel/plugin-transform-unicode-sets-regex@7.28.6':
+ resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/preset-env@7.29.7':
- resolution: {integrity: sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==}
+ '@babel/preset-env@7.29.0':
+ resolution: {integrity: sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
@@ -920,26 +878,26 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
- '@babel/preset-typescript@7.29.7':
- resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==}
+ '@babel/preset-typescript@7.28.5':
+ resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/runtime@7.29.7':
- resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==}
+ '@babel/runtime@7.28.6':
+ resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
engines: {node: '>=6.9.0'}
- '@babel/template@7.29.7':
- resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==}
+ '@babel/template@7.28.6':
+ resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
engines: {node: '>=6.9.0'}
- '@babel/traverse@7.29.7':
- resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==}
+ '@babel/traverse@7.29.0':
+ resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.29.7':
- resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
+ '@babel/types@7.29.0':
+ resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
'@bcoe/v8-coverage@0.2.3':
@@ -949,29 +907,14 @@ packages:
resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==}
engines: {node: '>=0.8.0'}
- '@emnapi/core@1.10.0':
- resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
-
- '@emnapi/runtime@1.10.0':
- resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
-
- '@emnapi/wasi-threads@1.2.1':
- resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
-
'@esbuild/aix-ppc64@0.21.5':
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
- '@esbuild/aix-ppc64@0.27.7':
- resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [aix]
-
- '@esbuild/aix-ppc64@0.28.0':
- resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==}
+ '@esbuild/aix-ppc64@0.27.3':
+ resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
@@ -982,14 +925,8 @@ packages:
cpu: [arm64]
os: [android]
- '@esbuild/android-arm64@0.27.7':
- resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [android]
-
- '@esbuild/android-arm64@0.28.0':
- resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==}
+ '@esbuild/android-arm64@0.27.3':
+ resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
@@ -1000,14 +937,8 @@ packages:
cpu: [arm]
os: [android]
- '@esbuild/android-arm@0.27.7':
- resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/android-arm@0.28.0':
- resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==}
+ '@esbuild/android-arm@0.27.3':
+ resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
@@ -1018,14 +949,8 @@ packages:
cpu: [x64]
os: [android]
- '@esbuild/android-x64@0.27.7':
- resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [android]
-
- '@esbuild/android-x64@0.28.0':
- resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==}
+ '@esbuild/android-x64@0.27.3':
+ resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
@@ -1036,14 +961,8 @@ packages:
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-arm64@0.27.7':
- resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [darwin]
-
- '@esbuild/darwin-arm64@0.28.0':
- resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==}
+ '@esbuild/darwin-arm64@0.27.3':
+ resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
@@ -1054,14 +973,8 @@ packages:
cpu: [x64]
os: [darwin]
- '@esbuild/darwin-x64@0.27.7':
- resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [darwin]
-
- '@esbuild/darwin-x64@0.28.0':
- resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==}
+ '@esbuild/darwin-x64@0.27.3':
+ resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
@@ -1072,14 +985,8 @@ packages:
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-arm64@0.27.7':
- resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [freebsd]
-
- '@esbuild/freebsd-arm64@0.28.0':
- resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==}
+ '@esbuild/freebsd-arm64@0.27.3':
+ resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
@@ -1090,14 +997,8 @@ packages:
cpu: [x64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.27.7':
- resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [freebsd]
-
- '@esbuild/freebsd-x64@0.28.0':
- resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==}
+ '@esbuild/freebsd-x64@0.27.3':
+ resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
@@ -1108,14 +1009,8 @@ packages:
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm64@0.27.7':
- resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [linux]
-
- '@esbuild/linux-arm64@0.28.0':
- resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==}
+ '@esbuild/linux-arm64@0.27.3':
+ resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
@@ -1126,14 +1021,8 @@ packages:
cpu: [arm]
os: [linux]
- '@esbuild/linux-arm@0.27.7':
- resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [linux]
-
- '@esbuild/linux-arm@0.28.0':
- resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==}
+ '@esbuild/linux-arm@0.27.3':
+ resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
@@ -1144,14 +1033,8 @@ packages:
cpu: [ia32]
os: [linux]
- '@esbuild/linux-ia32@0.27.7':
- resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [linux]
-
- '@esbuild/linux-ia32@0.28.0':
- resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==}
+ '@esbuild/linux-ia32@0.27.3':
+ resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
@@ -1162,14 +1045,8 @@ packages:
cpu: [loong64]
os: [linux]
- '@esbuild/linux-loong64@0.27.7':
- resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
- engines: {node: '>=18'}
- cpu: [loong64]
- os: [linux]
-
- '@esbuild/linux-loong64@0.28.0':
- resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==}
+ '@esbuild/linux-loong64@0.27.3':
+ resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
@@ -1180,14 +1057,8 @@ packages:
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-mips64el@0.27.7':
- resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
- engines: {node: '>=18'}
- cpu: [mips64el]
- os: [linux]
-
- '@esbuild/linux-mips64el@0.28.0':
- resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==}
+ '@esbuild/linux-mips64el@0.27.3':
+ resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
@@ -1198,14 +1069,8 @@ packages:
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-ppc64@0.27.7':
- resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [linux]
-
- '@esbuild/linux-ppc64@0.28.0':
- resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==}
+ '@esbuild/linux-ppc64@0.27.3':
+ resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
@@ -1216,14 +1081,8 @@ packages:
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-riscv64@0.27.7':
- resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
- engines: {node: '>=18'}
- cpu: [riscv64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.28.0':
- resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==}
+ '@esbuild/linux-riscv64@0.27.3':
+ resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
@@ -1234,14 +1093,8 @@ packages:
cpu: [s390x]
os: [linux]
- '@esbuild/linux-s390x@0.27.7':
- resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
- engines: {node: '>=18'}
- cpu: [s390x]
- os: [linux]
-
- '@esbuild/linux-s390x@0.28.0':
- resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==}
+ '@esbuild/linux-s390x@0.27.3':
+ resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
@@ -1252,26 +1105,14 @@ packages:
cpu: [x64]
os: [linux]
- '@esbuild/linux-x64@0.27.7':
- resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [linux]
-
- '@esbuild/linux-x64@0.28.0':
- resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==}
+ '@esbuild/linux-x64@0.27.3':
+ resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
- '@esbuild/netbsd-arm64@0.27.7':
- resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [netbsd]
-
- '@esbuild/netbsd-arm64@0.28.0':
- resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==}
+ '@esbuild/netbsd-arm64@0.27.3':
+ resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
@@ -1282,26 +1123,14 @@ packages:
cpu: [x64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.27.7':
- resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
+ '@esbuild/netbsd-x64@0.27.3':
+ resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.28.0':
- resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [netbsd]
-
- '@esbuild/openbsd-arm64@0.27.7':
- resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openbsd]
-
- '@esbuild/openbsd-arm64@0.28.0':
- resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==}
+ '@esbuild/openbsd-arm64@0.27.3':
+ resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
@@ -1312,26 +1141,14 @@ packages:
cpu: [x64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.27.7':
- resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [openbsd]
-
- '@esbuild/openbsd-x64@0.28.0':
- resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==}
+ '@esbuild/openbsd-x64@0.27.3':
+ resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
- '@esbuild/openharmony-arm64@0.27.7':
- resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openharmony]
-
- '@esbuild/openharmony-arm64@0.28.0':
- resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==}
+ '@esbuild/openharmony-arm64@0.27.3':
+ resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
@@ -1342,14 +1159,8 @@ packages:
cpu: [x64]
os: [sunos]
- '@esbuild/sunos-x64@0.27.7':
- resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [sunos]
-
- '@esbuild/sunos-x64@0.28.0':
- resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==}
+ '@esbuild/sunos-x64@0.27.3':
+ resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
@@ -1360,14 +1171,8 @@ packages:
cpu: [arm64]
os: [win32]
- '@esbuild/win32-arm64@0.27.7':
- resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [win32]
-
- '@esbuild/win32-arm64@0.28.0':
- resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==}
+ '@esbuild/win32-arm64@0.27.3':
+ resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
@@ -1378,14 +1183,8 @@ packages:
cpu: [ia32]
os: [win32]
- '@esbuild/win32-ia32@0.27.7':
- resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [win32]
-
- '@esbuild/win32-ia32@0.28.0':
- resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==}
+ '@esbuild/win32-ia32@0.27.3':
+ resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
@@ -1396,14 +1195,8 @@ packages:
cpu: [x64]
os: [win32]
- '@esbuild/win32-x64@0.27.7':
- resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [win32]
-
- '@esbuild/win32-x64@0.28.0':
- resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==}
+ '@esbuild/win32-x64@0.27.3':
+ resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
@@ -1418,18 +1211,6 @@ packages:
resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
- '@eslint/config-array@0.23.5':
- resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=24}
-
- '@eslint/config-helpers@0.6.0':
- resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=24}
-
- '@eslint/core@1.2.1':
- resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=24}
-
'@eslint/eslintrc@2.1.4':
resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -1438,14 +1219,6 @@ packages:
resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- '@eslint/object-schema@3.0.5':
- resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=24}
-
- '@eslint/plugin-kit@0.7.2':
- resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=24}
-
'@fastify/accept-negotiator@2.0.1':
resolution: {integrity: sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==}
@@ -1488,9 +1261,6 @@ packages:
'@fastify/proxy-addr@5.1.0':
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
- '@fastify/rate-limit@10.3.0':
- resolution: {integrity: sha512-eIGkG9XKQs0nyynatApA3EVrojHOuq4l6fhB4eeCk4PIOeadvOJz9/4w3vGI44Go17uaXOWEcPkaD8kuKm7g6Q==}
-
'@fastify/send@4.1.0':
resolution: {integrity: sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==}
@@ -1524,18 +1294,6 @@ packages:
'@hapi/topo@5.1.0':
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
- '@humanfs/core@0.19.2':
- resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
- engines: {node: '>=18.18.0'}
-
- '@humanfs/node@0.16.8':
- resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==}
- engines: {node: '>=18.18.0'}
-
- '@humanfs/types@0.15.0':
- resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==}
- engines: {node: '>=18.18.0'}
-
'@humanwhocodes/config-array@0.13.0':
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
engines: {node: '>=10.10.0'}
@@ -1549,12 +1307,8 @@ packages:
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
deprecated: Use @eslint/object-schema instead
- '@humanwhocodes/retry@0.4.3':
- resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
- engines: {node: '>=18.18'}
-
- '@ioredis/commands@1.10.0':
- resolution: {integrity: sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==}
+ '@ioredis/commands@1.5.1':
+ resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==}
'@isaacs/cliui@9.0.0':
resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==}
@@ -1568,8 +1322,8 @@ packages:
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
engines: {node: '>=8'}
- '@istanbuljs/schema@0.1.6':
- resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==}
+ '@istanbuljs/schema@0.1.3':
+ resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
engines: {node: '>=8'}
'@jest/console@29.7.0':
@@ -1665,12 +1419,6 @@ packages:
resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==}
engines: {node: '>=8'}
- '@napi-rs/wasm-runtime@1.1.4':
- resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==}
- peerDependencies:
- '@emnapi/core': ^1.7.1
- '@emnapi/runtime': ^1.7.1
-
'@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==}
@@ -1686,17 +1434,14 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
- '@package-json/types@0.0.12':
- resolution: {integrity: sha512-uu43FGU34B5VM9mCNjXCwLaGHYjXdNincqKLaraaCW+7S2+SmiBg1Nv8bPnmschrIfZmfKNY9f3fC376MRrObw==}
-
'@pinojs/redact@0.4.0':
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
- '@prisma/client@6.19.3':
- resolution: {integrity: sha512-mKq3jQFhjvko5LTJFHGilsuQs+W+T3Gm451NzuTDGQxwCzwXHYnIu2zGkRoW+Exq3Rob7yp2MfzSrdIiZVhrBg==}
+ '@prisma/client@6.19.2':
+ resolution: {integrity: sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==}
engines: {node: '>=18.18'}
peerDependencies:
prisma: '*'
@@ -1707,23 +1452,23 @@ packages:
typescript:
optional: true
- '@prisma/config@6.19.3':
- resolution: {integrity: sha512-CBPT44BjlQxEt8kiMEauji2WHTDoVBOKl7UlewXmUgBPnr/oPRZC3psci5chJnYmH0ivEIog2OU9PGWoki3DLQ==}
+ '@prisma/config@6.19.2':
+ resolution: {integrity: sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==}
- '@prisma/debug@6.19.3':
- resolution: {integrity: sha512-ljkJ+SgpXNktLG0Q/n4JGYCkKf0f8oYLyjImS2I8e2q2WCfdRRtWER062ZV/ixaNP2M2VKlWXVJiGzZaUgbKZw==}
+ '@prisma/debug@6.19.2':
+ resolution: {integrity: sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==}
'@prisma/engines-version@7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7':
resolution: {integrity: sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==}
- '@prisma/engines@6.19.3':
- resolution: {integrity: sha512-RSYxtlYFl5pJ8ZePgMv0lZ9IzVCOdTPOegrs2qcbAEFrBI1G33h6wyC9kjQvo0DnYEhEVY0X4LsuFHXLKQk88g==}
+ '@prisma/engines@6.19.2':
+ resolution: {integrity: sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==}
- '@prisma/fetch-engine@6.19.3':
- resolution: {integrity: sha512-tKtl/qco9Nt7LU5iKhpultD8O4vMCZcU2CHjNTnRrL1QvSUr5W/GcyFPjNL87GtRrwBc7ubXXD9xy4EvLvt8JA==}
+ '@prisma/fetch-engine@6.19.2':
+ resolution: {integrity: sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==}
- '@prisma/get-platform@6.19.3':
- resolution: {integrity: sha512-xFj1VcJ1N3MKooOQAGO0W5tsd0W2QzIvW7DD7c/8H14Zmp4jseeWAITm+w2LLoLrlhoHdPPh0NMZ8mfL6puoHA==}
+ '@prisma/get-platform@6.19.2':
+ resolution: {integrity: sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==}
'@react-native-async-storage/async-storage@2.2.0':
resolution: {integrity: sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==}
@@ -1872,25 +1617,25 @@ packages:
'@types/react':
optional: true
- '@react-navigation/bottom-tabs@7.16.2':
- resolution: {integrity: sha512-Lbp++BGMc7SQXnyKuO/JrQJIhFH0zyB5v4kIEbnzDJLJfgubd5hoSe+QfCqy4YHfLA4phC4Xf/6Q2Ic8x7datQ==}
+ '@react-navigation/bottom-tabs@7.15.5':
+ resolution: {integrity: sha512-wQHredlCrRmShWQ1vF4HUcLdaiJ8fUgnbaeQH7BJ7MQVQh4mdzab0IOY/4QSmUyNRB350oyu1biTycyQ5FKWMQ==}
peerDependencies:
- '@react-navigation/native': ^7.2.5
+ '@react-navigation/native': ^7.1.33
react: '>= 18.2.0'
react-native: '*'
react-native-safe-area-context: '>= 4.0.0'
react-native-screens: '>= 4.0.0'
- '@react-navigation/core@7.17.5':
- resolution: {integrity: sha512-6fDCwDTWC7DJn0SDb9DJGRlipaygHIc+2elpZBJI6Crl/2Pu+Z1d6W4jMJ2gZO6iHKf+Pe5sUiQ/uwepGprZtg==}
+ '@react-navigation/core@7.16.1':
+ resolution: {integrity: sha512-xhquoyhKdqDfiL7LuupbwYnmauUGfVFGDEJO34m26k8zSN1eDjQ2stBZcHN8ILOI1PrG9885nf8ZmfaQxPS0ww==}
peerDependencies:
react: '>= 18.2.0'
- '@react-navigation/elements@2.9.19':
- resolution: {integrity: sha512-gBUvCZuUkOGw1KpLQEZIkByUz8RYPwXeoA6mZFJy9K1mxd8GdqHDMFCIoB0lfPz9rgrHj99RvtdlGZ/ZzkZv2A==}
+ '@react-navigation/elements@2.9.10':
+ resolution: {integrity: sha512-N8tuBekzTRb0pkMHFJGvmC6Q5OisSbt6gzvw7RHMnp4NDo5auVllT12sWFaTXf8mTduaLKNSrD/NZNaOqThCBg==}
peerDependencies:
'@react-native-masked-view/masked-view': '>= 0.2.0'
- '@react-navigation/native': ^7.2.5
+ '@react-navigation/native': ^7.1.33
react: '>= 18.2.0'
react-native: '*'
react-native-safe-area-context: '>= 4.0.0'
@@ -1898,159 +1643,159 @@ packages:
'@react-native-masked-view/masked-view':
optional: true
- '@react-navigation/native-stack@7.16.0':
- resolution: {integrity: sha512-wM21rHYR2XifjDnKLrr3HeHUeGsWQZJRwPqEzy1Vp/a9k3ieiwTGpmpDItD/jtERH9qkYESwDPO6oEtrVBEpQg==}
+ '@react-navigation/native-stack@7.14.4':
+ resolution: {integrity: sha512-HFEnM5Q7JY3FmmiolD/zvgY+9sxZAyVGPZJoz7BdTvJmi1VHOdplf24YiH45mqeitlGnaOlvNT55rH4abHJ5eA==}
peerDependencies:
- '@react-navigation/native': ^7.2.5
+ '@react-navigation/native': ^7.1.33
react: '>= 18.2.0'
react-native: '*'
react-native-safe-area-context: '>= 4.0.0'
react-native-screens: '>= 4.0.0'
- '@react-navigation/native@7.2.5':
- resolution: {integrity: sha512-01AAUQiiHQAfTabq+ZyU1/ZWq+AbB/J3v0CB0UTJSON6M6cuadWNsbChzrZUdqQvHrXvg96U5i2PQLJzK3+zpg==}
+ '@react-navigation/native@7.1.33':
+ resolution: {integrity: sha512-DpFdWGcgLajKZ1TuIvDNQsblN2QaUFWpTQaB8v7WRP9Mix8H/6TFoIrZd93pbymI2hybd6UYrD+lI408eWVcfw==}
peerDependencies:
react: '>= 18.2.0'
react-native: '*'
- '@react-navigation/routers@7.5.5':
- resolution: {integrity: sha512-9/hhMte12Kgu+pMnLfA4EWJ0OQmIEAMVMX06FPH2yGkEQSQ3JhhCN/GkcRikzQhtEi97VYYQA15umptBUShcOQ==}
+ '@react-navigation/routers@7.5.3':
+ resolution: {integrity: sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==}
- '@rollup/rollup-android-arm-eabi@4.60.4':
- resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==}
+ '@rollup/rollup-android-arm-eabi@4.59.0':
+ resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
cpu: [arm]
os: [android]
- '@rollup/rollup-android-arm64@4.60.4':
- resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==}
+ '@rollup/rollup-android-arm64@4.59.0':
+ resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==}
cpu: [arm64]
os: [android]
- '@rollup/rollup-darwin-arm64@4.60.4':
- resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==}
+ '@rollup/rollup-darwin-arm64@4.59.0':
+ resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==}
cpu: [arm64]
os: [darwin]
- '@rollup/rollup-darwin-x64@4.60.4':
- resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==}
+ '@rollup/rollup-darwin-x64@4.59.0':
+ resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==}
cpu: [x64]
os: [darwin]
- '@rollup/rollup-freebsd-arm64@4.60.4':
- resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==}
+ '@rollup/rollup-freebsd-arm64@4.59.0':
+ resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==}
cpu: [arm64]
os: [freebsd]
- '@rollup/rollup-freebsd-x64@4.60.4':
- resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==}
+ '@rollup/rollup-freebsd-x64@4.59.0':
+ resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==}
cpu: [x64]
os: [freebsd]
- '@rollup/rollup-linux-arm-gnueabihf@4.60.4':
- resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==}
+ '@rollup/rollup-linux-arm-gnueabihf@4.59.0':
+ resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
cpu: [arm]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-arm-musleabihf@4.60.4':
- resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==}
+ '@rollup/rollup-linux-arm-musleabihf@4.59.0':
+ resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
cpu: [arm]
os: [linux]
libc: [musl]
- '@rollup/rollup-linux-arm64-gnu@4.60.4':
- resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==}
+ '@rollup/rollup-linux-arm64-gnu@4.59.0':
+ resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-arm64-musl@4.60.4':
- resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==}
+ '@rollup/rollup-linux-arm64-musl@4.59.0':
+ resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@rollup/rollup-linux-loong64-gnu@4.60.4':
- resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==}
+ '@rollup/rollup-linux-loong64-gnu@4.59.0':
+ resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
cpu: [loong64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-loong64-musl@4.60.4':
- resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==}
+ '@rollup/rollup-linux-loong64-musl@4.59.0':
+ resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
cpu: [loong64]
os: [linux]
libc: [musl]
- '@rollup/rollup-linux-ppc64-gnu@4.60.4':
- resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==}
+ '@rollup/rollup-linux-ppc64-gnu@4.59.0':
+ resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-ppc64-musl@4.60.4':
- resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==}
+ '@rollup/rollup-linux-ppc64-musl@4.59.0':
+ resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
cpu: [ppc64]
os: [linux]
libc: [musl]
- '@rollup/rollup-linux-riscv64-gnu@4.60.4':
- resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==}
+ '@rollup/rollup-linux-riscv64-gnu@4.59.0':
+ resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-riscv64-musl@4.60.4':
- resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==}
+ '@rollup/rollup-linux-riscv64-musl@4.59.0':
+ resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
cpu: [riscv64]
os: [linux]
libc: [musl]
- '@rollup/rollup-linux-s390x-gnu@4.60.4':
- resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==}
+ '@rollup/rollup-linux-s390x-gnu@4.59.0':
+ resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
cpu: [s390x]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-x64-gnu@4.60.4':
- resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==}
+ '@rollup/rollup-linux-x64-gnu@4.59.0':
+ resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-x64-musl@4.60.4':
- resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==}
+ '@rollup/rollup-linux-x64-musl@4.59.0':
+ resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
cpu: [x64]
os: [linux]
libc: [musl]
- '@rollup/rollup-openbsd-x64@4.60.4':
- resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==}
+ '@rollup/rollup-openbsd-x64@4.59.0':
+ resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
cpu: [x64]
os: [openbsd]
- '@rollup/rollup-openharmony-arm64@4.60.4':
- resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==}
+ '@rollup/rollup-openharmony-arm64@4.59.0':
+ resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==}
cpu: [arm64]
os: [openharmony]
- '@rollup/rollup-win32-arm64-msvc@4.60.4':
- resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==}
+ '@rollup/rollup-win32-arm64-msvc@4.59.0':
+ resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==}
cpu: [arm64]
os: [win32]
- '@rollup/rollup-win32-ia32-msvc@4.60.4':
- resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==}
+ '@rollup/rollup-win32-ia32-msvc@4.59.0':
+ resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==}
cpu: [ia32]
os: [win32]
- '@rollup/rollup-win32-x64-gnu@4.60.4':
- resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==}
+ '@rollup/rollup-win32-x64-gnu@4.59.0':
+ resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==}
cpu: [x64]
os: [win32]
- '@rollup/rollup-win32-x64-msvc@4.60.4':
- resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==}
+ '@rollup/rollup-win32-x64-msvc@4.59.0':
+ resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==}
cpu: [x64]
os: [win32]
@@ -2075,8 +1820,8 @@ packages:
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
- '@sveltejs/acorn-typescript@1.0.10':
- resolution: {integrity: sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==}
+ '@sveltejs/acorn-typescript@1.0.9':
+ resolution: {integrity: sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==}
peerDependencies:
acorn: ^8.9.0
@@ -2085,15 +1830,15 @@ packages:
peerDependencies:
'@sveltejs/kit': ^2.0.0
- '@sveltejs/kit@2.61.1':
- resolution: {integrity: sha512-Ny8s1SR1TyQS2hD2Rvw0XKzU2Nw1eUF52dTb6T2bdcgz7wSC+Nyb5IwjWYlR4b2dvbbR5NJDiQwHg3rnNseghg==}
+ '@sveltejs/kit@2.54.0':
+ resolution: {integrity: sha512-WDJApQ1ipZLbaC4YjqJjwYR9y7QQgTqVwEObgNZ8Mu/eVQJqn4Qzw9a+n7mr5xnBYiAYz9UdJOOl+aqVbfGXcA==}
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 || ^7.0.0
svelte: ^4.0.0 || ^5.0.0-next.0
- typescript: ^5.3.3 || ^6.0.0
+ typescript: ^5.3.3
vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0
peerDependenciesMeta:
'@opentelemetry/api':
@@ -2116,9 +1861,6 @@ packages:
svelte: ^5.0.0
vite: ^6.3.0 || ^7.0.0
- '@tybys/wasm-util@0.10.2':
- resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
-
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@@ -2134,15 +1876,9 @@ packages:
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
- '@types/esrecurse@4.3.1':
- resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==}
-
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
- '@types/estree@1.0.9':
- resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
-
'@types/graceful-fs@4.1.9':
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
@@ -2161,11 +1897,8 @@ packages:
'@types/jest@29.5.14':
resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==}
- '@types/json-schema@7.0.15':
- resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
-
- '@types/node@22.19.19':
- resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==}
+ '@types/node@22.19.15':
+ resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
'@types/qrcode@1.5.6':
resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==}
@@ -2179,8 +1912,8 @@ packages:
'@types/react-test-renderer@19.1.0':
resolution: {integrity: sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ==}
- '@types/react@19.2.15':
- resolution: {integrity: sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==}
+ '@types/react@19.2.14':
+ resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
'@types/stack-utils@2.0.3':
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
@@ -2194,187 +1927,67 @@ packages:
'@types/yargs@17.0.35':
resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==}
- '@typescript-eslint/eslint-plugin@8.60.0':
- resolution: {integrity: sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==}
+ '@typescript-eslint/eslint-plugin@8.57.0':
+ resolution: {integrity: sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- '@typescript-eslint/parser': ^8.60.0
+ '@typescript-eslint/parser': ^8.57.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
- typescript: '>=4.8.4 <6.1.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/parser@8.60.0':
- resolution: {integrity: sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==}
+ '@typescript-eslint/parser@8.57.0':
+ resolution: {integrity: sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
- typescript: '>=4.8.4 <6.1.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/project-service@8.60.0':
- resolution: {integrity: sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==}
+ '@typescript-eslint/project-service@8.57.0':
+ resolution: {integrity: sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- typescript: '>=4.8.4 <6.1.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/scope-manager@8.60.0':
- resolution: {integrity: sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==}
+ '@typescript-eslint/scope-manager@8.57.0':
+ resolution: {integrity: sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/tsconfig-utils@8.60.0':
- resolution: {integrity: sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==}
+ '@typescript-eslint/tsconfig-utils@8.57.0':
+ resolution: {integrity: sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- typescript: '>=4.8.4 <6.1.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/type-utils@8.60.0':
- resolution: {integrity: sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==}
+ '@typescript-eslint/type-utils@8.57.0':
+ resolution: {integrity: sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
- typescript: '>=4.8.4 <6.1.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/types@8.60.0':
- resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==}
+ '@typescript-eslint/types@8.57.0':
+ resolution: {integrity: sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/typescript-estree@8.60.0':
- resolution: {integrity: sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==}
+ '@typescript-eslint/typescript-estree@8.57.0':
+ resolution: {integrity: sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- typescript: '>=4.8.4 <6.1.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/utils@8.60.0':
- resolution: {integrity: sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==}
+ '@typescript-eslint/utils@8.57.0':
+ resolution: {integrity: sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
- typescript: '>=4.8.4 <6.1.0'
+ typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/visitor-keys@8.60.0':
- resolution: {integrity: sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==}
+ '@typescript-eslint/visitor-keys@8.57.0':
+ resolution: {integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@ungap/structured-clone@1.3.1':
- resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==}
-
- '@unrs/resolver-binding-android-arm-eabi@1.12.2':
- resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==}
- cpu: [arm]
- os: [android]
-
- '@unrs/resolver-binding-android-arm64@1.12.2':
- resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==}
- cpu: [arm64]
- os: [android]
-
- '@unrs/resolver-binding-darwin-arm64@1.12.2':
- resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==}
- cpu: [arm64]
- os: [darwin]
-
- '@unrs/resolver-binding-darwin-x64@1.12.2':
- resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==}
- cpu: [x64]
- os: [darwin]
-
- '@unrs/resolver-binding-freebsd-x64@1.12.2':
- resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==}
- cpu: [x64]
- os: [freebsd]
-
- '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2':
- resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==}
- cpu: [arm]
- os: [linux]
-
- '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2':
- resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==}
- cpu: [arm]
- os: [linux]
-
- '@unrs/resolver-binding-linux-arm64-gnu@1.12.2':
- resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==}
- cpu: [arm64]
- os: [linux]
- libc: [glibc]
-
- '@unrs/resolver-binding-linux-arm64-musl@1.12.2':
- resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==}
- cpu: [arm64]
- os: [linux]
- libc: [musl]
-
- '@unrs/resolver-binding-linux-loong64-gnu@1.12.2':
- resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==}
- cpu: [loong64]
- os: [linux]
- libc: [glibc]
-
- '@unrs/resolver-binding-linux-loong64-musl@1.12.2':
- resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==}
- cpu: [loong64]
- os: [linux]
- libc: [musl]
-
- '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2':
- resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==}
- cpu: [ppc64]
- os: [linux]
- libc: [glibc]
-
- '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2':
- resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==}
- cpu: [riscv64]
- os: [linux]
- libc: [glibc]
-
- '@unrs/resolver-binding-linux-riscv64-musl@1.12.2':
- resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==}
- cpu: [riscv64]
- os: [linux]
- libc: [musl]
-
- '@unrs/resolver-binding-linux-s390x-gnu@1.12.2':
- resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==}
- cpu: [s390x]
- os: [linux]
- libc: [glibc]
-
- '@unrs/resolver-binding-linux-x64-gnu@1.12.2':
- resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==}
- cpu: [x64]
- os: [linux]
- libc: [glibc]
-
- '@unrs/resolver-binding-linux-x64-musl@1.12.2':
- resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==}
- cpu: [x64]
- os: [linux]
- libc: [musl]
-
- '@unrs/resolver-binding-openharmony-arm64@1.12.2':
- resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==}
- cpu: [arm64]
- os: [openharmony]
-
- '@unrs/resolver-binding-wasm32-wasi@1.12.2':
- resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==}
- engines: {node: '>=14.0.0'}
- cpu: [wasm32]
-
- '@unrs/resolver-binding-win32-arm64-msvc@1.12.2':
- resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==}
- cpu: [arm64]
- os: [win32]
-
- '@unrs/resolver-binding-win32-ia32-msvc@1.12.2':
- resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==}
- cpu: [ia32]
- os: [win32]
-
- '@unrs/resolver-binding-win32-x64-msvc@1.12.2':
- resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==}
- cpu: [x64]
- os: [win32]
+ '@ungap/structured-clone@1.3.0':
+ resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
'@vitest/expect@2.1.9':
resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==}
@@ -2445,11 +2058,11 @@ packages:
ajv:
optional: true
- ajv@6.15.0:
- resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
+ ajv@6.14.0:
+ resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==}
- ajv@8.20.0:
- resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
+ ajv@8.18.0:
+ resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
anser@1.4.10:
resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==}
@@ -2576,8 +2189,8 @@ packages:
resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- babel-plugin-polyfill-corejs2@0.4.17:
- resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==}
+ babel-plugin-polyfill-corejs2@0.4.16:
+ resolution: {integrity: sha512-xaVwwSfebXf0ooE11BJovZYKhFjIvQo7TsyVpETuIeH2JHv0k/T6Y5j22pPTvqYqmpkxdlPAJlyJ0tfOJAoMxw==}
peerDependencies:
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
@@ -2586,13 +2199,13 @@ packages:
peerDependencies:
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
- babel-plugin-polyfill-corejs3@0.14.2:
- resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==}
+ babel-plugin-polyfill-corejs3@0.14.1:
+ resolution: {integrity: sha512-ENp89vM9Pw4kv/koBb5N2f9bDZsR0hpf3BdPMOg/pkS3pwO4dzNnQZVXtBbeyAadgm865DmQG2jMMLqmZXvuCw==}
peerDependencies:
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
- babel-plugin-polyfill-regenerator@0.6.8:
- resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==}
+ babel-plugin-polyfill-regenerator@0.6.7:
+ resolution: {integrity: sha512-OTYbUlSwXhNgr4g6efMZgsO8//jA61P7ZbRX3iTT53VON8l+WQS8IAUEVo4a4cWknrg2W8Cj4gQhRYNCJ8GkAA==}
peerDependencies:
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
@@ -2620,15 +2233,11 @@ packages:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22}
- base64-arraybuffer@1.0.2:
- resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
- engines: {node: '>= 0.6.0'}
-
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
- baseline-browser-mapping@2.10.33:
- resolution: {integrity: sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==}
+ baseline-browser-mapping@2.10.0:
+ resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -2638,26 +2247,26 @@ packages:
bn.js@4.12.3:
resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==}
- body-parser@1.20.5:
- resolution: {integrity: sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==}
+ body-parser@1.20.4:
+ resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
- brace-expansion@1.1.15:
- resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==}
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
- brace-expansion@5.0.6:
- resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
+ brace-expansion@5.0.4:
+ resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==}
engines: {node: 18 || 20 || >=22}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
- browserslist@4.28.2:
- resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
+ browserslist@4.28.1:
+ resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -2670,10 +2279,6 @@ packages:
buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
- builtin-modules@5.2.0:
- resolution: {integrity: sha512-02yxLeyxF4dNl6SlY6/5HfRSrSdZ/sCPoxy2kZNP5dZZX8LSAD9aE2gtJIUgWrsQTiMPl3mxESyrobSwvRGisQ==}
- engines: {node: '>=18.20'}
-
bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
@@ -2694,8 +2299,8 @@ packages:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
- call-bind@1.0.9:
- resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==}
+ call-bind@1.0.8:
+ resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
engines: {node: '>= 0.4'}
call-bound@1.0.4:
@@ -2714,8 +2319,8 @@ packages:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
- caniuse-lite@1.0.30001793:
- resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==}
+ caniuse-lite@1.0.30001778:
+ resolution: {integrity: sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==}
chai@5.3.3:
resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
@@ -2725,9 +2330,6 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
- change-case@5.4.4:
- resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
-
char-regex@1.0.2:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'}
@@ -2755,23 +2357,15 @@ packages:
resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
engines: {node: '>=8'}
- ci-info@4.4.0:
- resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==}
- engines: {node: '>=8'}
-
citty@0.1.6:
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
- citty@0.2.2:
- resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==}
+ citty@0.2.1:
+ resolution: {integrity: sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==}
cjs-module-lexer@1.4.3:
resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
- clean-regexp@1.0.0:
- resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
- engines: {node: '>=4'}
-
cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'}
@@ -2798,8 +2392,8 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
- cluster-key-slot@1.1.1:
- resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==}
+ cluster-key-slot@1.1.2:
+ resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'}
co@4.6.0:
@@ -2849,10 +2443,6 @@ packages:
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
engines: {node: ^12.20.0 || >=14}
- comment-parser@1.4.7:
- resolution: {integrity: sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==}
- engines: {node: '>= 12.0.0'}
-
compressible@2.0.18:
resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
engines: {node: '>= 0.6'}
@@ -2899,8 +2489,8 @@ packages:
resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
engines: {node: '>=18'}
- core-js-compat@3.49.0:
- resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==}
+ core-js-compat@3.48.0:
+ resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==}
cosmiconfig@9.0.1:
resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==}
@@ -2926,9 +2516,6 @@ packages:
css-in-js-utils@3.1.0:
resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==}
- css-line-break@2.1.0:
- resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
-
css-select@5.2.2:
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
@@ -2958,8 +2545,8 @@ packages:
dateformat@4.6.3:
resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==}
- dayjs@1.11.21:
- resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==}
+ dayjs@1.11.20:
+ resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==}
debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
@@ -3020,8 +2607,8 @@ packages:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
- defu@6.1.7:
- resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
+ defu@6.1.4:
+ resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
denque@2.1.0:
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
@@ -3046,8 +2633,8 @@ packages:
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
engines: {node: '>=8'}
- devalue@5.8.1:
- resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==}
+ devalue@5.6.4:
+ resolution: {integrity: sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==}
diff-sequences@29.6.3:
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
@@ -3091,11 +2678,11 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
- effect@3.21.0:
- resolution: {integrity: sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==}
+ effect@3.18.4:
+ resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==}
- electron-to-chromium@1.5.364:
- resolution: {integrity: sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==}
+ electron-to-chromium@1.5.313:
+ resolution: {integrity: sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==}
emittery@0.13.1:
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
@@ -3119,10 +2706,6 @@ packages:
end-of-stream@1.4.5:
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
- enhanced-resolve@5.22.1:
- resolution: {integrity: sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==}
- engines: {node: '>=10.13.0'}
-
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@@ -3146,8 +2729,8 @@ packages:
resolution: {integrity: sha512-kNAL7hESndBCrWwS72QyV3IVOTrVmj9D062FV5BQswNL5zEdeRmz/WJFyh6Aj/plvvSOrzddkxW57HgkZcR9Fw==}
engines: {node: '>= 0.8'}
- es-abstract@1.24.2:
- resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==}
+ es-abstract@1.24.1:
+ resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
@@ -3158,15 +2741,15 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
- es-iterator-helpers@1.3.2:
- resolution: {integrity: sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==}
+ es-iterator-helpers@1.3.0:
+ resolution: {integrity: sha512-04cg8iJFDOxWcYlu0GFFWgs7vtaEPCmr5w1nrj9V3z3axu/48HCMwK6VMp45Zh3ZB+xLP1ifbJfrq86+1ypKKQ==}
engines: {node: '>= 0.4'}
es-module-lexer@1.7.0:
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
- es-object-atoms@1.1.2:
- resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
@@ -3186,13 +2769,8 @@ packages:
engines: {node: '>=12'}
hasBin: true
- esbuild@0.27.7:
- resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
- engines: {node: '>=18'}
- hasBin: true
-
- esbuild@0.28.0:
- resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==}
+ esbuild@0.27.3:
+ resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==}
engines: {node: '>=18'}
hasBin: true
@@ -3215,46 +2793,12 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
- eslint-compat-utils@0.5.1:
- resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==}
- engines: {node: '>=12'}
- peerDependencies:
- eslint: '>=6.0.0'
-
eslint-config-prettier@8.10.2:
resolution: {integrity: sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
- eslint-import-context@0.1.9:
- resolution: {integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==}
- engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
- peerDependencies:
- unrs-resolver: ^1.0.0
- peerDependenciesMeta:
- unrs-resolver:
- optional: true
-
- eslint-import-resolver-typescript@4.4.4:
- resolution: {integrity: sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==}
- engines: {node: ^16.17.0 || >=18.6.0}
- peerDependencies:
- eslint: '*'
- eslint-plugin-import: '*'
- eslint-plugin-import-x: '*'
- peerDependenciesMeta:
- eslint-plugin-import:
- optional: true
- eslint-plugin-import-x:
- optional: true
-
- eslint-plugin-es-x@7.8.0:
- resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==}
- engines: {node: ^14.18.0 || >=16.0.0}
- peerDependencies:
- eslint: '>=8'
-
eslint-plugin-eslint-comments@3.2.0:
resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==}
engines: {node: '>=6.5.0'}
@@ -3268,27 +2812,14 @@ packages:
'@babel/eslint-parser': ^7.12.0
eslint: ^8.1.0
- eslint-plugin-import-x@4.16.2:
- resolution: {integrity: sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- '@typescript-eslint/utils': ^8.56.0
- eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
- eslint-import-resolver-node: '*'
- peerDependenciesMeta:
- '@typescript-eslint/utils':
- optional: true
- eslint-import-resolver-node:
- optional: true
-
- eslint-plugin-jest@29.15.2:
- resolution: {integrity: sha512-kEN4r9RZl1xcsb4arGq89LrcVdOUFII/JSCwtTPJyv16mDwmPrcuEQwpxqZHeINvcsd7oK5O/rhdGlxFRaZwvQ==}
+ eslint-plugin-jest@29.15.0:
+ resolution: {integrity: sha512-ZCGr7vTH2WSo2hrK5oM2RULFmMruQ7W3cX7YfwoTiPfzTGTFBMmrVIz45jZHd++cGKj/kWf02li/RhTGcANJSA==}
engines: {node: ^20.12.0 || ^22.0.0 || >=24.0.0}
peerDependencies:
'@typescript-eslint/eslint-plugin': ^8.0.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
jest: '*'
- typescript: '>=4.8.4 <7.0.0'
+ typescript: '>=4.8.4 <6.0.0'
peerDependenciesMeta:
'@typescript-eslint/eslint-plugin':
optional: true
@@ -3297,30 +2828,11 @@ packages:
typescript:
optional: true
- eslint-plugin-n@18.0.1:
- resolution: {integrity: sha512-q3ARhk+eZRc7myR0KHx+R3/GJeOHF+Ir6PK95Pu2tEX8Sl/4BIpmmVLva2kPrjC2gCmn6WHlHm+3yeo6Rxhycw==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=24}
- peerDependencies:
- eslint: '>=8.57.1'
- ts-declaration-location: ^1.0.6
- typescript: '>=5.0.0'
- peerDependenciesMeta:
- ts-declaration-location:
- optional: true
- typescript:
- optional: true
-
- eslint-plugin-promise@7.3.0:
- resolution: {integrity: sha512-6uGiOR0INuujr6PEQmeSSP7GbIMJ/ebEXXiEzb/nOj68LknH5Pxzb/AbZivmr6VE6TkTE8rTjRK9zhKpK6HsRA==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
-
- eslint-plugin-react-hooks@7.1.1:
- resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==}
+ eslint-plugin-react-hooks@7.0.1:
+ resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==}
engines: {node: '>=18'}
peerDependencies:
- eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0
+ 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-native-globals@0.1.2:
resolution: {integrity: sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==}
@@ -3336,16 +2848,6 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
- eslint-plugin-security@4.0.0:
- resolution: {integrity: sha512-tfuQT8K/Li1ZxhFzyD8wPIKtlzZxqBcPr9q0jFMQ77wWAbKBVEhaMPVQRTMTvCMUDhwBe5vPVqQPwAGk/ASfxQ==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
- eslint-plugin-unicorn@64.0.0:
- resolution: {integrity: sha512-rNZwalHh8i0UfPlhNwg5BTUO1CMdKNmjqe+TgzOTZnpKoi8VBgsW7u9qCHIdpxEzZ1uwrJrPF0uRb7l//K38gA==}
- engines: {node: ^20.10.0 || >=21.0.0}
- peerDependencies:
- eslint: '>=9.38.0'
-
eslint-scope@5.1.1:
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
engines: {node: '>=8.0.0'}
@@ -3354,10 +2856,6 @@ packages:
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- eslint-scope@9.1.2:
- resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=24}
-
eslint-visitor-keys@2.1.0:
resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
engines: {node: '>=10'}
@@ -3370,16 +2868,6 @@ packages:
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
- eslint@10.4.1:
- resolution: {integrity: sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=24}
- hasBin: true
- peerDependencies:
- jiti: '*'
- peerDependenciesMeta:
- jiti:
- optional: true
-
eslint@8.57.1:
resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -3389,10 +2877,6 @@ packages:
esm-env@1.2.2:
resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
- espree@11.2.0:
- resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=24}
-
espree@9.6.1:
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -3406,13 +2890,8 @@ packages:
resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
engines: {node: '>=0.10'}
- esrap@2.2.9:
- resolution: {integrity: sha512-4KijP+NxCWthMCUC3qHbE6n4vCjqgJS1uAYKhuT/GWfFTf1Qyive2TgOjep+gzbSzRfnNyaN/UU9YmdOt8Eg0A==}
- peerDependencies:
- '@typescript-eslint/types': ^8.2.0
- peerDependenciesMeta:
- '@typescript-eslint/types':
- optional: true
+ esrap@2.2.3:
+ resolution: {integrity: sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ==}
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
@@ -3467,8 +2946,8 @@ packages:
resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==}
engines: {node: '>=8.0.0'}
- fast-copy@4.0.3:
- resolution: {integrity: sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw==}
+ fast-copy@4.0.2:
+ resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==}
fast-decode-uri-component@1.0.1:
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
@@ -3483,8 +2962,8 @@ packages:
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
- fast-json-stringify@6.4.0:
- resolution: {integrity: sha512-ibRCQ0GZKJIQ+P3Et1h0LhPgp3PMTYk0MH8O+kW3lNYsvmaQww5Nn3f1jf73Q0jR1Yz3a1CDP4/NZD3vOajWJQ==}
+ fast-json-stringify@6.3.0:
+ resolution: {integrity: sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA==}
fast-jwt@5.0.6:
resolution: {integrity: sha512-LPE7OCGUl11q3ZgW681cEU2d0d2JZ37hhJAmetCgNyW8waVaJVZXhyFF6U2so1Iim58Yc7pfxJe2P7MNetQH2g==}
@@ -3499,11 +2978,11 @@ packages:
fast-safe-stringify@2.1.1:
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
- fast-uri@3.1.2:
- resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==}
+ fast-uri@3.1.0:
+ resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
- fast-xml-parser@4.5.6:
- resolution: {integrity: sha512-Yd4vkROfJf8AuJrDIVMVmYfULKmIJszVsMv7Vo71aocsKgFxpdlpSHXSaInvyYfgw2PRuObQSW2GFpVMUjxu9A==}
+ fast-xml-parser@4.5.4:
+ resolution: {integrity: sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ==}
hasBin: true
fastfall@1.5.1:
@@ -3513,8 +2992,8 @@ packages:
fastify-plugin@5.1.0:
resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==}
- fastify@5.8.5:
- resolution: {integrity: sha512-Yqptv59pQzPgQUSIm87hMqHJmdkb1+GPxdE6vW6FRyVE9G86mt7rOghitiU4JHRaTyDUk9pfeKmDeu70lAwM4Q==}
+ fastify@5.8.2:
+ resolution: {integrity: sha512-lZmt3navvZG915IE+f7/TIVamxIwmBd+OMB+O9WBzcpIwOo6F0LTh0sluoMFk5VkrKTvvrwIaoJPkir4Z+jtAg==}
fastparallel@2.4.1:
resolution: {integrity: sha512-qUmhxPgNHmvRjZKBFUNI0oZuuH9OlSIOXmJ98lhKPxMZZ7zS/Fi0wRHOihDSz0R1YiIOjxzOY4bq65YTcdBi2Q==}
@@ -3552,10 +3031,6 @@ packages:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0}
- 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'}
@@ -3568,14 +3043,10 @@ packages:
resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
engines: {node: '>= 0.8'}
- find-my-way@9.6.0:
- resolution: {integrity: sha512-Zf4Xve4RymLl7NgaavNebZ01joJ8MfVerOG43wy7SHLO+r+K0C6d/SE0BiR7AV5V1VOCFlOP7ecdo+I4qmiHrQ==}
+ find-my-way@9.5.0:
+ resolution: {integrity: sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ==}
engines: {node: '>=20'}
- find-up-simple@1.0.1:
- resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==}
- engines: {node: '>=18'}
-
find-up@4.1.0:
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
engines: {node: '>=8'}
@@ -3588,12 +3059,8 @@ packages:
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
engines: {node: ^10.12.0 || >=12.0.0}
- flat-cache@4.0.1:
- resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
- engines: {node: '>=16'}
-
- flatted@3.4.2:
- resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
+ flatted@3.4.1:
+ resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==}
flow-enums-runtime@0.0.6:
resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==}
@@ -3664,8 +3131,8 @@ packages:
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
engines: {node: '>= 0.4'}
- get-tsconfig@4.14.0:
- resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==}
+ get-tsconfig@4.13.6:
+ resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==}
giget@2.0.0:
resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==}
@@ -3693,21 +3160,10 @@ packages:
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
engines: {node: '>=8'}
- globals@15.15.0:
- resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
- engines: {node: '>=18'}
-
- globals@17.6.0:
- resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==}
- engines: {node: '>=18'}
-
globalthis@1.0.4:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
- globrex@0.1.2:
- resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
-
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
@@ -3741,8 +3197,8 @@ packages:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
- hasown@2.0.4:
- resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
helmet@7.2.0:
@@ -3761,8 +3217,8 @@ packages:
hermes-estree@0.32.0:
resolution: {integrity: sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==}
- hermes-estree@0.35.0:
- resolution: {integrity: sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==}
+ hermes-estree@0.33.3:
+ resolution: {integrity: sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==}
hermes-parser@0.25.1:
resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==}
@@ -3770,8 +3226,8 @@ packages:
hermes-parser@0.32.0:
resolution: {integrity: sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==}
- hermes-parser@0.35.0:
- resolution: {integrity: sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==}
+ hermes-parser@0.33.3:
+ resolution: {integrity: sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==}
hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
@@ -3779,10 +3235,6 @@ packages:
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
- html2canvas@1.4.1:
- resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
- engines: {node: '>=8.0.0'}
-
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
@@ -3831,10 +3283,6 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
- indent-string@5.0.0:
- resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
- engines: {node: '>=12'}
-
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.
@@ -3852,12 +3300,12 @@ packages:
invariant@2.2.4:
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
- ioredis@5.11.0:
- resolution: {integrity: sha512-EZBErytyVovD8f6pDfG3Kb37N6Y3lmDA9NNj+4+IP13CzzHGeX+OyeRM2Um13khRzoBSzzL+5lVnCX8V2RLeMg==}
+ ioredis@5.10.0:
+ resolution: {integrity: sha512-HVBe9OFuqs+Z6n64q09PQvP1/R4Bm+30PAyyD4wIEqssh3v9L21QjCVk4kRLucMBcDokJTcLjsGeVRlq/nH6DA==}
engines: {node: '>=12.22.0'}
- ipaddr.js@2.4.0:
- resolution: {integrity: sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==}
+ ipaddr.js@2.3.0:
+ resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==}
engines: {node: '>= 10'}
is-array-buffer@3.0.5:
@@ -3882,19 +3330,12 @@ packages:
resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
engines: {node: '>= 0.4'}
- is-builtin-module@5.0.0:
- resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==}
- engines: {node: '>=18.20'}
-
- 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.2:
- resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==}
+ is-core-module@2.16.1:
+ resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
is-data-view@1.0.2:
@@ -4188,8 +3629,8 @@ packages:
node-notifier:
optional: true
- jiti@2.7.0:
- resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true
joi@17.13.3:
@@ -4259,8 +3700,8 @@ packages:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
- launch-editor@2.14.0:
- resolution: {integrity: sha512-Pj3ZOx9dD1BClS7YcSQx0An1PCF9wz4JpvbEmKvDxQtm0jxlkk5NhW8x0SBAKA/acHBKZaqdd5FFOWlXo500JA==}
+ launch-editor@2.13.1:
+ resolution: {integrity: sha512-lPSddlAAluRKJ7/cjRFoXUFzaX7q/YKI7yPHuEvSJVqoXvFnJov1/Ud87Aa4zULIbA9Nja4mSPK8l0z/7eV2wA==}
leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
@@ -4293,14 +3734,20 @@ packages:
lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+ lodash.defaults@4.2.0:
+ resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
+
+ lodash.isarguments@3.1.0:
+ resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
+
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==}
- lodash@4.18.1:
- resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==}
+ lodash@4.17.23:
+ resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
@@ -4317,8 +3764,8 @@ packages:
loupe@3.2.1:
resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==}
- lru-cache@11.5.1:
- resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==}
+ lru-cache@11.2.6:
+ resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==}
engines: {node: 20 || >=22}
lru-cache@5.1.1:
@@ -4365,61 +3812,61 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
- metro-babel-transformer@0.83.7:
- resolution: {integrity: sha512-sBqBkt6kNut/88bv+Ucvm4yqdPetbvAEsHzi3MAgJEifOSYYzX5Z5Kgw3TFOrwf/mHJTOBG2ONlaMHoyfP15TA==}
+ metro-babel-transformer@0.83.5:
+ resolution: {integrity: sha512-d9FfmgUEVejTiSb7bkQeLRGl6aeno2UpuPm3bo3rCYwxewj03ymvOn8s8vnS4fBqAPQ+cE9iQM40wh7nGXR+eA==}
engines: {node: '>=20.19.4'}
- metro-cache-key@0.83.7:
- resolution: {integrity: sha512-W1c2Nmx8MiJTJt+eWhMO08z9VKi3kZOaz99IYGdqeqDgY9j+yZjXl62rUav4Di0heZfh4/n2s722PqRL1OODeg==}
+ metro-cache-key@0.83.5:
+ resolution: {integrity: sha512-Ycl8PBajB7bhbAI7Rt0xEyiF8oJ0RWX8EKkolV1KfCUlC++V/GStMSGpPLwnnBZXZWkCC5edBPzv1Hz1Yi0Euw==}
engines: {node: '>=20.19.4'}
- metro-cache@0.83.7:
- resolution: {integrity: sha512-E9SRePXQ1Zvlj79VcOk57q7VC7rMHMFQ+jhmPHBiq+dJ0bJB5BL87lWZF6oh5X76Cci5tpDuQNaDwwuSCToEeg==}
+ metro-cache@0.83.5:
+ resolution: {integrity: sha512-oH+s4U+IfZyg8J42bne2Skc90rcuESIYf86dYittcdWQtPfcaFXWpByPyTuWk3rR1Zz3Eh5HOrcVImfEhhJLng==}
engines: {node: '>=20.19.4'}
- metro-config@0.83.7:
- resolution: {integrity: sha512-83mjWFbFOt2GeJ6pFIum5mSnc1uTsZJAtD8o4ej0s4NVsYsA7fB+pHvTfHhFrpeMONaobu2riKavkPei05Er/Q==}
+ metro-config@0.83.5:
+ resolution: {integrity: sha512-JQ/PAASXH7yczgV6OCUSRhZYME+NU8NYjI2RcaG5ga4QfQ3T/XdiLzpSb3awWZYlDCcQb36l4Vl7i0Zw7/Tf9w==}
engines: {node: '>=20.19.4'}
- metro-core@0.83.7:
- resolution: {integrity: sha512-6yn3w1wnltT6RQl7p7YES2l95ArC+mWrOssEiH8p5/DDrJS65/szf9LsC9JrBv8c5DdvSY3V3f0GRYg0Ox7hCg==}
+ metro-core@0.83.5:
+ resolution: {integrity: sha512-YcVcLCrf0ed4mdLa82Qob0VxYqfhmlRxUS8+TO4gosZo/gLwSvtdeOjc/Vt0pe/lvMNrBap9LlmvZM8FIsMgJQ==}
engines: {node: '>=20.19.4'}
- metro-file-map@0.83.7:
- resolution: {integrity: sha512-+j0F1m+FQYVAQ6syf+mwhIPV5GoFQrkInX8bppuc50IzNsZbMrp8R5H/Sx/K2daQ3YEa9F/XwkeZT8gzJfgeCw==}
+ metro-file-map@0.83.5:
+ resolution: {integrity: sha512-ZEt8s3a1cnYbn40nyCD+CsZdYSlwtFh2kFym4lo+uvfM+UMMH+r/BsrC6rbNClSrt+B7rU9T+Te/sh/NL8ZZKQ==}
engines: {node: '>=20.19.4'}
- metro-minify-terser@0.83.7:
- resolution: {integrity: sha512-MfJar2IS4tBRuLb9svwb0Gu5l9BsH+pcRm8eGcEi/wy8MzZinfinh5dFLt2nWkocnulIgtGB5NkFDdbXqMXKhQ==}
+ metro-minify-terser@0.83.5:
+ resolution: {integrity: sha512-Toe4Md1wS1PBqbvB0cFxBzKEVyyuYTUb0sgifAZh/mSvLH84qA1NAWik9sISWatzvfWf3rOGoUoO5E3f193a3Q==}
engines: {node: '>=20.19.4'}
- metro-resolver@0.83.7:
- resolution: {integrity: sha512-WSJIENlMcoSsuz66IfBHOkgfp3KJt2UW2TnEHPf1b8pIG2eEXNOVmo2+03A0H17WY2XGXWgxL0CG7FAopqgB1A==}
+ metro-resolver@0.83.5:
+ resolution: {integrity: sha512-7p3GtzVUpbAweJeCcUJihJeOQl1bDuimO5ueo1K0BUpUtR41q5EilbQ3klt16UTPPMpA+tISWBtsrqU556mY1A==}
engines: {node: '>=20.19.4'}
- metro-runtime@0.83.7:
- resolution: {integrity: sha512-9GKkJURaB2iyYoEExKnedzAHzxmKtSi+k0tsZUvMoU27tBZJElchYt7JH/Ai/XzYAI9lCAaV7u5HZSI8J5Z+wQ==}
+ metro-runtime@0.83.5:
+ resolution: {integrity: sha512-f+b3ue9AWTVlZe2Xrki6TAoFtKIqw30jwfk7GQ1rDUBQaE0ZQ+NkiMEtb9uwH7uAjJ87U7Tdx1Jg1OJqUfEVlA==}
engines: {node: '>=20.19.4'}
- metro-source-map@0.83.7:
- resolution: {integrity: sha512-JgA1h7oc1a1jydBe1GhVFsUoMYo3wLPk7oRA32rjlDsq+sP2JLt9x2p2lWbNSxTm/u8NV4VRid3hvEJgcX8tKw==}
+ metro-source-map@0.83.5:
+ resolution: {integrity: sha512-VT9bb2KO2/4tWY9Z2yeZqTUao7CicKAOps9LUg2aQzsz+04QyuXL3qgf1cLUVRjA/D6G5u1RJAlN1w9VNHtODQ==}
engines: {node: '>=20.19.4'}
- metro-symbolicate@0.83.7:
- resolution: {integrity: sha512-g4suyxw20WOHWI680c+Kq4wC/NF+Hx5pRH9afrMp+sMTxqLeKcPR1Xf4wMhsjlbvx7LbIREdke6q928jEjvJWw==}
+ metro-symbolicate@0.83.5:
+ resolution: {integrity: sha512-EMIkrjNRz/hF+p0RDdxoE60+dkaTLPN3vaaGkFmX5lvFdO6HPfHA/Ywznzkev+za0VhPQ5KSdz49/MALBRteHA==}
engines: {node: '>=20.19.4'}
hasBin: true
- metro-transform-plugins@0.83.7:
- resolution: {integrity: sha512-Ss0FpBiZDjX2kwhukMDl5sNdYK8T/06IPqxNE4H6PTlRlfs9q11cef13c/xESY/Pm4VCkp1yJUZO3kXzvMxQFA==}
+ metro-transform-plugins@0.83.5:
+ resolution: {integrity: sha512-KxYKzZL+lt3Os5H2nx7YkbkWVduLZL5kPrE/Yq+Prm/DE1VLhpfnO6HtPs8vimYFKOa58ncl60GpoX0h7Wm0Vw==}
engines: {node: '>=20.19.4'}
- metro-transform-worker@0.83.7:
- resolution: {integrity: sha512-UegCo7ygB2fT64mRK2nbAjQVJ1zSwIIHy8d96jJv2nKZFDaViYBiughEdu5HM/Ceq0WN3LZrZk3zhl9aoiLYFw==}
+ metro-transform-worker@0.83.5:
+ resolution: {integrity: sha512-8N4pjkNXc6ytlP9oAM6MwqkvUepNSW39LKYl9NjUMpRDazBQ7oBpQDc8Sz4aI8jnH6AGhF7s1m/ayxkN1t04yA==}
engines: {node: '>=20.19.4'}
- metro@0.83.7:
- resolution: {integrity: sha512-SPaPEyvTsTmd0LpT7RaZciQyDw2i/JB7+iY9L5VfBo72+psescFxBqpI1TL9dnL+pmnfkU+l/J1mEEGLeF65EQ==}
+ metro@0.83.5:
+ resolution: {integrity: sha512-BgsXevY1MBac/3ZYv/RfNFf/4iuW9X7f4H8ZNkiH+r667HD9sVujxcmu4jvEzGCAm4/WyKdZCuyhAcyhTHOucQ==}
engines: {node: '>=20.19.4'}
hasBin: true
@@ -4465,8 +3912,8 @@ packages:
minimalistic-assert@1.0.1:
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
- minimatch@10.2.5:
- resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
+ minimatch@10.2.4:
+ resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==}
engines: {node: 18 || 20 || >=22}
minimatch@3.1.5:
@@ -4487,8 +3934,8 @@ packages:
mnemonist@0.40.0:
resolution: {integrity: sha512-kdd8AFNig2AD5Rkih7EPCXhu/iMvwevQFX/uEiGhZyPZi7fHqOoF4V4kHLpCfysxXMgQ4B52kdPMCwARshKvEg==}
- mnemonist@0.40.4:
- resolution: {integrity: sha512-ZAv+KNavneRVzu4tUeOgzkScI3W5BGwZ3rkxIpKtzzVgfTtWQFN1CgX0U72cyvyh3iTuHL3SiSmrQxTlryEIcw==}
+ mnemonist@0.40.3:
+ resolution: {integrity: sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==}
mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
@@ -4504,16 +3951,11 @@ packages:
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
- nanoid@3.3.12:
- resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
+ 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.4:
- resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==}
- engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
- hasBin: true
-
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
@@ -4552,9 +3994,8 @@ packages:
node-int64@0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
- node-releases@2.0.46:
- resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==}
- engines: {node: '>=18'}
+ node-releases@2.0.36:
+ resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==}
node-stream-zip@1.15.0:
resolution: {integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==}
@@ -4574,13 +4015,13 @@ packages:
nullthrows@1.1.1:
resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==}
- nypm@0.6.6:
- resolution: {integrity: sha512-vRyr0r4cbBapw07Xw8xrj9Teq3o7MUD35rSaTcanDbW+aK2XHDgJFiU6ZTj2GBw7Q12ysdsyFss+Vdz4hQ0Y6Q==}
+ nypm@0.6.5:
+ resolution: {integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==}
engines: {node: '>=18'}
hasBin: true
- ob1@0.83.7:
- resolution: {integrity: sha512-9M5kpuOLyTPogMtZiQUIxdAZxl7Dxs6tVBbJErSumsqGMuhVSoUbkfeZ3XNPpLpwBBtqY5QDUzGwggLHX3slQg==}
+ ob1@0.83.5:
+ resolution: {integrity: sha512-vNKPYC8L5ycVANANpF/S+WZHpfnRWKx/F3AYP4QMn6ZJTh+l2HOrId0clNkEmua58NB9vmI9Qh7YOoV/4folYg==}
engines: {node: '>=20.19.4'}
object-assign@4.1.1:
@@ -4733,12 +4174,12 @@ packages:
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
- picomatch@2.3.2:
- resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
- picomatch@4.0.4:
- resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
pino-abstract-transport@3.0.0:
@@ -4763,12 +4204,8 @@ packages:
resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
engines: {node: '>=8'}
- pkg-types@2.3.1:
- resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==}
-
- pluralize@8.0.0:
- resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
- engines: {node: '>=4'}
+ pkg-types@2.3.0:
+ resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
pngjs@5.0.0:
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
@@ -4781,8 +4218,8 @@ packages:
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
- postcss@8.5.15:
- resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
+ postcss@8.5.8:
+ resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.2.1:
@@ -4798,8 +4235,8 @@ packages:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- prisma@6.19.3:
- resolution: {integrity: sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==}
+ prisma@6.19.2:
+ resolution: {integrity: sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==}
engines: {node: '>=18.18'}
hasBin: true
peerDependencies:
@@ -4842,8 +4279,8 @@ packages:
engines: {node: '>=10.13.0'}
hasBin: true
- qs@6.15.2:
- resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==}
+ qs@6.14.2:
+ resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==}
engines: {node: '>=0.6'}
query-string@7.1.3:
@@ -4873,10 +4310,10 @@ packages:
react-devtools-core@6.1.5:
resolution: {integrity: sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==}
- react-dom@19.2.6:
- resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==}
+ react-dom@19.2.4:
+ resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
peerDependencies:
- react: ^19.2.6
+ react: ^19.2.4
react-freeze@1.0.4:
resolution: {integrity: sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==}
@@ -4890,15 +4327,8 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
- react-is@19.2.6:
- resolution: {integrity: sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==}
-
- react-native-camera-kit@14.2.0:
- resolution: {integrity: sha512-rPk/4Ux52/Kc6oIPk0x6NsrvDkeL+kd/GAUJ4xBtTlnmiWjLTgeA2Vjgg9ik03mmyf6rV+LaqaOBT7KejhuHKQ==}
- engines: {node: '>=18'}
- peerDependencies:
- react: '*'
- react-native: '*'
+ react-is@19.2.4:
+ resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==}
react-native-gesture-handler@2.31.2:
resolution: {integrity: sha512-rw5q74i2AfS7YGYdbxQDhOU7xqgY6WRM1132/CCm3erqjblhECZDZFHIm0tteHoC9ih24wogVBVVzcTBQtZ+5A==}
@@ -4926,20 +4356,20 @@ packages:
react: '*'
react-native: '*'
- react-native-safe-area-context@5.8.0:
- resolution: {integrity: sha512-t+ZsAVzY/wWzzx34vqGbo3/as9EEESJdbyZNL7Yg5EYX+toYMtMqFoDDCvqZUi35eeGVsXc6pAaEk4edMwbuCQ==}
+ react-native-safe-area-context@5.7.0:
+ resolution: {integrity: sha512-/9/MtQz8ODphjsLdZ+GZAIcC/RtoqW9EeShf7Uvnfgm/pzYrJ75y3PV/J1wuAV1T5Dye5ygq4EAW20RoBq0ABQ==}
peerDependencies:
react: '*'
react-native: '*'
- react-native-screens@4.25.2:
- resolution: {integrity: sha512-1Nj1fusFd+rIMKU/qC9yGKVG+3ofh11d3OdBQKL1iVvQfKvcB8vhvTGQf2TkfxW3bamxN+hCZIXmNuU0mRkyDg==}
+ react-native-screens@4.24.0:
+ resolution: {integrity: sha512-SyoiGaDofiyGPFrUkn1oGsAzkRuX1JUvTD9YQQK3G1JGQ5VWkvHgYSsc1K9OrLsDQxN7NmV71O0sHCAh8cBetA==}
peerDependencies:
react: '*'
- react-native: '>=0.82.0'
+ react-native: '*'
- react-native-svg@15.15.5:
- resolution: {integrity: sha512-L4go5jA+GWutdJ/JucuN20cjAbMg1HmMtAP+wZ+3JLCf6Jd0bhXQHxciRP/AQm/FlrIEZwkMcHNZP+FXAiic0w==}
+ react-native-svg@15.15.3:
+ resolution: {integrity: sha512-/k4KYwPBLGcx2f5d4FjE+vCScK7QOX14cl2lIASJ28u4slHHtIhL0SZKU7u9qmRBHxTCKPoPBtN6haT1NENJNA==}
peerDependencies:
react: '*'
react-native: '*'
@@ -4949,13 +4379,6 @@ packages:
deprecated: react-native-vector-icons package has moved to a new model of per-icon-family packages. See the https://github.com/oblador/react-native-vector-icons/blob/master/MIGRATION.md on how to migrate
hasBin: true
- react-native-view-shot@5.1.0:
- resolution: {integrity: sha512-JZgElCD82aO+hejIF/leUzI7JufL9mgJ6ChzGWIcdZ2ajpaEvvSnvIcw0qD32XWkrbId8wfSbyz/4u/ulTQzQA==}
- engines: {node: '>=20', npm: '>=10'}
- peerDependencies:
- react: '>=18.0.0'
- react-native: '>=0.76.0'
-
react-native-web@0.21.2:
resolution: {integrity: sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==}
peerDependencies:
@@ -4968,13 +4391,6 @@ packages:
react: '*'
react-native: '*'
- 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.84.1:
resolution: {integrity: sha512-0PjxOyXRu3tZ8EobabxSukvhKje2HJbsZikR0U+pvS0pYZza2hXKjcSBiBdFN4h9D0S3v6a8kkrDK6WTRKMwzg==}
engines: {node: '>= 20.19.4'}
@@ -5011,9 +4427,6 @@ packages:
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
engines: {node: '>= 12.13.0'}
- real-require@1.0.0:
- resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==}
-
redis-errors@1.2.0:
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
engines: {node: '>=4'}
@@ -5036,10 +4449,6 @@ packages:
regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
- regexp-tree@0.1.27:
- resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
- hasBin: true
-
regexp.prototype.flags@1.5.4:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
@@ -5051,8 +4460,8 @@ packages:
regjsgen@0.8.0:
resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==}
- regjsparser@0.13.1:
- resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==}
+ regjsparser@0.13.0:
+ resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==}
hasBin: true
require-directory@2.1.1:
@@ -5085,13 +4494,13 @@ packages:
resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==}
engines: {node: '>=10'}
- resolve@1.22.12:
- resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==}
+ resolve@1.22.11:
+ resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
engines: {node: '>= 0.4'}
hasBin: true
- resolve@2.0.0-next.7:
- resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==}
+ resolve@2.0.0-next.6:
+ resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==}
engines: {node: '>= 0.4'}
hasBin: true
@@ -5115,8 +4524,8 @@ packages:
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
- rollup@4.60.4:
- resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==}
+ rollup@4.59.0:
+ resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -5130,8 +4539,8 @@ packages:
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
engines: {node: '>=6'}
- safe-array-concat@1.1.4:
- resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==}
+ safe-array-concat@1.1.3:
+ resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
engines: {node: '>=0.4'}
safe-buffer@5.2.1:
@@ -5145,12 +4554,8 @@ packages:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
- safe-regex2@5.1.1:
- resolution: {integrity: sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==}
- hasBin: true
-
- safe-regex@2.1.1:
- resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==}
+ safe-regex2@5.0.0:
+ resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==}
safe-stable-stringify@2.5.0:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
@@ -5169,13 +4574,8 @@ packages:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
- semver@7.7.2:
- resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
- engines: {node: '>=10'}
- hasBin: true
-
- semver@7.8.1:
- resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==}
+ semver@7.7.4:
+ resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
hasBin: true
@@ -5197,8 +4597,8 @@ packages:
set-cookie-parser@2.7.2:
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
- set-cookie-parser@3.1.0:
- resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==}
+ set-cookie-parser@3.0.1:
+ resolution: {integrity: sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==}
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
@@ -5234,12 +4634,8 @@ packages:
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
engines: {node: '>= 0.4'}
- shell-quote@1.8.4:
- resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==}
- engines: {node: '>= 0.4'}
-
- side-channel-list@1.0.1:
- resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
+ side-channel-list@1.0.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
engines: {node: '>= 0.4'}
side-channel-map@1.0.1:
@@ -5314,10 +4710,6 @@ packages:
sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
- stable-hash-x@0.2.0:
- resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==}
- engines: {node: '>=12.0.0'}
-
stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
@@ -5406,10 +4798,6 @@ packages:
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
engines: {node: '>=6'}
- strip-indent@4.1.1:
- resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==}
- engines: {node: '>=12'}
-
strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@@ -5436,24 +4824,20 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
- svelte-check@4.4.8:
- resolution: {integrity: sha512-67adfgBox5eNSNIvIIwgFizKGdcRrGpiMoNO2obHcYuLz7iTa8Xgm/NGU3ntMFnNm8K1grFOIG6HhMLX/vcN8w==}
+ svelte-check@4.4.5:
+ resolution: {integrity: sha512-1bSwIRCvvmSHrlK52fOlZmVtUZgil43jNL/2H18pRpa+eQjzGt6e3zayxhp1S7GajPFKNM/2PMCG+DZFHlG9fw==}
engines: {node: '>= 18.0.0'}
hasBin: true
peerDependencies:
svelte: ^4.0.0 || ^5.0.0-next.0
typescript: '>=5.0.0'
- svelte@5.56.0:
- resolution: {integrity: sha512-kTXr26t1bchFp28ROrb957LtbujpBmBDibmqMGziVpUs7awBi96TGgX6SovrA8BNoEUDVRK2Fb9FkeYlGspoVg==}
+ svelte@5.53.10:
+ resolution: {integrity: sha512-UcNfWzbrjvYXYSk+U2hME25kpb87oq6/WVLeBF4khyQrb3Ob/URVlN23khal+RbdCUTMfg4qWjI9KZjCNFtYMQ==}
engines: {node: '>=18'}
- tapable@2.3.3:
- resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
- engines: {node: '>=6'}
-
- terser@5.48.0:
- resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==}
+ terser@5.46.0:
+ resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==}
engines: {node: '>=10'}
hasBin: true
@@ -5465,14 +4849,11 @@ packages:
resolution: {integrity: sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==}
deprecated: no longer maintained
- text-segmentation@1.0.3:
- resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
-
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
- thread-stream@4.2.0:
- resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==}
+ thread-stream@4.0.0:
+ resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==}
engines: {node: '>=20'}
throat@5.0.0:
@@ -5484,12 +4865,12 @@ packages:
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
- tinyexec@1.2.3:
- resolution: {integrity: sha512-g62dB+w1/OEFnPvmX0yd/HnetYITOL+1nJW7kitOycOeAvmbWC/nu0fwmmQ/kupNojqExzyC/T++pST/jRJ2mQ==}
+ tinyexec@1.0.2:
+ resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
engines: {node: '>=18'}
- tinyglobby@0.2.16:
- resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
tinypool@1.1.1:
@@ -5511,9 +4892,9 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
- toad-cache@3.7.1:
- resolution: {integrity: sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ==}
- engines: {node: '>=20'}
+ toad-cache@3.7.0:
+ resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
+ engines: {node: '>=12'}
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
@@ -5530,8 +4911,8 @@ packages:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
- ts-api-utils@2.5.0:
- resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
+ ts-api-utils@2.4.0:
+ resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
engines: {node: '>=18.12'}
peerDependencies:
typescript: '>=4.8.4'
@@ -5539,8 +4920,8 @@ packages:
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
- tsx@4.22.3:
- resolution: {integrity: sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==}
+ tsx@4.21.0:
+ resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
engines: {node: '>=18.0.0'}
hasBin: true
@@ -5580,17 +4961,10 @@ packages:
resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
engines: {node: '>= 0.4'}
- typed-array-length@1.0.8:
- resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==}
+ typed-array-length@1.0.7:
+ resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
- typescript-eslint@8.60.0:
- resolution: {integrity: sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw==}
- engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- peerDependencies:
- eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
- typescript: '>=4.8.4 <6.1.0'
-
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
@@ -5631,9 +5005,6 @@ packages:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
- unrs-resolver@1.12.2:
- resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==}
-
update-browserslist-db@1.2.3:
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
hasBin: true
@@ -5660,9 +5031,6 @@ packages:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
- utrie@1.0.2:
- resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
-
v8-to-istanbul@9.3.0:
resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==}
engines: {node: '>=10.12.0'}
@@ -5707,8 +5075,8 @@ packages:
terser:
optional: true
- vite@7.3.3:
- resolution: {integrity: sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==}
+ vite@7.3.1:
+ resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -5747,10 +5115,10 @@ packages:
yaml:
optional: true
- vitefu@1.1.3:
- resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==}
+ vitefu@1.1.2:
+ resolution: {integrity: sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw==}
peerDependencies:
- vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
+ vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0
peerDependenciesMeta:
vite:
optional: true
@@ -5816,8 +5184,8 @@ packages:
which-module@2.0.1:
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
- which-typed-array@1.1.21:
- resolution: {integrity: sha512-zbRA8cVm6io/d5W8uIe2hblzN76/Wm3v/yiythQvr+dpBWeqhPSWIDNj4zOyHi4zKbMK6DN34Xsr9jPHJERAEw==}
+ which-typed-array@1.1.20:
+ resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==}
engines: {node: '>= 0.4'}
which@2.0.2:
@@ -5849,8 +5217,8 @@ packages:
resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
- ws@6.2.4:
- resolution: {integrity: sha512-PNIUUyLI5YpkJZj60YBzX1o0ByQ4ovvfmq9N/Kig/PAYbVlGyz4R6G0SEWrD0O9acc0sT2+IdMBVLFv8FSi0Nw==}
+ ws@6.2.3:
+ resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
@@ -5860,8 +5228,8 @@ packages:
utf-8-validate:
optional: true
- ws@7.5.11:
- resolution: {integrity: sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==}
+ ws@7.5.10:
+ resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
engines: {node: '>=8.3.0'}
peerDependencies:
bufferutil: ^4.0.1
@@ -5886,8 +5254,8 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
- yaml@2.9.0:
- resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
+ yaml@2.8.2:
+ resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==}
engines: {node: '>= 14.6'}
hasBin: true
@@ -5933,25 +5301,25 @@ packages:
snapshots:
- '@babel/code-frame@7.29.7':
+ '@babel/code-frame@7.29.0':
dependencies:
- '@babel/helper-validator-identifier': 7.29.7
+ '@babel/helper-validator-identifier': 7.28.5
js-tokens: 4.0.0
picocolors: 1.1.1
- '@babel/compat-data@7.29.7': {}
+ '@babel/compat-data@7.29.0': {}
- '@babel/core@7.29.7':
+ '@babel/core@7.29.0':
dependencies:
- '@babel/code-frame': 7.29.7
- '@babel/generator': 7.29.7
- '@babel/helper-compilation-targets': 7.29.7
- '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
- '@babel/helpers': 7.29.7
- '@babel/parser': 7.29.7
- '@babel/template': 7.29.7
- '@babel/traverse': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@babel/helpers': 7.28.6
+ '@babel/parser': 7.29.0
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
debug: 4.4.3
@@ -5961,814 +5329,805 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/eslint-parser@7.29.7(@babel/core@7.29.7)(eslint@8.57.1)':
+ '@babel/eslint-parser@7.28.6(@babel/core@7.29.0)(eslint@8.57.1)':
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.0
'@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
eslint: 8.57.1
eslint-visitor-keys: 2.1.0
semver: 6.3.1
- '@babel/generator@7.29.7':
+ '@babel/generator@7.29.1':
dependencies:
- '@babel/parser': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31
jsesc: 3.1.0
- '@babel/helper-annotate-as-pure@7.29.7':
+ '@babel/helper-annotate-as-pure@7.27.3':
dependencies:
- '@babel/types': 7.29.7
+ '@babel/types': 7.29.0
- '@babel/helper-compilation-targets@7.29.7':
+ '@babel/helper-compilation-targets@7.28.6':
dependencies:
- '@babel/compat-data': 7.29.7
- '@babel/helper-validator-option': 7.29.7
- browserslist: 4.28.2
+ '@babel/compat-data': 7.29.0
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.28.1
lru-cache: 5.1.1
semver: 6.3.1
- '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)':
+ '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-annotate-as-pure': 7.29.7
- '@babel/helper-member-expression-to-functions': 7.29.7
- '@babel/helper-optimise-call-expression': 7.29.7
- '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-skip-transparent-expression-wrappers': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-member-expression-to-functions': 7.28.5
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/traverse': 7.29.0
semver: 6.3.1
transitivePeerDependencies:
- supports-color
- '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7)':
+ '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-annotate-as-pure': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-annotate-as-pure': 7.27.3
regexpu-core: 6.4.0
semver: 6.3.1
- '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7)':
+ '@babel/helper-define-polyfill-provider@0.6.7(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-compilation-targets': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
debug: 4.4.3
lodash.debounce: 4.0.8
- resolve: 1.22.12
+ resolve: 1.22.11
transitivePeerDependencies:
- supports-color
- '@babel/helper-globals@7.29.7': {}
+ '@babel/helper-globals@7.28.0': {}
- '@babel/helper-member-expression-to-functions@7.29.7':
+ '@babel/helper-member-expression-to-functions@7.28.5':
dependencies:
- '@babel/traverse': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/helper-module-imports@7.29.7':
+ '@babel/helper-module-imports@7.28.6':
dependencies:
- '@babel/traverse': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
+ '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-module-imports': 7.29.7
- '@babel/helper-validator-identifier': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/helper-optimise-call-expression@7.29.7':
+ '@babel/helper-optimise-call-expression@7.27.1':
dependencies:
- '@babel/types': 7.29.7
+ '@babel/types': 7.29.0
- '@babel/helper-plugin-utils@7.29.7': {}
+ '@babel/helper-plugin-utils@7.28.6': {}
- '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7)':
+ '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-annotate-as-pure': 7.29.7
- '@babel/helper-wrap-function': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-wrap-function': 7.28.6
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)':
+ '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-member-expression-to-functions': 7.29.7
- '@babel/helper-optimise-call-expression': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-member-expression-to-functions': 7.28.5
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/helper-skip-transparent-expression-wrappers@7.29.7':
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
dependencies:
- '@babel/traverse': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/helper-string-parser@7.29.7': {}
+ '@babel/helper-string-parser@7.27.1': {}
- '@babel/helper-validator-identifier@7.29.7': {}
+ '@babel/helper-validator-identifier@7.28.5': {}
- '@babel/helper-validator-option@7.29.7': {}
+ '@babel/helper-validator-option@7.27.1': {}
- '@babel/helper-wrap-function@7.29.7':
+ '@babel/helper-wrap-function@7.28.6':
dependencies:
- '@babel/template': 7.29.7
- '@babel/traverse': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/helpers@7.29.7':
+ '@babel/helpers@7.28.6':
dependencies:
- '@babel/template': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
- '@babel/parser@7.29.7':
+ '@babel/parser@7.29.0':
dependencies:
- '@babel/types': 7.29.7
+ '@babel/types': 7.29.0
- '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-skip-transparent-expression-wrappers': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0)
transitivePeerDependencies:
- supports-color
- '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-skip-transparent-expression-wrappers': 7.29.7
- '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/traverse': 7.29.7
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-proposal-export-default-from@7.29.7(@babel/core@7.29.7)':
- dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7)':
+ '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.0
- '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-export-default-from@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-export-default-from@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-flow@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7)':
+ '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7)
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0)
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-module-imports': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0)
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-block-scoped-functions@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-annotate-as-pure': 7.29.7
- '@babel/helper-compilation-targets': 7.29.7
- '@babel/helper-globals': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7)
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-globals': 7.28.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0)
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/template': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/template': 7.28.6
- '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-dotall-regex@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-duplicate-keys@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-dynamic-import@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0)
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-exponentiation-operator@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-flow-strip-types@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0)
- '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-skip-transparent-expression-wrappers': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-compilation-targets': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-json-strings@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-member-expression-literals@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-modules-amd@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-validator-identifier': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-modules-umd@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-new-target@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-compilation-targets': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7)
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0)
+ '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0)
+ '@babel/traverse': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-object-super@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0)
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-skip-transparent-expression-wrappers': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-annotate-as-pure': 7.29.7
- '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-property-literals@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-react-display-name@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-annotate-as-pure': 7.29.7
- '@babel/helper-module-imports': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
- '@babel/types': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0)
+ '@babel/types': 7.29.0
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-regexp-modifiers@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-reserved-words@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-module-imports': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7)
- babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.7)
- babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+ babel-plugin-polyfill-corejs2: 0.4.16(@babel/core@7.29.0)
+ babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.0)
+ babel-plugin-polyfill-regenerator: 0.6.7(@babel/core@7.29.0)
semver: 6.3.1
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-skip-transparent-expression-wrappers': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-typeof-symbol@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)':
+ '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-annotate-as-pure': 7.29.7
- '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-skip-transparent-expression-wrappers': 7.29.7
- '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0)
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-unicode-escapes@7.29.7(@babel/core@7.29.7)':
- dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
-
- '@babel/plugin-transform-unicode-property-regex@7.29.7(@babel/core@7.29.7)':
- dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
-
- '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7)':
- dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
-
- '@babel/plugin-transform-unicode-sets-regex@7.29.7(@babel/core@7.29.7)':
- dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7)
- '@babel/helper-plugin-utils': 7.29.7
-
- '@babel/preset-env@7.29.7(@babel/core@7.29.7)':
- dependencies:
- '@babel/compat-data': 7.29.7
- '@babel/core': 7.29.7
- '@babel/helper-compilation-targets': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-validator-option': 7.29.7
- '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7)
- '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.7)
- '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-block-scoped-functions': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-dotall-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-duplicate-keys': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-dynamic-import': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-exponentiation-operator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-json-strings': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-object-super': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-unicode-escapes': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-unicode-property-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-unicode-sets-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.7)
- babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7)
- babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7)
- babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7)
- core-js-compat: 3.49.0
+ '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0)
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/preset-env@7.29.0(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/compat-data': 7.29.0
+ '@babel/core': 7.29.0
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-validator-option': 7.27.1
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0)
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)
+ '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0)
+ '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0)
+ '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0)
+ '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0)
+ babel-plugin-polyfill-corejs2: 0.4.16(@babel/core@7.29.0)
+ babel-plugin-polyfill-corejs3: 0.14.1(@babel/core@7.29.0)
+ babel-plugin-polyfill-regenerator: 0.6.7(@babel/core@7.29.0)
+ core-js-compat: 3.48.0
semver: 6.3.1
transitivePeerDependencies:
- supports-color
- '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.7)':
+ '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/types': 7.29.0
esutils: 2.0.3
- '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)':
+ '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-plugin-utils': 7.29.7
- '@babel/helper-validator-option': 7.29.7
- '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+ '@babel/helper-validator-option': 7.27.1
+ '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0)
transitivePeerDependencies:
- supports-color
- '@babel/runtime@7.29.7': {}
+ '@babel/runtime@7.28.6': {}
- '@babel/template@7.29.7':
+ '@babel/template@7.28.6':
dependencies:
- '@babel/code-frame': 7.29.7
- '@babel/parser': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/code-frame': 7.29.0
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
- '@babel/traverse@7.29.7':
+ '@babel/traverse@7.29.0':
dependencies:
- '@babel/code-frame': 7.29.7
- '@babel/generator': 7.29.7
- '@babel/helper-globals': 7.29.7
- '@babel/parser': 7.29.7
- '@babel/template': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.29.0
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
debug: 4.4.3
transitivePeerDependencies:
- supports-color
- '@babel/types@7.29.7':
+ '@babel/types@7.29.0':
dependencies:
- '@babel/helper-string-parser': 7.29.7
- '@babel/helper-validator-identifier': 7.29.7
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
'@bcoe/v8-coverage@0.2.3': {}
@@ -6776,252 +6135,153 @@ snapshots:
dependencies:
'@types/hammerjs': 2.0.46
- '@emnapi/core@1.10.0':
- dependencies:
- '@emnapi/wasi-threads': 1.2.1
- tslib: 2.8.1
- optional: true
-
- '@emnapi/runtime@1.10.0':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@emnapi/wasi-threads@1.2.1':
- dependencies:
- tslib: 2.8.1
- optional: true
-
'@esbuild/aix-ppc64@0.21.5':
optional: true
- '@esbuild/aix-ppc64@0.27.7':
- optional: true
-
- '@esbuild/aix-ppc64@0.28.0':
+ '@esbuild/aix-ppc64@0.27.3':
optional: true
'@esbuild/android-arm64@0.21.5':
optional: true
- '@esbuild/android-arm64@0.27.7':
- optional: true
-
- '@esbuild/android-arm64@0.28.0':
+ '@esbuild/android-arm64@0.27.3':
optional: true
'@esbuild/android-arm@0.21.5':
optional: true
- '@esbuild/android-arm@0.27.7':
- optional: true
-
- '@esbuild/android-arm@0.28.0':
+ '@esbuild/android-arm@0.27.3':
optional: true
'@esbuild/android-x64@0.21.5':
optional: true
- '@esbuild/android-x64@0.27.7':
- optional: true
-
- '@esbuild/android-x64@0.28.0':
+ '@esbuild/android-x64@0.27.3':
optional: true
'@esbuild/darwin-arm64@0.21.5':
optional: true
- '@esbuild/darwin-arm64@0.27.7':
- optional: true
-
- '@esbuild/darwin-arm64@0.28.0':
+ '@esbuild/darwin-arm64@0.27.3':
optional: true
'@esbuild/darwin-x64@0.21.5':
optional: true
- '@esbuild/darwin-x64@0.27.7':
- optional: true
-
- '@esbuild/darwin-x64@0.28.0':
+ '@esbuild/darwin-x64@0.27.3':
optional: true
'@esbuild/freebsd-arm64@0.21.5':
optional: true
- '@esbuild/freebsd-arm64@0.27.7':
- optional: true
-
- '@esbuild/freebsd-arm64@0.28.0':
+ '@esbuild/freebsd-arm64@0.27.3':
optional: true
'@esbuild/freebsd-x64@0.21.5':
optional: true
- '@esbuild/freebsd-x64@0.27.7':
- optional: true
-
- '@esbuild/freebsd-x64@0.28.0':
+ '@esbuild/freebsd-x64@0.27.3':
optional: true
'@esbuild/linux-arm64@0.21.5':
optional: true
- '@esbuild/linux-arm64@0.27.7':
- optional: true
-
- '@esbuild/linux-arm64@0.28.0':
+ '@esbuild/linux-arm64@0.27.3':
optional: true
'@esbuild/linux-arm@0.21.5':
optional: true
- '@esbuild/linux-arm@0.27.7':
- optional: true
-
- '@esbuild/linux-arm@0.28.0':
+ '@esbuild/linux-arm@0.27.3':
optional: true
'@esbuild/linux-ia32@0.21.5':
optional: true
- '@esbuild/linux-ia32@0.27.7':
- optional: true
-
- '@esbuild/linux-ia32@0.28.0':
+ '@esbuild/linux-ia32@0.27.3':
optional: true
'@esbuild/linux-loong64@0.21.5':
optional: true
- '@esbuild/linux-loong64@0.27.7':
- optional: true
-
- '@esbuild/linux-loong64@0.28.0':
+ '@esbuild/linux-loong64@0.27.3':
optional: true
'@esbuild/linux-mips64el@0.21.5':
optional: true
- '@esbuild/linux-mips64el@0.27.7':
- optional: true
-
- '@esbuild/linux-mips64el@0.28.0':
+ '@esbuild/linux-mips64el@0.27.3':
optional: true
'@esbuild/linux-ppc64@0.21.5':
optional: true
- '@esbuild/linux-ppc64@0.27.7':
- optional: true
-
- '@esbuild/linux-ppc64@0.28.0':
+ '@esbuild/linux-ppc64@0.27.3':
optional: true
'@esbuild/linux-riscv64@0.21.5':
optional: true
- '@esbuild/linux-riscv64@0.27.7':
- optional: true
-
- '@esbuild/linux-riscv64@0.28.0':
+ '@esbuild/linux-riscv64@0.27.3':
optional: true
'@esbuild/linux-s390x@0.21.5':
optional: true
- '@esbuild/linux-s390x@0.27.7':
- optional: true
-
- '@esbuild/linux-s390x@0.28.0':
+ '@esbuild/linux-s390x@0.27.3':
optional: true
'@esbuild/linux-x64@0.21.5':
optional: true
- '@esbuild/linux-x64@0.27.7':
- optional: true
-
- '@esbuild/linux-x64@0.28.0':
- optional: true
-
- '@esbuild/netbsd-arm64@0.27.7':
- optional: true
-
- '@esbuild/netbsd-arm64@0.28.0':
- optional: true
-
- '@esbuild/netbsd-x64@0.21.5':
- optional: true
-
- '@esbuild/netbsd-x64@0.27.7':
- optional: true
-
- '@esbuild/netbsd-x64@0.28.0':
+ '@esbuild/linux-x64@0.27.3':
optional: true
- '@esbuild/openbsd-arm64@0.27.7':
+ '@esbuild/netbsd-arm64@0.27.3':
optional: true
- '@esbuild/openbsd-arm64@0.28.0':
+ '@esbuild/netbsd-x64@0.21.5':
optional: true
- '@esbuild/openbsd-x64@0.21.5':
+ '@esbuild/netbsd-x64@0.27.3':
optional: true
- '@esbuild/openbsd-x64@0.27.7':
+ '@esbuild/openbsd-arm64@0.27.3':
optional: true
- '@esbuild/openbsd-x64@0.28.0':
+ '@esbuild/openbsd-x64@0.21.5':
optional: true
- '@esbuild/openharmony-arm64@0.27.7':
+ '@esbuild/openbsd-x64@0.27.3':
optional: true
- '@esbuild/openharmony-arm64@0.28.0':
+ '@esbuild/openharmony-arm64@0.27.3':
optional: true
'@esbuild/sunos-x64@0.21.5':
optional: true
- '@esbuild/sunos-x64@0.27.7':
- optional: true
-
- '@esbuild/sunos-x64@0.28.0':
+ '@esbuild/sunos-x64@0.27.3':
optional: true
'@esbuild/win32-arm64@0.21.5':
optional: true
- '@esbuild/win32-arm64@0.27.7':
- optional: true
-
- '@esbuild/win32-arm64@0.28.0':
+ '@esbuild/win32-arm64@0.27.3':
optional: true
'@esbuild/win32-ia32@0.21.5':
optional: true
- '@esbuild/win32-ia32@0.27.7':
- optional: true
-
- '@esbuild/win32-ia32@0.28.0':
+ '@esbuild/win32-ia32@0.27.3':
optional: true
'@esbuild/win32-x64@0.21.5':
optional: true
- '@esbuild/win32-x64@0.27.7':
- optional: true
-
- '@esbuild/win32-x64@0.28.0':
+ '@esbuild/win32-x64@0.27.3':
optional: true
- '@eslint-community/eslint-utils@4.9.1(eslint@10.4.1(jiti@2.7.0))':
- dependencies:
- eslint: 10.4.1(jiti@2.7.0)
- eslint-visitor-keys: 3.4.3
-
'@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)':
dependencies:
eslint: 8.57.1
@@ -7029,25 +6289,9 @@ snapshots:
'@eslint-community/regexpp@4.12.2': {}
- '@eslint/config-array@0.23.5':
- dependencies:
- '@eslint/object-schema': 3.0.5
- debug: 4.4.3
- minimatch: 10.2.5
- transitivePeerDependencies:
- - supports-color
-
- '@eslint/config-helpers@0.6.0':
- dependencies:
- '@eslint/core': 1.2.1
-
- '@eslint/core@1.2.1':
- dependencies:
- '@types/json-schema': 7.0.15
-
'@eslint/eslintrc@2.1.4':
dependencies:
- ajv: 6.15.0
+ ajv: 6.14.0
debug: 4.4.3
espree: 9.6.1
globals: 13.24.0
@@ -7061,20 +6305,13 @@ snapshots:
'@eslint/js@8.57.1': {}
- '@eslint/object-schema@3.0.5': {}
-
- '@eslint/plugin-kit@0.7.2':
- dependencies:
- '@eslint/core': 1.2.1
- levn: 0.4.1
-
'@fastify/accept-negotiator@2.0.1': {}
'@fastify/ajv-compiler@4.0.5':
dependencies:
- ajv: 8.20.0
- ajv-formats: 3.0.1(ajv@8.20.0)
- fast-uri: 3.1.2
+ ajv: 8.18.0
+ ajv-formats: 3.0.1(ajv@8.18.0)
+ fast-uri: 3.1.0
'@fastify/busboy@3.2.0': {}
@@ -7094,7 +6331,7 @@ snapshots:
'@fastify/fast-json-stringify-compiler@5.0.3':
dependencies:
- fast-json-stringify: 6.4.0
+ fast-json-stringify: 6.3.0
'@fastify/forwarded@3.0.1': {}
@@ -7126,13 +6363,7 @@ snapshots:
'@fastify/proxy-addr@5.1.0':
dependencies:
'@fastify/forwarded': 3.0.1
- ipaddr.js: 2.4.0
-
- '@fastify/rate-limit@10.3.0':
- dependencies:
- '@lukeed/ms': 2.0.2
- fastify-plugin: 5.1.0
- toad-cache: 3.7.1
+ ipaddr.js: 2.3.0
'@fastify/send@4.1.0':
dependencies:
@@ -7151,23 +6382,23 @@ snapshots:
fastq: 1.20.1
glob: 11.1.0
- '@gorhom/bottom-sheet@5.2.14(@types/react-native@0.70.19)(@types/react@19.2.15)(react-native-gesture-handler@2.31.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-reanimated@3.19.5(@babel/core@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)':
+ '@gorhom/bottom-sheet@5.2.14(@types/react-native@0.70.19)(@types/react@19.2.14)(react-native-gesture-handler@2.31.2(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-reanimated@3.19.5(@babel/core@7.29.0)(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)':
dependencies:
- '@gorhom/portal': 1.0.14(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ '@gorhom/portal': 1.0.14(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
invariant: 2.2.4
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
- react-native-gesture-handler: 2.31.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
- react-native-reanimated: 3.19.5(@babel/core@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
+ react-native-gesture-handler: 2.31.2(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
+ react-native-reanimated: 3.19.5(@babel/core@7.29.0)(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
optionalDependencies:
- '@types/react': 19.2.15
+ '@types/react': 19.2.14
'@types/react-native': 0.70.19
- '@gorhom/portal@1.0.14(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)':
+ '@gorhom/portal@1.0.14(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)':
dependencies:
- nanoid: 3.3.12
+ nanoid: 3.3.11
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
'@hapi/hoek@9.3.0': {}
@@ -7175,18 +6406,6 @@ snapshots:
dependencies:
'@hapi/hoek': 9.3.0
- '@humanfs/core@0.19.2':
- dependencies:
- '@humanfs/types': 0.15.0
-
- '@humanfs/node@0.16.8':
- dependencies:
- '@humanfs/core': 0.19.2
- '@humanfs/types': 0.15.0
- '@humanwhocodes/retry': 0.4.3
-
- '@humanfs/types@0.15.0': {}
-
'@humanwhocodes/config-array@0.13.0':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
@@ -7199,9 +6418,7 @@ snapshots:
'@humanwhocodes/object-schema@2.0.3': {}
- '@humanwhocodes/retry@0.4.3': {}
-
- '@ioredis/commands@1.10.0': {}
+ '@ioredis/commands@1.5.1': {}
'@isaacs/cliui@9.0.0': {}
@@ -7215,12 +6432,12 @@ snapshots:
js-yaml: 3.14.2
resolve-from: 5.0.0
- '@istanbuljs/schema@0.1.6': {}
+ '@istanbuljs/schema@0.1.3': {}
'@jest/console@29.7.0':
dependencies:
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
chalk: 4.1.2
jest-message-util: 29.7.0
jest-util: 29.7.0
@@ -7233,14 +6450,14 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.9.0
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.7.0
- jest-config: 29.7.0(@types/node@22.19.19)
+ jest-config: 29.7.0(@types/node@22.19.15)
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@@ -7269,7 +6486,7 @@ snapshots:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
jest-mock: 29.7.0
'@jest/expect-utils@29.7.0':
@@ -7287,7 +6504,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -7309,7 +6526,7 @@ snapshots:
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.31
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
chalk: 4.1.2
collect-v8-coverage: 1.0.3
exit: 0.1.2
@@ -7356,7 +6573,7 @@ snapshots:
'@jest/transform@29.7.0':
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.0
'@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.31
babel-plugin-istanbul: 6.1.1
@@ -7379,7 +6596,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
'@types/yargs': 17.0.35
chalk: 4.1.2
@@ -7409,13 +6626,6 @@ snapshots:
'@lukeed/ms@2.0.2': {}
- '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
- dependencies:
- '@emnapi/core': 1.10.0
- '@emnapi/runtime': 1.10.0
- '@tybys/wasm-util': 0.10.2
- optional: true
-
'@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1':
dependencies:
eslint-scope: 5.1.1
@@ -7432,51 +6642,49 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.20.1
- '@package-json/types@0.0.12': {}
-
'@pinojs/redact@0.4.0': {}
'@polka/url@1.0.0-next.29': {}
- '@prisma/client@6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)':
+ '@prisma/client@6.19.2(prisma@6.19.2(typescript@5.9.3))(typescript@5.9.3)':
optionalDependencies:
- prisma: 6.19.3(typescript@5.9.3)
+ prisma: 6.19.2(typescript@5.9.3)
typescript: 5.9.3
- '@prisma/config@6.19.3':
+ '@prisma/config@6.19.2':
dependencies:
c12: 3.1.0
deepmerge-ts: 7.1.5
- effect: 3.21.0
+ effect: 3.18.4
empathic: 2.0.0
transitivePeerDependencies:
- magicast
- '@prisma/debug@6.19.3': {}
+ '@prisma/debug@6.19.2': {}
'@prisma/engines-version@7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7': {}
- '@prisma/engines@6.19.3':
+ '@prisma/engines@6.19.2':
dependencies:
- '@prisma/debug': 6.19.3
+ '@prisma/debug': 6.19.2
'@prisma/engines-version': 7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7
- '@prisma/fetch-engine': 6.19.3
- '@prisma/get-platform': 6.19.3
+ '@prisma/fetch-engine': 6.19.2
+ '@prisma/get-platform': 6.19.2
- '@prisma/fetch-engine@6.19.3':
+ '@prisma/fetch-engine@6.19.2':
dependencies:
- '@prisma/debug': 6.19.3
+ '@prisma/debug': 6.19.2
'@prisma/engines-version': 7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7
- '@prisma/get-platform': 6.19.3
+ '@prisma/get-platform': 6.19.2
- '@prisma/get-platform@6.19.3':
+ '@prisma/get-platform@6.19.2':
dependencies:
- '@prisma/debug': 6.19.3
+ '@prisma/debug': 6.19.2
- '@react-native-async-storage/async-storage@2.2.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))':
+ '@react-native-async-storage/async-storage@2.2.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))':
dependencies:
merge-options: 3.0.4
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
'@react-native-community/cli-clean@20.1.0':
dependencies:
@@ -7489,7 +6697,7 @@ snapshots:
dependencies:
'@react-native-community/cli-tools': 20.1.0
fast-glob: 3.3.3
- fast-xml-parser: 4.5.6
+ fast-xml-parser: 4.5.4
picocolors: 1.1.1
'@react-native-community/cli-config-apple@20.1.0':
@@ -7524,9 +6732,9 @@ snapshots:
node-stream-zip: 1.15.0
ora: 5.4.1
picocolors: 1.1.1
- semver: 7.8.1
+ semver: 7.7.4
wcwidth: 1.0.1
- yaml: 2.9.0
+ yaml: 2.8.2
transitivePeerDependencies:
- typescript
@@ -7543,7 +6751,7 @@ snapshots:
'@react-native-community/cli-config-apple': 20.1.0
'@react-native-community/cli-tools': 20.1.0
execa: 5.1.1
- fast-xml-parser: 4.5.6
+ fast-xml-parser: 4.5.4
picocolors: 1.1.1
'@react-native-community/cli-platform-ios@20.1.0':
@@ -7553,7 +6761,7 @@ snapshots:
'@react-native-community/cli-server-api@20.1.0':
dependencies:
'@react-native-community/cli-tools': 20.1.0
- body-parser: 1.20.5
+ body-parser: 1.20.4
compression: 1.8.1
connect: 3.7.0
errorhandler: 1.5.2
@@ -7561,7 +6769,7 @@ snapshots:
open: 6.4.0
pretty-format: 29.7.0
serve-static: 1.16.3
- ws: 6.2.4
+ ws: 6.2.3
transitivePeerDependencies:
- bufferutil
- supports-color
@@ -7573,12 +6781,12 @@ snapshots:
appdirsjs: 1.2.7
execa: 5.1.1
find-up: 5.0.0
- launch-editor: 2.14.0
+ launch-editor: 2.13.1
mime: 2.6.0
ora: 5.4.1
picocolors: 1.1.1
prompts: 2.4.2
- semver: 7.8.1
+ semver: 7.7.4
'@react-native-community/cli-types@20.1.0':
dependencies:
@@ -7600,7 +6808,7 @@ snapshots:
graceful-fs: 4.2.11
picocolors: 1.1.1
prompts: 2.4.2
- semver: 7.8.1
+ semver: 7.7.4
transitivePeerDependencies:
- bufferutil
- supports-color
@@ -7609,74 +6817,74 @@ snapshots:
'@react-native/assets-registry@0.84.1': {}
- '@react-native/babel-plugin-codegen@0.84.1(@babel/core@7.29.7)':
+ '@react-native/babel-plugin-codegen@0.84.1(@babel/core@7.29.0)':
dependencies:
- '@babel/traverse': 7.29.7
- '@react-native/codegen': 0.84.1(@babel/core@7.29.7)
+ '@babel/traverse': 7.29.0
+ '@react-native/codegen': 0.84.1(@babel/core@7.29.0)
transitivePeerDependencies:
- '@babel/core'
- supports-color
- '@react-native/babel-preset@0.84.1(@babel/core@7.29.7)':
- dependencies:
- '@babel/core': 7.29.7
- '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.7)
- '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7)
- '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7)
- '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
- '@react-native/babel-plugin-codegen': 0.84.1(@babel/core@7.29.7)
+ '@react-native/babel-preset@0.84.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0)
+ '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0)
+ '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0)
+ '@react-native/babel-plugin-codegen': 0.84.1(@babel/core@7.29.0)
babel-plugin-syntax-hermes-parser: 0.32.0
- babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7)
+ babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0)
react-refresh: 0.14.2
transitivePeerDependencies:
- supports-color
- '@react-native/codegen@0.84.1(@babel/core@7.29.7)':
+ '@react-native/codegen@0.84.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/parser': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/parser': 7.29.0
hermes-parser: 0.32.0
invariant: 2.2.4
nullthrows: 1.1.1
- tinyglobby: 0.2.16
+ tinyglobby: 0.2.15
yargs: 17.7.2
- '@react-native/community-cli-plugin@0.84.1(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))':
+ '@react-native/community-cli-plugin@0.84.1(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))':
dependencies:
'@react-native/dev-middleware': 0.84.1
debug: 4.4.3
invariant: 2.2.4
- metro: 0.83.7
- metro-config: 0.83.7
- metro-core: 0.83.7
- semver: 7.8.1
+ metro: 0.83.5
+ metro-config: 0.83.5
+ metro-core: 0.83.5
+ semver: 7.7.4
optionalDependencies:
'@react-native-community/cli': 20.1.0(typescript@5.9.3)
- '@react-native/metro-config': 0.84.1(@babel/core@7.29.7)
+ '@react-native/metro-config': 0.84.1(@babel/core@7.29.0)
transitivePeerDependencies:
- bufferutil
- supports-color
@@ -7705,26 +6913,26 @@ snapshots:
nullthrows: 1.1.1
open: 7.4.2
serve-static: 1.16.3
- ws: 7.5.11
+ ws: 7.5.10
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
- '@react-native/eslint-config@0.84.1(eslint@8.57.1)(jest@29.7.0(@types/node@22.19.19))(prettier@2.8.8)(typescript@5.9.3)':
+ '@react-native/eslint-config@0.84.1(eslint@8.57.1)(jest@29.7.0(@types/node@22.19.15))(prettier@2.8.8)(typescript@5.9.3)':
dependencies:
- '@babel/core': 7.29.7
- '@babel/eslint-parser': 7.29.7(@babel/core@7.29.7)(eslint@8.57.1)
+ '@babel/core': 7.29.0
+ '@babel/eslint-parser': 7.28.6(@babel/core@7.29.0)(eslint@8.57.1)
'@react-native/eslint-plugin': 0.84.1
- '@typescript-eslint/eslint-plugin': 8.60.0(@typescript-eslint/parser@8.60.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
- '@typescript-eslint/parser': 8.60.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/eslint-plugin': 8.57.0(@typescript-eslint/parser@8.57.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.57.0(eslint@8.57.1)(typescript@5.9.3)
eslint: 8.57.1
eslint-config-prettier: 8.10.2(eslint@8.57.1)
eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.1)
- eslint-plugin-ft-flow: 2.0.3(@babel/eslint-parser@7.29.7(@babel/core@7.29.7)(eslint@8.57.1))(eslint@8.57.1)
- eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@22.19.19))(typescript@5.9.3)
+ eslint-plugin-ft-flow: 2.0.3(@babel/eslint-parser@7.28.6(@babel/core@7.29.0)(eslint@8.57.1))(eslint@8.57.1)
+ eslint-plugin-jest: 29.15.0(@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@22.19.15))(typescript@5.9.3)
eslint-plugin-react: 7.37.5(eslint@8.57.1)
- eslint-plugin-react-hooks: 7.1.1(eslint@8.57.1)
+ eslint-plugin-react-hooks: 7.0.1(eslint@8.57.1)
eslint-plugin-react-native: 5.0.0(eslint@8.57.1)
prettier: 2.8.8
transitivePeerDependencies:
@@ -7738,33 +6946,33 @@ snapshots:
'@react-native/js-polyfills@0.84.1': {}
- '@react-native/metro-babel-transformer@0.84.1(@babel/core@7.29.7)':
+ '@react-native/metro-babel-transformer@0.84.1(@babel/core@7.29.0)':
dependencies:
- '@babel/core': 7.29.7
- '@react-native/babel-preset': 0.84.1(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@react-native/babel-preset': 0.84.1(@babel/core@7.29.0)
hermes-parser: 0.32.0
nullthrows: 1.1.1
transitivePeerDependencies:
- supports-color
- '@react-native/metro-config@0.84.1(@babel/core@7.29.7)':
+ '@react-native/metro-config@0.84.1(@babel/core@7.29.0)':
dependencies:
'@react-native/js-polyfills': 0.84.1
- '@react-native/metro-babel-transformer': 0.84.1(@babel/core@7.29.7)
- metro-config: 0.83.7
- metro-runtime: 0.83.7
+ '@react-native/metro-babel-transformer': 0.84.1(@babel/core@7.29.0)
+ metro-config: 0.83.5
+ metro-runtime: 0.83.5
transitivePeerDependencies:
- '@babel/core'
- bufferutil
- supports-color
- utf-8-validate
- '@react-native/new-app-screen@0.84.1(@types/react@19.2.15)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)':
+ '@react-native/new-app-screen@0.84.1(@types/react@19.2.14)(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)':
dependencies:
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
optionalDependencies:
- '@types/react': 19.2.15
+ '@types/react': 19.2.14
'@react-native/normalize-colors@0.74.89': {}
@@ -7772,151 +6980,151 @@ snapshots:
'@react-native/typescript-config@0.84.1': {}
- '@react-native/virtualized-lists@0.84.1(@types/react@19.2.15)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)':
+ '@react-native/virtualized-lists@0.84.1(@types/react@19.2.14)(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)':
dependencies:
invariant: 2.2.4
nullthrows: 1.1.1
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
optionalDependencies:
- '@types/react': 19.2.15
+ '@types/react': 19.2.14
- '@react-navigation/bottom-tabs@7.16.2(@react-navigation/native@7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-screens@4.25.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)':
+ '@react-navigation/bottom-tabs@7.15.5(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)':
dependencies:
- '@react-navigation/elements': 2.9.19(@react-navigation/native@7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
- '@react-navigation/native': 7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ '@react-navigation/elements': 2.9.10(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
+ '@react-navigation/native': 7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
color: 4.2.3
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
- react-native-safe-area-context: 5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
- react-native-screens: 4.25.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
+ react-native-safe-area-context: 5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
+ react-native-screens: 4.24.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
sf-symbols-typescript: 2.2.0
transitivePeerDependencies:
- '@react-native-masked-view/masked-view'
- '@react-navigation/core@7.17.5(react@19.2.3)':
+ '@react-navigation/core@7.16.1(react@19.2.3)':
dependencies:
- '@react-navigation/routers': 7.5.5
+ '@react-navigation/routers': 7.5.3
escape-string-regexp: 4.0.0
fast-deep-equal: 3.1.3
- nanoid: 3.3.12
+ nanoid: 3.3.11
query-string: 7.1.3
react: 19.2.3
- react-is: 19.2.6
+ react-is: 19.2.4
use-latest-callback: 0.2.6(react@19.2.3)
use-sync-external-store: 1.6.0(react@19.2.3)
- '@react-navigation/elements@2.9.19(@react-navigation/native@7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)':
+ '@react-navigation/elements@2.9.10(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)':
dependencies:
- '@react-navigation/native': 7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ '@react-navigation/native': 7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
color: 4.2.3
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
- react-native-safe-area-context: 5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
+ react-native-safe-area-context: 5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
use-latest-callback: 0.2.6(react@19.2.3)
use-sync-external-store: 1.6.0(react@19.2.3)
- '@react-navigation/native-stack@7.16.0(@react-navigation/native@7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-screens@4.25.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)':
+ '@react-navigation/native-stack@7.14.4(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)':
dependencies:
- '@react-navigation/elements': 2.9.19(@react-navigation/native@7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
- '@react-navigation/native': 7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ '@react-navigation/elements': 2.9.10(@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
+ '@react-navigation/native': 7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
color: 4.2.3
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
- react-native-safe-area-context: 5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
- react-native-screens: 4.25.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
+ react-native-safe-area-context: 5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
+ react-native-screens: 4.24.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
sf-symbols-typescript: 2.2.0
warn-once: 0.1.1
transitivePeerDependencies:
- '@react-native-masked-view/masked-view'
- '@react-navigation/native@7.2.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)':
+ '@react-navigation/native@7.1.33(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)':
dependencies:
- '@react-navigation/core': 7.17.5(react@19.2.3)
+ '@react-navigation/core': 7.16.1(react@19.2.3)
escape-string-regexp: 4.0.0
fast-deep-equal: 3.1.3
- nanoid: 3.3.12
+ nanoid: 3.3.11
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
use-latest-callback: 0.2.6(react@19.2.3)
- '@react-navigation/routers@7.5.5':
+ '@react-navigation/routers@7.5.3':
dependencies:
- nanoid: 3.3.12
+ nanoid: 3.3.11
- '@rollup/rollup-android-arm-eabi@4.60.4':
+ '@rollup/rollup-android-arm-eabi@4.59.0':
optional: true
- '@rollup/rollup-android-arm64@4.60.4':
+ '@rollup/rollup-android-arm64@4.59.0':
optional: true
- '@rollup/rollup-darwin-arm64@4.60.4':
+ '@rollup/rollup-darwin-arm64@4.59.0':
optional: true
- '@rollup/rollup-darwin-x64@4.60.4':
+ '@rollup/rollup-darwin-x64@4.59.0':
optional: true
- '@rollup/rollup-freebsd-arm64@4.60.4':
+ '@rollup/rollup-freebsd-arm64@4.59.0':
optional: true
- '@rollup/rollup-freebsd-x64@4.60.4':
+ '@rollup/rollup-freebsd-x64@4.59.0':
optional: true
- '@rollup/rollup-linux-arm-gnueabihf@4.60.4':
+ '@rollup/rollup-linux-arm-gnueabihf@4.59.0':
optional: true
- '@rollup/rollup-linux-arm-musleabihf@4.60.4':
+ '@rollup/rollup-linux-arm-musleabihf@4.59.0':
optional: true
- '@rollup/rollup-linux-arm64-gnu@4.60.4':
+ '@rollup/rollup-linux-arm64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-arm64-musl@4.60.4':
+ '@rollup/rollup-linux-arm64-musl@4.59.0':
optional: true
- '@rollup/rollup-linux-loong64-gnu@4.60.4':
+ '@rollup/rollup-linux-loong64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-loong64-musl@4.60.4':
+ '@rollup/rollup-linux-loong64-musl@4.59.0':
optional: true
- '@rollup/rollup-linux-ppc64-gnu@4.60.4':
+ '@rollup/rollup-linux-ppc64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-ppc64-musl@4.60.4':
+ '@rollup/rollup-linux-ppc64-musl@4.59.0':
optional: true
- '@rollup/rollup-linux-riscv64-gnu@4.60.4':
+ '@rollup/rollup-linux-riscv64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-riscv64-musl@4.60.4':
+ '@rollup/rollup-linux-riscv64-musl@4.59.0':
optional: true
- '@rollup/rollup-linux-s390x-gnu@4.60.4':
+ '@rollup/rollup-linux-s390x-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-x64-gnu@4.60.4':
+ '@rollup/rollup-linux-x64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-x64-musl@4.60.4':
+ '@rollup/rollup-linux-x64-musl@4.59.0':
optional: true
- '@rollup/rollup-openbsd-x64@4.60.4':
+ '@rollup/rollup-openbsd-x64@4.59.0':
optional: true
- '@rollup/rollup-openharmony-arm64@4.60.4':
+ '@rollup/rollup-openharmony-arm64@4.59.0':
optional: true
- '@rollup/rollup-win32-arm64-msvc@4.60.4':
+ '@rollup/rollup-win32-arm64-msvc@4.59.0':
optional: true
- '@rollup/rollup-win32-ia32-msvc@4.60.4':
+ '@rollup/rollup-win32-ia32-msvc@4.59.0':
optional: true
- '@rollup/rollup-win32-x64-gnu@4.60.4':
+ '@rollup/rollup-win32-x64-gnu@4.59.0':
optional: true
- '@rollup/rollup-win32-x64-msvc@4.60.4':
+ '@rollup/rollup-win32-x64-msvc@4.59.0':
optional: true
'@sideway/address@4.1.5':
@@ -7939,88 +7147,79 @@ snapshots:
'@standard-schema/spec@1.1.0': {}
- '@sveltejs/acorn-typescript@1.0.10(acorn@8.16.0)':
+ '@sveltejs/acorn-typescript@1.0.9(acorn@8.16.0)':
dependencies:
acorn: 8.16.0
- '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.56.0(@typescript-eslint/types@8.60.0))(typescript@5.9.3)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)))':
+ '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.54.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.10)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))':
dependencies:
- '@sveltejs/kit': 2.61.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.56.0(@typescript-eslint/types@8.60.0))(typescript@5.9.3)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))
+ '@sveltejs/kit': 2.54.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.10)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
- '@sveltejs/kit@2.61.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.56.0(@typescript-eslint/types@8.60.0))(typescript@5.9.3)(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))':
+ '@sveltejs/kit@2.54.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.10)(typescript@5.9.3)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
'@standard-schema/spec': 1.1.0
- '@sveltejs/acorn-typescript': 1.0.10(acorn@8.16.0)
- '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))
+ '@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0)
+ '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
'@types/cookie': 0.6.0
acorn: 8.16.0
cookie: 0.6.0
- devalue: 5.8.1
+ devalue: 5.6.4
esm-env: 1.2.2
kleur: 4.1.5
magic-string: 0.30.21
mrmime: 2.0.1
- set-cookie-parser: 3.1.0
+ set-cookie-parser: 3.0.1
sirv: 3.0.2
- svelte: 5.56.0(@typescript-eslint/types@8.60.0)
- vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)
+ svelte: 5.53.10
+ vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
optionalDependencies:
typescript: 5.9.3
- '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))':
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))
+ '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
obug: 2.1.1
- svelte: 5.56.0(@typescript-eslint/types@8.60.0)
- vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)
+ svelte: 5.53.10
+ vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
- '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))':
+ '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)))(svelte@5.56.0(@typescript-eslint/types@8.60.0))(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))
+ '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.10)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
deepmerge: 4.3.1
magic-string: 0.30.21
obug: 2.1.1
- svelte: 5.56.0(@typescript-eslint/types@8.60.0)
- vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)
- vitefu: 1.1.3(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0))
-
- '@tybys/wasm-util@0.10.2':
- dependencies:
- tslib: 2.8.1
- optional: true
+ svelte: 5.53.10
+ vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
+ vitefu: 1.1.2(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
'@types/babel__core@7.20.5':
dependencies:
- '@babel/parser': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
'@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.29.7
+ '@babel/types': 7.29.0
'@types/babel__template@7.4.4':
dependencies:
- '@babel/parser': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
'@types/babel__traverse@7.28.0':
dependencies:
- '@babel/types': 7.29.7
+ '@babel/types': 7.29.0
'@types/cookie@0.6.0': {}
- '@types/esrecurse@4.3.1': {}
-
'@types/estree@1.0.8': {}
- '@types/estree@1.0.9': {}
-
'@types/graceful-fs@4.1.9':
dependencies:
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
'@types/hammerjs@2.0.46': {}
@@ -8039,30 +7238,28 @@ snapshots:
expect: 29.7.0
pretty-format: 29.7.0
- '@types/json-schema@7.0.15': {}
-
- '@types/node@22.19.19':
+ '@types/node@22.19.15':
dependencies:
undici-types: 6.21.0
'@types/qrcode@1.5.6':
dependencies:
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
'@types/react-native-vector-icons@6.4.18':
dependencies:
- '@types/react': 19.2.15
+ '@types/react': 19.2.14
'@types/react-native': 0.70.19
'@types/react-native@0.70.19':
dependencies:
- '@types/react': 19.2.15
+ '@types/react': 19.2.14
'@types/react-test-renderer@19.1.0':
dependencies:
- '@types/react': 19.2.15
+ '@types/react': 19.2.14
- '@types/react@19.2.15':
+ '@types/react@19.2.14':
dependencies:
csstype: 3.2.3
@@ -8076,219 +7273,98 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
- '@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3))(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)':
+ '@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
- '@typescript-eslint/scope-manager': 8.60.0
- '@typescript-eslint/type-utils': 8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
- '@typescript-eslint/utils': 8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
- '@typescript-eslint/visitor-keys': 8.60.0
- eslint: 10.4.1(jiti@2.7.0)
- ignore: 7.0.5
- natural-compare: 1.4.0
- ts-api-utils: 2.5.0(typescript@5.9.3)
- typescript: 5.9.3
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)':
- dependencies:
- '@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.60.0(eslint@8.57.1)(typescript@5.9.3)
- '@typescript-eslint/scope-manager': 8.60.0
- '@typescript-eslint/type-utils': 8.60.0(eslint@8.57.1)(typescript@5.9.3)
- '@typescript-eslint/utils': 8.60.0(eslint@8.57.1)(typescript@5.9.3)
- '@typescript-eslint/visitor-keys': 8.60.0
+ '@typescript-eslint/parser': 8.57.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/scope-manager': 8.57.0
+ '@typescript-eslint/type-utils': 8.57.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.57.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.57.0
eslint: 8.57.1
ignore: 7.0.5
natural-compare: 1.4.0
- ts-api-utils: 2.5.0(typescript@5.9.3)
+ ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)':
+ '@typescript-eslint/parser@8.57.0(eslint@8.57.1)(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/scope-manager': 8.60.0
- '@typescript-eslint/types': 8.60.0
- '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3)
- '@typescript-eslint/visitor-keys': 8.60.0
- debug: 4.4.3
- eslint: 10.4.1(jiti@2.7.0)
- typescript: 5.9.3
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/parser@8.60.0(eslint@8.57.1)(typescript@5.9.3)':
- dependencies:
- '@typescript-eslint/scope-manager': 8.60.0
- '@typescript-eslint/types': 8.60.0
- '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3)
- '@typescript-eslint/visitor-keys': 8.60.0
+ '@typescript-eslint/scope-manager': 8.57.0
+ '@typescript-eslint/types': 8.57.0
+ '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.57.0
debug: 4.4.3
eslint: 8.57.1
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.60.0(typescript@5.9.3)':
+ '@typescript-eslint/project-service@8.57.0(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3)
- '@typescript-eslint/types': 8.60.0
+ '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3)
+ '@typescript-eslint/types': 8.57.0
debug: 4.4.3
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@8.60.0':
- dependencies:
- '@typescript-eslint/types': 8.60.0
- '@typescript-eslint/visitor-keys': 8.60.0
-
- '@typescript-eslint/tsconfig-utils@8.60.0(typescript@5.9.3)':
+ '@typescript-eslint/scope-manager@8.57.0':
dependencies:
- typescript: 5.9.3
+ '@typescript-eslint/types': 8.57.0
+ '@typescript-eslint/visitor-keys': 8.57.0
- '@typescript-eslint/type-utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)':
+ '@typescript-eslint/tsconfig-utils@8.57.0(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/types': 8.60.0
- '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3)
- '@typescript-eslint/utils': 8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
- debug: 4.4.3
- eslint: 10.4.1(jiti@2.7.0)
- ts-api-utils: 2.5.0(typescript@5.9.3)
typescript: 5.9.3
- transitivePeerDependencies:
- - supports-color
- '@typescript-eslint/type-utils@8.60.0(eslint@8.57.1)(typescript@5.9.3)':
+ '@typescript-eslint/type-utils@8.57.0(eslint@8.57.1)(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/types': 8.60.0
- '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3)
- '@typescript-eslint/utils': 8.60.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/types': 8.57.0
+ '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.57.0(eslint@8.57.1)(typescript@5.9.3)
debug: 4.4.3
eslint: 8.57.1
- ts-api-utils: 2.5.0(typescript@5.9.3)
+ ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/types@8.60.0': {}
+ '@typescript-eslint/types@8.57.0': {}
- '@typescript-eslint/typescript-estree@8.60.0(typescript@5.9.3)':
+ '@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/project-service': 8.60.0(typescript@5.9.3)
- '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3)
- '@typescript-eslint/types': 8.60.0
- '@typescript-eslint/visitor-keys': 8.60.0
+ '@typescript-eslint/project-service': 8.57.0(typescript@5.9.3)
+ '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3)
+ '@typescript-eslint/types': 8.57.0
+ '@typescript-eslint/visitor-keys': 8.57.0
debug: 4.4.3
- minimatch: 10.2.5
- semver: 7.8.1
- tinyglobby: 0.2.16
- ts-api-utils: 2.5.0(typescript@5.9.3)
- typescript: 5.9.3
- transitivePeerDependencies:
- - supports-color
-
- '@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)':
- dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0))
- '@typescript-eslint/scope-manager': 8.60.0
- '@typescript-eslint/types': 8.60.0
- '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3)
- eslint: 10.4.1(jiti@2.7.0)
+ minimatch: 10.2.4
+ semver: 7.7.4
+ tinyglobby: 0.2.15
+ ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.60.0(eslint@8.57.1)(typescript@5.9.3)':
+ '@typescript-eslint/utils@8.57.0(eslint@8.57.1)(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
- '@typescript-eslint/scope-manager': 8.60.0
- '@typescript-eslint/types': 8.60.0
- '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3)
+ '@typescript-eslint/scope-manager': 8.57.0
+ '@typescript-eslint/types': 8.57.0
+ '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3)
eslint: 8.57.1
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/visitor-keys@8.60.0':
+ '@typescript-eslint/visitor-keys@8.57.0':
dependencies:
- '@typescript-eslint/types': 8.60.0
+ '@typescript-eslint/types': 8.57.0
eslint-visitor-keys: 5.0.1
- '@ungap/structured-clone@1.3.1': {}
-
- '@unrs/resolver-binding-android-arm-eabi@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-android-arm64@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-darwin-arm64@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-darwin-x64@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-freebsd-x64@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-arm64-gnu@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-arm64-musl@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-loong64-gnu@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-loong64-musl@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-riscv64-musl@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-s390x-gnu@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-x64-gnu@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-linux-x64-musl@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-openharmony-arm64@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-wasm32-wasi@1.12.2':
- dependencies:
- '@emnapi/core': 1.10.0
- '@emnapi/runtime': 1.10.0
- '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
- optional: true
-
- '@unrs/resolver-binding-win32-arm64-msvc@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-win32-ia32-msvc@1.12.2':
- optional: true
-
- '@unrs/resolver-binding-win32-x64-msvc@1.12.2':
- optional: true
+ '@ungap/structured-clone@1.3.0': {}
'@vitest/expect@2.1.9':
dependencies:
@@ -8297,13 +7373,13 @@ snapshots:
chai: 5.3.3
tinyrainbow: 1.2.0
- '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.19.19)(terser@5.48.0))':
+ '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.19.15)(terser@5.46.0))':
dependencies:
'@vitest/spy': 2.1.9
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- vite: 5.4.21(@types/node@22.19.19)(terser@5.48.0)
+ vite: 5.4.21(@types/node@22.19.15)(terser@5.46.0)
'@vitest/pretty-format@2.1.9':
dependencies:
@@ -8356,21 +7432,21 @@ snapshots:
agent-base@7.1.4: {}
- ajv-formats@3.0.1(ajv@8.20.0):
+ ajv-formats@3.0.1(ajv@8.18.0):
optionalDependencies:
- ajv: 8.20.0
+ ajv: 8.18.0
- ajv@6.15.0:
+ ajv@6.14.0:
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
- ajv@8.20.0:
+ ajv@8.18.0:
dependencies:
fast-deep-equal: 3.1.3
- fast-uri: 3.1.2
+ fast-uri: 3.1.0
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
@@ -8403,7 +7479,7 @@ snapshots:
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
- picomatch: 2.3.2
+ picomatch: 2.3.1
appdirsjs@1.2.7: {}
@@ -8422,52 +7498,52 @@ snapshots:
array-includes@3.1.9:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-abstract: 1.24.2
- es-object-atoms: 1.1.2
+ es-abstract: 1.24.1
+ 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.9
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.24.2
+ es-abstract: 1.24.1
es-errors: 1.3.0
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
es-shim-unscopables: 1.1.0
array.prototype.flat@1.3.3:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.24.2
+ es-abstract: 1.24.1
es-shim-unscopables: 1.1.0
array.prototype.flatmap@1.3.3:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.24.2
+ es-abstract: 1.24.1
es-shim-unscopables: 1.1.0
array.prototype.tosorted@1.1.4:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.24.2
+ es-abstract: 1.24.1
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.9
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.24.2
+ es-abstract: 1.24.1
es-errors: 1.3.0
get-intrinsic: 1.3.0
is-array-buffer: 3.0.5
@@ -8502,13 +7578,13 @@ snapshots:
axobject-query@4.1.0: {}
- babel-jest@29.7.0(@babel/core@7.29.7):
+ babel-jest@29.7.0(@babel/core@7.29.0):
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.0
'@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.29.7)
+ babel-preset-jest: 29.6.3(@babel/core@7.29.0)
chalk: 4.1.2
graceful-fs: 4.2.11
slash: 3.0.0
@@ -8517,9 +7593,9 @@ snapshots:
babel-plugin-istanbul@6.1.1:
dependencies:
- '@babel/helper-plugin-utils': 7.29.7
+ '@babel/helper-plugin-utils': 7.28.6
'@istanbuljs/load-nyc-config': 1.1.0
- '@istanbuljs/schema': 0.1.6
+ '@istanbuljs/schema': 0.1.3
istanbul-lib-instrument: 5.2.1
test-exclude: 6.0.0
transitivePeerDependencies:
@@ -8527,40 +7603,40 @@ snapshots:
babel-plugin-jest-hoist@29.6.3:
dependencies:
- '@babel/template': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
'@types/babel__core': 7.20.5
'@types/babel__traverse': 7.28.0
- babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7):
+ babel-plugin-polyfill-corejs2@0.4.16(@babel/core@7.29.0):
dependencies:
- '@babel/compat-data': 7.29.7
- '@babel/core': 7.29.7
- '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7)
+ '@babel/compat-data': 7.29.0
+ '@babel/core': 7.29.0
+ '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0)
semver: 6.3.1
transitivePeerDependencies:
- supports-color
- babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.7):
+ babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.0):
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7)
- core-js-compat: 3.49.0
+ '@babel/core': 7.29.0
+ '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0)
+ core-js-compat: 3.48.0
transitivePeerDependencies:
- supports-color
- babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.7):
+ babel-plugin-polyfill-corejs3@0.14.1(@babel/core@7.29.0):
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7)
- core-js-compat: 3.49.0
+ '@babel/core': 7.29.0
+ '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0)
+ core-js-compat: 3.48.0
transitivePeerDependencies:
- supports-color
- babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7):
+ babel-plugin-polyfill-regenerator@0.6.7(@babel/core@7.29.0):
dependencies:
- '@babel/core': 7.29.7
- '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7)
+ '@babel/core': 7.29.0
+ '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0)
transitivePeerDependencies:
- supports-color
@@ -8568,46 +7644,44 @@ snapshots:
dependencies:
hermes-parser: 0.32.0
- babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.7):
+ babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.0):
dependencies:
- '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.7)
+ '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0)
transitivePeerDependencies:
- '@babel/core'
- babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.7):
- dependencies:
- '@babel/core': 7.29.7
- '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.7)
- '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.7)
- '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.7)
- '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.7)
- '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7)
- '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.7)
- '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.7)
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7)
- '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.7)
- '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.7)
- '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.7)
- '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7)
- '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7)
- '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7)
-
- babel-preset-jest@29.6.3(@babel/core@7.29.7):
- dependencies:
- '@babel/core': 7.29.7
+ babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0):
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0)
+ '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0)
+ '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0)
+ '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0)
+ '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0)
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0)
+ '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0)
+
+ babel-preset-jest@29.6.3(@babel/core@7.29.0):
+ dependencies:
+ '@babel/core': 7.29.0
babel-plugin-jest-hoist: 29.6.3
- babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7)
+ babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0)
balanced-match@1.0.2: {}
balanced-match@4.0.4: {}
- base64-arraybuffer@1.0.2: {}
-
base64-js@1.5.1: {}
- baseline-browser-mapping@2.10.33: {}
+ baseline-browser-mapping@2.10.0: {}
bl@4.1.0:
dependencies:
@@ -8617,7 +7691,7 @@ snapshots:
bn.js@4.12.3: {}
- body-parser@1.20.5:
+ body-parser@1.20.4:
dependencies:
bytes: 3.1.2
content-type: 1.0.5
@@ -8627,7 +7701,7 @@ snapshots:
http-errors: 2.0.1
iconv-lite: 0.4.24
on-finished: 2.4.1
- qs: 6.15.2
+ qs: 6.14.2
raw-body: 2.5.3
type-is: 1.6.18
unpipe: 1.0.0
@@ -8636,12 +7710,12 @@ snapshots:
boolbase@1.0.0: {}
- brace-expansion@1.1.15:
+ brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
- brace-expansion@5.0.6:
+ brace-expansion@5.0.4:
dependencies:
balanced-match: 4.0.4
@@ -8649,13 +7723,13 @@ snapshots:
dependencies:
fill-range: 7.1.1
- browserslist@4.28.2:
+ browserslist@4.28.1:
dependencies:
- baseline-browser-mapping: 2.10.33
- caniuse-lite: 1.0.30001793
- electron-to-chromium: 1.5.364
- node-releases: 2.0.46
- update-browserslist-db: 1.2.3(browserslist@4.28.2)
+ baseline-browser-mapping: 2.10.0
+ caniuse-lite: 1.0.30001778
+ electron-to-chromium: 1.5.313
+ node-releases: 2.0.36
+ update-browserslist-db: 1.2.3(browserslist@4.28.1)
bser@2.1.1:
dependencies:
@@ -8668,23 +7742,21 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
- builtin-modules@5.2.0: {}
-
bytes@3.1.2: {}
c12@3.1.0:
dependencies:
chokidar: 4.0.3
confbox: 0.2.4
- defu: 6.1.7
+ defu: 6.1.4
dotenv: 16.6.1
exsolve: 1.0.8
giget: 2.0.0
- jiti: 2.7.0
+ jiti: 2.6.1
ohash: 2.0.11
pathe: 2.0.3
perfect-debounce: 1.0.0
- pkg-types: 2.3.1
+ pkg-types: 2.3.0
rc9: 2.1.2
cac@6.7.14: {}
@@ -8694,7 +7766,7 @@ snapshots:
es-errors: 1.3.0
function-bind: 1.1.2
- call-bind@1.0.9:
+ call-bind@1.0.8:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
@@ -8712,7 +7784,7 @@ snapshots:
camelcase@6.3.0: {}
- caniuse-lite@1.0.30001793: {}
+ caniuse-lite@1.0.30001778: {}
chai@5.3.3:
dependencies:
@@ -8727,8 +7799,6 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
- change-case@5.4.4: {}
-
char-regex@1.0.2: {}
check-error@2.1.3: {}
@@ -8739,7 +7809,7 @@ snapshots:
chrome-launcher@0.15.2:
dependencies:
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@@ -8748,7 +7818,7 @@ snapshots:
chromium-edge-launcher@0.2.0:
dependencies:
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@@ -8761,20 +7831,14 @@ snapshots:
ci-info@3.9.0: {}
- ci-info@4.4.0: {}
-
citty@0.1.6:
dependencies:
consola: 3.4.2
- citty@0.2.2: {}
+ citty@0.2.1: {}
cjs-module-lexer@1.4.3: {}
- clean-regexp@1.0.0:
- dependencies:
- escape-string-regexp: 1.0.5
-
cli-cursor@3.1.0:
dependencies:
restore-cursor: 3.1.0
@@ -8803,7 +7867,7 @@ snapshots:
clsx@2.1.1: {}
- cluster-key-slot@1.1.1: {}
+ cluster-key-slot@1.1.2: {}
co@4.6.0: {}
@@ -8843,8 +7907,6 @@ snapshots:
commander@9.5.0: {}
- comment-parser@1.4.7: {}
-
compressible@2.0.18:
dependencies:
mime-db: 1.54.0
@@ -8897,9 +7959,9 @@ snapshots:
cookie@1.1.1: {}
- core-js-compat@3.49.0:
+ core-js-compat@3.48.0:
dependencies:
- browserslist: 4.28.2
+ browserslist: 4.28.1
cosmiconfig@9.0.1(typescript@5.9.3):
dependencies:
@@ -8910,13 +7972,13 @@ snapshots:
optionalDependencies:
typescript: 5.9.3
- create-jest@29.7.0(@types/node@22.19.19):
+ create-jest@29.7.0(@types/node@22.19.15):
dependencies:
'@jest/types': 29.6.3
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
- jest-config: 29.7.0(@types/node@22.19.19)
+ jest-config: 29.7.0(@types/node@22.19.15)
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@@ -8941,10 +8003,6 @@ snapshots:
dependencies:
hyphenate-style-name: 1.1.0
- css-line-break@2.1.0:
- dependencies:
- utrie: 1.0.2
-
css-select@5.2.2:
dependencies:
boolbase: 1.0.0
@@ -8982,7 +8040,7 @@ snapshots:
dateformat@4.6.3: {}
- dayjs@1.11.21: {}
+ dayjs@1.11.20: {}
debug@2.6.9:
dependencies:
@@ -9022,7 +8080,7 @@ snapshots:
has-property-descriptors: 1.0.2
object-keys: 1.1.1
- defu@6.1.7: {}
+ defu@6.1.4: {}
denque@2.1.0: {}
@@ -9036,7 +8094,7 @@ snapshots:
detect-newline@3.1.0: {}
- devalue@5.8.1: {}
+ devalue@5.6.4: {}
diff-sequences@29.6.3: {}
@@ -9082,12 +8140,12 @@ snapshots:
ee-first@1.1.1: {}
- effect@3.21.0:
+ effect@3.18.4:
dependencies:
'@standard-schema/spec': 1.1.0
fast-check: 3.23.2
- electron-to-chromium@1.5.364: {}
+ electron-to-chromium@1.5.313: {}
emittery@0.13.1: {}
@@ -9103,11 +8161,6 @@ snapshots:
dependencies:
once: 1.4.0
- enhanced-resolve@5.22.1:
- dependencies:
- graceful-fs: 4.2.11
- tapable: 2.3.3
-
entities@4.5.0: {}
env-paths@2.2.1: {}
@@ -9127,19 +8180,19 @@ snapshots:
accepts: 1.3.8
escape-html: 1.0.3
- es-abstract@1.24.2:
+ es-abstract@1.24.1:
dependencies:
array-buffer-byte-length: 1.0.2
arraybuffer.prototype.slice: 1.0.4
available-typed-arrays: 1.0.7
- call-bind: 1.0.9
+ 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.2
+ es-object-atoms: 1.1.1
es-set-tostringtag: 2.1.0
es-to-primitive: 1.3.0
function.prototype.name: 1.1.8
@@ -9151,7 +8204,7 @@ snapshots:
has-property-descriptors: 1.0.2
has-proto: 1.2.0
has-symbols: 1.1.0
- hasown: 2.0.4
+ hasown: 2.0.2
internal-slot: 1.1.0
is-array-buffer: 3.0.5
is-callable: 1.2.7
@@ -9169,7 +8222,7 @@ snapshots:
object.assign: 4.1.7
own-keys: 1.0.1
regexp.prototype.flags: 1.5.4
- safe-array-concat: 1.1.4
+ safe-array-concat: 1.1.3
safe-push-apply: 1.0.0
safe-regex-test: 1.1.0
set-proto: 1.0.0
@@ -9180,20 +8233,20 @@ snapshots:
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.8
+ typed-array-length: 1.0.7
unbox-primitive: 1.1.0
- which-typed-array: 1.1.21
+ which-typed-array: 1.1.20
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
- es-iterator-helpers@1.3.2:
+ es-iterator-helpers@1.3.0:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-abstract: 1.24.2
+ es-abstract: 1.24.1
es-errors: 1.3.0
es-set-tostringtag: 2.1.0
function-bind: 1.1.2
@@ -9206,10 +8259,11 @@ snapshots:
internal-slot: 1.1.0
iterator.prototype: 1.1.5
math-intrinsics: 1.1.0
+ safe-array-concat: 1.1.3
es-module-lexer@1.7.0: {}
- es-object-atoms@1.1.2:
+ es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
@@ -9218,11 +8272,11 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
- hasown: 2.0.4
+ hasown: 2.0.2
es-shim-unscopables@1.1.0:
dependencies:
- hasown: 2.0.4
+ hasown: 2.0.2
es-to-primitive@1.3.0:
dependencies:
@@ -9256,63 +8310,34 @@ snapshots:
'@esbuild/win32-ia32': 0.21.5
'@esbuild/win32-x64': 0.21.5
- esbuild@0.27.7:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.27.7
- '@esbuild/android-arm': 0.27.7
- '@esbuild/android-arm64': 0.27.7
- '@esbuild/android-x64': 0.27.7
- '@esbuild/darwin-arm64': 0.27.7
- '@esbuild/darwin-x64': 0.27.7
- '@esbuild/freebsd-arm64': 0.27.7
- '@esbuild/freebsd-x64': 0.27.7
- '@esbuild/linux-arm': 0.27.7
- '@esbuild/linux-arm64': 0.27.7
- '@esbuild/linux-ia32': 0.27.7
- '@esbuild/linux-loong64': 0.27.7
- '@esbuild/linux-mips64el': 0.27.7
- '@esbuild/linux-ppc64': 0.27.7
- '@esbuild/linux-riscv64': 0.27.7
- '@esbuild/linux-s390x': 0.27.7
- '@esbuild/linux-x64': 0.27.7
- '@esbuild/netbsd-arm64': 0.27.7
- '@esbuild/netbsd-x64': 0.27.7
- '@esbuild/openbsd-arm64': 0.27.7
- '@esbuild/openbsd-x64': 0.27.7
- '@esbuild/openharmony-arm64': 0.27.7
- '@esbuild/sunos-x64': 0.27.7
- '@esbuild/win32-arm64': 0.27.7
- '@esbuild/win32-ia32': 0.27.7
- '@esbuild/win32-x64': 0.27.7
-
- esbuild@0.28.0:
+ esbuild@0.27.3:
optionalDependencies:
- '@esbuild/aix-ppc64': 0.28.0
- '@esbuild/android-arm': 0.28.0
- '@esbuild/android-arm64': 0.28.0
- '@esbuild/android-x64': 0.28.0
- '@esbuild/darwin-arm64': 0.28.0
- '@esbuild/darwin-x64': 0.28.0
- '@esbuild/freebsd-arm64': 0.28.0
- '@esbuild/freebsd-x64': 0.28.0
- '@esbuild/linux-arm': 0.28.0
- '@esbuild/linux-arm64': 0.28.0
- '@esbuild/linux-ia32': 0.28.0
- '@esbuild/linux-loong64': 0.28.0
- '@esbuild/linux-mips64el': 0.28.0
- '@esbuild/linux-ppc64': 0.28.0
- '@esbuild/linux-riscv64': 0.28.0
- '@esbuild/linux-s390x': 0.28.0
- '@esbuild/linux-x64': 0.28.0
- '@esbuild/netbsd-arm64': 0.28.0
- '@esbuild/netbsd-x64': 0.28.0
- '@esbuild/openbsd-arm64': 0.28.0
- '@esbuild/openbsd-x64': 0.28.0
- '@esbuild/openharmony-arm64': 0.28.0
- '@esbuild/sunos-x64': 0.28.0
- '@esbuild/win32-arm64': 0.28.0
- '@esbuild/win32-ia32': 0.28.0
- '@esbuild/win32-x64': 0.28.0
+ '@esbuild/aix-ppc64': 0.27.3
+ '@esbuild/android-arm': 0.27.3
+ '@esbuild/android-arm64': 0.27.3
+ '@esbuild/android-x64': 0.27.3
+ '@esbuild/darwin-arm64': 0.27.3
+ '@esbuild/darwin-x64': 0.27.3
+ '@esbuild/freebsd-arm64': 0.27.3
+ '@esbuild/freebsd-x64': 0.27.3
+ '@esbuild/linux-arm': 0.27.3
+ '@esbuild/linux-arm64': 0.27.3
+ '@esbuild/linux-ia32': 0.27.3
+ '@esbuild/linux-loong64': 0.27.3
+ '@esbuild/linux-mips64el': 0.27.3
+ '@esbuild/linux-ppc64': 0.27.3
+ '@esbuild/linux-riscv64': 0.27.3
+ '@esbuild/linux-s390x': 0.27.3
+ '@esbuild/linux-x64': 0.27.3
+ '@esbuild/netbsd-arm64': 0.27.3
+ '@esbuild/netbsd-x64': 0.27.3
+ '@esbuild/openbsd-arm64': 0.27.3
+ '@esbuild/openbsd-x64': 0.27.3
+ '@esbuild/openharmony-arm64': 0.27.3
+ '@esbuild/sunos-x64': 0.27.3
+ '@esbuild/win32-arm64': 0.27.3
+ '@esbuild/win32-ia32': 0.27.3
+ '@esbuild/win32-x64': 0.27.3
escalade@3.2.0: {}
@@ -9324,109 +8349,38 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-compat-utils@0.5.1(eslint@10.4.1(jiti@2.7.0)):
- dependencies:
- eslint: 10.4.1(jiti@2.7.0)
- semver: 7.8.1
-
eslint-config-prettier@8.10.2(eslint@8.57.1):
dependencies:
eslint: 8.57.1
- eslint-import-context@0.1.9(unrs-resolver@1.12.2):
- dependencies:
- get-tsconfig: 4.14.0
- stable-hash-x: 0.2.0
- optionalDependencies:
- unrs-resolver: 1.12.2
-
- eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3))(eslint@10.4.1(jiti@2.7.0)))(eslint@10.4.1(jiti@2.7.0)):
- dependencies:
- debug: 4.4.3
- eslint: 10.4.1(jiti@2.7.0)
- eslint-import-context: 0.1.9(unrs-resolver@1.12.2)
- get-tsconfig: 4.14.0
- is-bun-module: 2.0.0
- stable-hash-x: 0.2.0
- tinyglobby: 0.2.16
- unrs-resolver: 1.12.2
- optionalDependencies:
- eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3))(eslint@10.4.1(jiti@2.7.0))
- transitivePeerDependencies:
- - supports-color
-
- eslint-plugin-es-x@7.8.0(eslint@10.4.1(jiti@2.7.0)):
- dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0))
- '@eslint-community/regexpp': 4.12.2
- eslint: 10.4.1(jiti@2.7.0)
- eslint-compat-utils: 0.5.1(eslint@10.4.1(jiti@2.7.0))
-
eslint-plugin-eslint-comments@3.2.0(eslint@8.57.1):
dependencies:
escape-string-regexp: 1.0.5
eslint: 8.57.1
ignore: 5.3.2
- eslint-plugin-ft-flow@2.0.3(@babel/eslint-parser@7.29.7(@babel/core@7.29.7)(eslint@8.57.1))(eslint@8.57.1):
+ eslint-plugin-ft-flow@2.0.3(@babel/eslint-parser@7.28.6(@babel/core@7.29.0)(eslint@8.57.1))(eslint@8.57.1):
dependencies:
- '@babel/eslint-parser': 7.29.7(@babel/core@7.29.7)(eslint@8.57.1)
+ '@babel/eslint-parser': 7.28.6(@babel/core@7.29.0)(eslint@8.57.1)
eslint: 8.57.1
- lodash: 4.18.1
+ lodash: 4.17.23
string-natural-compare: 3.0.1
- eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3))(eslint@10.4.1(jiti@2.7.0)):
- dependencies:
- '@package-json/types': 0.0.12
- '@typescript-eslint/types': 8.60.0
- comment-parser: 1.4.7
- debug: 4.4.3
- eslint: 10.4.1(jiti@2.7.0)
- eslint-import-context: 0.1.9(unrs-resolver@1.12.2)
- is-glob: 4.0.3
- minimatch: 10.2.5
- semver: 7.8.1
- stable-hash-x: 0.2.0
- unrs-resolver: 1.12.2
- optionalDependencies:
- '@typescript-eslint/utils': 8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
- transitivePeerDependencies:
- - supports-color
-
- eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@22.19.19))(typescript@5.9.3):
+ eslint-plugin-jest@29.15.0(@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(jest@29.7.0(@types/node@22.19.15))(typescript@5.9.3):
dependencies:
- '@typescript-eslint/utils': 8.60.0(eslint@8.57.1)(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.57.0(eslint@8.57.1)(typescript@5.9.3)
eslint: 8.57.1
optionalDependencies:
- '@typescript-eslint/eslint-plugin': 8.60.0(@typescript-eslint/parser@8.60.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
- jest: 29.7.0(@types/node@22.19.19)
+ '@typescript-eslint/eslint-plugin': 8.57.0(@typescript-eslint/parser@8.57.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
+ jest: 29.7.0(@types/node@22.19.15)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- eslint-plugin-n@18.0.1(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3):
- dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0))
- enhanced-resolve: 5.22.1
- eslint: 10.4.1(jiti@2.7.0)
- eslint-plugin-es-x: 7.8.0(eslint@10.4.1(jiti@2.7.0))
- get-tsconfig: 4.14.0
- globals: 15.15.0
- globrex: 0.1.2
- ignore: 5.3.2
- semver: 7.8.1
- optionalDependencies:
- typescript: 5.9.3
-
- eslint-plugin-promise@7.3.0(eslint@10.4.1(jiti@2.7.0)):
- dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0))
- eslint: 10.4.1(jiti@2.7.0)
-
- eslint-plugin-react-hooks@7.1.1(eslint@8.57.1):
+ eslint-plugin-react-hooks@7.0.1(eslint@8.57.1):
dependencies:
- '@babel/core': 7.29.7
- '@babel/parser': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/parser': 7.29.0
eslint: 8.57.1
hermes-parser: 0.25.1
zod: 3.25.76
@@ -9448,45 +8402,21 @@ snapshots:
array.prototype.flatmap: 1.3.3
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
- es-iterator-helpers: 1.3.2
+ es-iterator-helpers: 1.3.0
eslint: 8.57.1
estraverse: 5.3.0
- hasown: 2.0.4
+ hasown: 2.0.2
jsx-ast-utils: 3.3.5
minimatch: 3.1.5
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.7
+ resolve: 2.0.0-next.6
semver: 6.3.1
string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0
- eslint-plugin-security@4.0.0:
- dependencies:
- safe-regex: 2.1.1
-
- eslint-plugin-unicorn@64.0.0(eslint@10.4.1(jiti@2.7.0)):
- dependencies:
- '@babel/helper-validator-identifier': 7.29.7
- '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0))
- change-case: 5.4.4
- ci-info: 4.4.0
- clean-regexp: 1.0.0
- core-js-compat: 3.49.0
- eslint: 10.4.1(jiti@2.7.0)
- find-up-simple: 1.0.1
- globals: 17.6.0
- indent-string: 5.0.0
- is-builtin-module: 5.0.0
- jsesc: 3.1.0
- pluralize: 8.0.0
- regexp-tree: 0.1.27
- regjsparser: 0.13.1
- semver: 7.8.1
- strip-indent: 4.1.1
-
eslint-scope@5.1.1:
dependencies:
esrecurse: 4.3.0
@@ -9497,56 +8427,12 @@ snapshots:
esrecurse: 4.3.0
estraverse: 5.3.0
- eslint-scope@9.1.2:
- dependencies:
- '@types/esrecurse': 4.3.1
- '@types/estree': 1.0.9
- esrecurse: 4.3.0
- estraverse: 5.3.0
-
eslint-visitor-keys@2.1.0: {}
eslint-visitor-keys@3.4.3: {}
eslint-visitor-keys@5.0.1: {}
- eslint@10.4.1(jiti@2.7.0):
- dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.1(jiti@2.7.0))
- '@eslint-community/regexpp': 4.12.2
- '@eslint/config-array': 0.23.5
- '@eslint/config-helpers': 0.6.0
- '@eslint/core': 1.2.1
- '@eslint/plugin-kit': 0.7.2
- '@humanfs/node': 0.16.8
- '@humanwhocodes/module-importer': 1.0.1
- '@humanwhocodes/retry': 0.4.3
- '@types/estree': 1.0.9
- ajv: 6.15.0
- cross-spawn: 7.0.6
- debug: 4.4.3
- escape-string-regexp: 4.0.0
- eslint-scope: 9.1.2
- eslint-visitor-keys: 5.0.1
- espree: 11.2.0
- esquery: 1.7.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
- minimatch: 10.2.5
- natural-compare: 1.4.0
- optionator: 0.9.4
- optionalDependencies:
- jiti: 2.7.0
- transitivePeerDependencies:
- - supports-color
-
eslint@8.57.1:
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
@@ -9556,8 +8442,8 @@ snapshots:
'@humanwhocodes/config-array': 0.13.0
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
- '@ungap/structured-clone': 1.3.1
- ajv: 6.15.0
+ '@ungap/structured-clone': 1.3.0
+ ajv: 6.14.0
chalk: 4.1.2
cross-spawn: 7.0.6
debug: 4.4.3
@@ -9592,12 +8478,6 @@ snapshots:
esm-env@1.2.2: {}
- espree@11.2.0:
- dependencies:
- acorn: 8.16.0
- acorn-jsx: 5.3.2(acorn@8.16.0)
- eslint-visitor-keys: 5.0.1
-
espree@9.6.1:
dependencies:
acorn: 8.16.0
@@ -9610,11 +8490,9 @@ snapshots:
dependencies:
estraverse: 5.3.0
- esrap@2.2.9(@typescript-eslint/types@8.60.0):
+ esrap@2.2.3:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
- optionalDependencies:
- '@typescript-eslint/types': 8.60.0
esrecurse@4.3.0:
dependencies:
@@ -9626,7 +8504,7 @@ snapshots:
estree-walker@3.0.3:
dependencies:
- '@types/estree': 1.0.9
+ '@types/estree': 1.0.8
esutils@2.0.3: {}
@@ -9666,7 +8544,7 @@ snapshots:
dependencies:
pure-rand: 6.1.0
- fast-copy@4.0.3: {}
+ fast-copy@4.0.2: {}
fast-decode-uri-component@1.0.1: {}
@@ -9682,12 +8560,12 @@ snapshots:
fast-json-stable-stringify@2.1.0: {}
- fast-json-stringify@6.4.0:
+ fast-json-stringify@6.3.0:
dependencies:
'@fastify/merge-json-schemas': 0.2.1
- ajv: 8.20.0
- ajv-formats: 3.0.1(ajv@8.20.0)
- fast-uri: 3.1.2
+ ajv: 8.18.0
+ ajv-formats: 3.0.1(ajv@8.18.0)
+ fast-uri: 3.1.0
json-schema-ref-resolver: 3.0.0
rfdc: 1.4.1
@@ -9696,7 +8574,7 @@ snapshots:
'@lukeed/ms': 2.0.2
asn1.js: 5.4.1
ecdsa-sig-formatter: 1.0.11
- mnemonist: 0.40.4
+ mnemonist: 0.40.3
fast-levenshtein@2.0.6: {}
@@ -9706,9 +8584,9 @@ snapshots:
fast-safe-stringify@2.1.1: {}
- fast-uri@3.1.2: {}
+ fast-uri@3.1.0: {}
- fast-xml-parser@4.5.6:
+ fast-xml-parser@4.5.4:
dependencies:
strnum: 1.1.2
@@ -9718,7 +8596,7 @@ snapshots:
fastify-plugin@5.1.0: {}
- fastify@5.8.5:
+ fastify@5.8.2:
dependencies:
'@fastify/ajv-compiler': 4.0.5
'@fastify/error': 4.2.0
@@ -9726,15 +8604,15 @@ snapshots:
'@fastify/proxy-addr': 5.1.0
abstract-logging: 2.0.1
avvio: 9.2.0
- fast-json-stringify: 6.4.0
- find-my-way: 9.6.0
+ fast-json-stringify: 6.3.0
+ find-my-way: 9.5.0
light-my-request: 6.6.0
pino: 10.3.1
process-warning: 5.0.0
rfdc: 1.4.1
secure-json-parse: 4.1.0
- semver: 7.8.1
- toad-cache: 3.7.1
+ semver: 7.7.4
+ toad-cache: 3.7.0
fastparallel@2.4.1:
dependencies:
@@ -9770,18 +8648,14 @@ snapshots:
transitivePeerDependencies:
- encoding
- fdir@6.5.0(picomatch@4.0.4):
+ fdir@6.5.0(picomatch@4.0.3):
optionalDependencies:
- picomatch: 4.0.4
+ picomatch: 4.0.3
file-entry-cache@6.0.1:
dependencies:
flat-cache: 3.2.0
- file-entry-cache@8.0.0:
- dependencies:
- flat-cache: 4.0.1
-
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -9800,13 +8674,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- find-my-way@9.6.0:
+ find-my-way@9.5.0:
dependencies:
fast-deep-equal: 3.1.3
fast-querystring: 1.1.2
- safe-regex2: 5.1.1
-
- find-up-simple@1.0.1: {}
+ safe-regex2: 5.0.0
find-up@4.1.0:
dependencies:
@@ -9820,16 +8692,11 @@ snapshots:
flat-cache@3.2.0:
dependencies:
- flatted: 3.4.2
+ flatted: 3.4.1
keyv: 4.5.4
rimraf: 3.0.2
- flat-cache@4.0.1:
- dependencies:
- flatted: 3.4.2
- keyv: 4.5.4
-
- flatted@3.4.2: {}
+ flatted@3.4.1: {}
flow-enums-runtime@0.0.6: {}
@@ -9859,11 +8726,11 @@ snapshots:
function.prototype.name@1.1.8:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
functions-have-names: 1.2.3
- hasown: 2.0.4
+ hasown: 2.0.2
is-callable: 1.2.7
functions-have-names@1.2.3: {}
@@ -9879,12 +8746,12 @@ snapshots:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
- es-object-atoms: 1.1.2
+ 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.4
+ hasown: 2.0.2
math-intrinsics: 1.1.0
get-package-type@0.1.0: {}
@@ -9892,7 +8759,7 @@ snapshots:
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
get-stream@6.0.1: {}
@@ -9902,7 +8769,7 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.3.0
- get-tsconfig@4.14.0:
+ get-tsconfig@4.13.6:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -9910,9 +8777,9 @@ snapshots:
dependencies:
citty: 0.1.6
consola: 3.4.2
- defu: 6.1.7
+ defu: 6.1.4
node-fetch-native: 1.6.7
- nypm: 0.6.6
+ nypm: 0.6.5
pathe: 2.0.3
glob-parent@5.1.2:
@@ -9927,7 +8794,7 @@ snapshots:
dependencies:
foreground-child: 3.3.1
jackspeak: 4.2.3
- minimatch: 10.2.5
+ minimatch: 10.2.4
minipass: 7.1.3
package-json-from-dist: 1.0.1
path-scurry: 2.0.2
@@ -9945,17 +8812,11 @@ snapshots:
dependencies:
type-fest: 0.20.2
- globals@15.15.0: {}
-
- globals@17.6.0: {}
-
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
gopd: 1.2.0
- globrex@0.1.2: {}
-
gopd@1.2.0: {}
graceful-fs@4.2.11: {}
@@ -9980,7 +8841,7 @@ snapshots:
dependencies:
has-symbols: 1.1.0
- hasown@2.0.4:
+ hasown@2.0.2:
dependencies:
function-bind: 1.1.2
@@ -9994,7 +8855,7 @@ snapshots:
hermes-estree@0.32.0: {}
- hermes-estree@0.35.0: {}
+ hermes-estree@0.33.3: {}
hermes-parser@0.25.1:
dependencies:
@@ -10004,9 +8865,9 @@ snapshots:
dependencies:
hermes-estree: 0.32.0
- hermes-parser@0.35.0:
+ hermes-parser@0.33.3:
dependencies:
- hermes-estree: 0.35.0
+ hermes-estree: 0.33.3
hoist-non-react-statics@3.3.2:
dependencies:
@@ -10014,11 +8875,6 @@ snapshots:
html-escaper@2.0.2: {}
- html2canvas@1.4.1:
- dependencies:
- css-line-break: 2.1.0
- text-segmentation: 1.0.3
-
http-errors@2.0.1:
dependencies:
depd: 2.0.0
@@ -10064,8 +8920,6 @@ snapshots:
imurmurhash@0.1.4: {}
- indent-string@5.0.0: {}
-
inflight@1.0.6:
dependencies:
once: 1.4.0
@@ -10080,30 +8934,32 @@ snapshots:
internal-slot@1.1.0:
dependencies:
es-errors: 1.3.0
- hasown: 2.0.4
+ hasown: 2.0.2
side-channel: 1.1.0
invariant@2.2.4:
dependencies:
loose-envify: 1.4.0
- ioredis@5.11.0:
+ ioredis@5.10.0:
dependencies:
- '@ioredis/commands': 1.10.0
- cluster-key-slot: 1.1.1
+ '@ioredis/commands': 1.5.1
+ cluster-key-slot: 1.1.2
debug: 4.4.3
denque: 2.1.0
+ lodash.defaults: 4.2.0
+ lodash.isarguments: 3.1.0
redis-errors: 1.2.0
redis-parser: 3.0.0
standard-as-callback: 2.1.0
transitivePeerDependencies:
- supports-color
- ipaddr.js@2.4.0: {}
+ ipaddr.js@2.3.0: {}
is-array-buffer@3.0.5:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
get-intrinsic: 1.3.0
@@ -10128,19 +8984,11 @@ snapshots:
call-bound: 1.0.4
has-tostringtag: 1.0.2
- is-builtin-module@5.0.0:
- dependencies:
- builtin-modules: 5.2.0
-
- is-bun-module@2.0.0:
- dependencies:
- semver: 7.8.1
-
is-callable@1.2.7: {}
- is-core-module@2.16.2:
+ is-core-module@2.16.1:
dependencies:
- hasown: 2.0.4
+ hasown: 2.0.2
is-data-view@1.0.2:
dependencies:
@@ -10198,14 +9046,14 @@ snapshots:
is-reference@3.0.3:
dependencies:
- '@types/estree': 1.0.9
+ '@types/estree': 1.0.8
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
gopd: 1.2.0
has-tostringtag: 1.0.2
- hasown: 2.0.4
+ hasown: 2.0.2
is-set@2.0.3: {}
@@ -10228,7 +9076,7 @@ snapshots:
is-typed-array@1.1.15:
dependencies:
- which-typed-array: 1.1.21
+ which-typed-array: 1.1.20
is-unicode-supported@0.1.0: {}
@@ -10257,9 +9105,9 @@ snapshots:
istanbul-lib-instrument@5.2.1:
dependencies:
- '@babel/core': 7.29.7
- '@babel/parser': 7.29.7
- '@istanbuljs/schema': 0.1.6
+ '@babel/core': 7.29.0
+ '@babel/parser': 7.29.0
+ '@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 6.3.1
transitivePeerDependencies:
@@ -10267,11 +9115,11 @@ snapshots:
istanbul-lib-instrument@6.0.3:
dependencies:
- '@babel/core': 7.29.7
- '@babel/parser': 7.29.7
- '@istanbuljs/schema': 0.1.6
+ '@babel/core': 7.29.0
+ '@babel/parser': 7.29.0
+ '@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
- semver: 7.8.1
+ semver: 7.7.4
transitivePeerDependencies:
- supports-color
@@ -10297,7 +9145,7 @@ snapshots:
iterator.prototype@1.1.5:
dependencies:
define-data-property: 1.1.4
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
get-proto: 1.0.1
has-symbols: 1.1.0
@@ -10319,7 +9167,7 @@ snapshots:
'@jest/expect': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
chalk: 4.1.2
co: 4.6.0
dedent: 1.7.2
@@ -10339,16 +9187,16 @@ snapshots:
- babel-plugin-macros
- supports-color
- jest-cli@29.7.0(@types/node@22.19.19):
+ jest-cli@29.7.0(@types/node@22.19.15):
dependencies:
'@jest/core': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
- create-jest: 29.7.0(@types/node@22.19.19)
+ create-jest: 29.7.0(@types/node@22.19.15)
exit: 0.1.2
import-local: 3.2.0
- jest-config: 29.7.0(@types/node@22.19.19)
+ jest-config: 29.7.0(@types/node@22.19.15)
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@@ -10358,12 +9206,12 @@ snapshots:
- supports-color
- ts-node
- jest-config@29.7.0(@types/node@22.19.19):
+ jest-config@29.7.0(@types/node@22.19.15):
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.0
'@jest/test-sequencer': 29.7.0
'@jest/types': 29.6.3
- babel-jest: 29.7.0(@babel/core@7.29.7)
+ babel-jest: 29.7.0(@babel/core@7.29.0)
chalk: 4.1.2
ci-info: 3.9.0
deepmerge: 4.3.1
@@ -10383,7 +9231,7 @@ snapshots:
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@@ -10412,7 +9260,7 @@ snapshots:
'@jest/environment': 29.7.0
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -10422,7 +9270,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@@ -10448,7 +9296,7 @@ snapshots:
jest-message-util@29.7.0:
dependencies:
- '@babel/code-frame': 7.29.7
+ '@babel/code-frame': 7.29.0
'@jest/types': 29.6.3
'@types/stack-utils': 2.0.3
chalk: 4.1.2
@@ -10461,7 +9309,7 @@ snapshots:
jest-mock@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
jest-util: 29.7.0
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@@ -10485,7 +9333,7 @@ snapshots:
jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0)
jest-util: 29.7.0
jest-validate: 29.7.0
- resolve: 1.22.12
+ resolve: 1.22.11
resolve.exports: 2.0.3
slash: 3.0.0
@@ -10496,7 +9344,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
@@ -10524,7 +9372,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
chalk: 4.1.2
cjs-module-lexer: 1.4.3
collect-v8-coverage: 1.0.3
@@ -10544,15 +9392,15 @@ snapshots:
jest-snapshot@29.7.0:
dependencies:
- '@babel/core': 7.29.7
- '@babel/generator': 7.29.7
- '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
- '@babel/types': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0)
+ '@babel/types': 7.29.0
'@jest/expect-utils': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7)
+ babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0)
chalk: 4.1.2
expect: 29.7.0
graceful-fs: 4.2.11
@@ -10563,18 +9411,18 @@ snapshots:
jest-util: 29.7.0
natural-compare: 1.4.0
pretty-format: 29.7.0
- semver: 7.8.1
+ semver: 7.7.4
transitivePeerDependencies:
- supports-color
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
- picomatch: 2.3.2
+ picomatch: 2.3.1
jest-validate@29.7.0:
dependencies:
@@ -10589,7 +9437,7 @@ snapshots:
dependencies:
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
@@ -10598,24 +9446,24 @@ snapshots:
jest-worker@29.7.0:
dependencies:
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
- jest@29.7.0(@types/node@22.19.19):
+ jest@29.7.0(@types/node@22.19.15):
dependencies:
'@jest/core': 29.7.0
'@jest/types': 29.6.3
import-local: 3.2.0
- jest-cli: 29.7.0(@types/node@22.19.19)
+ jest-cli: 29.7.0(@types/node@22.19.15)
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
- supports-color
- ts-node
- jiti@2.7.0: {}
+ jiti@2.6.1: {}
joi@17.13.3:
dependencies:
@@ -10677,10 +9525,10 @@ snapshots:
kleur@4.1.5: {}
- launch-editor@2.14.0:
+ launch-editor@2.13.1:
dependencies:
picocolors: 1.1.1
- shell-quote: 1.8.4
+ shell-quote: 1.8.3
leven@3.1.0: {}
@@ -10716,11 +9564,15 @@ snapshots:
lodash.debounce@4.0.8: {}
+ lodash.defaults@4.2.0: {}
+
+ lodash.isarguments@3.1.0: {}
+
lodash.merge@4.6.2: {}
lodash.throttle@4.1.1: {}
- lodash@4.18.1: {}
+ lodash@4.17.23: {}
log-symbols@4.1.0:
dependencies:
@@ -10730,7 +9582,7 @@ snapshots:
logkitty@0.7.1:
dependencies:
ansi-fragments: 0.2.1
- dayjs: 1.11.21
+ dayjs: 1.11.20
yargs: 15.4.1
loose-envify@1.4.0:
@@ -10739,7 +9591,7 @@ snapshots:
loupe@3.2.1: {}
- lru-cache@11.5.1: {}
+ lru-cache@11.2.6: {}
lru-cache@5.1.1:
dependencies:
@@ -10751,7 +9603,7 @@ snapshots:
make-dir@4.0.0:
dependencies:
- semver: 7.8.1
+ semver: 7.7.4
makeerror@1.0.12:
dependencies:
@@ -10777,51 +9629,50 @@ snapshots:
merge2@1.4.1: {}
- metro-babel-transformer@0.83.7:
+ metro-babel-transformer@0.83.5:
dependencies:
- '@babel/core': 7.29.7
+ '@babel/core': 7.29.0
flow-enums-runtime: 0.0.6
- hermes-parser: 0.35.0
- metro-cache-key: 0.83.7
+ hermes-parser: 0.33.3
nullthrows: 1.1.1
transitivePeerDependencies:
- supports-color
- metro-cache-key@0.83.7:
+ metro-cache-key@0.83.5:
dependencies:
flow-enums-runtime: 0.0.6
- metro-cache@0.83.7:
+ metro-cache@0.83.5:
dependencies:
exponential-backoff: 3.1.3
flow-enums-runtime: 0.0.6
https-proxy-agent: 7.0.6
- metro-core: 0.83.7
+ metro-core: 0.83.5
transitivePeerDependencies:
- supports-color
- metro-config@0.83.7:
+ metro-config@0.83.5:
dependencies:
connect: 3.7.0
flow-enums-runtime: 0.0.6
jest-validate: 29.7.0
- metro: 0.83.7
- metro-cache: 0.83.7
- metro-core: 0.83.7
- metro-runtime: 0.83.7
- yaml: 2.9.0
+ metro: 0.83.5
+ metro-cache: 0.83.5
+ metro-core: 0.83.5
+ metro-runtime: 0.83.5
+ yaml: 2.8.2
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
- metro-core@0.83.7:
+ metro-core@0.83.5:
dependencies:
flow-enums-runtime: 0.0.6
lodash.throttle: 4.1.1
- metro-resolver: 0.83.7
+ metro-resolver: 0.83.5
- metro-file-map@0.83.7:
+ metro-file-map@0.83.5:
dependencies:
debug: 4.4.3
fb-watchman: 2.0.2
@@ -10835,116 +9686,117 @@ snapshots:
transitivePeerDependencies:
- supports-color
- metro-minify-terser@0.83.7:
+ metro-minify-terser@0.83.5:
dependencies:
flow-enums-runtime: 0.0.6
- terser: 5.48.0
+ terser: 5.46.0
- metro-resolver@0.83.7:
+ metro-resolver@0.83.5:
dependencies:
flow-enums-runtime: 0.0.6
- metro-runtime@0.83.7:
+ metro-runtime@0.83.5:
dependencies:
- '@babel/runtime': 7.29.7
+ '@babel/runtime': 7.28.6
flow-enums-runtime: 0.0.6
- metro-source-map@0.83.7:
+ metro-source-map@0.83.5:
dependencies:
- '@babel/traverse': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
flow-enums-runtime: 0.0.6
invariant: 2.2.4
- metro-symbolicate: 0.83.7
+ metro-symbolicate: 0.83.5
nullthrows: 1.1.1
- ob1: 0.83.7
+ ob1: 0.83.5
source-map: 0.5.7
vlq: 1.0.1
transitivePeerDependencies:
- supports-color
- metro-symbolicate@0.83.7:
+ metro-symbolicate@0.83.5:
dependencies:
flow-enums-runtime: 0.0.6
invariant: 2.2.4
- metro-source-map: 0.83.7
+ metro-source-map: 0.83.5
nullthrows: 1.1.1
source-map: 0.5.7
vlq: 1.0.1
transitivePeerDependencies:
- supports-color
- metro-transform-plugins@0.83.7:
+ metro-transform-plugins@0.83.5:
dependencies:
- '@babel/core': 7.29.7
- '@babel/generator': 7.29.7
- '@babel/template': 7.29.7
- '@babel/traverse': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.29.0
flow-enums-runtime: 0.0.6
nullthrows: 1.1.1
transitivePeerDependencies:
- supports-color
- metro-transform-worker@0.83.7:
+ metro-transform-worker@0.83.5:
dependencies:
- '@babel/core': 7.29.7
- '@babel/generator': 7.29.7
- '@babel/parser': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/core': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
flow-enums-runtime: 0.0.6
- metro: 0.83.7
- metro-babel-transformer: 0.83.7
- metro-cache: 0.83.7
- metro-cache-key: 0.83.7
- metro-minify-terser: 0.83.7
- metro-source-map: 0.83.7
- metro-transform-plugins: 0.83.7
+ metro: 0.83.5
+ metro-babel-transformer: 0.83.5
+ metro-cache: 0.83.5
+ metro-cache-key: 0.83.5
+ metro-minify-terser: 0.83.5
+ metro-source-map: 0.83.5
+ metro-transform-plugins: 0.83.5
nullthrows: 1.1.1
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
- metro@0.83.7:
+ metro@0.83.5:
dependencies:
- '@babel/code-frame': 7.29.7
- '@babel/core': 7.29.7
- '@babel/generator': 7.29.7
- '@babel/parser': 7.29.7
- '@babel/template': 7.29.7
- '@babel/traverse': 7.29.7
- '@babel/types': 7.29.7
+ '@babel/code-frame': 7.29.0
+ '@babel/core': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/parser': 7.29.0
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
accepts: 2.0.0
+ 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.35.0
+ hermes-parser: 0.33.3
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.7
- metro-cache: 0.83.7
- metro-cache-key: 0.83.7
- metro-config: 0.83.7
- metro-core: 0.83.7
- metro-file-map: 0.83.7
- metro-resolver: 0.83.7
- metro-runtime: 0.83.7
- metro-source-map: 0.83.7
- metro-symbolicate: 0.83.7
- metro-transform-plugins: 0.83.7
- metro-transform-worker: 0.83.7
+ metro-babel-transformer: 0.83.5
+ metro-cache: 0.83.5
+ metro-cache-key: 0.83.5
+ metro-config: 0.83.5
+ metro-core: 0.83.5
+ metro-file-map: 0.83.5
+ metro-resolver: 0.83.5
+ metro-runtime: 0.83.5
+ metro-source-map: 0.83.5
+ metro-symbolicate: 0.83.5
+ metro-transform-plugins: 0.83.5
+ metro-transform-worker: 0.83.5
mime-types: 3.0.2
nullthrows: 1.1.1
serialize-error: 2.1.0
source-map: 0.5.7
throat: 5.0.0
- ws: 7.5.11
+ ws: 7.5.10
yargs: 17.7.2
transitivePeerDependencies:
- bufferutil
@@ -10954,7 +9806,7 @@ snapshots:
micromatch@4.0.8:
dependencies:
braces: 3.0.3
- picomatch: 2.3.2
+ picomatch: 2.3.1
mime-db@1.52.0: {}
@@ -10978,13 +9830,13 @@ snapshots:
minimalistic-assert@1.0.1: {}
- minimatch@10.2.5:
+ minimatch@10.2.4:
dependencies:
- brace-expansion: 5.0.6
+ brace-expansion: 5.0.4
minimatch@3.1.5:
dependencies:
- brace-expansion: 1.1.15
+ brace-expansion: 1.1.12
minimist@1.2.8: {}
@@ -10996,7 +9848,7 @@ snapshots:
dependencies:
obliterator: 2.0.5
- mnemonist@0.40.4:
+ mnemonist@0.40.3:
dependencies:
obliterator: 2.0.5
@@ -11008,9 +9860,7 @@ snapshots:
ms@2.1.3: {}
- nanoid@3.3.12: {}
-
- napi-postinstall@0.3.4: {}
+ nanoid@3.3.11: {}
natural-compare@1.4.0: {}
@@ -11037,7 +9887,7 @@ snapshots:
node-int64@0.4.0: {}
- node-releases@2.0.46: {}
+ node-releases@2.0.36: {}
node-stream-zip@1.15.0: {}
@@ -11053,13 +9903,13 @@ snapshots:
nullthrows@1.1.1: {}
- nypm@0.6.6:
+ nypm@0.6.5:
dependencies:
- citty: 0.2.2
+ citty: 0.2.1
pathe: 2.0.3
- tinyexec: 1.2.3
+ tinyexec: 1.0.2
- ob1@0.83.7:
+ ob1@0.83.5:
dependencies:
flow-enums-runtime: 0.0.6
@@ -11071,33 +9921,33 @@ snapshots:
object.assign@4.1.7:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-object-atoms: 1.1.2
+ 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.9
+ call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
object.fromentries@2.0.8:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.24.2
- es-object-atoms: 1.1.2
+ es-abstract: 1.24.1
+ es-object-atoms: 1.1.1
object.values@1.2.1:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
obliterator@2.0.5: {}
@@ -11187,7 +10037,7 @@ snapshots:
parse-json@5.2.0:
dependencies:
- '@babel/code-frame': 7.29.7
+ '@babel/code-frame': 7.29.0
error-ex: 1.3.4
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
@@ -11204,7 +10054,7 @@ snapshots:
path-scurry@2.0.2:
dependencies:
- lru-cache: 11.5.1
+ lru-cache: 11.2.6
minipass: 7.1.3
pathe@1.1.2: {}
@@ -11217,9 +10067,9 @@ snapshots:
picocolors@1.1.1: {}
- picomatch@2.3.2: {}
+ picomatch@2.3.1: {}
- picomatch@4.0.4: {}
+ picomatch@4.0.3: {}
pino-abstract-transport@3.0.0:
dependencies:
@@ -11229,7 +10079,7 @@ snapshots:
dependencies:
colorette: 2.0.20
dateformat: 4.6.3
- fast-copy: 4.0.3
+ fast-copy: 4.0.2
fast-safe-stringify: 2.1.1
help-me: 5.0.0
joycon: 3.1.1
@@ -11255,7 +10105,7 @@ snapshots:
real-require: 0.2.0
safe-stable-stringify: 2.5.0
sonic-boom: 4.2.1
- thread-stream: 4.2.0
+ thread-stream: 4.0.0
pirates@4.0.7: {}
@@ -11263,23 +10113,21 @@ snapshots:
dependencies:
find-up: 4.1.0
- pkg-types@2.3.1:
+ pkg-types@2.3.0:
dependencies:
confbox: 0.2.4
exsolve: 1.0.8
pathe: 2.0.3
- pluralize@8.0.0: {}
-
pngjs@5.0.0: {}
possible-typed-array-names@1.1.0: {}
postcss-value-parser@4.2.0: {}
- postcss@8.5.15:
+ postcss@8.5.8:
dependencies:
- nanoid: 3.3.12
+ nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
@@ -11293,10 +10141,10 @@ snapshots:
ansi-styles: 5.2.0
react-is: 18.3.1
- prisma@6.19.3(typescript@5.9.3):
+ prisma@6.19.2(typescript@5.9.3):
dependencies:
- '@prisma/config': 6.19.3
- '@prisma/engines': 6.19.3
+ '@prisma/config': 6.19.2
+ '@prisma/engines': 6.19.2
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
@@ -11340,7 +10188,7 @@ snapshots:
pngjs: 5.0.0
yargs: 15.4.1
- qs@6.15.2:
+ qs@6.14.2:
dependencies:
side-channel: 1.1.0
@@ -11370,18 +10218,18 @@ snapshots:
rc9@2.1.2:
dependencies:
- defu: 6.1.7
+ defu: 6.1.4
destr: 2.0.5
react-devtools-core@6.1.5:
dependencies:
- shell-quote: 1.8.4
- ws: 7.5.11
+ shell-quote: 1.8.3
+ ws: 7.5.10
transitivePeerDependencies:
- bufferutil
- utf-8-validate
- react-dom@19.2.6(react@19.2.3):
+ react-dom@19.2.4(react@19.2.3):
dependencies:
react: 19.2.3
scheduler: 0.27.0
@@ -11394,89 +10242,79 @@ snapshots:
react-is@18.3.1: {}
- react-is@19.2.6: {}
-
- react-native-camera-kit@14.2.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
- dependencies:
- react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-is@19.2.4: {}
- react-native-gesture-handler@2.31.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
+ react-native-gesture-handler@2.31.2(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3):
dependencies:
'@egjs/hammerjs': 2.0.17
'@types/react-test-renderer': 19.1.0
hoist-non-react-statics: 3.3.2
invariant: 2.2.4
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
- react-native-is-edge-to-edge@1.1.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
+ react-native-is-edge-to-edge@1.1.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3):
dependencies:
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
- react-native-qrcode-svg@6.3.21(react-native-svg@15.15.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
+ react-native-qrcode-svg@6.3.21(react-native-svg@15.15.3(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3):
dependencies:
prop-types: 15.8.1
qrcode: 1.5.4
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
- react-native-svg: 15.15.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
+ react-native-svg: 15.15.3(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
text-encoding: 0.7.0
- react-native-reanimated@3.19.5(@babel/core@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
- dependencies:
- '@babel/core': 7.29.7
- '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7)
+ react-native-reanimated@3.19.5(@babel/core@7.29.0)(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3):
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0)
+ '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0)
convert-source-map: 2.0.0
invariant: 2.2.4
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
- react-native-is-edge-to-edge: 1.1.7(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
+ react-native-is-edge-to-edge: 1.1.7(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
transitivePeerDependencies:
- supports-color
- react-native-safe-area-context@5.8.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
+ react-native-safe-area-context@5.7.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3):
dependencies:
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
- react-native-screens@4.25.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
+ react-native-screens@4.24.0(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3):
dependencies:
react: 19.2.3
react-freeze: 1.0.4(react@19.2.3)
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
warn-once: 0.1.1
- react-native-svg@15.15.5(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
+ react-native-svg@15.15.3(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3):
dependencies:
css-select: 5.2.2
css-tree: 1.1.3
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
+ warn-once: 0.1.1
react-native-vector-icons@10.3.0:
dependencies:
prop-types: 15.8.1
yargs: 16.2.0
- react-native-view-shot@5.1.0(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
- dependencies:
- html2canvas: 1.4.1
- react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
-
- react-native-web@0.21.2(react-dom@19.2.6(react@19.2.3))(react@19.2.3):
+ react-native-web@0.21.2(react-dom@19.2.4(react@19.2.3))(react@19.2.3):
dependencies:
- '@babel/runtime': 7.29.7
+ '@babel/runtime': 7.28.6
'@react-native/normalize-colors': 0.74.89
fbjs: 3.0.5
inline-style-prefixer: 7.0.1
@@ -11484,51 +10322,32 @@ snapshots:
nullthrows: 1.1.1
postcss-value-parser: 4.2.0
react: 19.2.3
- react-dom: 19.2.6(react@19.2.3)
+ react-dom: 19.2.4(react@19.2.3)
styleq: 0.1.3
transitivePeerDependencies:
- encoding
- react-native-webview@13.16.1(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
+ react-native-webview@13.16.1(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3):
dependencies:
escape-string-regexp: 4.0.0
invariant: 2.2.4
react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
-
- react-native-worklets@0.5.1(@babel/core@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3):
- dependencies:
- '@babel/core': 7.29.7
- '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7)
- '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7)
- '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7)
- convert-source-map: 2.0.0
- react: 19.2.3
- react-native: 0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3)
- semver: 7.7.2
- transitivePeerDependencies:
- - supports-color
+ react-native: 0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3)
- react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3):
+ react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3):
dependencies:
'@jest/create-cache-key-function': 29.7.0
'@react-native/assets-registry': 0.84.1
- '@react-native/codegen': 0.84.1(@babel/core@7.29.7)
- '@react-native/community-cli-plugin': 0.84.1(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))
+ '@react-native/codegen': 0.84.1(@babel/core@7.29.0)
+ '@react-native/community-cli-plugin': 0.84.1(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))
'@react-native/gradle-plugin': 0.84.1
'@react-native/js-polyfills': 0.84.1
'@react-native/normalize-colors': 0.84.1
- '@react-native/virtualized-lists': 0.84.1(@types/react@19.2.15)(react-native@0.84.1(@babel/core@7.29.7)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.3))(react@19.2.3)
+ '@react-native/virtualized-lists': 0.84.1(@types/react@19.2.14)(react-native@0.84.1(@babel/core@7.29.0)(@react-native-community/cli@20.1.0(typescript@5.9.3))(@react-native/metro-config@0.84.1(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react@19.2.3)
abort-controller: 3.0.0
anser: 1.4.10
ansi-regex: 5.0.1
- babel-jest: 29.7.0(@babel/core@7.29.7)
+ babel-jest: 29.7.0(@babel/core@7.29.0)
babel-plugin-syntax-hermes-parser: 0.32.0
base64-js: 1.5.1
commander: 12.1.0
@@ -11537,8 +10356,8 @@ snapshots:
invariant: 2.2.4
jest-environment-node: 29.7.0
memoize-one: 5.2.1
- metro-runtime: 0.83.7
- metro-source-map: 0.83.7
+ metro-runtime: 0.83.5
+ metro-source-map: 0.83.5
nullthrows: 1.1.1
pretty-format: 29.7.0
promise: 8.3.0
@@ -11547,14 +10366,14 @@ snapshots:
react-refresh: 0.14.2
regenerator-runtime: 0.13.11
scheduler: 0.27.0
- semver: 7.8.1
+ semver: 7.7.4
stacktrace-parser: 0.1.11
- tinyglobby: 0.2.16
+ tinyglobby: 0.2.15
whatwg-fetch: 3.6.20
- ws: 7.5.11
+ ws: 7.5.10
yargs: 17.7.2
optionalDependencies:
- '@types/react': 19.2.15
+ '@types/react': 19.2.14
transitivePeerDependencies:
- '@babel/core'
- '@react-native-community/cli'
@@ -11568,7 +10387,7 @@ snapshots:
react-test-renderer@19.2.3(react@19.2.3):
dependencies:
react: 19.2.3
- react-is: 19.2.6
+ react-is: 19.2.4
scheduler: 0.27.0
react@19.2.3: {}
@@ -11583,8 +10402,6 @@ snapshots:
real-require@0.2.0: {}
- real-require@1.0.0: {}
-
redis-errors@1.2.0: {}
redis-parser@3.0.0:
@@ -11593,11 +10410,11 @@ snapshots:
reflect.getprototypeof@1.0.10:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-abstract: 1.24.2
+ es-abstract: 1.24.1
es-errors: 1.3.0
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
get-proto: 1.0.1
which-builtin-type: 1.2.1
@@ -11610,11 +10427,9 @@ snapshots:
regenerator-runtime@0.13.11: {}
- regexp-tree@0.1.27: {}
-
regexp.prototype.flags@1.5.4:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
define-properties: 1.2.1
es-errors: 1.3.0
get-proto: 1.0.1
@@ -11626,13 +10441,13 @@ snapshots:
regenerate: 1.4.2
regenerate-unicode-properties: 10.2.2
regjsgen: 0.8.0
- regjsparser: 0.13.1
+ regjsparser: 0.13.0
unicode-match-property-ecmascript: 2.0.0
unicode-match-property-value-ecmascript: 2.2.1
regjsgen@0.8.0: {}
- regjsparser@0.13.1:
+ regjsparser@0.13.0:
dependencies:
jsesc: 3.1.0
@@ -11654,17 +10469,16 @@ snapshots:
resolve.exports@2.0.3: {}
- resolve@1.22.12:
+ resolve@1.22.11:
dependencies:
- es-errors: 1.3.0
- is-core-module: 2.16.2
+ is-core-module: 2.16.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
- resolve@2.0.0-next.7:
+ resolve@2.0.0-next.6:
dependencies:
es-errors: 1.3.0
- is-core-module: 2.16.2
+ is-core-module: 2.16.1
node-exports-info: 1.6.0
object-keys: 1.1.1
path-parse: 1.0.7
@@ -11685,35 +10499,35 @@ snapshots:
dependencies:
glob: 7.2.3
- rollup@4.60.4:
+ rollup@4.59.0:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.60.4
- '@rollup/rollup-android-arm64': 4.60.4
- '@rollup/rollup-darwin-arm64': 4.60.4
- '@rollup/rollup-darwin-x64': 4.60.4
- '@rollup/rollup-freebsd-arm64': 4.60.4
- '@rollup/rollup-freebsd-x64': 4.60.4
- '@rollup/rollup-linux-arm-gnueabihf': 4.60.4
- '@rollup/rollup-linux-arm-musleabihf': 4.60.4
- '@rollup/rollup-linux-arm64-gnu': 4.60.4
- '@rollup/rollup-linux-arm64-musl': 4.60.4
- '@rollup/rollup-linux-loong64-gnu': 4.60.4
- '@rollup/rollup-linux-loong64-musl': 4.60.4
- '@rollup/rollup-linux-ppc64-gnu': 4.60.4
- '@rollup/rollup-linux-ppc64-musl': 4.60.4
- '@rollup/rollup-linux-riscv64-gnu': 4.60.4
- '@rollup/rollup-linux-riscv64-musl': 4.60.4
- '@rollup/rollup-linux-s390x-gnu': 4.60.4
- '@rollup/rollup-linux-x64-gnu': 4.60.4
- '@rollup/rollup-linux-x64-musl': 4.60.4
- '@rollup/rollup-openbsd-x64': 4.60.4
- '@rollup/rollup-openharmony-arm64': 4.60.4
- '@rollup/rollup-win32-arm64-msvc': 4.60.4
- '@rollup/rollup-win32-ia32-msvc': 4.60.4
- '@rollup/rollup-win32-x64-gnu': 4.60.4
- '@rollup/rollup-win32-x64-msvc': 4.60.4
+ '@rollup/rollup-android-arm-eabi': 4.59.0
+ '@rollup/rollup-android-arm64': 4.59.0
+ '@rollup/rollup-darwin-arm64': 4.59.0
+ '@rollup/rollup-darwin-x64': 4.59.0
+ '@rollup/rollup-freebsd-arm64': 4.59.0
+ '@rollup/rollup-freebsd-x64': 4.59.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.59.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.59.0
+ '@rollup/rollup-linux-arm64-gnu': 4.59.0
+ '@rollup/rollup-linux-arm64-musl': 4.59.0
+ '@rollup/rollup-linux-loong64-gnu': 4.59.0
+ '@rollup/rollup-linux-loong64-musl': 4.59.0
+ '@rollup/rollup-linux-ppc64-gnu': 4.59.0
+ '@rollup/rollup-linux-ppc64-musl': 4.59.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.59.0
+ '@rollup/rollup-linux-riscv64-musl': 4.59.0
+ '@rollup/rollup-linux-s390x-gnu': 4.59.0
+ '@rollup/rollup-linux-x64-gnu': 4.59.0
+ '@rollup/rollup-linux-x64-musl': 4.59.0
+ '@rollup/rollup-openbsd-x64': 4.59.0
+ '@rollup/rollup-openharmony-arm64': 4.59.0
+ '@rollup/rollup-win32-arm64-msvc': 4.59.0
+ '@rollup/rollup-win32-ia32-msvc': 4.59.0
+ '@rollup/rollup-win32-x64-gnu': 4.59.0
+ '@rollup/rollup-win32-x64-msvc': 4.59.0
fsevents: 2.3.3
run-parallel@1.2.0:
@@ -11728,9 +10542,9 @@ snapshots:
dependencies:
mri: 1.2.0
- safe-array-concat@1.1.4:
+ safe-array-concat@1.1.3:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
get-intrinsic: 1.3.0
has-symbols: 1.1.0
@@ -11749,14 +10563,10 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.2.1
- safe-regex2@5.1.1:
+ safe-regex2@5.0.0:
dependencies:
ret: 0.5.0
- safe-regex@2.1.1:
- dependencies:
- regexp-tree: 0.1.27
-
safe-stable-stringify@2.5.0: {}
safer-buffer@2.1.2: {}
@@ -11767,9 +10577,7 @@ snapshots:
semver@6.3.1: {}
- semver@7.7.2: {}
-
- semver@7.8.1: {}
+ semver@7.7.4: {}
send@0.19.2:
dependencies:
@@ -11804,7 +10612,7 @@ snapshots:
set-cookie-parser@2.7.2: {}
- set-cookie-parser@3.1.0: {}
+ set-cookie-parser@3.0.1: {}
set-function-length@1.2.2:
dependencies:
@@ -11826,7 +10634,7 @@ snapshots:
dependencies:
dunder-proto: 1.0.1
es-errors: 1.3.0
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
setimmediate@1.0.5: {}
@@ -11842,9 +10650,7 @@ snapshots:
shell-quote@1.8.3: {}
- shell-quote@1.8.4: {}
-
- side-channel-list@1.0.1:
+ side-channel-list@1.0.0:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.4
@@ -11868,7 +10674,7 @@ snapshots:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.4
- side-channel-list: 1.0.1
+ side-channel-list: 1.0.0
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
@@ -11924,8 +10730,6 @@ snapshots:
sprintf-js@1.0.3: {}
- stable-hash-x@0.2.0: {}
-
stack-utils@2.0.6:
dependencies:
escape-string-regexp: 2.0.0
@@ -11976,12 +10780,12 @@ snapshots:
string.prototype.matchall@4.0.12:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-abstract: 1.24.2
+ es-abstract: 1.24.1
es-errors: 1.3.0
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
get-intrinsic: 1.3.0
gopd: 1.2.0
has-symbols: 1.1.0
@@ -11993,30 +10797,30 @@ snapshots:
string.prototype.repeat@1.0.0:
dependencies:
define-properties: 1.2.1
- es-abstract: 1.24.2
+ es-abstract: 1.24.1
string.prototype.trim@1.2.10:
dependencies:
- call-bind: 1.0.9
+ 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.2
- es-object-atoms: 1.1.2
+ es-abstract: 1.24.1
+ es-object-atoms: 1.1.1
has-property-descriptors: 1.0.2
string.prototype.trimend@1.0.9:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
define-properties: 1.2.1
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
string.prototype.trimstart@1.0.8:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
define-properties: 1.2.1
- es-object-atoms: 1.1.2
+ es-object-atoms: 1.1.1
string_decoder@1.3.0:
dependencies:
@@ -12034,8 +10838,6 @@ snapshots:
strip-final-newline@2.0.0: {}
- strip-indent@4.1.1: {}
-
strip-json-comments@3.1.1: {}
strip-json-comments@5.0.3: {}
@@ -12054,42 +10856,38 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
- svelte-check@4.4.8(picomatch@4.0.4)(svelte@5.56.0(@typescript-eslint/types@8.60.0))(typescript@5.9.3):
+ svelte-check@4.4.5(picomatch@4.0.3)(svelte@5.53.10)(typescript@5.9.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
chokidar: 4.0.3
- fdir: 6.5.0(picomatch@4.0.4)
+ fdir: 6.5.0(picomatch@4.0.3)
picocolors: 1.1.1
sade: 1.8.1
- svelte: 5.56.0(@typescript-eslint/types@8.60.0)
+ svelte: 5.53.10
typescript: 5.9.3
transitivePeerDependencies:
- picomatch
- svelte@5.56.0(@typescript-eslint/types@8.60.0):
+ svelte@5.53.10:
dependencies:
'@jridgewell/remapping': 2.3.5
'@jridgewell/sourcemap-codec': 1.5.5
- '@sveltejs/acorn-typescript': 1.0.10(acorn@8.16.0)
- '@types/estree': 1.0.9
+ '@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0)
+ '@types/estree': 1.0.8
'@types/trusted-types': 2.0.7
acorn: 8.16.0
aria-query: 5.3.1
axobject-query: 4.1.0
clsx: 2.1.1
- devalue: 5.8.1
+ devalue: 5.6.4
esm-env: 1.2.2
- esrap: 2.2.9(@typescript-eslint/types@8.60.0)
+ esrap: 2.2.3
is-reference: 3.0.3
locate-character: 3.0.0
magic-string: 0.30.21
zimmerframe: 1.1.4
- transitivePeerDependencies:
- - '@typescript-eslint/types'
- tapable@2.3.3: {}
-
- terser@5.48.0:
+ terser@5.46.0:
dependencies:
'@jridgewell/source-map': 0.3.11
acorn: 8.16.0
@@ -12098,21 +10896,17 @@ snapshots:
test-exclude@6.0.0:
dependencies:
- '@istanbuljs/schema': 0.1.6
+ '@istanbuljs/schema': 0.1.3
glob: 7.2.3
minimatch: 3.1.5
text-encoding@0.7.0: {}
- text-segmentation@1.0.3:
- dependencies:
- utrie: 1.0.2
-
text-table@0.2.0: {}
- thread-stream@4.2.0:
+ thread-stream@4.0.0:
dependencies:
- real-require: 1.0.0
+ real-require: 0.2.0
throat@5.0.0: {}
@@ -12120,12 +10914,12 @@ snapshots:
tinyexec@0.3.2: {}
- tinyexec@1.2.3: {}
+ tinyexec@1.0.2: {}
- tinyglobby@0.2.16:
+ tinyglobby@0.2.15:
dependencies:
- fdir: 6.5.0(picomatch@4.0.4)
- picomatch: 4.0.4
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
tinypool@1.1.1: {}
@@ -12139,7 +10933,7 @@ snapshots:
dependencies:
is-number: 7.0.0
- toad-cache@3.7.1: {}
+ toad-cache@3.7.0: {}
toidentifier@1.0.1: {}
@@ -12149,15 +10943,16 @@ snapshots:
tree-kill@1.2.2: {}
- ts-api-utils@2.5.0(typescript@5.9.3):
+ ts-api-utils@2.4.0(typescript@5.9.3):
dependencies:
typescript: 5.9.3
tslib@2.8.1: {}
- tsx@4.22.3:
+ tsx@4.21.0:
dependencies:
- esbuild: 0.28.0
+ esbuild: 0.27.3
+ get-tsconfig: 4.13.6
optionalDependencies:
fsevents: 2.3.3
@@ -12186,7 +10981,7 @@ snapshots:
typed-array-byte-length@1.0.3:
dependencies:
- call-bind: 1.0.9
+ call-bind: 1.0.8
for-each: 0.3.5
gopd: 1.2.0
has-proto: 1.2.0
@@ -12195,33 +10990,22 @@ snapshots:
typed-array-byte-offset@1.0.4:
dependencies:
available-typed-arrays: 1.0.7
- call-bind: 1.0.9
+ 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.8:
+ typed-array-length@1.0.7:
dependencies:
- call-bind: 1.0.9
+ 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-eslint@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3):
- dependencies:
- '@typescript-eslint/eslint-plugin': 8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3))(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
- '@typescript-eslint/parser': 8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
- '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3)
- '@typescript-eslint/utils': 8.60.0(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3)
- eslint: 10.4.1(jiti@2.7.0)
- typescript: 5.9.3
- transitivePeerDependencies:
- - supports-color
-
typescript@5.9.3: {}
ua-parser-js@1.0.41: {}
@@ -12250,36 +11034,9 @@ snapshots:
unpipe@1.0.0: {}
- unrs-resolver@1.12.2:
+ update-browserslist-db@1.2.3(browserslist@4.28.1):
dependencies:
- napi-postinstall: 0.3.4
- optionalDependencies:
- '@unrs/resolver-binding-android-arm-eabi': 1.12.2
- '@unrs/resolver-binding-android-arm64': 1.12.2
- '@unrs/resolver-binding-darwin-arm64': 1.12.2
- '@unrs/resolver-binding-darwin-x64': 1.12.2
- '@unrs/resolver-binding-freebsd-x64': 1.12.2
- '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2
- '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2
- '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2
- '@unrs/resolver-binding-linux-arm64-musl': 1.12.2
- '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2
- '@unrs/resolver-binding-linux-loong64-musl': 1.12.2
- '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2
- '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2
- '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2
- '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2
- '@unrs/resolver-binding-linux-x64-gnu': 1.12.2
- '@unrs/resolver-binding-linux-x64-musl': 1.12.2
- '@unrs/resolver-binding-openharmony-arm64': 1.12.2
- '@unrs/resolver-binding-wasm32-wasi': 1.12.2
- '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2
- '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2
- '@unrs/resolver-binding-win32-x64-msvc': 1.12.2
-
- update-browserslist-db@1.2.3(browserslist@4.28.2):
- dependencies:
- browserslist: 4.28.2
+ browserslist: 4.28.1
escalade: 3.2.0
picocolors: 1.1.1
@@ -12299,10 +11056,6 @@ snapshots:
utils-merge@1.0.1: {}
- utrie@1.0.2:
- dependencies:
- base64-arraybuffer: 1.0.2
-
v8-to-istanbul@9.3.0:
dependencies:
'@jridgewell/trace-mapping': 0.3.31
@@ -12311,13 +11064,13 @@ snapshots:
vary@1.1.2: {}
- vite-node@2.1.9(@types/node@22.19.19)(terser@5.48.0):
+ vite-node@2.1.9(@types/node@22.19.15)(terser@5.46.0):
dependencies:
cac: 6.7.14
debug: 4.4.3
es-module-lexer: 1.7.0
pathe: 1.1.2
- vite: 5.4.21(@types/node@22.19.19)(terser@5.48.0)
+ vite: 5.4.21(@types/node@22.19.15)(terser@5.46.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -12329,40 +11082,40 @@ snapshots:
- supports-color
- terser
- vite@5.4.21(@types/node@22.19.19)(terser@5.48.0):
+ vite@5.4.21(@types/node@22.19.15)(terser@5.46.0):
dependencies:
esbuild: 0.21.5
- postcss: 8.5.15
- rollup: 4.60.4
+ postcss: 8.5.8
+ rollup: 4.59.0
optionalDependencies:
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
fsevents: 2.3.3
- terser: 5.48.0
+ terser: 5.46.0
- vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0):
+ vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2):
dependencies:
- esbuild: 0.27.7
- fdir: 6.5.0(picomatch@4.0.4)
- picomatch: 4.0.4
- postcss: 8.5.15
- rollup: 4.60.4
- tinyglobby: 0.2.16
+ esbuild: 0.27.3
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.8
+ rollup: 4.59.0
+ tinyglobby: 0.2.15
optionalDependencies:
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
fsevents: 2.3.3
- jiti: 2.7.0
- terser: 5.48.0
- tsx: 4.22.3
- yaml: 2.9.0
+ jiti: 2.6.1
+ terser: 5.46.0
+ tsx: 4.21.0
+ yaml: 2.8.2
- vitefu@1.1.3(vite@7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)):
+ vitefu@1.1.2(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)):
optionalDependencies:
- vite: 7.3.3(@types/node@22.19.19)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.3)(yaml@2.9.0)
+ vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
- vitest@2.1.9(@types/node@22.19.19)(terser@5.48.0):
+ vitest@2.1.9(@types/node@22.19.15)(terser@5.46.0):
dependencies:
'@vitest/expect': 2.1.9
- '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.19.19)(terser@5.48.0))
+ '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.19.15)(terser@5.46.0))
'@vitest/pretty-format': 2.1.9
'@vitest/runner': 2.1.9
'@vitest/snapshot': 2.1.9
@@ -12378,11 +11131,11 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.1.1
tinyrainbow: 1.2.0
- vite: 5.4.21(@types/node@22.19.19)(terser@5.48.0)
- vite-node: 2.1.9(@types/node@22.19.19)(terser@5.48.0)
+ vite: 5.4.21(@types/node@22.19.15)(terser@5.46.0)
+ vite-node: 2.1.9(@types/node@22.19.15)(terser@5.46.0)
why-is-node-running: 2.3.0
optionalDependencies:
- '@types/node': 22.19.19
+ '@types/node': 22.19.15
transitivePeerDependencies:
- less
- lightningcss
@@ -12437,7 +11190,7 @@ snapshots:
isarray: 2.0.5
which-boxed-primitive: 1.1.1
which-collection: 1.0.2
- which-typed-array: 1.1.21
+ which-typed-array: 1.1.20
which-collection@1.0.2:
dependencies:
@@ -12448,10 +11201,10 @@ snapshots:
which-module@2.0.1: {}
- which-typed-array@1.1.21:
+ which-typed-array@1.1.20:
dependencies:
available-typed-arrays: 1.0.7
- call-bind: 1.0.9
+ call-bind: 1.0.8
call-bound: 1.0.4
for-each: 0.3.5
get-proto: 1.0.1
@@ -12488,11 +11241,11 @@ snapshots:
imurmurhash: 0.1.4
signal-exit: 3.0.7
- ws@6.2.4:
+ ws@6.2.3:
dependencies:
async-limiter: 1.0.1
- ws@7.5.11: {}
+ ws@7.5.10: {}
xtend@4.0.2: {}
@@ -12502,7 +11255,7 @@ snapshots:
yallist@3.1.1: {}
- yaml@2.9.0: {}
+ yaml@2.8.2: {}
yargs-parser@18.1.3:
dependencies: