Compare commits
33 Commits
v3.8.9
...
ff44f0e537
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff44f0e537 | ||
|
|
dec34fc384 | ||
|
|
f5727daedb | ||
|
|
3857f2fd1a | ||
|
|
e951e97790 | ||
|
|
d0cade633a | ||
|
|
490059666f | ||
|
|
5852bb00f2 | ||
|
|
749ad4a694 | ||
|
|
0f752acae5 | ||
|
|
652bd220c2 | ||
|
|
9f2eaaf04e | ||
|
|
63d3f0e86c | ||
|
|
25e7d234ba | ||
|
|
7a3f734728 | ||
|
|
d59a07a8c5 | ||
|
|
5a77ae5084 | ||
|
|
dcb5bd80e3 | ||
|
|
3b8fc99f06 | ||
|
|
bda8d10f10 | ||
|
|
0fe7b433d6 | ||
|
|
8d96e176b4 | ||
|
|
af9bc861cf | ||
|
|
ab4bbea9fd | ||
|
|
fbd4b3739e | ||
|
|
395e71164f | ||
|
|
99c277e2ee | ||
|
|
9287e0d29b | ||
|
|
f2f2bafc39 | ||
|
|
5876198b5e | ||
|
|
e46bf381c7 | ||
|
|
3bf0ccf207 | ||
|
|
e41b4ffc56 |
14
.github/workflows/docker-build.yml
vendored
@@ -101,26 +101,30 @@ jobs:
|
|||||||
# Build and push Docker image
|
# Build and push Docker image
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
provenance: false # Disable provenance to avoid unknown/unknown
|
||||||
|
sbom: false # Disable sbom to avoid unknown/unknown
|
||||||
|
|
||||||
# Load image locally for security scanning (PRs only)
|
# Load image locally for security scanning (PRs only)
|
||||||
- name: Load image for scanning
|
- name: Load image for scanning
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
load: true
|
load: true
|
||||||
tags: gitea-mirror:scan
|
tags: gitea-mirror:scan
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
|
provenance: false # Disable provenance to avoid unknown/unknown
|
||||||
|
sbom: false # Disable sbom to avoid unknown/unknown
|
||||||
|
|
||||||
# Wait for image to be available in registry
|
# Wait for image to be available in registry
|
||||||
- name: Wait for image availability
|
- name: Wait for image availability
|
||||||
@@ -169,8 +173,8 @@ jobs:
|
|||||||
- BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:4321
|
- BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:4321
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
> 💡 **Note:** PR images are tagged as \`pr-<number>\` and only built for \`linux/amd64\` to speed up CI.
|
> 💡 **Note:** PR images are tagged as \`pr-<number>\` and built for both \`linux/amd64\` and \`linux/arm64\`.
|
||||||
> Production images (\`latest\`, version tags) are multi-platform (\`linux/amd64\`, \`linux/arm64\`).
|
> Production images (\`latest\`, version tags) use the same multi-platform set.
|
||||||
|
|
||||||
---
|
---
|
||||||
📦 View in [GitHub Packages](https://github.com/${{ github.repository }}/pkgs/container/gitea-mirror)`;
|
📦 View in [GitHub Packages](https://github.com/${{ github.repository }}/pkgs/container/gitea-mirror)`;
|
||||||
|
|||||||
20
Dockerfile
@@ -1,6 +1,6 @@
|
|||||||
# syntax=docker/dockerfile:1.4
|
# syntax=docker/dockerfile:1.4
|
||||||
|
|
||||||
FROM oven/bun:1.2.23-alpine AS base
|
FROM oven/bun:1.3.1-alpine AS base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apk add --no-cache libc6-compat python3 make g++ gcc wget sqlite openssl ca-certificates
|
RUN apk add --no-cache libc6-compat python3 make g++ gcc wget sqlite openssl ca-certificates
|
||||||
|
|
||||||
@@ -15,9 +15,9 @@ FROM deps AS builder
|
|||||||
COPY . .
|
COPY . .
|
||||||
RUN bun run build
|
RUN bun run build
|
||||||
RUN mkdir -p dist/scripts && \
|
RUN mkdir -p dist/scripts && \
|
||||||
for script in scripts/*.ts; do \
|
for script in scripts/*.ts; do \
|
||||||
bun build "$script" --target=bun --outfile=dist/scripts/$(basename "${script%.ts}.js"); \
|
bun build "$script" --target=bun --outfile=dist/scripts/$(basename "${script%.ts}.js"); \
|
||||||
done
|
done
|
||||||
|
|
||||||
# ----------------------------
|
# ----------------------------
|
||||||
FROM deps AS pruner
|
FROM deps AS pruner
|
||||||
@@ -40,12 +40,12 @@ ENV DATABASE_URL=file:data/gitea-mirror.db
|
|||||||
|
|
||||||
# Create directories and setup permissions
|
# Create directories and setup permissions
|
||||||
RUN mkdir -p /app/certs && \
|
RUN mkdir -p /app/certs && \
|
||||||
chmod +x ./docker-entrypoint.sh && \
|
chmod +x ./docker-entrypoint.sh && \
|
||||||
mkdir -p /app/data && \
|
mkdir -p /app/data && \
|
||||||
addgroup --system --gid 1001 nodejs && \
|
addgroup --system --gid 1001 nodejs && \
|
||||||
adduser --system --uid 1001 gitea-mirror && \
|
adduser --system --uid 1001 gitea-mirror && \
|
||||||
chown -R gitea-mirror:nodejs /app/data && \
|
chown -R gitea-mirror:nodejs /app/data && \
|
||||||
chown -R gitea-mirror:nodejs /app/certs
|
chown -R gitea-mirror:nodejs /app/certs
|
||||||
|
|
||||||
USER gitea-mirror
|
USER gitea-mirror
|
||||||
|
|
||||||
|
|||||||
@@ -326,6 +326,8 @@ Enable users to sign in with external identity providers like Google, Azure AD,
|
|||||||
https://your-domain.com/api/auth/sso/callback/{provider-id}
|
https://your-domain.com/api/auth/sso/callback/{provider-id}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Need help? The [SSO & OIDC guide](docs/SSO-OIDC-SETUP.md) now includes a working Authentik walkthrough plus troubleshooting tips. If you upgraded from a version earlier than v3.8.10 and see `TypeError … url.startsWith` after the callback, delete the old provider and add it again using the Discover button (see [#73](https://github.com/RayLabsHQ/gitea-mirror/issues/73) and [#122](https://github.com/RayLabsHQ/gitea-mirror/issues/122)).
|
||||||
|
|
||||||
### 3. Header Authentication (Reverse Proxy)
|
### 3. Header Authentication (Reverse Proxy)
|
||||||
Perfect for automatic authentication when using reverse proxies like Authentik, Authelia, or Traefik Forward Auth.
|
Perfect for automatic authentication when using reverse proxies like Authentik, Authelia, or Traefik Forward Auth.
|
||||||
|
|
||||||
|
|||||||
282
bun.lock
@@ -5,55 +5,55 @@
|
|||||||
"name": "gitea-mirror",
|
"name": "gitea-mirror",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.5",
|
"@astrojs/check": "^0.9.5",
|
||||||
"@astrojs/mdx": "4.3.7",
|
"@astrojs/mdx": "4.3.10",
|
||||||
"@astrojs/node": "9.5.0",
|
"@astrojs/node": "9.5.0",
|
||||||
"@astrojs/react": "^4.4.0",
|
"@astrojs/react": "^4.4.2",
|
||||||
"@better-auth/sso": "1.4.0-beta.12",
|
"@better-auth/sso": "1.4.0-beta.12",
|
||||||
"@octokit/plugin-throttling": "^11.0.2",
|
"@octokit/plugin-throttling": "^11.0.3",
|
||||||
"@octokit/rest": "^22.0.0",
|
"@octokit/rest": "^22.0.1",
|
||||||
"@radix-ui/react-accordion": "^1.2.12",
|
"@radix-ui/react-accordion": "^1.2.12",
|
||||||
"@radix-ui/react-avatar": "^1.1.10",
|
"@radix-ui/react-avatar": "^1.1.11",
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-hover-card": "^1.1.15",
|
"@radix-ui/react-hover-card": "^1.1.15",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-popover": "^1.1.15",
|
"@radix-ui/react-popover": "^1.1.15",
|
||||||
"@radix-ui/react-progress": "^1.1.7",
|
"@radix-ui/react-progress": "^1.1.8",
|
||||||
"@radix-ui/react-radio-group": "^1.3.8",
|
"@radix-ui/react-radio-group": "^1.3.8",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tabs": "^1.1.13",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tailwindcss/vite": "^4.1.15",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@tanstack/react-virtual": "^3.13.12",
|
"@tanstack/react-virtual": "^3.13.12",
|
||||||
"@types/canvas-confetti": "^1.9.0",
|
"@types/canvas-confetti": "^1.9.0",
|
||||||
"@types/react": "^19.2.2",
|
"@types/react": "^19.2.2",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
"astro": "^5.14.8",
|
"astro": "^5.15.4",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.3",
|
||||||
"better-auth": "1.4.0-beta.12",
|
"better-auth": "1.4.0-beta.13",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"canvas-confetti": "^1.9.3",
|
"canvas-confetti": "^1.9.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"drizzle-orm": "^0.44.6",
|
"drizzle-orm": "^0.44.7",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lucide-react": "^0.546.0",
|
"lucide-react": "^0.553.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss": "^4.1.15",
|
"tailwindcss": "^4.1.17",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
@@ -64,11 +64,11 @@
|
|||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@types/bcryptjs": "^3.0.0",
|
"@types/bcryptjs": "^3.0.0",
|
||||||
"@types/bun": "^1.3.0",
|
"@types/bun": "^1.3.1",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@vitejs/plugin-react": "^5.0.4",
|
"@vitejs/plugin-react": "^5.1.0",
|
||||||
"drizzle-kit": "^0.31.5",
|
"drizzle-kit": "^0.31.6",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"tsx": "^4.20.6",
|
"tsx": "^4.20.6",
|
||||||
"vitest": "^3.2.4",
|
"vitest": "^3.2.4",
|
||||||
@@ -76,8 +76,8 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@esbuild-kit/esm-loader": "npm:tsx@^4.20.5",
|
"@esbuild-kit/esm-loader": "npm:tsx@^4.20.6",
|
||||||
"devalue": "^5.3.2",
|
"devalue": "^5.4.2",
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@adobe/css-tools": ["@adobe/css-tools@4.4.3", "", {}, "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA=="],
|
"@adobe/css-tools": ["@adobe/css-tools@4.4.3", "", {}, "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA=="],
|
||||||
@@ -96,13 +96,13 @@
|
|||||||
|
|
||||||
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.8", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.4", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.13.0", "smol-toml": "^1.4.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-uFNyFWadnULWK2cOw4n0hLKeu+xaVWeuECdP10cQ3K2fkybtTlhb7J7TcScdjmS8Yps7oje9S/ehYMfZrhrgCg=="],
|
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.8", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.4", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.13.0", "smol-toml": "^1.4.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-uFNyFWadnULWK2cOw4n0hLKeu+xaVWeuECdP10cQ3K2fkybtTlhb7J7TcScdjmS8Yps7oje9S/ehYMfZrhrgCg=="],
|
||||||
|
|
||||||
"@astrojs/mdx": ["@astrojs/mdx@4.3.7", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.8", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.15.0", "es-module-lexer": "^1.7.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "kleur": "^4.1.5", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-5SRmvMyT/UMWaU2eoD+htnXtE2mUZZEH2K/nEzhuEy+iCsOSuS/DUry59WuKUJRQETi1mgJFdNR4dZLJHYVuRA=="],
|
"@astrojs/mdx": ["@astrojs/mdx@4.3.10", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.8", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.15.0", "es-module-lexer": "^1.7.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "picocolors": "^1.1.1", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-2T5+XIr7PMqMeXhRofXY5NlY4lA0Km+wkfsqmr9lq5KXUHpGlKPQ9dlDZJP9E/CtljJyEBNS17zq66LrIJ1tiQ=="],
|
||||||
|
|
||||||
"@astrojs/node": ["@astrojs/node@9.5.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.4", "send": "^1.2.0", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^5.14.3" } }, "sha512-x1whLIatmCefaqJA8FjfI+P6FStF+bqmmrib0OUGM1M3cZhAXKLgPx6UF2AzQ3JgpXgCWYM24MHtraPvZhhyLQ=="],
|
"@astrojs/node": ["@astrojs/node@9.5.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.4", "send": "^1.2.0", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^5.14.3" } }, "sha512-x1whLIatmCefaqJA8FjfI+P6FStF+bqmmrib0OUGM1M3cZhAXKLgPx6UF2AzQ3JgpXgCWYM24MHtraPvZhhyLQ=="],
|
||||||
|
|
||||||
"@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="],
|
"@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="],
|
||||||
|
|
||||||
"@astrojs/react": ["@astrojs/react@4.4.0", "", { "dependencies": { "@vitejs/plugin-react": "^4.7.0", "ultrahtml": "^1.6.0", "vite": "^6.3.6" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" } }, "sha512-RzblkVImAFdV1C0AWsSWzS70Z0FMtW2p0XXkNYu3QePfyVJta3JIy8m8jY8271etaCZtpFjsE2UaiHGZIBm6nw=="],
|
"@astrojs/react": ["@astrojs/react@4.4.2", "", { "dependencies": { "@vitejs/plugin-react": "^4.7.0", "ultrahtml": "^1.6.0", "vite": "^6.4.1" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" } }, "sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ=="],
|
||||||
|
|
||||||
"@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="],
|
"@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="],
|
||||||
|
|
||||||
@@ -150,11 +150,11 @@
|
|||||||
|
|
||||||
"@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
|
"@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
|
||||||
|
|
||||||
"@better-auth/core": ["@better-auth/core@1.4.0-beta.12", "", { "dependencies": { "zod": "^4.1.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "better-call": "1.0.24", "better-sqlite3": "^12.4.1", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-2GisAGuSVZS4gtnwP5Owk3RyC6GevZe9zcODTrtbwRCvBTrHUmu0j6bcklK9uNG8DaWDmzCK1+VGA5qIHzg5Pw=="],
|
"@better-auth/core": ["@better-auth/core@1.4.0-beta.13", "", { "dependencies": { "zod": "^4.1.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "better-call": "1.0.24", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-EGySsNv6HQYnlRQDIa7otIMrwFoC0gGLxBum9lC6C3wAsF4l4pn/ECcdIriFpc9ewLb8mGkeMSpvjVBUBND6ew=="],
|
||||||
|
|
||||||
"@better-auth/sso": ["@better-auth/sso@1.4.0-beta.12", "", { "dependencies": { "@better-fetch/fetch": "1.1.18", "fast-xml-parser": "^5.2.5", "jose": "^6.1.0", "oauth2-mock-server": "^7.2.1", "samlify": "^2.10.1", "zod": "^4.1.5" }, "peerDependencies": { "better-auth": "1.4.0-beta.12" } }, "sha512-iuRuy59J3yXQihZJ34rqYClWyuVjSkxuBkdFblccKbOhNy7pmRO1lfmBMpyeth3ET5Cp0PDVV/z1XBbDcQp0LA=="],
|
"@better-auth/sso": ["@better-auth/sso@1.4.0-beta.12", "", { "dependencies": { "@better-fetch/fetch": "1.1.18", "fast-xml-parser": "^5.2.5", "jose": "^6.1.0", "oauth2-mock-server": "^7.2.1", "samlify": "^2.10.1", "zod": "^4.1.5" }, "peerDependencies": { "better-auth": "1.4.0-beta.12" } }, "sha512-iuRuy59J3yXQihZJ34rqYClWyuVjSkxuBkdFblccKbOhNy7pmRO1lfmBMpyeth3ET5Cp0PDVV/z1XBbDcQp0LA=="],
|
||||||
|
|
||||||
"@better-auth/telemetry": ["@better-auth/telemetry@1.4.0-beta.12", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18" }, "peerDependencies": { "@better-auth/core": "1.4.0-beta.12" } }, "sha512-pQ5HITRGXMHQPcPCDnz0xlxFqqxvpD4kQMvY6cdt1vDsPVePHAj9R3S318XEfaw3NAgtw3af/wCN6eBt2u4Kew=="],
|
"@better-auth/telemetry": ["@better-auth/telemetry@1.4.0-beta.13", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18" }, "peerDependencies": { "@better-auth/core": "1.4.0-beta.13" } }, "sha512-910f+APALhhD79TiujzXp85Pnd2M3TlcTgBfiYF+mk3ouIkBJkl2N6D2ElcgwfiNTg50cFuTkP3AFPYioz8Arw=="],
|
||||||
|
|
||||||
"@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="],
|
"@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="],
|
||||||
|
|
||||||
@@ -326,29 +326,29 @@
|
|||||||
|
|
||||||
"@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="],
|
"@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="],
|
||||||
|
|
||||||
"@octokit/core": ["@octokit/core@7.0.2", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.1", "@octokit/request": "^10.0.2", "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g=="],
|
"@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="],
|
||||||
|
|
||||||
"@octokit/endpoint": ["@octokit/endpoint@11.0.0", "", { "dependencies": { "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ=="],
|
"@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="],
|
||||||
|
|
||||||
"@octokit/graphql": ["@octokit/graphql@9.0.1", "", { "dependencies": { "@octokit/request": "^10.0.2", "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg=="],
|
"@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="],
|
||||||
|
|
||||||
"@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
"@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||||
|
|
||||||
"@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.0.1", "", { "dependencies": { "@octokit/types": "^14.1.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-m1KvHlueScy4mQJWvFDCxFBTIdXS0K1SgFGLmqHyX90mZdCIv6gWBbKRhatxRjhGlONuTK/hztYdaqrTXcFZdQ=="],
|
"@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@14.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw=="],
|
||||||
|
|
||||||
"@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="],
|
"@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="],
|
||||||
|
|
||||||
"@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@16.0.0", "", { "dependencies": { "@octokit/types": "^14.1.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-kJVUQk6/dx/gRNLWUnAWKFs1kVPn5O5CYZyssyEoNYaFedqZxsfYs7DwI3d67hGz4qOwaJ1dpm07hOAD1BXx6g=="],
|
"@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@17.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw=="],
|
||||||
|
|
||||||
"@octokit/plugin-throttling": ["@octokit/plugin-throttling@11.0.2", "", { "dependencies": { "@octokit/types": "^15.0.0", "bottleneck": "^2.15.3" }, "peerDependencies": { "@octokit/core": "^7.0.0" } }, "sha512-ntNIig4zZhQVOZF4fG9Wt8QCoz9ehb+xnlUwp74Ic2ANChCk8oKmRwV9zDDCtrvU1aERIOvtng8wsalEX7Jk5Q=="],
|
"@octokit/plugin-throttling": ["@octokit/plugin-throttling@11.0.3", "", { "dependencies": { "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" }, "peerDependencies": { "@octokit/core": "^7.0.0" } }, "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg=="],
|
||||||
|
|
||||||
"@octokit/request": ["@octokit/request@10.0.2", "", { "dependencies": { "@octokit/endpoint": "^11.0.0", "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA=="],
|
"@octokit/request": ["@octokit/request@10.0.6", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-FO+UgZCUu+pPnZAR+iKdUt64kPE7QW7ciqpldaMXaNzixz5Jld8dJ31LAUewk0cfSRkNSRKyqG438ba9c/qDlQ=="],
|
||||||
|
|
||||||
"@octokit/request-error": ["@octokit/request-error@7.0.0", "", { "dependencies": { "@octokit/types": "^14.0.0" } }, "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg=="],
|
"@octokit/request-error": ["@octokit/request-error@7.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-U8piOROoQQUyExw5c6dTkU3GKxts5/ERRThIauNL7yaRoeXW0q/5bgHWT7JfWBw1UyrbK8ERId2wVkcB32n0uQ=="],
|
||||||
|
|
||||||
"@octokit/rest": ["@octokit/rest@22.0.0", "", { "dependencies": { "@octokit/core": "^7.0.2", "@octokit/plugin-paginate-rest": "^13.0.1", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^16.0.0" } }, "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA=="],
|
"@octokit/rest": ["@octokit/rest@22.0.1", "", { "dependencies": { "@octokit/core": "^7.0.6", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^17.0.0" } }, "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw=="],
|
||||||
|
|
||||||
"@octokit/types": ["@octokit/types@15.0.0", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ=="],
|
"@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||||
|
|
||||||
"@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="],
|
"@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="],
|
||||||
|
|
||||||
@@ -370,7 +370,7 @@
|
|||||||
|
|
||||||
"@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
|
"@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
|
||||||
|
|
||||||
"@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog=="],
|
"@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.11", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q=="],
|
||||||
|
|
||||||
"@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="],
|
"@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="],
|
||||||
|
|
||||||
@@ -398,7 +398,7 @@
|
|||||||
|
|
||||||
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
|
"@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
|
||||||
|
|
||||||
"@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="],
|
"@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="],
|
||||||
|
|
||||||
"@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="],
|
"@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="],
|
||||||
|
|
||||||
@@ -412,7 +412,7 @@
|
|||||||
|
|
||||||
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
"@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||||
|
|
||||||
"@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="],
|
"@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.8", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA=="],
|
||||||
|
|
||||||
"@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="],
|
"@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="],
|
||||||
|
|
||||||
@@ -422,9 +422,9 @@
|
|||||||
|
|
||||||
"@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="],
|
"@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="],
|
||||||
|
|
||||||
"@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="],
|
"@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g=="],
|
||||||
|
|
||||||
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="],
|
||||||
|
|
||||||
"@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="],
|
"@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="],
|
||||||
|
|
||||||
@@ -454,7 +454,7 @@
|
|||||||
|
|
||||||
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
||||||
|
|
||||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.38", "", {}, "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw=="],
|
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.43", "", {}, "sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ=="],
|
||||||
|
|
||||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
||||||
|
|
||||||
@@ -498,17 +498,17 @@
|
|||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="],
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="],
|
||||||
|
|
||||||
"@shikijs/core": ["@shikijs/core@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L1Safnhra3tX/oJK5kYHaWmLEBJi1irASwewzY3taX5ibyXyMkkSDZlq01qigjryOBwrXSdFgTiZ3ryzSNeu7Q=="],
|
"@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
||||||
|
|
||||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Nm3/azSsaVS7hk6EwtHEnTythjQfwvrO5tKqMlaH9TwG1P+PNaR8M0EAKZ+GaH2DFwvcr4iSfTveyxMIvXEHMw=="],
|
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
|
||||||
|
|
||||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-hozwnFHsLvujK4/CPVHNo3Bcg2EsnG8krI/ZQ2FlBlCRpPZW4XAEQmEwqegJsypsTAN9ehu2tEYe30lYKSZW/w=="],
|
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
|
||||||
|
|
||||||
"@shikijs/langs": ["@shikijs/langs@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2" } }, "sha512-bVx5PfuZHDSHoBal+KzJZGheFuyH4qwwcwG/n+MsWno5cTlKmaNtTsGzJpHYQ8YPbB5BdEdKU1rga5/6JGY8ww=="],
|
"@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
|
||||||
|
|
||||||
"@shikijs/themes": ["@shikijs/themes@3.12.2", "", { "dependencies": { "@shikijs/types": "3.12.2" } }, "sha512-fTR3QAgnwYpfGczpIbzPjlRnxyONJOerguQv1iwpyQZ9QXX4qy/XFQqXlf17XTsorxnHoJGbH/LXBvwtqDsF5A=="],
|
"@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
|
||||||
|
|
||||||
"@shikijs/types": ["@shikijs/types@3.12.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-K5UIBzxCyv0YoxN3LMrKB9zuhp1bV+LgewxuVwHdl4Gz5oePoUFrr9EfgJlGlDeXCU1b/yhdnXeuRvAnz8HN8Q=="],
|
"@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
||||||
|
|
||||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||||
|
|
||||||
@@ -518,35 +518,35 @@
|
|||||||
|
|
||||||
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
|
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.15", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.0", "lightningcss": "1.30.2", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.15" } }, "sha512-HF4+7QxATZWY3Jr8OlZrBSXmwT3Watj0OogeDvdUY/ByXJHQ+LBtqA2brDb3sBxYslIFx6UP94BJ4X6a4L9Bmw=="],
|
"@tailwindcss/node": ["@tailwindcss/node@4.1.17", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.17" } }, "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.15", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.15", "@tailwindcss/oxide-darwin-arm64": "4.1.15", "@tailwindcss/oxide-darwin-x64": "4.1.15", "@tailwindcss/oxide-freebsd-x64": "4.1.15", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.15", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.15", "@tailwindcss/oxide-linux-arm64-musl": "4.1.15", "@tailwindcss/oxide-linux-x64-gnu": "4.1.15", "@tailwindcss/oxide-linux-x64-musl": "4.1.15", "@tailwindcss/oxide-wasm32-wasi": "4.1.15", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.15", "@tailwindcss/oxide-win32-x64-msvc": "4.1.15" } }, "sha512-krhX+UOOgnsUuks2SR7hFafXmLQrKxB4YyRTERuCE59JlYL+FawgaAlSkOYmDRJdf1Q+IFNDMl9iRnBW7QBDfQ=="],
|
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.17", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.17", "@tailwindcss/oxide-darwin-arm64": "4.1.17", "@tailwindcss/oxide-darwin-x64": "4.1.17", "@tailwindcss/oxide-freebsd-x64": "4.1.17", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", "@tailwindcss/oxide-linux-x64-musl": "4.1.17", "@tailwindcss/oxide-wasm32-wasi": "4.1.17", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.15", "", { "os": "android", "cpu": "arm64" }, "sha512-TkUkUgAw8At4cBjCeVCRMc/guVLKOU1D+sBPrHt5uVcGhlbVKxrCaCW9OKUIBv1oWkjh4GbunD/u/Mf0ql6kEA=="],
|
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.17", "", { "os": "android", "cpu": "arm64" }, "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xt5XEJpn2piMSfvd1UFN6jrWXyaKCwikP4Pidcf+yfHTSzSpYhG3dcMktjNkQO3JiLCp+0bG0HoWGvz97K162w=="],
|
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-TnWaxP6Bx2CojZEXAV2M01Yl13nYPpp0EtGpUrY+LMciKfIXiLL2r/SiSRpagE5Fp2gX+rflp/Os1VJDAyqymg=="],
|
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-quISQDWqiB6Cqhjc3iWptXVZHNVENsWoI77L1qgGEHNIdLDLFnw3/AfY7DidAiiCIkGX/MjIdB3bbBZR/G2aJg=="],
|
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.15", "", { "os": "linux", "cpu": "arm" }, "sha512-ObG76+vPlab65xzVUQbExmDU9FIeYLQ5k2LrQdR2Ud6hboR+ZobXpDoKEYXf/uOezOfIYmy2Ta3w0ejkTg9yxg=="],
|
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17", "", { "os": "linux", "cpu": "arm" }, "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-4WbBacRmk43pkb8/xts3wnOZMDKsPFyEH/oisCm2q3aLZND25ufvJKcDUpAu0cS+CBOL05dYa8D4U5OWECuH/Q=="],
|
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-AbvmEiteEj1nf42nE8skdHv73NoR+EwXVSgPY6l39X12Ex8pzOwwfi3Kc8GAmjsnsaDEbk+aj9NyL3UeyHcTLg=="],
|
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.15", "", { "os": "linux", "cpu": "x64" }, "sha512-+rzMVlvVgrXtFiS+ES78yWgKqpThgV19ISKD58Ck+YO5pO5KjyxLt7AWKsWMbY0R9yBDC82w6QVGz837AKQcHg=="],
|
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.15", "", { "os": "linux", "cpu": "x64" }, "sha512-fPdEy7a8eQN9qOIK3Em9D3TO1z41JScJn8yxl/76mp4sAXFDfV4YXxsiptJcOwy6bGR+70ZSwFIZhTXzQeqwQg=="],
|
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.17", "", { "os": "linux", "cpu": "x64" }, "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.15", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-sJ4yd6iXXdlgIMfIBXuVGp/NvmviEoMVWMOAGxtxhzLPp9LOj5k0pMEMZdjeMCl4C6Up+RM8T3Zgk+BMQ0bGcQ=="],
|
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.17", "", { "dependencies": { "@emnapi/core": "^1.6.0", "@emnapi/runtime": "^1.6.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-sJGE5faXnNQ1iXeqmRin7Ds/ru2fgCiaQZQQz3ZGIDtvbkeV85rAZ0QJFMDg0FrqsffZG96H1U9AQlNBRLsHVg=="],
|
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.15", "", { "os": "win32", "cpu": "x64" }, "sha512-NLeHE7jUV6HcFKS504bpOohyi01zPXi2PXmjFfkzTph8xRxDdxkRsXm/xDO5uV5K3brrE1cCwbUYmFUSHR3u1w=="],
|
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="],
|
||||||
|
|
||||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.15", "", { "dependencies": { "@tailwindcss/node": "4.1.15", "@tailwindcss/oxide": "4.1.15", "tailwindcss": "4.1.15" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-B6s60MZRTUil+xKoZoGe6i0Iar5VuW+pmcGlda2FX+guDuQ1G1sjiIy1W0frneVpeL/ZjZ4KEgWZHNrIm++2qA=="],
|
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.17", "", { "dependencies": { "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "tailwindcss": "4.1.17" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA=="],
|
||||||
|
|
||||||
"@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.12", "", { "dependencies": { "@tanstack/virtual-core": "3.13.12" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA=="],
|
"@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.12", "", { "dependencies": { "@tanstack/virtual-core": "3.13.12" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA=="],
|
||||||
|
|
||||||
@@ -570,7 +570,7 @@
|
|||||||
|
|
||||||
"@types/bcryptjs": ["@types/bcryptjs@3.0.0", "", { "dependencies": { "bcryptjs": "*" } }, "sha512-WRZOuCuaz8UcZZE4R5HXTco2goQSI2XxjGY3hbM/xDvwmqFWd4ivooImsMx65OKM6CtNKbnZ5YL+YwAwK7c1dg=="],
|
"@types/bcryptjs": ["@types/bcryptjs@3.0.0", "", { "dependencies": { "bcryptjs": "*" } }, "sha512-WRZOuCuaz8UcZZE4R5HXTco2goQSI2XxjGY3hbM/xDvwmqFWd4ivooImsMx65OKM6CtNKbnZ5YL+YwAwK7c1dg=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
|
"@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
|
||||||
|
|
||||||
"@types/canvas-confetti": ["@types/canvas-confetti@1.9.0", "", {}, "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg=="],
|
"@types/canvas-confetti": ["@types/canvas-confetti@1.9.0", "", {}, "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg=="],
|
||||||
|
|
||||||
@@ -610,7 +610,7 @@
|
|||||||
|
|
||||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
||||||
|
|
||||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.0.4", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.38", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA=="],
|
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.0", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.43", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew=="],
|
||||||
|
|
||||||
"@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
|
"@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
|
||||||
|
|
||||||
@@ -682,7 +682,7 @@
|
|||||||
|
|
||||||
"astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
|
"astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
|
||||||
|
|
||||||
"astro": ["astro@5.14.8", "", { "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.7.4", "@astrojs/markdown-remark": "6.3.8", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^3.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.2.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.1", "deterministic-object-hash": "^2.0.2", "devalue": "^5.3.2", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.18", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.3.0", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.2", "shiki": "^3.12.0", "smol-toml": "^1.4.2", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.6.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.0", "vfile": "^6.0.3", "vite": "^6.3.6", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.24.6", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-nKqCLs7BFvGQL9QWQOUqxHhlHtV0UMLXz1ANJygozvjcexBWS7FYkWI2LzRPMNYmbW4msIWNWnX2RvLdvI5Cnw=="],
|
"astro": ["astro@5.15.4", "", { "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.7.4", "@astrojs/markdown-remark": "6.3.8", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^3.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.2.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.1", "deterministic-object-hash": "^2.0.2", "devalue": "^5.3.2", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.0", "magic-string": "^0.30.18", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.3.0", "picocolors": "^1.1.1", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.2", "shiki": "^3.12.0", "smol-toml": "^1.4.2", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.6.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.0", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.24.6", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-0g/68hLHEJZF2nYUcZM5O0kOnzCsCIf8eA9+0jfBAxp4ycujrIHRgIOdZCFKL9GoTsn8AypWbziypH5aEIF+aA=="],
|
||||||
|
|
||||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||||
|
|
||||||
@@ -694,20 +694,14 @@
|
|||||||
|
|
||||||
"basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="],
|
"basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="],
|
||||||
|
|
||||||
"bcryptjs": ["bcryptjs@3.0.2", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog=="],
|
"bcryptjs": ["bcryptjs@3.0.3", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g=="],
|
||||||
|
|
||||||
"before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
|
"before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
|
||||||
|
|
||||||
"better-auth": ["better-auth@1.4.0-beta.12", "", { "dependencies": { "@better-auth/core": "1.4.0-beta.12", "@better-auth/telemetry": "1.4.0-beta.12", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "1.0.24", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.5" } }, "sha512-IvrSBmQkHgOinDh6JyJCoKwbMPmHpkmt98/0hBU9Nc0s7Y7u72AOx1Z35J2dRQxxX4SzvFQ9pHqlV6wPnm72Ww=="],
|
"better-auth": ["better-auth@1.4.0-beta.13", "", { "dependencies": { "@better-auth/core": "1.4.0-beta.13", "@better-auth/telemetry": "1.4.0-beta.13", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "1.0.24", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.5" } }, "sha512-VOzbsCldupk2AdNfzDmpCVajX83nwITX8S9I8TdEUURgr3kB/CDVrsN6S8t0AClMnGgB4XaeKiXUNN30CCG4aA=="],
|
||||||
|
|
||||||
"better-call": ["better-call@1.0.24", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-iGqL29cstPp4xLD2MjKL1EmyAqQHjYS+cBMt4W27rPs3vf+kuqkVPA0NYaf7JciBOzVsJdNj4cbZWXC5TardWQ=="],
|
"better-call": ["better-call@1.0.24", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-iGqL29cstPp4xLD2MjKL1EmyAqQHjYS+cBMt4W27rPs3vf+kuqkVPA0NYaf7JciBOzVsJdNj4cbZWXC5TardWQ=="],
|
||||||
|
|
||||||
"better-sqlite3": ["better-sqlite3@12.4.1", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ=="],
|
|
||||||
|
|
||||||
"bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
|
|
||||||
|
|
||||||
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
|
|
||||||
|
|
||||||
"body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g=="],
|
"body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g=="],
|
||||||
|
|
||||||
"bottleneck": ["bottleneck@2.19.5", "", {}, "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="],
|
"bottleneck": ["bottleneck@2.19.5", "", {}, "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="],
|
||||||
@@ -724,7 +718,7 @@
|
|||||||
|
|
||||||
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
|
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
|
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
||||||
|
|
||||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||||
|
|
||||||
@@ -758,8 +752,6 @@
|
|||||||
|
|
||||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||||
|
|
||||||
"chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
|
|
||||||
|
|
||||||
"ci-info": ["ci-info@4.3.0", "", {}, "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ=="],
|
"ci-info": ["ci-info@4.3.0", "", {}, "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ=="],
|
||||||
|
|
||||||
"class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
|
"class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
|
||||||
@@ -822,12 +814,8 @@
|
|||||||
|
|
||||||
"decode-named-character-reference": ["decode-named-character-reference@1.1.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w=="],
|
"decode-named-character-reference": ["decode-named-character-reference@1.1.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w=="],
|
||||||
|
|
||||||
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
|
|
||||||
|
|
||||||
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
|
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
|
||||||
|
|
||||||
"deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
|
|
||||||
|
|
||||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||||
|
|
||||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||||
@@ -844,7 +832,7 @@
|
|||||||
|
|
||||||
"deterministic-object-hash": ["deterministic-object-hash@2.0.2", "", { "dependencies": { "base-64": "^1.0.0" } }, "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ=="],
|
"deterministic-object-hash": ["deterministic-object-hash@2.0.2", "", { "dependencies": { "base-64": "^1.0.0" } }, "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ=="],
|
||||||
|
|
||||||
"devalue": ["devalue@5.3.2", "", {}, "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw=="],
|
"devalue": ["devalue@5.4.2", "", {}, "sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw=="],
|
||||||
|
|
||||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||||
|
|
||||||
@@ -858,9 +846,9 @@
|
|||||||
|
|
||||||
"dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
|
"dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
|
||||||
|
|
||||||
"drizzle-kit": ["drizzle-kit@0.31.5", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-+CHgPFzuoTQTt7cOYCV6MOw2w8vqEn/ap1yv4bpZOWL03u7rlVRQhUY0WYT3rHsgVTXwYQDZaSUJSQrMBUKuWg=="],
|
"drizzle-kit": ["drizzle-kit@0.31.6", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-/B4e/4pwnx25QwD5xXgdpo1S+077a2VZdosXbItE/oNmUgQwZydGDz9qJYmnQl/b+5IX0rLfwRhrPnroGtrg8Q=="],
|
||||||
|
|
||||||
"drizzle-orm": ["drizzle-orm@0.44.6", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ=="],
|
"drizzle-orm": ["drizzle-orm@0.44.7", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ=="],
|
||||||
|
|
||||||
"dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="],
|
"dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="],
|
||||||
|
|
||||||
@@ -878,8 +866,6 @@
|
|||||||
|
|
||||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||||
|
|
||||||
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
|
|
||||||
|
|
||||||
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
||||||
|
|
||||||
"entities": ["entities@6.0.0", "", {}, "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw=="],
|
"entities": ["entities@6.0.0", "", {}, "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw=="],
|
||||||
@@ -924,8 +910,6 @@
|
|||||||
|
|
||||||
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
|
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
|
||||||
|
|
||||||
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
|
|
||||||
|
|
||||||
"expect-type": ["expect-type@1.2.1", "", {}, "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw=="],
|
"expect-type": ["expect-type@1.2.1", "", {}, "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw=="],
|
||||||
|
|
||||||
"express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="],
|
"express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="],
|
||||||
@@ -946,8 +930,6 @@
|
|||||||
|
|
||||||
"fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="],
|
"fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="],
|
||||||
|
|
||||||
"file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
|
|
||||||
|
|
||||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||||
|
|
||||||
"finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ=="],
|
"finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ=="],
|
||||||
@@ -962,8 +944,6 @@
|
|||||||
|
|
||||||
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||||
|
|
||||||
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
|
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
@@ -984,8 +964,6 @@
|
|||||||
|
|
||||||
"get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
|
"get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
|
||||||
|
|
||||||
"github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
|
|
||||||
|
|
||||||
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
||||||
|
|
||||||
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
@@ -1052,8 +1030,6 @@
|
|||||||
|
|
||||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||||
|
|
||||||
"ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
|
||||||
|
|
||||||
"inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
|
"inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
|
||||||
|
|
||||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||||
@@ -1164,7 +1140,7 @@
|
|||||||
|
|
||||||
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
"lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
|
||||||
"lucide-react": ["lucide-react@0.546.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ=="],
|
"lucide-react": ["lucide-react@0.553.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-BRgX5zrWmNy/lkVAe0dXBgd7XQdZ3HTf+Hwe3c9WK6dqgnj9h+hxV+MDncM88xDWlCq27+TKvHGE70ViODNILw=="],
|
||||||
|
|
||||||
"lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="],
|
"lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="],
|
||||||
|
|
||||||
@@ -1300,14 +1276,8 @@
|
|||||||
|
|
||||||
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||||
|
|
||||||
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
|
|
||||||
|
|
||||||
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
|
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
|
||||||
|
|
||||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
|
||||||
|
|
||||||
"mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
|
|
||||||
|
|
||||||
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
|
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
@@ -1318,8 +1288,6 @@
|
|||||||
|
|
||||||
"nanostores": ["nanostores@1.0.1", "", {}, "sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw=="],
|
"nanostores": ["nanostores@1.0.1", "", {}, "sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw=="],
|
||||||
|
|
||||||
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
|
|
||||||
|
|
||||||
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||||
|
|
||||||
"neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="],
|
"neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="],
|
||||||
@@ -1328,8 +1296,6 @@
|
|||||||
|
|
||||||
"nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="],
|
"nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="],
|
||||||
|
|
||||||
"node-abi": ["node-abi@3.78.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ=="],
|
|
||||||
|
|
||||||
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
|
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
|
||||||
|
|
||||||
"node-forge": ["node-forge@1.3.1", "", {}, "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="],
|
"node-forge": ["node-forge@1.3.1", "", {}, "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="],
|
||||||
@@ -1356,8 +1322,6 @@
|
|||||||
|
|
||||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||||
|
|
||||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
|
||||||
|
|
||||||
"oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
|
"oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
|
||||||
|
|
||||||
"oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="],
|
"oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="],
|
||||||
@@ -1394,8 +1358,6 @@
|
|||||||
|
|
||||||
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
|
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
|
||||||
|
|
||||||
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
|
||||||
|
|
||||||
"prettier": ["prettier@2.8.7", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw=="],
|
"prettier": ["prettier@2.8.7", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw=="],
|
||||||
|
|
||||||
"pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="],
|
"pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="],
|
||||||
@@ -1408,8 +1370,6 @@
|
|||||||
|
|
||||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||||
|
|
||||||
"pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
|
|
||||||
|
|
||||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
||||||
"pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="],
|
"pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="],
|
||||||
@@ -1426,8 +1386,6 @@
|
|||||||
|
|
||||||
"raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="],
|
"raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="],
|
||||||
|
|
||||||
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
|
|
||||||
|
|
||||||
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
|
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
|
||||||
|
|
||||||
"react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
|
"react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
|
||||||
@@ -1436,7 +1394,7 @@
|
|||||||
|
|
||||||
"react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
|
"react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
|
||||||
|
|
||||||
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
"react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
|
||||||
|
|
||||||
"react-remove-scroll": ["react-remove-scroll@2.7.0", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-sGsQtcjMqdQyijAHytfGEELB8FufGbfXIsvUTe+NLx1GDRJCXtCFLBLUI1eyZCKXXvbEU2C6gai0PZKoIE9Vbg=="],
|
"react-remove-scroll": ["react-remove-scroll@2.7.0", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-sGsQtcjMqdQyijAHytfGEELB8FufGbfXIsvUTe+NLx1GDRJCXtCFLBLUI1eyZCKXXvbEU2C6gai0PZKoIE9Vbg=="],
|
||||||
|
|
||||||
@@ -1444,8 +1402,6 @@
|
|||||||
|
|
||||||
"react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
|
"react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
|
||||||
|
|
||||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
|
||||||
|
|
||||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||||
|
|
||||||
"recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="],
|
"recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="],
|
||||||
@@ -1538,7 +1494,7 @@
|
|||||||
|
|
||||||
"sharp": ["sharp@0.34.3", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="],
|
"sharp": ["sharp@0.34.3", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="],
|
||||||
|
|
||||||
"shiki": ["shiki@3.12.2", "", { "dependencies": { "@shikijs/core": "3.12.2", "@shikijs/engine-javascript": "3.12.2", "@shikijs/engine-oniguruma": "3.12.2", "@shikijs/langs": "3.12.2", "@shikijs/themes": "3.12.2", "@shikijs/types": "3.12.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-uIrKI+f9IPz1zDT+GMz+0RjzKJiijVr6WDWm9Pe3NNY6QigKCfifCEv9v9R2mDASKKjzjQ2QpFLcxaR3iHSnMA=="],
|
"shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
|
||||||
|
|
||||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||||
|
|
||||||
@@ -1550,10 +1506,6 @@
|
|||||||
|
|
||||||
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
|
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
|
||||||
|
|
||||||
"simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
|
|
||||||
|
|
||||||
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
|
|
||||||
|
|
||||||
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
|
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
|
||||||
|
|
||||||
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||||
@@ -1576,16 +1528,12 @@
|
|||||||
|
|
||||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
|
||||||
|
|
||||||
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
|
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
|
||||||
|
|
||||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
"strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="],
|
"strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="],
|
||||||
|
|
||||||
"strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
|
|
||||||
|
|
||||||
"strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="],
|
"strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="],
|
||||||
|
|
||||||
"strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
|
"strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
|
||||||
@@ -1600,14 +1548,10 @@
|
|||||||
|
|
||||||
"tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
|
"tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
|
||||||
|
|
||||||
"tailwindcss": ["tailwindcss@4.1.15", "", {}, "sha512-k2WLnWkYFkdpRv+Oby3EBXIyQC8/s1HOFMBUViwtAh6Z5uAozeUSMQlIsn/c6Q2iJzqG6aJT3wdPaRNj70iYxQ=="],
|
"tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="],
|
||||||
|
|
||||||
"tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="],
|
"tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="],
|
||||||
|
|
||||||
"tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="],
|
|
||||||
|
|
||||||
"tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
|
|
||||||
|
|
||||||
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
|
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
|
||||||
|
|
||||||
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
||||||
@@ -1644,8 +1588,6 @@
|
|||||||
|
|
||||||
"tsx": ["tsx@4.20.6", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg=="],
|
"tsx": ["tsx@4.20.6", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg=="],
|
||||||
|
|
||||||
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
|
|
||||||
|
|
||||||
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],
|
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],
|
||||||
|
|
||||||
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||||
@@ -1708,8 +1650,6 @@
|
|||||||
|
|
||||||
"use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="],
|
"use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="],
|
||||||
|
|
||||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
|
||||||
|
|
||||||
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
||||||
|
|
||||||
"uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
|
"uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
|
||||||
@@ -1724,7 +1664,7 @@
|
|||||||
|
|
||||||
"vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="],
|
"vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="],
|
||||||
|
|
||||||
"vite": ["vite@6.3.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA=="],
|
"vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||||
|
|
||||||
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
|
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
|
||||||
|
|
||||||
@@ -1786,8 +1726,6 @@
|
|||||||
|
|
||||||
"wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="],
|
"wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="],
|
||||||
|
|
||||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
|
||||||
|
|
||||||
"ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="],
|
"ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="],
|
||||||
|
|
||||||
"xml": ["xml@1.0.1", "", {}, "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="],
|
"xml": ["xml@1.0.1", "", {}, "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="],
|
||||||
@@ -1834,8 +1772,6 @@
|
|||||||
|
|
||||||
"@ampproject/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
"@ampproject/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||||
|
|
||||||
"@astrojs/markdown-remark/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="],
|
|
||||||
|
|
||||||
"@astrojs/react/@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
|
"@astrojs/react/@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
|
||||||
|
|
||||||
"@astrojs/telemetry/ci-info": ["ci-info@4.2.0", "", {}, "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg=="],
|
"@astrojs/telemetry/ci-info": ["ci-info@4.2.0", "", {}, "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg=="],
|
||||||
@@ -1860,19 +1796,31 @@
|
|||||||
|
|
||||||
"@babel/template/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="],
|
"@babel/template/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="],
|
||||||
|
|
||||||
"@octokit/core/@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
"@radix-ui/react-avatar/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="],
|
||||||
|
|
||||||
"@octokit/endpoint/@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
"@radix-ui/react-avatar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||||
|
|
||||||
"@octokit/graphql/@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
"@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||||
|
|
||||||
"@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
"@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||||
|
|
||||||
"@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
"@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||||
|
|
||||||
"@octokit/request/@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
"@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||||
|
|
||||||
"@octokit/request-error/@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="],
|
"@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||||
|
|
||||||
|
"@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||||
|
|
||||||
|
"@radix-ui/react-progress/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="],
|
||||||
|
|
||||||
|
"@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||||
|
|
||||||
|
"@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||||
|
|
||||||
|
"@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||||
|
|
||||||
|
"@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||||
|
|
||||||
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||||
|
|
||||||
@@ -1882,7 +1830,7 @@
|
|||||||
|
|
||||||
"@tailwindcss/node/lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
|
"@tailwindcss/node/lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
|
||||||
|
|
||||||
"@tailwindcss/node/magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
|
"@tailwindcss/node/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.6.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg=="],
|
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.6.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg=="],
|
||||||
|
|
||||||
@@ -1912,6 +1860,8 @@
|
|||||||
|
|
||||||
"@types/babel__traverse/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="],
|
"@types/babel__traverse/@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="],
|
||||||
|
|
||||||
|
"@types/bcryptjs/bcryptjs": ["bcryptjs@3.0.2", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog=="],
|
||||||
|
|
||||||
"accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
"accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
@@ -1926,8 +1876,6 @@
|
|||||||
|
|
||||||
"basic-auth/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
"basic-auth/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||||
|
|
||||||
"bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
|
||||||
|
|
||||||
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||||
@@ -2016,22 +1964,12 @@
|
|||||||
|
|
||||||
"yaml-language-server/yaml": ["yaml@2.2.2", "", {}, "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA=="],
|
"yaml-language-server/yaml": ["yaml@2.2.2", "", {}, "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA=="],
|
||||||
|
|
||||||
"@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA=="],
|
|
||||||
|
|
||||||
"@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="],
|
|
||||||
|
|
||||||
"@astrojs/markdown-remark/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
|
|
||||||
|
|
||||||
"@astrojs/markdown-remark/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
|
|
||||||
|
|
||||||
"@astrojs/markdown-remark/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
|
|
||||||
|
|
||||||
"@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
|
|
||||||
|
|
||||||
"@astrojs/react/@vitejs/plugin-react/@babel/core": ["@babel/core@7.28.3", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.3", "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ=="],
|
"@astrojs/react/@vitejs/plugin-react/@babel/core": ["@babel/core@7.28.3", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.3", "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ=="],
|
||||||
|
|
||||||
"@astrojs/react/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
"@astrojs/react/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
||||||
|
|
||||||
|
"@astrojs/react/@vitejs/plugin-react/react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
||||||
|
|
||||||
"@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.27.3", "", { "dependencies": { "@babel/parser": "^7.27.3", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q=="],
|
"@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.27.3", "", { "dependencies": { "@babel/parser": "^7.27.3", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q=="],
|
||||||
|
|
||||||
"@babel/helper-module-imports/@babel/traverse/@babel/parser": ["@babel/parser@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw=="],
|
"@babel/helper-module-imports/@babel/traverse/@babel/parser": ["@babel/parser@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw=="],
|
||||||
@@ -2040,20 +1978,6 @@
|
|||||||
|
|
||||||
"@babel/helper-module-transforms/@babel/traverse/@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="],
|
"@babel/helper-module-transforms/@babel/traverse/@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="],
|
||||||
|
|
||||||
"@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
|
||||||
|
|
||||||
"@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
|
||||||
|
|
||||||
"@octokit/graphql/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
|
||||||
|
|
||||||
"@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
|
||||||
|
|
||||||
"@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
|
||||||
|
|
||||||
"@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
|
||||||
|
|
||||||
"@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="],
|
|
||||||
|
|
||||||
"@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
|
"@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
|
||||||
|
|
||||||
"@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
|
"@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
|
||||||
@@ -2094,6 +2018,8 @@
|
|||||||
|
|
||||||
"cmdk/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="],
|
"cmdk/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="],
|
||||||
|
|
||||||
|
"cmdk/@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||||
|
|
||||||
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
"express/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
|
"express/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
|
||||||
@@ -2116,6 +2042,8 @@
|
|||||||
|
|
||||||
"vaul/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="],
|
"vaul/@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="],
|
||||||
|
|
||||||
|
"vaul/@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||||
|
|
||||||
"widest-line/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
|
"widest-line/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
|
||||||
|
|
||||||
"widest-line/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
"widest-line/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||||
|
|||||||
@@ -81,6 +81,26 @@ Replace `{provider-id}` with your chosen Provider ID.
|
|||||||
- Client Secret: [Your Okta Client Secret]
|
- Client Secret: [Your Okta Client Secret]
|
||||||
- Click "Discover" to auto-fill endpoints
|
- Click "Discover" to auto-fill endpoints
|
||||||
|
|
||||||
|
### Example: Authentik SSO Setup
|
||||||
|
|
||||||
|
Working Authentik deployments (see [#134](https://github.com/RayLabsHQ/gitea-mirror/issues/134)) follow these steps:
|
||||||
|
|
||||||
|
1. In Authentik, create a new **Application** and OIDC **Provider** (implicit flow works well for testing).
|
||||||
|
2. Start creating an SSO provider inside Gitea Mirror so you can copy the redirect URL shown (`https://your-domain.com/api/auth/sso/callback/authentik` if you pick `authentik` as your Provider ID).
|
||||||
|
3. Paste that redirect URL into the Authentik Provider configuration and finish creating the provider.
|
||||||
|
4. Copy the Authentik issuer URL, client ID, and client secret.
|
||||||
|
5. Back in Gitea Mirror:
|
||||||
|
- Issuer URL: the exact value from Authentik (keep any trailing slash Authentik shows).
|
||||||
|
- Provider ID: match the one you used in step 2.
|
||||||
|
- Click **Discover** so Gitea Mirror stores the authorization, token, and JWKS endpoints (Authentik publishes them via discovery).
|
||||||
|
- Domain: enter the email domain you expect to match (e.g. `example.com`).
|
||||||
|
6. Save the provider and test the login flow.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Make sure `BETTER_AUTH_URL` and (if you serve the UI from multiple origins) `BETTER_AUTH_TRUSTED_ORIGINS` point at the public URL users reach. A mismatch can surface as 500 errors after redirect.
|
||||||
|
- Authentik must report the user’s email as verified (default behavior) so Gitea Mirror can auto-link accounts.
|
||||||
|
- If you created an Authentik provider before v3.8.10 you should delete it and re-add it after upgrading; older versions saved incomplete endpoint data which leads to the `url.startsWith` error explained in the Troubleshooting section.
|
||||||
|
|
||||||
## Setting up OIDC Provider
|
## Setting up OIDC Provider
|
||||||
|
|
||||||
The OIDC Provider feature allows other applications to use Gitea Mirror as their authentication provider.
|
The OIDC Provider feature allows other applications to use Gitea Mirror as their authentication provider.
|
||||||
@@ -165,6 +185,7 @@ When an application requests authentication:
|
|||||||
1. **"Invalid origin" error**: Check that your Gitea Mirror URL matches the configured redirect URI
|
1. **"Invalid origin" error**: Check that your Gitea Mirror URL matches the configured redirect URI
|
||||||
2. **"Provider not found" error**: Ensure the provider is properly configured and enabled
|
2. **"Provider not found" error**: Ensure the provider is properly configured and enabled
|
||||||
3. **Redirect loop**: Verify the redirect URI in both Gitea Mirror and the SSO provider match exactly
|
3. **Redirect loop**: Verify the redirect URI in both Gitea Mirror and the SSO provider match exactly
|
||||||
|
4. **`TypeError: undefined is not an object (evaluating 'url.startsWith')`**: This indicates the stored provider configuration is missing OIDC endpoints. Delete the provider from Gitea Mirror and re-register it using the **Discover** button so authorization/token URLs are saved (see [#73](https://github.com/RayLabsHQ/gitea-mirror/issues/73) and [#122](https://github.com/RayLabsHQ/gitea-mirror/issues/122) for examples).
|
||||||
|
|
||||||
### OIDC Provider Issues
|
### OIDC Provider Issues
|
||||||
|
|
||||||
@@ -202,4 +223,4 @@ This immediately prevents the application from authenticating new users.
|
|||||||
If migrating from the previous JWT-based authentication:
|
If migrating from the previous JWT-based authentication:
|
||||||
- Existing users remain unaffected
|
- Existing users remain unaffected
|
||||||
- Users can continue using email/password authentication
|
- Users can continue using email/password authentication
|
||||||
- SSO can be added as an additional authentication method
|
- SSO can be added as an additional authentication method
|
||||||
|
|||||||
4
drizzle/0006_military_la_nuit.sql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE `accounts` ADD `id_token` text;--> statement-breakpoint
|
||||||
|
ALTER TABLE `accounts` ADD `access_token_expires_at` integer;--> statement-breakpoint
|
||||||
|
ALTER TABLE `accounts` ADD `refresh_token_expires_at` integer;--> statement-breakpoint
|
||||||
|
ALTER TABLE `accounts` ADD `scope` text;
|
||||||
18
drizzle/0007_whole_hellion.sql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
ALTER TABLE `organizations` ADD `normalized_name` text NOT NULL DEFAULT '';--> statement-breakpoint
|
||||||
|
UPDATE `organizations` SET `normalized_name` = lower(trim(`name`));--> statement-breakpoint
|
||||||
|
DELETE FROM `organizations`
|
||||||
|
WHERE rowid NOT IN (
|
||||||
|
SELECT MIN(rowid)
|
||||||
|
FROM `organizations`
|
||||||
|
GROUP BY `user_id`, `normalized_name`
|
||||||
|
);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `uniq_organizations_user_normalized_name` ON `organizations` (`user_id`,`normalized_name`);--> statement-breakpoint
|
||||||
|
ALTER TABLE `repositories` ADD `normalized_full_name` text NOT NULL DEFAULT '';--> statement-breakpoint
|
||||||
|
UPDATE `repositories` SET `normalized_full_name` = lower(trim(`full_name`));--> statement-breakpoint
|
||||||
|
DELETE FROM `repositories`
|
||||||
|
WHERE rowid NOT IN (
|
||||||
|
SELECT MIN(rowid)
|
||||||
|
FROM `repositories`
|
||||||
|
GROUP BY `user_id`, `normalized_full_name`
|
||||||
|
);--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `uniq_repositories_user_normalized_full_name` ON `repositories` (`user_id`,`normalized_full_name`);
|
||||||
1
drizzle/0008_serious_thena.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `repositories` ADD `metadata` text;
|
||||||
1969
drizzle/meta/0006_snapshot.json
Normal file
1999
drizzle/meta/0007_snapshot.json
Normal file
2006
drizzle/meta/0008_snapshot.json
Normal file
@@ -43,6 +43,27 @@
|
|||||||
"when": 1757786449446,
|
"when": 1757786449446,
|
||||||
"tag": "0005_polite_preak",
|
"tag": "0005_polite_preak",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 6,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1761483928546,
|
||||||
|
"tag": "0006_military_la_nuit",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 7,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1761534391115,
|
||||||
|
"tag": "0007_whole_hellion",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 8,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1761802056073,
|
||||||
|
"tag": "0008_serious_thena",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
46
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "gitea-mirror",
|
"name": "gitea-mirror",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "3.8.9",
|
"version": "3.9.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"bun": ">=1.2.9"
|
"bun": ">=1.2.9"
|
||||||
},
|
},
|
||||||
@@ -38,60 +38,60 @@
|
|||||||
"astro": "bunx --bun astro"
|
"astro": "bunx --bun astro"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@esbuild-kit/esm-loader": "npm:tsx@^4.20.5",
|
"@esbuild-kit/esm-loader": "npm:tsx@^4.20.6",
|
||||||
"devalue": "^5.3.2"
|
"devalue": "^5.4.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.5",
|
"@astrojs/check": "^0.9.5",
|
||||||
"@astrojs/mdx": "4.3.7",
|
"@astrojs/mdx": "4.3.10",
|
||||||
"@astrojs/node": "9.5.0",
|
"@astrojs/node": "9.5.0",
|
||||||
"@astrojs/react": "^4.4.0",
|
"@astrojs/react": "^4.4.2",
|
||||||
"@better-auth/sso": "1.4.0-beta.12",
|
"@better-auth/sso": "1.4.0-beta.12",
|
||||||
"@octokit/plugin-throttling": "^11.0.2",
|
"@octokit/plugin-throttling": "^11.0.3",
|
||||||
"@octokit/rest": "^22.0.0",
|
"@octokit/rest": "^22.0.1",
|
||||||
"@radix-ui/react-accordion": "^1.2.12",
|
"@radix-ui/react-accordion": "^1.2.12",
|
||||||
"@radix-ui/react-avatar": "^1.1.10",
|
"@radix-ui/react-avatar": "^1.1.11",
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-hover-card": "^1.1.15",
|
"@radix-ui/react-hover-card": "^1.1.15",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.8",
|
||||||
"@radix-ui/react-popover": "^1.1.15",
|
"@radix-ui/react-popover": "^1.1.15",
|
||||||
"@radix-ui/react-progress": "^1.1.7",
|
"@radix-ui/react-progress": "^1.1.8",
|
||||||
"@radix-ui/react-radio-group": "^1.3.8",
|
"@radix-ui/react-radio-group": "^1.3.8",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
"@radix-ui/react-select": "^2.2.6",
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tabs": "^1.1.13",
|
"@radix-ui/react-tabs": "^1.1.13",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tailwindcss/vite": "^4.1.15",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@tanstack/react-virtual": "^3.13.12",
|
"@tanstack/react-virtual": "^3.13.12",
|
||||||
"@types/canvas-confetti": "^1.9.0",
|
"@types/canvas-confetti": "^1.9.0",
|
||||||
"@types/react": "^19.2.2",
|
"@types/react": "^19.2.2",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
"astro": "^5.14.8",
|
"astro": "^5.15.4",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.3",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"better-auth": "1.4.0-beta.12",
|
"better-auth": "1.4.0-beta.13",
|
||||||
"canvas-confetti": "^1.9.3",
|
"canvas-confetti": "^1.9.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"drizzle-orm": "^0.44.6",
|
"drizzle-orm": "^0.44.7",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lucide-react": "^0.546.0",
|
"lucide-react": "^0.553.0",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss": "^4.1.15",
|
"tailwindcss": "^4.1.17",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
@@ -102,14 +102,14 @@
|
|||||||
"@testing-library/jest-dom": "^6.9.1",
|
"@testing-library/jest-dom": "^6.9.1",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@types/bcryptjs": "^3.0.0",
|
"@types/bcryptjs": "^3.0.0",
|
||||||
"@types/bun": "^1.3.0",
|
"@types/bun": "^1.3.1",
|
||||||
"@types/jsonwebtoken": "^9.0.10",
|
"@types/jsonwebtoken": "^9.0.10",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@vitejs/plugin-react": "^5.0.4",
|
"@vitejs/plugin-react": "^5.1.0",
|
||||||
"drizzle-kit": "^0.31.5",
|
"drizzle-kit": "^0.31.6",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"tsx": "^4.20.6",
|
"tsx": "^4.20.6",
|
||||||
"vitest": "^3.2.4"
|
"vitest": "^3.2.4"
|
||||||
},
|
},
|
||||||
"packageManager": "bun@1.2.23"
|
"packageManager": "bun@1.3.1"
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
3
public/favicon.svg
Normal file
|
After Width: | Height: | Size: 216 KiB |
@@ -114,10 +114,10 @@ EOF
|
|||||||
echo "======================================"
|
echo "======================================"
|
||||||
echo "1. Access Authentik at http://localhost:9000"
|
echo "1. Access Authentik at http://localhost:9000"
|
||||||
echo "2. Login with akadmin / admin-password"
|
echo "2. Login with akadmin / admin-password"
|
||||||
echo "3. Create OAuth2 Provider for Gitea Mirror:"
|
echo "3. Create an Authentik OIDC Provider for Gitea Mirror:"
|
||||||
echo " - Name: gitea-mirror"
|
echo " - Name: gitea-mirror"
|
||||||
echo " - Redirect URIs:"
|
echo " - Redirect URI:"
|
||||||
echo " http://localhost:4321/api/auth/callback/sso-provider"
|
echo " http://localhost:4321/api/auth/sso/callback/authentik"
|
||||||
echo " - Scopes: openid, profile, email"
|
echo " - Scopes: openid, profile, email"
|
||||||
echo ""
|
echo ""
|
||||||
echo "4. Create Application:"
|
echo "4. Create Application:"
|
||||||
@@ -131,10 +131,14 @@ EOF
|
|||||||
echo "6. Configure SSO in Gitea Mirror:"
|
echo "6. Configure SSO in Gitea Mirror:"
|
||||||
echo " - Go to Settings → Authentication & SSO"
|
echo " - Go to Settings → Authentication & SSO"
|
||||||
echo " - Add provider with:"
|
echo " - Add provider with:"
|
||||||
|
echo " - Provider ID: authentik"
|
||||||
echo " - Issuer URL: http://localhost:9000/application/o/gitea-mirror/"
|
echo " - Issuer URL: http://localhost:9000/application/o/gitea-mirror/"
|
||||||
|
echo " - Click Discover to pull Authentik endpoints"
|
||||||
echo " - Client ID: (from Authentik provider)"
|
echo " - Client ID: (from Authentik provider)"
|
||||||
echo " - Client Secret: (from Authentik provider)"
|
echo " - Client Secret: (from Authentik provider)"
|
||||||
echo ""
|
echo ""
|
||||||
|
echo "If you previously registered this provider on a version earlier than v3.8.10, delete it and re-add it after upgrading to avoid missing endpoint data."
|
||||||
|
echo ""
|
||||||
;;
|
;;
|
||||||
|
|
||||||
stop)
|
stop)
|
||||||
@@ -177,4 +181,4 @@ EOF
|
|||||||
echo " status - Show service status"
|
echo " status - Show service status"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -20,9 +20,11 @@ interface AddOrganizationDialogProps {
|
|||||||
onAddOrganization: ({
|
onAddOrganization: ({
|
||||||
org,
|
org,
|
||||||
role,
|
role,
|
||||||
|
force,
|
||||||
}: {
|
}: {
|
||||||
org: string;
|
org: string;
|
||||||
role: MembershipRole;
|
role: MembershipRole;
|
||||||
|
force?: boolean;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +38,14 @@ export default function AddOrganizationDialog({
|
|||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isDialogOpen) {
|
||||||
|
setError("");
|
||||||
|
setOrg("");
|
||||||
|
setRole("member");
|
||||||
|
}
|
||||||
|
}, [isDialogOpen]);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -54,7 +64,7 @@ export default function AddOrganizationDialog({
|
|||||||
setRole("member");
|
setRole("member");
|
||||||
setIsDialogOpen(false);
|
setIsDialogOpen(false);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err?.message || "Failed to add repository.");
|
setError(err?.message || "Failed to add organization.");
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@@ -139,7 +149,7 @@ export default function AddOrganizationDialog({
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<LoaderCircle className="h-4 w-4 animate-spin" />
|
<LoaderCircle className="h-4 w-4 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
"Add Repository"
|
"Add Organization"
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Search, RefreshCw, FlipHorizontal, Filter } from "lucide-react";
|
import { Search, RefreshCw, FlipHorizontal, Filter, LoaderCircle, Trash2 } from "lucide-react";
|
||||||
import type { MirrorJob, Organization } from "@/lib/db/schema";
|
import type { MirrorJob, Organization } from "@/lib/db/schema";
|
||||||
import { OrganizationList } from "./OrganizationsList";
|
import { OrganizationList } from "./OrganizationsList";
|
||||||
import AddOrganizationDialog from "./AddOrganizationDialog";
|
import AddOrganizationDialog from "./AddOrganizationDialog";
|
||||||
@@ -37,6 +37,14 @@ import {
|
|||||||
DrawerTitle,
|
DrawerTitle,
|
||||||
DrawerTrigger,
|
DrawerTrigger,
|
||||||
} from "@/components/ui/drawer";
|
} from "@/components/ui/drawer";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
|
||||||
export function Organization() {
|
export function Organization() {
|
||||||
const [organizations, setOrganizations] = useState<Organization[]>([]);
|
const [organizations, setOrganizations] = useState<Organization[]>([]);
|
||||||
@@ -52,6 +60,15 @@ export function Organization() {
|
|||||||
status: "",
|
status: "",
|
||||||
});
|
});
|
||||||
const [loadingOrgIds, setLoadingOrgIds] = useState<Set<string>>(new Set()); // this is used when the api actions are performed
|
const [loadingOrgIds, setLoadingOrgIds] = useState<Set<string>>(new Set()); // this is used when the api actions are performed
|
||||||
|
const [duplicateOrgCandidate, setDuplicateOrgCandidate] = useState<{
|
||||||
|
org: string;
|
||||||
|
role: MembershipRole;
|
||||||
|
} | null>(null);
|
||||||
|
const [isDuplicateOrgDialogOpen, setIsDuplicateOrgDialogOpen] = useState(false);
|
||||||
|
const [isProcessingDuplicateOrg, setIsProcessingDuplicateOrg] = useState(false);
|
||||||
|
const [orgToDelete, setOrgToDelete] = useState<Organization | null>(null);
|
||||||
|
const [isDeleteOrgDialogOpen, setIsDeleteOrgDialogOpen] = useState(false);
|
||||||
|
const [isDeletingOrg, setIsDeletingOrg] = useState(false);
|
||||||
|
|
||||||
// Create a stable callback using useCallback
|
// Create a stable callback using useCallback
|
||||||
const handleNewMessage = useCallback((data: MirrorJob) => {
|
const handleNewMessage = useCallback((data: MirrorJob) => {
|
||||||
@@ -256,19 +273,45 @@ export function Organization() {
|
|||||||
const handleAddOrganization = async ({
|
const handleAddOrganization = async ({
|
||||||
org,
|
org,
|
||||||
role,
|
role,
|
||||||
|
force = false,
|
||||||
}: {
|
}: {
|
||||||
org: string;
|
org: string;
|
||||||
role: MembershipRole;
|
role: MembershipRole;
|
||||||
|
force?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
if (!user || !user.id) {
|
||||||
if (!user || !user.id) {
|
return;
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
const trimmedOrg = org.trim();
|
||||||
|
const normalizedOrg = trimmedOrg.toLowerCase();
|
||||||
|
|
||||||
|
if (!trimmedOrg) {
|
||||||
|
toast.error("Please enter a valid organization name.");
|
||||||
|
throw new Error("Invalid organization name");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force) {
|
||||||
|
const alreadyExists = organizations.some(
|
||||||
|
(existing) => existing.name?.trim().toLowerCase() === normalizedOrg
|
||||||
|
);
|
||||||
|
|
||||||
|
if (alreadyExists) {
|
||||||
|
toast.warning("Organization already exists.");
|
||||||
|
setDuplicateOrgCandidate({ org: trimmedOrg, role });
|
||||||
|
setIsDuplicateOrgDialogOpen(true);
|
||||||
|
throw new Error("Organization already exists");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
const reqPayload: AddOrganizationApiRequest = {
|
const reqPayload: AddOrganizationApiRequest = {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
org,
|
org: trimmedOrg,
|
||||||
role,
|
role,
|
||||||
|
force,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await apiRequest<AddOrganizationApiResponse>(
|
const response = await apiRequest<AddOrganizationApiResponse>(
|
||||||
@@ -280,25 +323,100 @@ export function Organization() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
toast.success(`Organization added successfully`);
|
const message = force
|
||||||
setOrganizations((prev) => [...prev, response.organization]);
|
? "Organization already exists; using existing entry."
|
||||||
|
: "Organization added successfully";
|
||||||
|
toast.success(message);
|
||||||
|
|
||||||
await fetchOrganizations();
|
await fetchOrganizations(false);
|
||||||
|
|
||||||
setFilter((prev) => ({
|
setFilter((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
searchTerm: org,
|
searchTerm: trimmedOrg,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (force) {
|
||||||
|
setIsDuplicateOrgDialogOpen(false);
|
||||||
|
setDuplicateOrgCandidate(null);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showErrorToast(response.error || "Error adding organization", toast);
|
showErrorToast(response.error || "Error adding organization", toast);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error, toast);
|
showErrorToast(error, toast);
|
||||||
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleConfirmDuplicateOrganization = async () => {
|
||||||
|
if (!duplicateOrgCandidate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsProcessingDuplicateOrg(true);
|
||||||
|
try {
|
||||||
|
await handleAddOrganization({
|
||||||
|
org: duplicateOrgCandidate.org,
|
||||||
|
role: duplicateOrgCandidate.role,
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
setDuplicateOrgCandidate(null);
|
||||||
|
setIsDuplicateOrgDialogOpen(false);
|
||||||
|
} catch (error) {
|
||||||
|
// Error already surfaced via toast
|
||||||
|
} finally {
|
||||||
|
setIsProcessingDuplicateOrg(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancelDuplicateOrganization = () => {
|
||||||
|
setIsDuplicateOrgDialogOpen(false);
|
||||||
|
setDuplicateOrgCandidate(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRequestDeleteOrganization = (orgId: string) => {
|
||||||
|
const org = organizations.find((item) => item.id === orgId);
|
||||||
|
if (!org) {
|
||||||
|
toast.error("Organization not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOrgToDelete(org);
|
||||||
|
setIsDeleteOrgDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteOrganization = async () => {
|
||||||
|
if (!user || !user.id || !orgToDelete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsDeletingOrg(true);
|
||||||
|
try {
|
||||||
|
const response = await apiRequest<{ success: boolean; error?: string }>(
|
||||||
|
`/organizations/${orgToDelete.id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
toast.success(`Removed ${orgToDelete.name} from Gitea Mirror.`);
|
||||||
|
await fetchOrganizations(false);
|
||||||
|
} else {
|
||||||
|
showErrorToast(response.error || "Failed to delete organization", toast);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showErrorToast(error, toast);
|
||||||
|
} finally {
|
||||||
|
setIsDeletingOrg(false);
|
||||||
|
setIsDeleteOrgDialogOpen(false);
|
||||||
|
setOrgToDelete(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleMirrorAllOrgs = async () => {
|
const handleMirrorAllOrgs = async () => {
|
||||||
try {
|
try {
|
||||||
if (!user || !user.id || organizations.length === 0) {
|
if (!user || !user.id || organizations.length === 0) {
|
||||||
@@ -711,6 +829,7 @@ export function Organization() {
|
|||||||
onMirror={handleMirrorOrg}
|
onMirror={handleMirrorOrg}
|
||||||
onIgnore={handleIgnoreOrg}
|
onIgnore={handleIgnoreOrg}
|
||||||
onAddOrganization={() => setIsDialogOpen(true)}
|
onAddOrganization={() => setIsDialogOpen(true)}
|
||||||
|
onDelete={handleRequestDeleteOrganization}
|
||||||
onRefresh={async () => {
|
onRefresh={async () => {
|
||||||
await fetchOrganizations(false);
|
await fetchOrganizations(false);
|
||||||
}}
|
}}
|
||||||
@@ -721,6 +840,68 @@ export function Organization() {
|
|||||||
isDialogOpen={isDialogOpen}
|
isDialogOpen={isDialogOpen}
|
||||||
setIsDialogOpen={setIsDialogOpen}
|
setIsDialogOpen={setIsDialogOpen}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Dialog open={isDuplicateOrgDialogOpen} onOpenChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
handleCancelDuplicateOrganization();
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Organization already exists</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
{duplicateOrgCandidate?.org ?? "This organization"} is already synced in Gitea Mirror.
|
||||||
|
Continuing will reuse the existing entry without creating a duplicate. You can remove it later if needed.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={handleCancelDuplicateOrganization} disabled={isProcessingDuplicateOrg}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleConfirmDuplicateOrganization} disabled={isProcessingDuplicateOrg}>
|
||||||
|
{isProcessingDuplicateOrg ? (
|
||||||
|
<LoaderCircle className="h-4 w-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
"Continue"
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog open={isDeleteOrgDialogOpen} onOpenChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
setIsDeleteOrgDialogOpen(false);
|
||||||
|
setOrgToDelete(null);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Remove organization from Gitea Mirror?</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
{orgToDelete?.name ?? "This organization"} will be deleted from Gitea Mirror only. Nothing will be removed from Gitea; you will need to clean it up manually in Gitea if desired.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={() => {
|
||||||
|
setIsDeleteOrgDialogOpen(false);
|
||||||
|
setOrgToDelete(null);
|
||||||
|
}} disabled={isDeletingOrg}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant="destructive" onClick={handleDeleteOrganization} disabled={isDeletingOrg}>
|
||||||
|
{isDeletingOrg ? (
|
||||||
|
<LoaderCircle className="h-4 w-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Delete
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useMemo } from "react";
|
|||||||
import { Card } from "@/components/ui/card";
|
import { Card } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Plus, RefreshCw, Building2, Check, AlertCircle, Clock, MoreVertical, Ban } from "lucide-react";
|
import { Plus, RefreshCw, Building2, Check, AlertCircle, Clock, MoreVertical, Ban, Trash2 } from "lucide-react";
|
||||||
import { SiGithub, SiGitea } from "react-icons/si";
|
import { SiGithub, SiGitea } from "react-icons/si";
|
||||||
import type { Organization } from "@/lib/db/schema";
|
import type { Organization } from "@/lib/db/schema";
|
||||||
import type { FilterParams } from "@/types/filter";
|
import type { FilterParams } from "@/types/filter";
|
||||||
@@ -30,6 +30,7 @@ interface OrganizationListProps {
|
|||||||
loadingOrgIds: Set<string>;
|
loadingOrgIds: Set<string>;
|
||||||
onAddOrganization?: () => void;
|
onAddOrganization?: () => void;
|
||||||
onRefresh?: () => Promise<void>;
|
onRefresh?: () => Promise<void>;
|
||||||
|
onDelete?: (orgId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get status badge variant and icon
|
// Helper function to get status badge variant and icon
|
||||||
@@ -60,6 +61,7 @@ export function OrganizationList({
|
|||||||
loadingOrgIds,
|
loadingOrgIds,
|
||||||
onAddOrganization,
|
onAddOrganization,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
|
onDelete,
|
||||||
}: OrganizationListProps) {
|
}: OrganizationListProps) {
|
||||||
const { giteaConfig } = useGiteaConfig();
|
const { giteaConfig } = useGiteaConfig();
|
||||||
|
|
||||||
@@ -414,7 +416,7 @@ export function OrganizationList({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Dropdown menu for additional actions */}
|
{/* Dropdown menu for additional actions */}
|
||||||
{org.status !== "ignored" && org.status !== "mirroring" && (
|
{org.status !== "mirroring" && (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="ghost" size="icon" disabled={isLoading} className="h-10 w-10">
|
<Button variant="ghost" size="icon" disabled={isLoading} className="h-10 w-10">
|
||||||
@@ -422,12 +424,26 @@ export function OrganizationList({
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem
|
{org.status !== "ignored" && (
|
||||||
onClick={() => org.id && onIgnore && onIgnore({ orgId: org.id, ignore: true })}
|
<DropdownMenuItem
|
||||||
>
|
onClick={() => org.id && onIgnore && onIgnore({ orgId: org.id, ignore: true })}
|
||||||
<Ban className="h-4 w-4 mr-2" />
|
>
|
||||||
Ignore Organization
|
<Ban className="h-4 w-4 mr-2" />
|
||||||
</DropdownMenuItem>
|
Ignore Organization
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
{onDelete && (
|
||||||
|
<>
|
||||||
|
{org.status !== "ignored" && <DropdownMenuSeparator />}
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="text-destructive focus:text-destructive"
|
||||||
|
onClick={() => org.id && onDelete(org.id)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 mr-2" />
|
||||||
|
Delete from Mirror
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
)}
|
)}
|
||||||
@@ -561,7 +577,7 @@ export function OrganizationList({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Dropdown menu for additional actions */}
|
{/* Dropdown menu for additional actions */}
|
||||||
{org.status !== "ignored" && org.status !== "mirroring" && (
|
{org.status !== "mirroring" && (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="ghost" size="icon" disabled={isLoading}>
|
<Button variant="ghost" size="icon" disabled={isLoading}>
|
||||||
@@ -569,12 +585,26 @@ export function OrganizationList({
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem
|
{org.status !== "ignored" && (
|
||||||
onClick={() => org.id && onIgnore && onIgnore({ orgId: org.id, ignore: true })}
|
<DropdownMenuItem
|
||||||
>
|
onClick={() => org.id && onIgnore && onIgnore({ orgId: org.id, ignore: true })}
|
||||||
<Ban className="h-4 w-4 mr-2" />
|
>
|
||||||
Ignore Organization
|
<Ban className="h-4 w-4 mr-2" />
|
||||||
</DropdownMenuItem>
|
Ignore Organization
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
{onDelete && (
|
||||||
|
<>
|
||||||
|
{org.status !== "ignored" && <DropdownMenuSeparator />}
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="text-destructive focus:text-destructive"
|
||||||
|
onClick={() => org.id && onDelete(org.id)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 mr-2" />
|
||||||
|
Delete from Mirror
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -17,9 +17,11 @@ interface AddRepositoryDialogProps {
|
|||||||
onAddRepository: ({
|
onAddRepository: ({
|
||||||
repo,
|
repo,
|
||||||
owner,
|
owner,
|
||||||
|
force,
|
||||||
}: {
|
}: {
|
||||||
repo: string;
|
repo: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
|
force?: boolean;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +35,14 @@ export default function AddRepositoryDialog({
|
|||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isDialogOpen) {
|
||||||
|
setError("");
|
||||||
|
setRepo("");
|
||||||
|
setOwner("");
|
||||||
|
}
|
||||||
|
}, [isDialogOpen]);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "../ui/select";
|
} from "../ui/select";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Search, RefreshCw, FlipHorizontal, RotateCcw, X, Filter, Ban, Check } from "lucide-react";
|
import { Search, RefreshCw, FlipHorizontal, RotateCcw, X, Filter, Ban, Check, LoaderCircle, Trash2 } from "lucide-react";
|
||||||
import type { MirrorRepoRequest, MirrorRepoResponse } from "@/types/mirror";
|
import type { MirrorRepoRequest, MirrorRepoResponse } from "@/types/mirror";
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
@@ -30,6 +30,14 @@ import {
|
|||||||
DrawerTitle,
|
DrawerTitle,
|
||||||
DrawerTrigger,
|
DrawerTrigger,
|
||||||
} from "@/components/ui/drawer";
|
} from "@/components/ui/drawer";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
import { useSSE } from "@/hooks/useSEE";
|
import { useSSE } from "@/hooks/useSEE";
|
||||||
import { useFilterParams } from "@/hooks/useFilterParams";
|
import { useFilterParams } from "@/hooks/useFilterParams";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -69,6 +77,15 @@ export default function Repository() {
|
|||||||
}, [setFilter]);
|
}, [setFilter]);
|
||||||
|
|
||||||
const [loadingRepoIds, setLoadingRepoIds] = useState<Set<string>>(new Set()); // this is used when the api actions are performed
|
const [loadingRepoIds, setLoadingRepoIds] = useState<Set<string>>(new Set()); // this is used when the api actions are performed
|
||||||
|
const [duplicateRepoCandidate, setDuplicateRepoCandidate] = useState<{
|
||||||
|
owner: string;
|
||||||
|
repo: string;
|
||||||
|
} | null>(null);
|
||||||
|
const [isDuplicateRepoDialogOpen, setIsDuplicateRepoDialogOpen] = useState(false);
|
||||||
|
const [isProcessingDuplicateRepo, setIsProcessingDuplicateRepo] = useState(false);
|
||||||
|
const [repoToDelete, setRepoToDelete] = useState<Repository | null>(null);
|
||||||
|
const [isDeleteRepoDialogOpen, setIsDeleteRepoDialogOpen] = useState(false);
|
||||||
|
const [isDeletingRepo, setIsDeletingRepo] = useState(false);
|
||||||
|
|
||||||
// Create a stable callback using useCallback
|
// Create a stable callback using useCallback
|
||||||
const handleNewMessage = useCallback((data: MirrorJob) => {
|
const handleNewMessage = useCallback((data: MirrorJob) => {
|
||||||
@@ -618,19 +635,45 @@ export default function Repository() {
|
|||||||
const handleAddRepository = async ({
|
const handleAddRepository = async ({
|
||||||
repo,
|
repo,
|
||||||
owner,
|
owner,
|
||||||
|
force = false,
|
||||||
}: {
|
}: {
|
||||||
repo: string;
|
repo: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
|
force?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
if (!user || !user.id) {
|
||||||
if (!user || !user.id) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
const trimmedRepo = repo.trim();
|
||||||
|
const trimmedOwner = owner.trim();
|
||||||
|
|
||||||
|
if (!trimmedRepo || !trimmedOwner) {
|
||||||
|
toast.error("Please provide both owner and repository name.");
|
||||||
|
throw new Error("Invalid repository details");
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedFullName = `${trimmedOwner}/${trimmedRepo}`.toLowerCase();
|
||||||
|
|
||||||
|
if (!force) {
|
||||||
|
const duplicateRepo = repositories.find(
|
||||||
|
(existing) => existing.normalizedFullName?.toLowerCase() === normalizedFullName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (duplicateRepo) {
|
||||||
|
toast.warning("Repository already exists.");
|
||||||
|
setDuplicateRepoCandidate({ repo: trimmedRepo, owner: trimmedOwner });
|
||||||
|
setIsDuplicateRepoDialogOpen(true);
|
||||||
|
throw new Error("Repository already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const reqPayload: AddRepositoriesApiRequest = {
|
const reqPayload: AddRepositoriesApiRequest = {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
repo,
|
repo: trimmedRepo,
|
||||||
owner,
|
owner: trimmedOwner,
|
||||||
|
force,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await apiRequest<AddRepositoriesApiResponse>(
|
const response = await apiRequest<AddRepositoriesApiResponse>(
|
||||||
@@ -642,20 +685,28 @@ export default function Repository() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
toast.success(`Repository added successfully`);
|
const message = force
|
||||||
setRepositories((prevRepos) => [...prevRepos, response.repository]);
|
? "Repository already exists; metadata refreshed."
|
||||||
|
: "Repository added successfully";
|
||||||
|
toast.success(message);
|
||||||
|
|
||||||
await fetchRepositories(false); // Manual refresh after adding repository
|
await fetchRepositories(false);
|
||||||
|
|
||||||
setFilter((prev) => ({
|
setFilter((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
searchTerm: repo,
|
searchTerm: trimmedRepo,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (force) {
|
||||||
|
setDuplicateRepoCandidate(null);
|
||||||
|
setIsDuplicateRepoDialogOpen(false);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showErrorToast(response.error || "Error adding repository", toast);
|
showErrorToast(response.error || "Error adding repository", toast);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error, toast);
|
showErrorToast(error, toast);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -673,6 +724,71 @@ export default function Repository() {
|
|||||||
)
|
)
|
||||||
).sort();
|
).sort();
|
||||||
|
|
||||||
|
const handleConfirmDuplicateRepository = async () => {
|
||||||
|
if (!duplicateRepoCandidate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsProcessingDuplicateRepo(true);
|
||||||
|
try {
|
||||||
|
await handleAddRepository({
|
||||||
|
repo: duplicateRepoCandidate.repo,
|
||||||
|
owner: duplicateRepoCandidate.owner,
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
} catch (error) {
|
||||||
|
// Error already shown
|
||||||
|
} finally {
|
||||||
|
setIsProcessingDuplicateRepo(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancelDuplicateRepository = () => {
|
||||||
|
setDuplicateRepoCandidate(null);
|
||||||
|
setIsDuplicateRepoDialogOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRequestDeleteRepository = (repoId: string) => {
|
||||||
|
const repo = repositories.find((item) => item.id === repoId);
|
||||||
|
if (!repo) {
|
||||||
|
toast.error("Repository not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRepoToDelete(repo);
|
||||||
|
setIsDeleteRepoDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteRepository = async () => {
|
||||||
|
if (!user || !user.id || !repoToDelete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsDeletingRepo(true);
|
||||||
|
try {
|
||||||
|
const response = await apiRequest<{ success: boolean; error?: string }>(
|
||||||
|
`/repositories/${repoToDelete.id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
toast.success(`Removed ${repoToDelete.fullName} from Gitea Mirror.`);
|
||||||
|
await fetchRepositories(false);
|
||||||
|
} else {
|
||||||
|
showErrorToast(response.error || "Failed to delete repository", toast);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showErrorToast(error, toast);
|
||||||
|
} finally {
|
||||||
|
setIsDeletingRepo(false);
|
||||||
|
setIsDeleteRepoDialogOpen(false);
|
||||||
|
setRepoToDelete(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Determine what actions are available for selected repositories
|
// Determine what actions are available for selected repositories
|
||||||
const getAvailableActions = () => {
|
const getAvailableActions = () => {
|
||||||
if (selectedRepoIds.size === 0) return [];
|
if (selectedRepoIds.size === 0) return [];
|
||||||
@@ -1198,6 +1314,7 @@ export default function Repository() {
|
|||||||
onRefresh={async () => {
|
onRefresh={async () => {
|
||||||
await fetchRepositories(false);
|
await fetchRepositories(false);
|
||||||
}}
|
}}
|
||||||
|
onDelete={handleRequestDeleteRepository}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -1206,6 +1323,77 @@ export default function Repository() {
|
|||||||
isDialogOpen={isDialogOpen}
|
isDialogOpen={isDialogOpen}
|
||||||
setIsDialogOpen={setIsDialogOpen}
|
setIsDialogOpen={setIsDialogOpen}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={isDuplicateRepoDialogOpen}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
handleCancelDuplicateRepository();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Repository already exists</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
{duplicateRepoCandidate ? `${duplicateRepoCandidate.owner}/${duplicateRepoCandidate.repo}` : "This repository"} is already tracked in Gitea Mirror. Continuing will refresh the existing entry without creating a duplicate.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={handleCancelDuplicateRepository} disabled={isProcessingDuplicateRepo}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleConfirmDuplicateRepository} disabled={isProcessingDuplicateRepo}>
|
||||||
|
{isProcessingDuplicateRepo ? (
|
||||||
|
<LoaderCircle className="h-4 w-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
"Continue"
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={isDeleteRepoDialogOpen}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
setIsDeleteRepoDialogOpen(false);
|
||||||
|
setRepoToDelete(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Remove repository from Gitea Mirror?</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
{repoToDelete?.fullName ?? "This repository"} will be deleted from Gitea Mirror only. The mirror on Gitea will remain untouched; remove it manually in Gitea if needed.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setIsDeleteRepoDialogOpen(false);
|
||||||
|
setRepoToDelete(null);
|
||||||
|
}}
|
||||||
|
disabled={isDeletingRepo}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant="destructive" onClick={handleDeleteRepository} disabled={isDeletingRepo}>
|
||||||
|
{isDeletingRepo ? (
|
||||||
|
<LoaderCircle className="h-4 w-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Delete
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useMemo, useRef } from "react";
|
import { useMemo, useRef } from "react";
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||||
import { FlipHorizontal, GitFork, RefreshCw, RotateCcw, Star, Lock, Ban, Check, ChevronDown } from "lucide-react";
|
import { FlipHorizontal, GitFork, RefreshCw, RotateCcw, Star, Lock, Ban, Check, ChevronDown, Trash2 } from "lucide-react";
|
||||||
import { SiGithub, SiGitea } from "react-icons/si";
|
import { SiGithub, SiGitea } from "react-icons/si";
|
||||||
import type { Repository } from "@/lib/db/schema";
|
import type { Repository } from "@/lib/db/schema";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ interface RepositoryTableProps {
|
|||||||
selectedRepoIds: Set<string>;
|
selectedRepoIds: Set<string>;
|
||||||
onSelectionChange: (selectedIds: Set<string>) => void;
|
onSelectionChange: (selectedIds: Set<string>) => void;
|
||||||
onRefresh?: () => Promise<void>;
|
onRefresh?: () => Promise<void>;
|
||||||
|
onDelete?: (repoId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RepositoryTable({
|
export default function RepositoryTable({
|
||||||
@@ -56,6 +58,7 @@ export default function RepositoryTable({
|
|||||||
selectedRepoIds,
|
selectedRepoIds,
|
||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
|
onDelete,
|
||||||
}: RepositoryTableProps) {
|
}: RepositoryTableProps) {
|
||||||
const tableParentRef = useRef<HTMLDivElement>(null);
|
const tableParentRef = useRef<HTMLDivElement>(null);
|
||||||
const { giteaConfig } = useGiteaConfig();
|
const { giteaConfig } = useGiteaConfig();
|
||||||
@@ -676,6 +679,7 @@ export default function RepositoryTable({
|
|||||||
onSync={() => onSync({ repoId: repo.id ?? "" })}
|
onSync={() => onSync({ repoId: repo.id ?? "" })}
|
||||||
onRetry={() => onRetry({ repoId: repo.id ?? "" })}
|
onRetry={() => onRetry({ repoId: repo.id ?? "" })}
|
||||||
onSkip={(skip) => onSkip({ repoId: repo.id ?? "", skip })}
|
onSkip={(skip) => onSkip({ repoId: repo.id ?? "", skip })}
|
||||||
|
onDelete={onDelete && repo.id ? () => onDelete(repo.id as string) : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Links */}
|
{/* Links */}
|
||||||
@@ -786,6 +790,7 @@ function RepoActionButton({
|
|||||||
onSync,
|
onSync,
|
||||||
onRetry,
|
onRetry,
|
||||||
onSkip,
|
onSkip,
|
||||||
|
onDelete,
|
||||||
}: {
|
}: {
|
||||||
repo: { id: string; status: string };
|
repo: { id: string; status: string };
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
@@ -793,6 +798,7 @@ function RepoActionButton({
|
|||||||
onSync: () => void;
|
onSync: () => void;
|
||||||
onRetry: () => void;
|
onRetry: () => void;
|
||||||
onSkip: (skip: boolean) => void;
|
onSkip: (skip: boolean) => void;
|
||||||
|
onDelete?: () => void;
|
||||||
}) {
|
}) {
|
||||||
// For ignored repos, show an "Include" action
|
// For ignored repos, show an "Include" action
|
||||||
if (repo.status === "ignored") {
|
if (repo.status === "ignored") {
|
||||||
@@ -849,7 +855,7 @@ function RepoActionButton({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show primary action with dropdown for skip option
|
// Show primary action with dropdown for additional actions
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
@@ -886,6 +892,18 @@ function RepoActionButton({
|
|||||||
<Ban className="h-4 w-4 mr-2" />
|
<Ban className="h-4 w-4 mr-2" />
|
||||||
Ignore Repository
|
Ignore Repository
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
{onDelete && (
|
||||||
|
<>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="text-destructive focus:text-destructive"
|
||||||
|
onClick={onDelete}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 mr-2" />
|
||||||
|
Delete from Mirror
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -166,6 +166,8 @@ export const auth = betterAuth({
|
|||||||
defaultOverrideUserInfo: true,
|
defaultOverrideUserInfo: true,
|
||||||
// Allow implicit sign up for new users
|
// Allow implicit sign up for new users
|
||||||
disableImplicitSignUp: false,
|
disableImplicitSignUp: false,
|
||||||
|
// Trust email_verified claims from the upstream provider so we can link by matching email
|
||||||
|
trustEmailVerified: true,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export const repositorySchema = z.object({
|
|||||||
configId: z.string(),
|
configId: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
fullName: z.string(),
|
fullName: z.string(),
|
||||||
|
normalizedFullName: z.string(),
|
||||||
url: z.url(),
|
url: z.url(),
|
||||||
cloneUrl: z.url(),
|
cloneUrl: z.url(),
|
||||||
owner: z.string(),
|
owner: z.string(),
|
||||||
@@ -163,6 +164,7 @@ export const repositorySchema = z.object({
|
|||||||
lastMirrored: z.coerce.date().optional().nullable(),
|
lastMirrored: z.coerce.date().optional().nullable(),
|
||||||
errorMessage: z.string().optional().nullable(),
|
errorMessage: z.string().optional().nullable(),
|
||||||
destinationOrg: z.string().optional().nullable(),
|
destinationOrg: z.string().optional().nullable(),
|
||||||
|
metadata: z.string().optional().nullable(), // JSON string for metadata sync state
|
||||||
createdAt: z.coerce.date(),
|
createdAt: z.coerce.date(),
|
||||||
updatedAt: z.coerce.date(),
|
updatedAt: z.coerce.date(),
|
||||||
});
|
});
|
||||||
@@ -209,6 +211,7 @@ export const organizationSchema = z.object({
|
|||||||
userId: z.string(),
|
userId: z.string(),
|
||||||
configId: z.string(),
|
configId: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
|
normalizedName: z.string(),
|
||||||
avatarUrl: z.string(),
|
avatarUrl: z.string(),
|
||||||
membershipRole: z.enum(["member", "admin", "owner", "billing_manager"]).default("member"),
|
membershipRole: z.enum(["member", "admin", "owner", "billing_manager"]).default("member"),
|
||||||
isIncluded: z.boolean().default(true),
|
isIncluded: z.boolean().default(true),
|
||||||
@@ -334,6 +337,7 @@ export const repositories = sqliteTable("repositories", {
|
|||||||
.references(() => configs.id),
|
.references(() => configs.id),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
fullName: text("full_name").notNull(),
|
fullName: text("full_name").notNull(),
|
||||||
|
normalizedFullName: text("normalized_full_name").notNull(),
|
||||||
url: text("url").notNull(),
|
url: text("url").notNull(),
|
||||||
cloneUrl: text("clone_url").notNull(),
|
cloneUrl: text("clone_url").notNull(),
|
||||||
owner: text("owner").notNull(),
|
owner: text("owner").notNull(),
|
||||||
@@ -373,6 +377,8 @@ export const repositories = sqliteTable("repositories", {
|
|||||||
|
|
||||||
destinationOrg: text("destination_org"),
|
destinationOrg: text("destination_org"),
|
||||||
|
|
||||||
|
metadata: text("metadata"), // JSON string storing metadata sync state (issues, PRs, releases, etc.)
|
||||||
|
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: integer("created_at", { mode: "timestamp" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(sql`(unixepoch())`),
|
.default(sql`(unixepoch())`),
|
||||||
@@ -388,6 +394,7 @@ export const repositories = sqliteTable("repositories", {
|
|||||||
index("idx_repositories_is_fork").on(table.isForked),
|
index("idx_repositories_is_fork").on(table.isForked),
|
||||||
index("idx_repositories_is_starred").on(table.isStarred),
|
index("idx_repositories_is_starred").on(table.isStarred),
|
||||||
uniqueIndex("uniq_repositories_user_full_name").on(table.userId, table.fullName),
|
uniqueIndex("uniq_repositories_user_full_name").on(table.userId, table.fullName),
|
||||||
|
uniqueIndex("uniq_repositories_user_normalized_full_name").on(table.userId, table.normalizedFullName),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const mirrorJobs = sqliteTable("mirror_jobs", {
|
export const mirrorJobs = sqliteTable("mirror_jobs", {
|
||||||
@@ -438,6 +445,7 @@ export const organizations = sqliteTable("organizations", {
|
|||||||
.notNull()
|
.notNull()
|
||||||
.references(() => configs.id),
|
.references(() => configs.id),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
|
normalizedName: text("normalized_name").notNull(),
|
||||||
|
|
||||||
avatarUrl: text("avatar_url").notNull(),
|
avatarUrl: text("avatar_url").notNull(),
|
||||||
|
|
||||||
@@ -469,6 +477,7 @@ export const organizations = sqliteTable("organizations", {
|
|||||||
index("idx_organizations_config_id").on(table.configId),
|
index("idx_organizations_config_id").on(table.configId),
|
||||||
index("idx_organizations_status").on(table.status),
|
index("idx_organizations_status").on(table.status),
|
||||||
index("idx_organizations_is_included").on(table.isIncluded),
|
index("idx_organizations_is_included").on(table.isIncluded),
|
||||||
|
uniqueIndex("uniq_organizations_user_normalized_name").on(table.userId, table.normalizedName),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ===== Better Auth Tables =====
|
// ===== Better Auth Tables =====
|
||||||
@@ -502,6 +511,10 @@ export const accounts = sqliteTable("accounts", {
|
|||||||
providerUserId: text("provider_user_id"), // Make nullable for email/password auth
|
providerUserId: text("provider_user_id"), // Make nullable for email/password auth
|
||||||
accessToken: text("access_token"),
|
accessToken: text("access_token"),
|
||||||
refreshToken: text("refresh_token"),
|
refreshToken: text("refresh_token"),
|
||||||
|
idToken: text("id_token"),
|
||||||
|
accessTokenExpiresAt: integer("access_token_expires_at", { mode: "timestamp" }),
|
||||||
|
refreshTokenExpiresAt: integer("refresh_token_expires_at", { mode: "timestamp" }),
|
||||||
|
scope: text("scope"),
|
||||||
expiresAt: integer("expires_at", { mode: "timestamp" }),
|
expiresAt: integer("expires_at", { mode: "timestamp" }),
|
||||||
password: text("password"), // For credential provider
|
password: text("password"), // For credential provider
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: integer("created_at", { mode: "timestamp" })
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ mock.module("@/lib/helpers", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const mockMirrorGitHubReleasesToGitea = mock(() => Promise.resolve());
|
const mockMirrorGitHubReleasesToGitea = mock(() => Promise.resolve());
|
||||||
|
const mockMirrorGitRepoIssuesToGitea = mock(() => Promise.resolve());
|
||||||
|
const mockMirrorGitRepoPullRequestsToGitea = mock(() => Promise.resolve());
|
||||||
|
const mockMirrorGitRepoLabelsToGitea = mock(() => Promise.resolve());
|
||||||
|
const mockMirrorGitRepoMilestonesToGitea = mock(() => Promise.resolve());
|
||||||
const mockGetGiteaRepoOwnerAsync = mock(() => Promise.resolve("starred"));
|
const mockGetGiteaRepoOwnerAsync = mock(() => Promise.resolve("starred"));
|
||||||
|
|
||||||
// Mock the database module
|
// Mock the database module
|
||||||
@@ -128,6 +132,36 @@ const mockHttpGet = mock(async (url: string, headers?: any) => {
|
|||||||
headers: new Headers()
|
headers: new Headers()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (url.includes("/api/v1/repos/starred/metadata-repo")) {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
id: 790,
|
||||||
|
name: "metadata-repo",
|
||||||
|
mirror: true,
|
||||||
|
owner: { login: "starred" },
|
||||||
|
mirror_interval: "8h",
|
||||||
|
private: false,
|
||||||
|
},
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
headers: new Headers(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (url.includes("/api/v1/repos/starred/already-synced-repo")) {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
id: 791,
|
||||||
|
name: "already-synced-repo",
|
||||||
|
mirror: true,
|
||||||
|
owner: { login: "starred" },
|
||||||
|
mirror_interval: "8h",
|
||||||
|
private: false,
|
||||||
|
},
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
headers: new Headers(),
|
||||||
|
};
|
||||||
|
}
|
||||||
if (url.includes("/api/v1/repos/")) {
|
if (url.includes("/api/v1/repos/")) {
|
||||||
throw new MockHttpError("Not Found", 404, "Not Found");
|
throw new MockHttpError("Not Found", 404, "Not Found");
|
||||||
}
|
}
|
||||||
@@ -224,6 +258,10 @@ describe("Enhanced Gitea Operations", () => {
|
|||||||
mockDb.insert.mockClear();
|
mockDb.insert.mockClear();
|
||||||
mockDb.update.mockClear();
|
mockDb.update.mockClear();
|
||||||
mockMirrorGitHubReleasesToGitea.mockClear();
|
mockMirrorGitHubReleasesToGitea.mockClear();
|
||||||
|
mockMirrorGitRepoIssuesToGitea.mockClear();
|
||||||
|
mockMirrorGitRepoPullRequestsToGitea.mockClear();
|
||||||
|
mockMirrorGitRepoLabelsToGitea.mockClear();
|
||||||
|
mockMirrorGitRepoMilestonesToGitea.mockClear();
|
||||||
mockGetGiteaRepoOwnerAsync.mockClear();
|
mockGetGiteaRepoOwnerAsync.mockClear();
|
||||||
mockGetGiteaRepoOwnerAsync.mockImplementation(() => Promise.resolve("starred"));
|
mockGetGiteaRepoOwnerAsync.mockImplementation(() => Promise.resolve("starred"));
|
||||||
// Reset tracking variables
|
// Reset tracking variables
|
||||||
@@ -426,6 +464,10 @@ describe("Enhanced Gitea Operations", () => {
|
|||||||
{
|
{
|
||||||
getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync,
|
getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync,
|
||||||
mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea,
|
mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea,
|
||||||
|
mirrorGitRepoIssuesToGitea: mockMirrorGitRepoIssuesToGitea,
|
||||||
|
mirrorGitRepoPullRequestsToGitea: mockMirrorGitRepoPullRequestsToGitea,
|
||||||
|
mirrorGitRepoLabelsToGitea: mockMirrorGitRepoLabelsToGitea,
|
||||||
|
mirrorGitRepoMilestonesToGitea: mockMirrorGitRepoMilestonesToGitea,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).rejects.toThrow("Repository non-mirror-repo is not a mirror. Cannot sync.");
|
).rejects.toThrow("Repository non-mirror-repo is not a mirror. Cannot sync.");
|
||||||
@@ -470,6 +512,10 @@ describe("Enhanced Gitea Operations", () => {
|
|||||||
{
|
{
|
||||||
getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync,
|
getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync,
|
||||||
mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea,
|
mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea,
|
||||||
|
mirrorGitRepoIssuesToGitea: mockMirrorGitRepoIssuesToGitea,
|
||||||
|
mirrorGitRepoPullRequestsToGitea: mockMirrorGitRepoPullRequestsToGitea,
|
||||||
|
mirrorGitRepoLabelsToGitea: mockMirrorGitRepoLabelsToGitea,
|
||||||
|
mirrorGitRepoMilestonesToGitea: mockMirrorGitRepoMilestonesToGitea,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -482,6 +528,130 @@ describe("Enhanced Gitea Operations", () => {
|
|||||||
expect(releaseCall.config.githubConfig?.token).toBe("github-token");
|
expect(releaseCall.config.githubConfig?.token).toBe("github-token");
|
||||||
expect(releaseCall.octokit).toBeDefined();
|
expect(releaseCall.octokit).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("mirrors metadata components when enabled and not previously synced", async () => {
|
||||||
|
const config: Partial<Config> = {
|
||||||
|
userId: "user123",
|
||||||
|
githubConfig: {
|
||||||
|
username: "testuser",
|
||||||
|
token: "github-token",
|
||||||
|
privateRepositories: true,
|
||||||
|
mirrorStarred: false,
|
||||||
|
},
|
||||||
|
giteaConfig: {
|
||||||
|
url: "https://gitea.example.com",
|
||||||
|
token: "encrypted-token",
|
||||||
|
defaultOwner: "testuser",
|
||||||
|
mirrorReleases: true,
|
||||||
|
mirrorMetadata: true,
|
||||||
|
mirrorIssues: true,
|
||||||
|
mirrorPullRequests: true,
|
||||||
|
mirrorLabels: true,
|
||||||
|
mirrorMilestones: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const repository: Repository = {
|
||||||
|
id: "repo789",
|
||||||
|
name: "metadata-repo",
|
||||||
|
fullName: "user/metadata-repo",
|
||||||
|
owner: "user",
|
||||||
|
cloneUrl: "https://github.com/user/metadata-repo.git",
|
||||||
|
isPrivate: false,
|
||||||
|
isStarred: false,
|
||||||
|
status: repoStatusEnum.parse("mirrored"),
|
||||||
|
visibility: "public",
|
||||||
|
userId: "user123",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
metadata: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
await syncGiteaRepoEnhanced(
|
||||||
|
{ config, repository },
|
||||||
|
{
|
||||||
|
getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync,
|
||||||
|
mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea,
|
||||||
|
mirrorGitRepoIssuesToGitea: mockMirrorGitRepoIssuesToGitea,
|
||||||
|
mirrorGitRepoPullRequestsToGitea: mockMirrorGitRepoPullRequestsToGitea,
|
||||||
|
mirrorGitRepoLabelsToGitea: mockMirrorGitRepoLabelsToGitea,
|
||||||
|
mirrorGitRepoMilestonesToGitea: mockMirrorGitRepoMilestonesToGitea,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockMirrorGitHubReleasesToGitea).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockMirrorGitRepoIssuesToGitea).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockMirrorGitRepoPullRequestsToGitea).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockMirrorGitRepoMilestonesToGitea).toHaveBeenCalledTimes(1);
|
||||||
|
// Labels should be skipped because issues already import them
|
||||||
|
expect(mockMirrorGitRepoLabelsToGitea).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("skips metadata mirroring when components already synced", async () => {
|
||||||
|
const config: Partial<Config> = {
|
||||||
|
userId: "user123",
|
||||||
|
githubConfig: {
|
||||||
|
username: "testuser",
|
||||||
|
token: "github-token",
|
||||||
|
privateRepositories: true,
|
||||||
|
mirrorStarred: false,
|
||||||
|
},
|
||||||
|
giteaConfig: {
|
||||||
|
url: "https://gitea.example.com",
|
||||||
|
token: "encrypted-token",
|
||||||
|
defaultOwner: "testuser",
|
||||||
|
mirrorReleases: false,
|
||||||
|
mirrorMetadata: true,
|
||||||
|
mirrorIssues: true,
|
||||||
|
mirrorPullRequests: true,
|
||||||
|
mirrorLabels: true,
|
||||||
|
mirrorMilestones: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const repository: Repository = {
|
||||||
|
id: "repo790",
|
||||||
|
name: "already-synced-repo",
|
||||||
|
fullName: "user/already-synced-repo",
|
||||||
|
owner: "user",
|
||||||
|
cloneUrl: "https://github.com/user/already-synced-repo.git",
|
||||||
|
isPrivate: false,
|
||||||
|
isStarred: false,
|
||||||
|
status: repoStatusEnum.parse("mirrored"),
|
||||||
|
visibility: "public",
|
||||||
|
userId: "user123",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
metadata: JSON.stringify({
|
||||||
|
components: {
|
||||||
|
releases: true,
|
||||||
|
issues: true,
|
||||||
|
pullRequests: true,
|
||||||
|
labels: true,
|
||||||
|
milestones: true,
|
||||||
|
},
|
||||||
|
lastSyncedAt: new Date().toISOString(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
await syncGiteaRepoEnhanced(
|
||||||
|
{ config, repository },
|
||||||
|
{
|
||||||
|
getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync,
|
||||||
|
mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea,
|
||||||
|
mirrorGitRepoIssuesToGitea: mockMirrorGitRepoIssuesToGitea,
|
||||||
|
mirrorGitRepoPullRequestsToGitea: mockMirrorGitRepoPullRequestsToGitea,
|
||||||
|
mirrorGitRepoLabelsToGitea: mockMirrorGitRepoLabelsToGitea,
|
||||||
|
mirrorGitRepoMilestonesToGitea: mockMirrorGitRepoMilestonesToGitea,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockMirrorGitHubReleasesToGitea).not.toHaveBeenCalled();
|
||||||
|
expect(mockMirrorGitRepoIssuesToGitea).not.toHaveBeenCalled();
|
||||||
|
expect(mockMirrorGitRepoPullRequestsToGitea).not.toHaveBeenCalled();
|
||||||
|
expect(mockMirrorGitRepoLabelsToGitea).not.toHaveBeenCalled();
|
||||||
|
expect(mockMirrorGitRepoMilestonesToGitea).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("handleExistingNonMirrorRepo", () => {
|
describe("handleExistingNonMirrorRepo", () => {
|
||||||
|
|||||||
@@ -15,10 +15,18 @@ import { httpPost, httpGet, httpPatch, HttpError } from "./http-client";
|
|||||||
import { db, repositories } from "./db";
|
import { db, repositories } from "./db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { repoStatusEnum } from "@/types/Repository";
|
import { repoStatusEnum } from "@/types/Repository";
|
||||||
|
import {
|
||||||
|
parseRepositoryMetadataState,
|
||||||
|
serializeRepositoryMetadataState,
|
||||||
|
} from "./metadata-state";
|
||||||
|
|
||||||
type SyncDependencies = {
|
type SyncDependencies = {
|
||||||
getGiteaRepoOwnerAsync: typeof import("./gitea")["getGiteaRepoOwnerAsync"];
|
getGiteaRepoOwnerAsync: typeof import("./gitea")["getGiteaRepoOwnerAsync"];
|
||||||
mirrorGitHubReleasesToGitea: typeof import("./gitea")["mirrorGitHubReleasesToGitea"];
|
mirrorGitHubReleasesToGitea: typeof import("./gitea")["mirrorGitHubReleasesToGitea"];
|
||||||
|
mirrorGitRepoIssuesToGitea: typeof import("./gitea")["mirrorGitRepoIssuesToGitea"];
|
||||||
|
mirrorGitRepoPullRequestsToGitea: typeof import("./gitea")["mirrorGitRepoPullRequestsToGitea"];
|
||||||
|
mirrorGitRepoLabelsToGitea: typeof import("./gitea")["mirrorGitRepoLabelsToGitea"];
|
||||||
|
mirrorGitRepoMilestonesToGitea: typeof import("./gitea")["mirrorGitRepoMilestonesToGitea"];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -330,36 +338,236 @@ export async function syncGiteaRepoEnhanced({
|
|||||||
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const metadataState = parseRepositoryMetadataState(repository.metadata);
|
||||||
|
let metadataUpdated = false;
|
||||||
|
const skipMetadataForStarred =
|
||||||
|
repository.isStarred && config.githubConfig?.starredCodeOnly;
|
||||||
|
let metadataOctokit: Octokit | null = null;
|
||||||
|
|
||||||
|
const ensureOctokit = (): Octokit | null => {
|
||||||
|
if (metadataOctokit) {
|
||||||
|
return metadataOctokit;
|
||||||
|
}
|
||||||
|
if (!decryptedConfig.githubConfig?.token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
metadataOctokit = new Octokit({
|
||||||
|
auth: decryptedConfig.githubConfig.token,
|
||||||
|
});
|
||||||
|
return metadataOctokit;
|
||||||
|
};
|
||||||
|
|
||||||
const shouldMirrorReleases =
|
const shouldMirrorReleases =
|
||||||
decryptedConfig.giteaConfig?.mirrorReleases &&
|
!!config.giteaConfig?.mirrorReleases && !skipMetadataForStarred;
|
||||||
!(repository.isStarred && decryptedConfig.githubConfig?.starredCodeOnly);
|
const shouldMirrorIssuesThisRun =
|
||||||
|
!!config.giteaConfig?.mirrorIssues &&
|
||||||
|
!skipMetadataForStarred &&
|
||||||
|
!metadataState.components.issues;
|
||||||
|
const shouldMirrorPullRequests =
|
||||||
|
!!config.giteaConfig?.mirrorPullRequests &&
|
||||||
|
!skipMetadataForStarred &&
|
||||||
|
!metadataState.components.pullRequests;
|
||||||
|
const shouldMirrorLabels =
|
||||||
|
!!config.giteaConfig?.mirrorLabels &&
|
||||||
|
!skipMetadataForStarred &&
|
||||||
|
!shouldMirrorIssuesThisRun &&
|
||||||
|
!metadataState.components.labels;
|
||||||
|
const shouldMirrorMilestones =
|
||||||
|
!!config.giteaConfig?.mirrorMilestones &&
|
||||||
|
!skipMetadataForStarred &&
|
||||||
|
!metadataState.components.milestones;
|
||||||
|
|
||||||
if (shouldMirrorReleases) {
|
if (shouldMirrorReleases) {
|
||||||
if (!decryptedConfig.githubConfig?.token) {
|
const octokit = ensureOctokit();
|
||||||
|
if (!octokit) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`[Sync] Skipping release mirroring for ${repository.name}: Missing GitHub token`
|
`[Sync] Skipping release mirroring for ${repository.name}: Missing GitHub token`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const octokit = new Octokit({ auth: decryptedConfig.githubConfig.token });
|
|
||||||
await dependencies.mirrorGitHubReleasesToGitea({
|
await dependencies.mirrorGitHubReleasesToGitea({
|
||||||
config: decryptedConfig,
|
config,
|
||||||
octokit,
|
octokit,
|
||||||
repository,
|
repository,
|
||||||
giteaOwner: repoOwner,
|
giteaOwner: repoOwner,
|
||||||
giteaRepoName: repository.name,
|
giteaRepoName: repository.name,
|
||||||
});
|
});
|
||||||
console.log(`[Sync] Mirrored releases for ${repository.name} after sync`);
|
metadataState.components.releases = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Sync] Mirrored releases for ${repository.name} after sync`
|
||||||
|
);
|
||||||
} catch (releaseError) {
|
} catch (releaseError) {
|
||||||
console.error(
|
console.error(
|
||||||
`[Sync] Failed to mirror releases for ${repository.name}: ${
|
`[Sync] Failed to mirror releases for ${repository.name}: ${
|
||||||
releaseError instanceof Error ? releaseError.message : String(releaseError)
|
releaseError instanceof Error
|
||||||
|
? releaseError.message
|
||||||
|
: String(releaseError)
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldMirrorIssuesThisRun) {
|
||||||
|
const octokit = ensureOctokit();
|
||||||
|
if (!octokit) {
|
||||||
|
console.warn(
|
||||||
|
`[Sync] Skipping issue mirroring for ${repository.name}: Missing GitHub token`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await dependencies.mirrorGitRepoIssuesToGitea({
|
||||||
|
config,
|
||||||
|
octokit,
|
||||||
|
repository,
|
||||||
|
giteaOwner: repoOwner,
|
||||||
|
giteaRepoName: repository.name,
|
||||||
|
});
|
||||||
|
metadataState.components.issues = true;
|
||||||
|
metadataState.components.labels = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Sync] Mirrored issues for ${repository.name} after sync`
|
||||||
|
);
|
||||||
|
} catch (issueError) {
|
||||||
|
console.error(
|
||||||
|
`[Sync] Failed to mirror issues for ${repository.name}: ${
|
||||||
|
issueError instanceof Error
|
||||||
|
? issueError.message
|
||||||
|
: String(issueError)
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorIssues &&
|
||||||
|
metadataState.components.issues
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Sync] Issues already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldMirrorPullRequests) {
|
||||||
|
const octokit = ensureOctokit();
|
||||||
|
if (!octokit) {
|
||||||
|
console.warn(
|
||||||
|
`[Sync] Skipping pull request mirroring for ${repository.name}: Missing GitHub token`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await dependencies.mirrorGitRepoPullRequestsToGitea({
|
||||||
|
config,
|
||||||
|
octokit,
|
||||||
|
repository,
|
||||||
|
giteaOwner: repoOwner,
|
||||||
|
giteaRepoName: repository.name,
|
||||||
|
});
|
||||||
|
metadataState.components.pullRequests = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Sync] Mirrored pull requests for ${repository.name} after sync`
|
||||||
|
);
|
||||||
|
} catch (prError) {
|
||||||
|
console.error(
|
||||||
|
`[Sync] Failed to mirror pull requests for ${repository.name}: ${
|
||||||
|
prError instanceof Error ? prError.message : String(prError)
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorPullRequests &&
|
||||||
|
metadataState.components.pullRequests
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Sync] Pull requests already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldMirrorLabels) {
|
||||||
|
const octokit = ensureOctokit();
|
||||||
|
if (!octokit) {
|
||||||
|
console.warn(
|
||||||
|
`[Sync] Skipping label mirroring for ${repository.name}: Missing GitHub token`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await dependencies.mirrorGitRepoLabelsToGitea({
|
||||||
|
config,
|
||||||
|
octokit,
|
||||||
|
repository,
|
||||||
|
giteaOwner: repoOwner,
|
||||||
|
giteaRepoName: repository.name,
|
||||||
|
});
|
||||||
|
metadataState.components.labels = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Sync] Mirrored labels for ${repository.name} after sync`
|
||||||
|
);
|
||||||
|
} catch (labelError) {
|
||||||
|
console.error(
|
||||||
|
`[Sync] Failed to mirror labels for ${repository.name}: ${
|
||||||
|
labelError instanceof Error
|
||||||
|
? labelError.message
|
||||||
|
: String(labelError)
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorLabels &&
|
||||||
|
metadataState.components.labels
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Sync] Labels already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldMirrorMilestones) {
|
||||||
|
const octokit = ensureOctokit();
|
||||||
|
if (!octokit) {
|
||||||
|
console.warn(
|
||||||
|
`[Sync] Skipping milestone mirroring for ${repository.name}: Missing GitHub token`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await dependencies.mirrorGitRepoMilestonesToGitea({
|
||||||
|
config,
|
||||||
|
octokit,
|
||||||
|
repository,
|
||||||
|
giteaOwner: repoOwner,
|
||||||
|
giteaRepoName: repository.name,
|
||||||
|
});
|
||||||
|
metadataState.components.milestones = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Sync] Mirrored milestones for ${repository.name} after sync`
|
||||||
|
);
|
||||||
|
} catch (milestoneError) {
|
||||||
|
console.error(
|
||||||
|
`[Sync] Failed to mirror milestones for ${repository.name}: ${
|
||||||
|
milestoneError instanceof Error
|
||||||
|
? milestoneError.message
|
||||||
|
: String(milestoneError)
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorMilestones &&
|
||||||
|
metadataState.components.milestones
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Sync] Milestones already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadataUpdated) {
|
||||||
|
metadataState.lastSyncedAt = new Date().toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
// Mark repo as "synced" in DB
|
// Mark repo as "synced" in DB
|
||||||
await db
|
await db
|
||||||
.update(repositories)
|
.update(repositories)
|
||||||
@@ -369,6 +577,9 @@ export async function syncGiteaRepoEnhanced({
|
|||||||
lastMirrored: new Date(),
|
lastMirrored: new Date(),
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
mirroredLocation: `${repoOwner}/${repository.name}`,
|
mirroredLocation: `${repoOwner}/${repository.name}`,
|
||||||
|
metadata: metadataUpdated
|
||||||
|
? serializeRepositoryMetadataState(metadataState)
|
||||||
|
: repository.metadata ?? null,
|
||||||
})
|
})
|
||||||
.where(eq(repositories.id, repository.id!));
|
.where(eq(repositories.id, repository.id!));
|
||||||
|
|
||||||
|
|||||||
489
src/lib/gitea.ts
@@ -13,6 +13,10 @@ import { db, organizations, repositories } from "./db";
|
|||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { decryptConfigTokens } from "./utils/config-encryption";
|
import { decryptConfigTokens } from "./utils/config-encryption";
|
||||||
import { formatDateShort } from "./utils";
|
import { formatDateShort } from "./utils";
|
||||||
|
import {
|
||||||
|
parseRepositoryMetadataState,
|
||||||
|
serializeRepositoryMetadataState,
|
||||||
|
} from "./metadata-state";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to get organization configuration including destination override
|
* Helper function to get organization configuration including destination override
|
||||||
@@ -587,12 +591,18 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
//mirror releases
|
const metadataState = parseRepositoryMetadataState(repository.metadata);
|
||||||
// Skip releases for starred repos if starredCodeOnly is enabled
|
let metadataUpdated = false;
|
||||||
const shouldMirrorReleases = config.giteaConfig?.mirrorReleases &&
|
const skipMetadataForStarred =
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
repository.isStarred && config.githubConfig?.starredCodeOnly;
|
||||||
|
|
||||||
console.log(`[Metadata] Release mirroring check: mirrorReleases=${config.giteaConfig?.mirrorReleases}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorReleases=${shouldMirrorReleases}`);
|
// Mirror releases if enabled (always allowed to rerun for updates)
|
||||||
|
const shouldMirrorReleases =
|
||||||
|
!!config.giteaConfig?.mirrorReleases && !skipMetadataForStarred;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Release mirroring check: mirrorReleases=${config.giteaConfig?.mirrorReleases}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorReleases=${shouldMirrorReleases}`
|
||||||
|
);
|
||||||
|
|
||||||
if (shouldMirrorReleases) {
|
if (shouldMirrorReleases) {
|
||||||
try {
|
try {
|
||||||
@@ -603,21 +613,32 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
giteaOwner: repoOwner,
|
giteaOwner: repoOwner,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored releases for ${repository.name}`);
|
metadataState.components.releases = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored releases for ${repository.name}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror releases for ${repository.name}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror releases for ${repository.name}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other operations even if releases fail
|
// Continue with other operations even if releases fail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clone issues
|
// Determine metadata operations to avoid duplicates
|
||||||
// Skip issues for starred repos if starredCodeOnly is enabled
|
const shouldMirrorIssuesThisRun =
|
||||||
const shouldMirrorIssues = config.giteaConfig?.mirrorIssues &&
|
!!config.giteaConfig?.mirrorIssues &&
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
!skipMetadataForStarred &&
|
||||||
|
!metadataState.components.issues;
|
||||||
console.log(`[Metadata] Issue mirroring check: mirrorIssues=${config.giteaConfig?.mirrorIssues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorIssues=${shouldMirrorIssues}`);
|
|
||||||
|
console.log(
|
||||||
if (shouldMirrorIssues) {
|
`[Metadata] Issue mirroring check: mirrorIssues=${config.giteaConfig?.mirrorIssues}, alreadyMirrored=${metadataState.components.issues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorIssues=${shouldMirrorIssuesThisRun}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldMirrorIssuesThisRun) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitRepoIssuesToGitea({
|
await mirrorGitRepoIssuesToGitea({
|
||||||
config,
|
config,
|
||||||
@@ -626,19 +647,34 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
giteaOwner: repoOwner,
|
giteaOwner: repoOwner,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored issues for ${repository.name}`);
|
metadataState.components.issues = true;
|
||||||
|
metadataState.components.labels = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored issues for ${repository.name}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror issues for ${repository.name}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror issues for ${repository.name}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other metadata operations even if issues fail
|
// Continue with other metadata operations even if issues fail
|
||||||
}
|
}
|
||||||
|
} else if (config.giteaConfig?.mirrorIssues && metadataState.components.issues) {
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Issues already mirrored for ${repository.name}; skipping to avoid duplicates`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirror pull requests if enabled
|
const shouldMirrorPullRequests =
|
||||||
// Skip pull requests for starred repos if starredCodeOnly is enabled
|
!!config.giteaConfig?.mirrorPullRequests &&
|
||||||
const shouldMirrorPullRequests = config.giteaConfig?.mirrorPullRequests &&
|
!skipMetadataForStarred &&
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
!metadataState.components.pullRequests;
|
||||||
|
|
||||||
console.log(`[Metadata] Pull request mirroring check: mirrorPullRequests=${config.giteaConfig?.mirrorPullRequests}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorPullRequests=${shouldMirrorPullRequests}`);
|
console.log(
|
||||||
|
`[Metadata] Pull request mirroring check: mirrorPullRequests=${config.giteaConfig?.mirrorPullRequests}, alreadyMirrored=${metadataState.components.pullRequests}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorPullRequests=${shouldMirrorPullRequests}`
|
||||||
|
);
|
||||||
|
|
||||||
if (shouldMirrorPullRequests) {
|
if (shouldMirrorPullRequests) {
|
||||||
try {
|
try {
|
||||||
@@ -649,19 +685,37 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
giteaOwner: repoOwner,
|
giteaOwner: repoOwner,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored pull requests for ${repository.name}`);
|
metadataState.components.pullRequests = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored pull requests for ${repository.name}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror pull requests for ${repository.name}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror pull requests for ${repository.name}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other metadata operations even if PRs fail
|
// Continue with other metadata operations even if PRs fail
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorPullRequests &&
|
||||||
|
metadataState.components.pullRequests
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Pull requests already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirror labels if enabled (and not already done via issues)
|
const shouldMirrorLabels =
|
||||||
// Skip labels for starred repos if starredCodeOnly is enabled
|
!!config.giteaConfig?.mirrorLabels &&
|
||||||
const shouldMirrorLabels = config.giteaConfig?.mirrorLabels && !shouldMirrorIssues &&
|
!skipMetadataForStarred &&
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
!shouldMirrorIssuesThisRun &&
|
||||||
|
!metadataState.components.labels;
|
||||||
|
|
||||||
console.log(`[Metadata] Label mirroring check: mirrorLabels=${config.giteaConfig?.mirrorLabels}, shouldMirrorIssues=${shouldMirrorIssues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorLabels=${shouldMirrorLabels}`);
|
console.log(
|
||||||
|
`[Metadata] Label mirroring check: mirrorLabels=${config.giteaConfig?.mirrorLabels}, alreadyMirrored=${metadataState.components.labels}, issuesRunning=${shouldMirrorIssuesThisRun}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorLabels=${shouldMirrorLabels}`
|
||||||
|
);
|
||||||
|
|
||||||
if (shouldMirrorLabels) {
|
if (shouldMirrorLabels) {
|
||||||
try {
|
try {
|
||||||
@@ -672,19 +726,33 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
giteaOwner: repoOwner,
|
giteaOwner: repoOwner,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored labels for ${repository.name}`);
|
metadataState.components.labels = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored labels for ${repository.name}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror labels for ${repository.name}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror labels for ${repository.name}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other metadata operations even if labels fail
|
// Continue with other metadata operations even if labels fail
|
||||||
}
|
}
|
||||||
|
} else if (config.giteaConfig?.mirrorLabels && metadataState.components.labels) {
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Labels already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirror milestones if enabled
|
const shouldMirrorMilestones =
|
||||||
// Skip milestones for starred repos if starredCodeOnly is enabled
|
!!config.giteaConfig?.mirrorMilestones &&
|
||||||
const shouldMirrorMilestones = config.giteaConfig?.mirrorMilestones &&
|
!skipMetadataForStarred &&
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
!metadataState.components.milestones;
|
||||||
|
|
||||||
console.log(`[Metadata] Milestone mirroring check: mirrorMilestones=${config.giteaConfig?.mirrorMilestones}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorMilestones=${shouldMirrorMilestones}`);
|
console.log(
|
||||||
|
`[Metadata] Milestone mirroring check: mirrorMilestones=${config.giteaConfig?.mirrorMilestones}, alreadyMirrored=${metadataState.components.milestones}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorMilestones=${shouldMirrorMilestones}`
|
||||||
|
);
|
||||||
|
|
||||||
if (shouldMirrorMilestones) {
|
if (shouldMirrorMilestones) {
|
||||||
try {
|
try {
|
||||||
@@ -695,11 +763,30 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
giteaOwner: repoOwner,
|
giteaOwner: repoOwner,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored milestones for ${repository.name}`);
|
metadataState.components.milestones = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored milestones for ${repository.name}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror milestones for ${repository.name}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror milestones for ${repository.name}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other metadata operations even if milestones fail
|
// Continue with other metadata operations even if milestones fail
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorMilestones &&
|
||||||
|
metadataState.components.milestones
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Milestones already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadataUpdated) {
|
||||||
|
metadataState.lastSyncedAt = new Date().toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Repository ${repository.name} mirrored successfully as ${targetRepoName}`);
|
console.log(`Repository ${repository.name} mirrored successfully as ${targetRepoName}`);
|
||||||
@@ -713,6 +800,9 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
lastMirrored: new Date(),
|
lastMirrored: new Date(),
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
mirroredLocation: `${repoOwner}/${targetRepoName}`,
|
mirroredLocation: `${repoOwner}/${targetRepoName}`,
|
||||||
|
metadata: metadataUpdated
|
||||||
|
? serializeRepositoryMetadataState(metadataState)
|
||||||
|
: repository.metadata ?? null,
|
||||||
})
|
})
|
||||||
.where(eq(repositories.id, repository.id!));
|
.where(eq(repositories.id, repository.id!));
|
||||||
|
|
||||||
@@ -1053,12 +1143,17 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
//mirror releases
|
const metadataState = parseRepositoryMetadataState(repository.metadata);
|
||||||
// Skip releases for starred repos if starredCodeOnly is enabled
|
let metadataUpdated = false;
|
||||||
const shouldMirrorReleases = config.giteaConfig?.mirrorReleases &&
|
const skipMetadataForStarred =
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
repository.isStarred && config.githubConfig?.starredCodeOnly;
|
||||||
|
|
||||||
console.log(`[Metadata] Release mirroring check: mirrorReleases=${config.giteaConfig?.mirrorReleases}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorReleases=${shouldMirrorReleases}`);
|
const shouldMirrorReleases =
|
||||||
|
!!config.giteaConfig?.mirrorReleases && !skipMetadataForStarred;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Release mirroring check: mirrorReleases=${config.giteaConfig?.mirrorReleases}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorReleases=${shouldMirrorReleases}`
|
||||||
|
);
|
||||||
|
|
||||||
if (shouldMirrorReleases) {
|
if (shouldMirrorReleases) {
|
||||||
try {
|
try {
|
||||||
@@ -1069,21 +1164,31 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
giteaOwner: orgName,
|
giteaOwner: orgName,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored releases for ${repository.name}`);
|
metadataState.components.releases = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored releases for ${repository.name}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror releases for ${repository.name}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror releases for ${repository.name}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other operations even if releases fail
|
// Continue with other operations even if releases fail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone issues
|
const shouldMirrorIssuesThisRun =
|
||||||
// Skip issues for starred repos if starredCodeOnly is enabled
|
!!config.giteaConfig?.mirrorIssues &&
|
||||||
const shouldMirrorIssues = config.giteaConfig?.mirrorIssues &&
|
!skipMetadataForStarred &&
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
!metadataState.components.issues;
|
||||||
|
|
||||||
console.log(`[Metadata] Issue mirroring check: mirrorIssues=${config.giteaConfig?.mirrorIssues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorIssues=${shouldMirrorIssues}`);
|
console.log(
|
||||||
|
`[Metadata] Issue mirroring check: mirrorIssues=${config.giteaConfig?.mirrorIssues}, alreadyMirrored=${metadataState.components.issues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorIssues=${shouldMirrorIssuesThisRun}`
|
||||||
if (shouldMirrorIssues) {
|
);
|
||||||
|
|
||||||
|
if (shouldMirrorIssuesThisRun) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitRepoIssuesToGitea({
|
await mirrorGitRepoIssuesToGitea({
|
||||||
config,
|
config,
|
||||||
@@ -1092,19 +1197,37 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
giteaOwner: orgName,
|
giteaOwner: orgName,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored issues for ${repository.name} to org ${orgName}/${targetRepoName}`);
|
metadataState.components.issues = true;
|
||||||
|
metadataState.components.labels = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored issues for ${repository.name} to org ${orgName}/${targetRepoName}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror issues for ${repository.name} to org ${orgName}/${targetRepoName}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror issues for ${repository.name} to org ${orgName}/${targetRepoName}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other metadata operations even if issues fail
|
// Continue with other metadata operations even if issues fail
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorIssues &&
|
||||||
|
metadataState.components.issues
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Issues already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirror pull requests if enabled
|
const shouldMirrorPullRequests =
|
||||||
// Skip pull requests for starred repos if starredCodeOnly is enabled
|
!!config.giteaConfig?.mirrorPullRequests &&
|
||||||
const shouldMirrorPullRequests = config.giteaConfig?.mirrorPullRequests &&
|
!skipMetadataForStarred &&
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
!metadataState.components.pullRequests;
|
||||||
|
|
||||||
console.log(`[Metadata] Pull request mirroring check: mirrorPullRequests=${config.giteaConfig?.mirrorPullRequests}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorPullRequests=${shouldMirrorPullRequests}`);
|
console.log(
|
||||||
|
`[Metadata] Pull request mirroring check: mirrorPullRequests=${config.giteaConfig?.mirrorPullRequests}, alreadyMirrored=${metadataState.components.pullRequests}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorPullRequests=${shouldMirrorPullRequests}`
|
||||||
|
);
|
||||||
|
|
||||||
if (shouldMirrorPullRequests) {
|
if (shouldMirrorPullRequests) {
|
||||||
try {
|
try {
|
||||||
@@ -1115,19 +1238,37 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
giteaOwner: orgName,
|
giteaOwner: orgName,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored pull requests for ${repository.name} to org ${orgName}/${targetRepoName}`);
|
metadataState.components.pullRequests = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored pull requests for ${repository.name} to org ${orgName}/${targetRepoName}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror pull requests for ${repository.name} to org ${orgName}/${targetRepoName}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror pull requests for ${repository.name} to org ${orgName}/${targetRepoName}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other metadata operations even if PRs fail
|
// Continue with other metadata operations even if PRs fail
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorPullRequests &&
|
||||||
|
metadataState.components.pullRequests
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Pull requests already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirror labels if enabled (and not already done via issues)
|
const shouldMirrorLabels =
|
||||||
// Skip labels for starred repos if starredCodeOnly is enabled
|
!!config.giteaConfig?.mirrorLabels &&
|
||||||
const shouldMirrorLabels = config.giteaConfig?.mirrorLabels && !shouldMirrorIssues &&
|
!skipMetadataForStarred &&
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
!shouldMirrorIssuesThisRun &&
|
||||||
|
!metadataState.components.labels;
|
||||||
|
|
||||||
console.log(`[Metadata] Label mirroring check: mirrorLabels=${config.giteaConfig?.mirrorLabels}, shouldMirrorIssues=${shouldMirrorIssues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorLabels=${shouldMirrorLabels}`);
|
console.log(
|
||||||
|
`[Metadata] Label mirroring check: mirrorLabels=${config.giteaConfig?.mirrorLabels}, alreadyMirrored=${metadataState.components.labels}, issuesRunning=${shouldMirrorIssuesThisRun}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorLabels=${shouldMirrorLabels}`
|
||||||
|
);
|
||||||
|
|
||||||
if (shouldMirrorLabels) {
|
if (shouldMirrorLabels) {
|
||||||
try {
|
try {
|
||||||
@@ -1138,19 +1279,36 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
giteaOwner: orgName,
|
giteaOwner: orgName,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored labels for ${repository.name} to org ${orgName}/${targetRepoName}`);
|
metadataState.components.labels = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored labels for ${repository.name} to org ${orgName}/${targetRepoName}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror labels for ${repository.name} to org ${orgName}/${targetRepoName}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror labels for ${repository.name} to org ${orgName}/${targetRepoName}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other metadata operations even if labels fail
|
// Continue with other metadata operations even if labels fail
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorLabels &&
|
||||||
|
metadataState.components.labels
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Labels already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirror milestones if enabled
|
const shouldMirrorMilestones =
|
||||||
// Skip milestones for starred repos if starredCodeOnly is enabled
|
!!config.giteaConfig?.mirrorMilestones &&
|
||||||
const shouldMirrorMilestones = config.giteaConfig?.mirrorMilestones &&
|
!skipMetadataForStarred &&
|
||||||
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
!metadataState.components.milestones;
|
||||||
|
|
||||||
console.log(`[Metadata] Milestone mirroring check: mirrorMilestones=${config.giteaConfig?.mirrorMilestones}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorMilestones=${shouldMirrorMilestones}`);
|
console.log(
|
||||||
|
`[Metadata] Milestone mirroring check: mirrorMilestones=${config.giteaConfig?.mirrorMilestones}, alreadyMirrored=${metadataState.components.milestones}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorMilestones=${shouldMirrorMilestones}`
|
||||||
|
);
|
||||||
|
|
||||||
if (shouldMirrorMilestones) {
|
if (shouldMirrorMilestones) {
|
||||||
try {
|
try {
|
||||||
@@ -1161,11 +1319,30 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
giteaOwner: orgName,
|
giteaOwner: orgName,
|
||||||
giteaRepoName: targetRepoName,
|
giteaRepoName: targetRepoName,
|
||||||
});
|
});
|
||||||
console.log(`[Metadata] Successfully mirrored milestones for ${repository.name} to org ${orgName}/${targetRepoName}`);
|
metadataState.components.milestones = true;
|
||||||
|
metadataUpdated = true;
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Successfully mirrored milestones for ${repository.name} to org ${orgName}/${targetRepoName}`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Metadata] Failed to mirror milestones for ${repository.name} to org ${orgName}/${targetRepoName}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(
|
||||||
|
`[Metadata] Failed to mirror milestones for ${repository.name} to org ${orgName}/${targetRepoName}: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`
|
||||||
|
);
|
||||||
// Continue with other metadata operations even if milestones fail
|
// Continue with other metadata operations even if milestones fail
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
config.giteaConfig?.mirrorMilestones &&
|
||||||
|
metadataState.components.milestones
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[Metadata] Milestones already mirrored for ${repository.name}; skipping`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadataUpdated) {
|
||||||
|
metadataState.lastSyncedAt = new Date().toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
@@ -1181,6 +1358,9 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
lastMirrored: new Date(),
|
lastMirrored: new Date(),
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
mirroredLocation: `${orgName}/${targetRepoName}`,
|
mirroredLocation: `${orgName}/${targetRepoName}`,
|
||||||
|
metadata: metadataUpdated
|
||||||
|
? serializeRepositoryMetadataState(metadataState)
|
||||||
|
: repository.metadata ?? null,
|
||||||
})
|
})
|
||||||
.where(eq(repositories.id, repository.id!));
|
.where(eq(repositories.id, repository.id!));
|
||||||
|
|
||||||
@@ -1812,23 +1992,138 @@ export async function mirrorGitHubReleasesToGitea({
|
|||||||
let mirroredCount = 0;
|
let mirroredCount = 0;
|
||||||
let skippedCount = 0;
|
let skippedCount = 0;
|
||||||
|
|
||||||
// Sort releases by created_at to ensure we get the most recent ones
|
const getReleaseTimestamp = (release: typeof releases.data[number]) => {
|
||||||
const sortedReleases = releases.data.sort((a, b) =>
|
// Use published_at first (when the release was published on GitHub)
|
||||||
new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
|
// Fall back to created_at (when the git tag was created) only if published_at is missing
|
||||||
).slice(0, releaseLimit);
|
// This matches GitHub's sorting behavior and handles cases where multiple tags
|
||||||
|
// point to the same commit but have different publish dates
|
||||||
|
const sourceDate = release.published_at ?? release.created_at ?? "";
|
||||||
|
const timestamp = sourceDate ? new Date(sourceDate).getTime() : 0;
|
||||||
|
return Number.isFinite(timestamp) ? timestamp : 0;
|
||||||
|
};
|
||||||
|
|
||||||
for (const release of sortedReleases) {
|
// Capture the latest releases, then process them oldest-to-newest so Gitea mirrors keep chronological order
|
||||||
|
const releasesToProcess = releases.data
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => getReleaseTimestamp(b) - getReleaseTimestamp(a))
|
||||||
|
.slice(0, releaseLimit)
|
||||||
|
.sort((a, b) => getReleaseTimestamp(a) - getReleaseTimestamp(b));
|
||||||
|
|
||||||
|
console.log(`[Releases] Processing ${releasesToProcess.length} releases in chronological order (oldest to newest by published date)`);
|
||||||
|
releasesToProcess.forEach((rel, idx) => {
|
||||||
|
const publishedDate = new Date(rel.published_at || rel.created_at);
|
||||||
|
const createdDate = new Date(rel.created_at);
|
||||||
|
const dateInfo = rel.published_at !== rel.created_at
|
||||||
|
? `published ${publishedDate.toISOString()} (tag created ${createdDate.toISOString()})`
|
||||||
|
: `published ${publishedDate.toISOString()}`;
|
||||||
|
console.log(`[Releases] ${idx + 1}. ${rel.tag_name} - ${dateInfo}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if existing releases in Gitea are in the wrong order
|
||||||
|
// If so, we need to delete and recreate them to fix the ordering
|
||||||
|
let needsRecreation = false;
|
||||||
|
try {
|
||||||
|
const existingReleasesResponse = await httpGet(
|
||||||
|
`${config.giteaConfig.url}/api/v1/repos/${repoOwner}/${repoName}/releases?per_page=100`,
|
||||||
|
{
|
||||||
|
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
||||||
|
}
|
||||||
|
).catch(() => null);
|
||||||
|
|
||||||
|
if (existingReleasesResponse && existingReleasesResponse.data && Array.isArray(existingReleasesResponse.data)) {
|
||||||
|
const existingReleases = existingReleasesResponse.data;
|
||||||
|
|
||||||
|
if (existingReleases.length > 0) {
|
||||||
|
console.log(`[Releases] Found ${existingReleases.length} existing releases in Gitea, checking chronological order...`);
|
||||||
|
|
||||||
|
// Create a map of tag_name to expected chronological index (0 = oldest, n = newest)
|
||||||
|
const expectedOrder = new Map<string, number>();
|
||||||
|
releasesToProcess.forEach((rel, idx) => {
|
||||||
|
expectedOrder.set(rel.tag_name, idx);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if existing releases are in the correct order based on created_unix
|
||||||
|
// Gitea sorts by created_unix DESC, so newer releases should have higher created_unix values
|
||||||
|
const releasesThatShouldExist = existingReleases.filter(r => expectedOrder.has(r.tag_name));
|
||||||
|
|
||||||
|
if (releasesThatShouldExist.length > 1) {
|
||||||
|
for (let i = 0; i < releasesThatShouldExist.length - 1; i++) {
|
||||||
|
const current = releasesThatShouldExist[i];
|
||||||
|
const next = releasesThatShouldExist[i + 1];
|
||||||
|
|
||||||
|
const currentExpectedIdx = expectedOrder.get(current.tag_name)!;
|
||||||
|
const nextExpectedIdx = expectedOrder.get(next.tag_name)!;
|
||||||
|
|
||||||
|
// Since Gitea returns releases sorted by created_unix DESC:
|
||||||
|
// - Earlier releases in the list should have HIGHER expected indices (newer)
|
||||||
|
// - Later releases in the list should have LOWER expected indices (older)
|
||||||
|
if (currentExpectedIdx < nextExpectedIdx) {
|
||||||
|
console.log(`[Releases] ⚠️ Incorrect ordering detected: ${current.tag_name} (index ${currentExpectedIdx}) appears before ${next.tag_name} (index ${nextExpectedIdx})`);
|
||||||
|
needsRecreation = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsRecreation) {
|
||||||
|
console.log(`[Releases] ⚠️ Releases are in incorrect chronological order. Will delete and recreate all releases.`);
|
||||||
|
|
||||||
|
// Delete all existing releases that we're about to recreate
|
||||||
|
for (const existingRelease of releasesThatShouldExist) {
|
||||||
|
try {
|
||||||
|
console.log(`[Releases] Deleting incorrectly ordered release: ${existingRelease.tag_name}`);
|
||||||
|
await httpDelete(
|
||||||
|
`${config.giteaConfig.url}/api/v1/repos/${repoOwner}/${repoName}/releases/${existingRelease.id}`,
|
||||||
|
{
|
||||||
|
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (deleteError) {
|
||||||
|
console.error(`[Releases] Failed to delete release ${existingRelease.tag_name}: ${deleteError instanceof Error ? deleteError.message : String(deleteError)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[Releases] ✅ Deleted ${releasesThatShouldExist.length} releases. Will recreate in correct chronological order.`);
|
||||||
|
} else {
|
||||||
|
console.log(`[Releases] ✅ Existing releases are in correct chronological order.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (orderCheckError) {
|
||||||
|
console.warn(`[Releases] Could not verify release order: ${orderCheckError instanceof Error ? orderCheckError.message : String(orderCheckError)}`);
|
||||||
|
// Continue with normal processing
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const release of releasesToProcess) {
|
||||||
try {
|
try {
|
||||||
// Check if release already exists
|
// Check if release already exists (skip check if we just deleted all releases)
|
||||||
const existingReleasesResponse = await httpGet(
|
const existingReleasesResponse = needsRecreation ? null : await httpGet(
|
||||||
`${config.giteaConfig.url}/api/v1/repos/${repoOwner}/${repoName}/releases/tags/${release.tag_name}`,
|
`${config.giteaConfig.url}/api/v1/repos/${repoOwner}/${repoName}/releases/tags/${release.tag_name}`,
|
||||||
{
|
{
|
||||||
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
||||||
}
|
}
|
||||||
).catch(() => null);
|
).catch(() => null);
|
||||||
|
|
||||||
const releaseNote = release.body || "";
|
// Prepare release body with GitHub original date header
|
||||||
|
const githubPublishedDate = release.published_at || release.created_at;
|
||||||
|
const githubTagCreatedDate = release.created_at;
|
||||||
|
|
||||||
|
let githubDateHeader = '';
|
||||||
|
if (githubPublishedDate) {
|
||||||
|
githubDateHeader = `> 📅 **Originally published on GitHub:** ${new Date(githubPublishedDate).toUTCString()}`;
|
||||||
|
|
||||||
|
// If the tag was created on a different date than the release was published,
|
||||||
|
// show both dates (helps with repos that create multiple tags from the same commit)
|
||||||
|
if (release.published_at && release.created_at && release.published_at !== release.created_at) {
|
||||||
|
githubDateHeader += `\n> 🏷️ **Git tag created:** ${new Date(githubTagCreatedDate).toUTCString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
githubDateHeader += '\n\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalReleaseNote = release.body || "";
|
||||||
|
const releaseNote = githubDateHeader + originalReleaseNote;
|
||||||
|
|
||||||
if (existingReleasesResponse) {
|
if (existingReleasesResponse) {
|
||||||
// Update existing release if the changelog/body differs
|
// Update existing release if the changelog/body differs
|
||||||
const existingRelease = existingReleasesResponse.data;
|
const existingRelease = existingReleasesResponse.data;
|
||||||
@@ -1851,9 +2146,11 @@ export async function mirrorGitHubReleasesToGitea({
|
|||||||
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (releaseNote) {
|
if (originalReleaseNote) {
|
||||||
console.log(`[Releases] Updated changelog for ${release.tag_name} (${releaseNote.length} characters)`);
|
console.log(`[Releases] Updated changelog for ${release.tag_name} (${originalReleaseNote.length} characters + GitHub date header)`);
|
||||||
|
} else {
|
||||||
|
console.log(`[Releases] Updated release ${release.tag_name} with GitHub date header`);
|
||||||
}
|
}
|
||||||
mirroredCount++;
|
mirroredCount++;
|
||||||
} else {
|
} else {
|
||||||
@@ -1863,9 +2160,11 @@ export async function mirrorGitHubReleasesToGitea({
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new release with changelog/body content
|
// Create new release with changelog/body content (includes GitHub date header)
|
||||||
if (releaseNote) {
|
if (originalReleaseNote) {
|
||||||
console.log(`[Releases] Including changelog for ${release.tag_name} (${releaseNote.length} characters)`);
|
console.log(`[Releases] Including changelog for ${release.tag_name} (${originalReleaseNote.length} characters + GitHub date header)`);
|
||||||
|
} else {
|
||||||
|
console.log(`[Releases] Creating release ${release.tag_name} with GitHub date header (no changelog)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const createReleaseResponse = await httpPost(
|
const createReleaseResponse = await httpPost(
|
||||||
@@ -1933,8 +2232,14 @@ export async function mirrorGitHubReleasesToGitea({
|
|||||||
}
|
}
|
||||||
|
|
||||||
mirroredCount++;
|
mirroredCount++;
|
||||||
const noteInfo = releaseNote ? ` with ${releaseNote.length} character changelog` : " without changelog";
|
const noteInfo = originalReleaseNote ? ` with ${originalReleaseNote.length} character changelog` : " without changelog";
|
||||||
console.log(`[Releases] Successfully mirrored release: ${release.tag_name}${noteInfo}`);
|
console.log(`[Releases] Successfully mirrored release: ${release.tag_name}${noteInfo}`);
|
||||||
|
|
||||||
|
// Add delay to ensure proper timestamp ordering in Gitea
|
||||||
|
// Gitea sorts releases by created_unix DESC, and all releases created in quick succession
|
||||||
|
// will have nearly identical timestamps. The 1-second delay ensures proper chronological order.
|
||||||
|
console.log(`[Releases] Waiting 1 second to ensure proper timestamp ordering in Gitea...`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Releases] Failed to mirror release ${release.tag_name}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(`[Releases] Failed to mirror release ${release.tag_name}: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
}
|
}
|
||||||
|
|||||||
75
src/lib/metadata-state.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
interface MetadataComponentsState {
|
||||||
|
releases: boolean;
|
||||||
|
issues: boolean;
|
||||||
|
pullRequests: boolean;
|
||||||
|
labels: boolean;
|
||||||
|
milestones: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RepositoryMetadataState {
|
||||||
|
components: MetadataComponentsState;
|
||||||
|
lastSyncedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultComponents: MetadataComponentsState = {
|
||||||
|
releases: false,
|
||||||
|
issues: false,
|
||||||
|
pullRequests: false,
|
||||||
|
labels: false,
|
||||||
|
milestones: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createDefaultMetadataState(): RepositoryMetadataState {
|
||||||
|
return {
|
||||||
|
components: { ...defaultComponents },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseRepositoryMetadataState(
|
||||||
|
raw: unknown
|
||||||
|
): RepositoryMetadataState {
|
||||||
|
const base = createDefaultMetadataState();
|
||||||
|
|
||||||
|
if (!raw) {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed: any = raw;
|
||||||
|
|
||||||
|
if (typeof raw === "string") {
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(raw);
|
||||||
|
} catch {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parsed || typeof parsed !== "object") {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.components && typeof parsed.components === "object") {
|
||||||
|
base.components = {
|
||||||
|
...base.components,
|
||||||
|
releases: Boolean(parsed.components.releases),
|
||||||
|
issues: Boolean(parsed.components.issues),
|
||||||
|
pullRequests: Boolean(parsed.components.pullRequests),
|
||||||
|
labels: Boolean(parsed.components.labels),
|
||||||
|
milestones: Boolean(parsed.components.milestones),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof parsed.lastSyncedAt === "string") {
|
||||||
|
base.lastSyncedAt = parsed.lastSyncedAt;
|
||||||
|
} else if (typeof parsed.lastMetadataSync === "string") {
|
||||||
|
base.lastSyncedAt = parsed.lastMetadataSync;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializeRepositoryMetadataState(
|
||||||
|
state: RepositoryMetadataState
|
||||||
|
): string {
|
||||||
|
return JSON.stringify(state);
|
||||||
|
}
|
||||||
@@ -62,6 +62,7 @@ describe('normalizeGitRepoToInsert', () => {
|
|||||||
expect(insert.description).toBeNull();
|
expect(insert.description).toBeNull();
|
||||||
expect(insert.lastMirrored).toBeNull();
|
expect(insert.lastMirrored).toBeNull();
|
||||||
expect(insert.errorMessage).toBeNull();
|
expect(insert.errorMessage).toBeNull();
|
||||||
|
expect(insert.normalizedFullName).toBe(repo.fullName.toLowerCase());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -72,4 +73,3 @@ describe('calcBatchSizeForInsert', () => {
|
|||||||
expect(batch * 29).toBeLessThanOrEqual(999);
|
expect(batch * 29).toBeLessThanOrEqual(999);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export function normalizeGitRepoToInsert(
|
|||||||
configId,
|
configId,
|
||||||
name: repo.name,
|
name: repo.name,
|
||||||
fullName: repo.fullName,
|
fullName: repo.fullName,
|
||||||
|
normalizedFullName: repo.fullName.toLowerCase(),
|
||||||
url: repo.url,
|
url: repo.url,
|
||||||
cloneUrl: repo.cloneUrl,
|
cloneUrl: repo.cloneUrl,
|
||||||
owner: repo.owner,
|
owner: repo.owner,
|
||||||
@@ -68,4 +69,3 @@ export function calcBatchSizeForInsert(columnCount: number, maxParams = 999): nu
|
|||||||
const effectiveMax = Math.max(1, maxParams - safety);
|
const effectiveMax = Math.max(1, maxParams - safety);
|
||||||
return Math.max(1, Math.floor(effectiveMax / columnCount));
|
return Math.max(1, Math.floor(effectiveMax / columnCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,12 +99,12 @@ async function runScheduledSync(config: any): Promise<void> {
|
|||||||
|
|
||||||
// Check for new repositories
|
// Check for new repositories
|
||||||
const existingRepos = await db
|
const existingRepos = await db
|
||||||
.select({ fullName: repositories.fullName })
|
.select({ normalizedFullName: repositories.normalizedFullName })
|
||||||
.from(repositories)
|
.from(repositories)
|
||||||
.where(eq(repositories.userId, userId));
|
.where(eq(repositories.userId, userId));
|
||||||
|
|
||||||
const existingRepoNames = new Set(existingRepos.map(r => r.fullName));
|
const existingRepoNames = new Set(existingRepos.map(r => r.normalizedFullName));
|
||||||
const newRepos = allGithubRepos.filter(r => !existingRepoNames.has(r.fullName));
|
const newRepos = allGithubRepos.filter(r => !existingRepoNames.has(r.fullName.toLowerCase()));
|
||||||
|
|
||||||
if (newRepos.length > 0) {
|
if (newRepos.length > 0) {
|
||||||
console.log(`[Scheduler] Found ${newRepos.length} new repositories for user ${userId}`);
|
console.log(`[Scheduler] Found ${newRepos.length} new repositories for user ${userId}`);
|
||||||
@@ -123,7 +123,7 @@ async function runScheduledSync(config: any): Promise<void> {
|
|||||||
await db
|
await db
|
||||||
.insert(repositories)
|
.insert(repositories)
|
||||||
.values(batch)
|
.values(batch)
|
||||||
.onConflictDoNothing({ target: [repositories.userId, repositories.fullName] });
|
.onConflictDoNothing({ target: [repositories.userId, repositories.normalizedFullName] });
|
||||||
}
|
}
|
||||||
console.log(`[Scheduler] Successfully imported ${newRepos.length} new repositories for user ${userId}`);
|
console.log(`[Scheduler] Successfully imported ${newRepos.length} new repositories for user ${userId}`);
|
||||||
} else {
|
} else {
|
||||||
@@ -432,12 +432,12 @@ async function performInitialAutoStart(): Promise<void> {
|
|||||||
|
|
||||||
// Check for new repositories
|
// Check for new repositories
|
||||||
const existingRepos = await db
|
const existingRepos = await db
|
||||||
.select({ fullName: repositories.fullName })
|
.select({ normalizedFullName: repositories.normalizedFullName })
|
||||||
.from(repositories)
|
.from(repositories)
|
||||||
.where(eq(repositories.userId, config.userId));
|
.where(eq(repositories.userId, config.userId));
|
||||||
|
|
||||||
const existingRepoNames = new Set(existingRepos.map(r => r.fullName));
|
const existingRepoNames = new Set(existingRepos.map(r => r.normalizedFullName));
|
||||||
const reposToImport = allGithubRepos.filter(r => !existingRepoNames.has(r.fullName));
|
const reposToImport = allGithubRepos.filter(r => !existingRepoNames.has(r.fullName.toLowerCase()));
|
||||||
|
|
||||||
if (reposToImport.length > 0) {
|
if (reposToImport.length > 0) {
|
||||||
console.log(`[Scheduler] Importing ${reposToImport.length} repositories for user ${config.userId}...`);
|
console.log(`[Scheduler] Importing ${reposToImport.length} repositories for user ${config.userId}...`);
|
||||||
@@ -456,7 +456,7 @@ async function performInitialAutoStart(): Promise<void> {
|
|||||||
await db
|
await db
|
||||||
.insert(repositories)
|
.insert(repositories)
|
||||||
.values(batch)
|
.values(batch)
|
||||||
.onConflictDoNothing({ target: [repositories.userId, repositories.fullName] });
|
.onConflictDoNothing({ target: [repositories.userId, repositories.normalizedFullName] });
|
||||||
}
|
}
|
||||||
console.log(`[Scheduler] Successfully imported ${reposToImport.length} repositories`);
|
console.log(`[Scheduler] Successfully imported ${reposToImport.length} repositories`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ describe("normalizeOidcProviderConfig", () => {
|
|||||||
expect(result.oidcConfig.userInfoEndpoint).toBe("https://auth.example.com/userinfo");
|
expect(result.oidcConfig.userInfoEndpoint).toBe("https://auth.example.com/userinfo");
|
||||||
expect(result.oidcConfig.scopes).toEqual(["openid", "email"]);
|
expect(result.oidcConfig.scopes).toEqual(["openid", "email"]);
|
||||||
expect(result.oidcConfig.pkce).toBe(false);
|
expect(result.oidcConfig.pkce).toBe(false);
|
||||||
|
expect(result.oidcConfig.discoveryEndpoint).toBe("https://auth.example.com/.well-known/openid-configuration");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("derives missing fields from discovery", async () => {
|
it("derives missing fields from discovery", async () => {
|
||||||
@@ -46,6 +47,24 @@ describe("normalizeOidcProviderConfig", () => {
|
|||||||
expect(result.oidcConfig.jwksEndpoint).toBe("https://auth.example.com/jwks");
|
expect(result.oidcConfig.jwksEndpoint).toBe("https://auth.example.com/jwks");
|
||||||
expect(result.oidcConfig.userInfoEndpoint).toBe("https://auth.example.com/userinfo");
|
expect(result.oidcConfig.userInfoEndpoint).toBe("https://auth.example.com/userinfo");
|
||||||
expect(result.oidcConfig.scopes).toEqual(["openid", "email", "profile"]);
|
expect(result.oidcConfig.scopes).toEqual(["openid", "email", "profile"]);
|
||||||
|
expect(result.oidcConfig.discoveryEndpoint).toBe("https://auth.example.com/.well-known/openid-configuration");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("preserves trailing slash issuers when building discovery endpoints", async () => {
|
||||||
|
const trailingIssuer = "https://auth.example.com/application/o/example/";
|
||||||
|
const requestedUrls: string[] = [];
|
||||||
|
const fetchMock: typeof fetch = async (url) => {
|
||||||
|
requestedUrls.push(typeof url === "string" ? url : url.url);
|
||||||
|
return new Response(JSON.stringify({
|
||||||
|
authorization_endpoint: "https://auth.example.com/application/o/example/auth",
|
||||||
|
token_endpoint: "https://auth.example.com/application/o/example/token",
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await normalizeOidcProviderConfig(trailingIssuer, {}, fetchMock);
|
||||||
|
|
||||||
|
expect(requestedUrls[0]).toBe("https://auth.example.com/application/o/example/.well-known/openid-configuration");
|
||||||
|
expect(result.oidcConfig.discoveryEndpoint).toBe("https://auth.example.com/application/o/example/.well-known/openid-configuration");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws for invalid issuer URL", async () => {
|
it("throws for invalid issuer URL", async () => {
|
||||||
|
|||||||
@@ -131,18 +131,21 @@ export async function normalizeOidcProviderConfig(
|
|||||||
throw new OidcConfigError("Issuer is required");
|
throw new OidcConfigError("Issuer is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
let normalizedIssuer: string;
|
const trimmedIssuer = issuer.trim();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const issuerUrl = new URL(issuer.trim());
|
// Validate issuer but keep caller-provided formatting so we don't break provider expectations
|
||||||
normalizedIssuer = issuerUrl.toString().replace(/\/$/, "");
|
new URL(trimmedIssuer);
|
||||||
} catch {
|
} catch {
|
||||||
throw new OidcConfigError(`Invalid issuer URL: ${issuer}`);
|
throw new OidcConfigError(`Invalid issuer URL: ${issuer}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const issuerForDiscovery = trimmedIssuer.replace(/\/$/, "");
|
||||||
|
|
||||||
const discoveryEndpoint = cleanUrl(
|
const discoveryEndpoint = cleanUrl(
|
||||||
rawConfig.discoveryEndpoint,
|
rawConfig.discoveryEndpoint,
|
||||||
"discovery endpoint",
|
"discovery endpoint",
|
||||||
) ?? `${normalizedIssuer}/.well-known/openid-configuration`;
|
) ?? `${issuerForDiscovery}/.well-known/openid-configuration`;
|
||||||
|
|
||||||
const authorizationEndpoint = cleanUrl(rawConfig.authorizationEndpoint, "authorization endpoint");
|
const authorizationEndpoint = cleanUrl(rawConfig.authorizationEndpoint, "authorization endpoint");
|
||||||
const tokenEndpoint = cleanUrl(rawConfig.tokenEndpoint, "token endpoint");
|
const tokenEndpoint = cleanUrl(rawConfig.tokenEndpoint, "token endpoint");
|
||||||
|
|||||||
@@ -29,12 +29,13 @@ export async function POST(context: APIContext) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate issuer URL format
|
// Validate issuer URL format while preserving trailing slash when provided
|
||||||
let validatedIssuer = issuer;
|
let validatedIssuer = issuer;
|
||||||
if (issuer && typeof issuer === 'string' && issuer.trim() !== '') {
|
if (issuer && typeof issuer === 'string' && issuer.trim() !== '') {
|
||||||
try {
|
try {
|
||||||
const issuerUrl = new URL(issuer.trim());
|
const trimmedIssuer = issuer.trim();
|
||||||
validatedIssuer = issuerUrl.toString().replace(/\/$/, ''); // Remove trailing slash
|
new URL(trimmedIssuer);
|
||||||
|
validatedIssuer = trimmedIssuer;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({ error: `Invalid issuer URL format: ${issuer}` }),
|
JSON.stringify({ error: `Invalid issuer URL format: ${issuer}` }),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { APIRoute } from "astro";
|
import type { APIRoute } from "astro";
|
||||||
import { db, organizations } from "@/lib/db";
|
import { db, organizations, repositories } from "@/lib/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { createSecureErrorResponse } from "@/lib/utils";
|
import { createSecureErrorResponse } from "@/lib/utils";
|
||||||
import { requireAuth } from "@/lib/utils/auth-helpers";
|
import { requireAuth } from "@/lib/utils/auth-helpers";
|
||||||
@@ -61,3 +61,60 @@ export const PATCH: APIRoute = async (context) => {
|
|||||||
return createSecureErrorResponse(error, "Update organization destination", 500);
|
return createSecureErrorResponse(error, "Update organization destination", 500);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DELETE: APIRoute = async (context) => {
|
||||||
|
try {
|
||||||
|
const { user, response } = await requireAuth(context);
|
||||||
|
if (response) return response;
|
||||||
|
|
||||||
|
const userId = user!.id;
|
||||||
|
const orgId = context.params.id;
|
||||||
|
|
||||||
|
if (!orgId) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Organization ID is required" }),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [existingOrg] = await db
|
||||||
|
.select()
|
||||||
|
.from(organizations)
|
||||||
|
.where(and(eq(organizations.id, orgId), eq(organizations.userId, userId)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!existingOrg) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Organization not found" }),
|
||||||
|
{
|
||||||
|
status: 404,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.delete(repositories).where(
|
||||||
|
and(
|
||||||
|
eq(repositories.userId, userId),
|
||||||
|
eq(repositories.organization, existingOrg.name)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
await db
|
||||||
|
.delete(organizations)
|
||||||
|
.where(and(eq(organizations.id, orgId), eq(organizations.userId, userId)));
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ success: true }),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
return createSecureErrorResponse(error, "Delete organization", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { APIRoute } from "astro";
|
import type { APIRoute } from "astro";
|
||||||
import { db, repositories } from "@/lib/db";
|
import { db, repositories, mirrorJobs } from "@/lib/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { createSecureErrorResponse } from "@/lib/utils";
|
import { createSecureErrorResponse } from "@/lib/utils";
|
||||||
import { requireAuth } from "@/lib/utils/auth-helpers";
|
import { requireAuth } from "@/lib/utils/auth-helpers";
|
||||||
@@ -60,4 +60,55 @@ export const PATCH: APIRoute = async (context) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return createSecureErrorResponse(error, "Update repository destination", 500);
|
return createSecureErrorResponse(error, "Update repository destination", 500);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DELETE: APIRoute = async (context) => {
|
||||||
|
try {
|
||||||
|
const { user, response } = await requireAuth(context);
|
||||||
|
if (response) return response;
|
||||||
|
|
||||||
|
const userId = user!.id;
|
||||||
|
const repoId = context.params.id;
|
||||||
|
|
||||||
|
if (!repoId) {
|
||||||
|
return new Response(JSON.stringify({ error: "Repository ID is required" }), {
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const [existingRepo] = await db
|
||||||
|
.select()
|
||||||
|
.from(repositories)
|
||||||
|
.where(and(eq(repositories.id, repoId), eq(repositories.userId, userId)))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!existingRepo) {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Repository not found" }),
|
||||||
|
{
|
||||||
|
status: 404,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db
|
||||||
|
.delete(repositories)
|
||||||
|
.where(and(eq(repositories.id, repoId), eq(repositories.userId, userId)));
|
||||||
|
|
||||||
|
await db
|
||||||
|
.delete(mirrorJobs)
|
||||||
|
.where(and(eq(mirrorJobs.repositoryId, repoId), eq(mirrorJobs.userId, userId)));
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ success: true }),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
return createSecureErrorResponse(error, "Delete repository", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ export async function POST(context: APIContext) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate issuer URL format
|
// Validate issuer URL format while keeping trailing slash if provided
|
||||||
let cleanIssuer: string;
|
const trimmedIssuer = issuer.trim();
|
||||||
|
let parsedIssuer: URL;
|
||||||
try {
|
try {
|
||||||
const issuerUrl = new URL(issuer.trim());
|
parsedIssuer = new URL(trimmedIssuer);
|
||||||
cleanIssuer = issuerUrl.toString().replace(/\/$/, ""); // Remove trailing slash
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@@ -35,7 +35,8 @@ export async function POST(context: APIContext) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const discoveryUrl = `${cleanIssuer}/.well-known/openid-configuration`;
|
const issuerForDiscovery = trimmedIssuer.replace(/\/$/, "");
|
||||||
|
const discoveryUrl = `${issuerForDiscovery}/.well-known/openid-configuration`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch OIDC discovery document with timeout
|
// Fetch OIDC discovery document with timeout
|
||||||
@@ -52,9 +53,9 @@ export async function POST(context: APIContext) {
|
|||||||
});
|
});
|
||||||
} catch (fetchError) {
|
} catch (fetchError) {
|
||||||
if (fetchError instanceof Error && fetchError.name === 'AbortError') {
|
if (fetchError instanceof Error && fetchError.name === 'AbortError') {
|
||||||
throw new Error(`Request timeout: The OIDC provider at ${cleanIssuer} did not respond within 10 seconds`);
|
throw new Error(`Request timeout: The OIDC provider at ${trimmedIssuer} did not respond within 10 seconds`);
|
||||||
}
|
}
|
||||||
throw new Error(`Network error: Could not connect to ${cleanIssuer}. Please verify the URL is correct and accessible.`);
|
throw new Error(`Network error: Could not connect to ${trimmedIssuer}. Please verify the URL is correct and accessible.`);
|
||||||
} finally {
|
} finally {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
}
|
}
|
||||||
@@ -63,7 +64,7 @@ export async function POST(context: APIContext) {
|
|||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
throw new Error(`OIDC discovery document not found at ${discoveryUrl}. For Authentik, ensure you're using the correct application slug in the URL.`);
|
throw new Error(`OIDC discovery document not found at ${discoveryUrl}. For Authentik, ensure you're using the correct application slug in the URL.`);
|
||||||
} else if (response.status >= 500) {
|
} else if (response.status >= 500) {
|
||||||
throw new Error(`OIDC provider error (${response.status}): The server at ${cleanIssuer} returned an error.`);
|
throw new Error(`OIDC provider error (${response.status}): The server at ${trimmedIssuer} returned an error.`);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Failed to fetch discovery document (${response.status}): ${response.statusText}`);
|
throw new Error(`Failed to fetch discovery document (${response.status}): ${response.statusText}`);
|
||||||
}
|
}
|
||||||
@@ -73,12 +74,12 @@ export async function POST(context: APIContext) {
|
|||||||
try {
|
try {
|
||||||
config = await response.json();
|
config = await response.json();
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
throw new Error(`Invalid response: The discovery document from ${cleanIssuer} is not valid JSON.`);
|
throw new Error(`Invalid response: The discovery document from ${trimmedIssuer} is not valid JSON.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the essential endpoints
|
// Extract the essential endpoints
|
||||||
const discoveredConfig = {
|
const discoveredConfig = {
|
||||||
issuer: config.issuer || cleanIssuer,
|
issuer: config.issuer || trimmedIssuer,
|
||||||
authorizationEndpoint: config.authorization_endpoint,
|
authorizationEndpoint: config.authorization_endpoint,
|
||||||
tokenEndpoint: config.token_endpoint,
|
tokenEndpoint: config.token_endpoint,
|
||||||
userInfoEndpoint: config.userinfo_endpoint,
|
userInfoEndpoint: config.userinfo_endpoint,
|
||||||
@@ -88,7 +89,7 @@ export async function POST(context: APIContext) {
|
|||||||
responseTypes: config.response_types_supported || ["code"],
|
responseTypes: config.response_types_supported || ["code"],
|
||||||
grantTypes: config.grant_types_supported || ["authorization_code"],
|
grantTypes: config.grant_types_supported || ["authorization_code"],
|
||||||
// Suggested domain from issuer
|
// Suggested domain from issuer
|
||||||
suggestedDomain: new URL(cleanIssuer).hostname.replace("www.", ""),
|
suggestedDomain: parsedIssuer.hostname.replace("www.", ""),
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Response(JSON.stringify(discoveredConfig), {
|
return new Response(JSON.stringify(discoveredConfig), {
|
||||||
@@ -111,4 +112,4 @@ export async function POST(context: APIContext) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return createSecureErrorResponse(error, "SSO discover API");
|
return createSecureErrorResponse(error, "SSO discover API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,11 +82,10 @@ export async function POST(context: APIContext) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean issuer URL (remove trailing slash); validate format
|
// Validate issuer URL format but keep trailing slash if provided
|
||||||
let cleanIssuer = issuer;
|
const trimmedIssuer = issuer.toString().trim();
|
||||||
try {
|
try {
|
||||||
const issuerUrl = new URL(issuer.toString().trim());
|
new URL(trimmedIssuer);
|
||||||
cleanIssuer = issuerUrl.toString().replace(/\/$/, "");
|
|
||||||
} catch {
|
} catch {
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({ error: `Invalid issuer URL format: ${issuer}` }),
|
JSON.stringify({ error: `Invalid issuer URL format: ${issuer}` }),
|
||||||
@@ -99,7 +98,7 @@ export async function POST(context: APIContext) {
|
|||||||
|
|
||||||
let normalized;
|
let normalized;
|
||||||
try {
|
try {
|
||||||
normalized = await normalizeOidcProviderConfig(cleanIssuer, {
|
normalized = await normalizeOidcProviderConfig(trimmedIssuer, {
|
||||||
clientId,
|
clientId,
|
||||||
clientSecret,
|
clientSecret,
|
||||||
authorizationEndpoint,
|
authorizationEndpoint,
|
||||||
@@ -134,7 +133,7 @@ export async function POST(context: APIContext) {
|
|||||||
.insert(ssoProviders)
|
.insert(ssoProviders)
|
||||||
.values({
|
.values({
|
||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
issuer: cleanIssuer,
|
issuer: trimmedIssuer,
|
||||||
domain,
|
domain,
|
||||||
oidcConfig: JSON.stringify(storedOidcConfig),
|
oidcConfig: JSON.stringify(storedOidcConfig),
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
@@ -213,12 +212,10 @@ export async function PUT(context: APIContext) {
|
|||||||
|
|
||||||
// Parse existing config
|
// Parse existing config
|
||||||
const existingConfig = JSON.parse(existingProvider.oidcConfig);
|
const existingConfig = JSON.parse(existingProvider.oidcConfig);
|
||||||
const effectiveIssuer = issuer || existingProvider.issuer;
|
const effectiveIssuer = issuer?.toString().trim() || existingProvider.issuer;
|
||||||
|
|
||||||
let cleanIssuer = effectiveIssuer;
|
|
||||||
try {
|
try {
|
||||||
const issuerUrl = new URL(effectiveIssuer.toString().trim());
|
new URL(effectiveIssuer);
|
||||||
cleanIssuer = issuerUrl.toString().replace(/\/$/, "");
|
|
||||||
} catch {
|
} catch {
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({ error: `Invalid issuer URL format: ${effectiveIssuer}` }),
|
JSON.stringify({ error: `Invalid issuer URL format: ${effectiveIssuer}` }),
|
||||||
@@ -244,7 +241,7 @@ export async function PUT(context: APIContext) {
|
|||||||
|
|
||||||
let normalized;
|
let normalized;
|
||||||
try {
|
try {
|
||||||
normalized = await normalizeOidcProviderConfig(cleanIssuer, mergedConfig);
|
normalized = await normalizeOidcProviderConfig(effectiveIssuer, mergedConfig);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof OidcConfigError) {
|
if (error instanceof OidcConfigError) {
|
||||||
return new Response(
|
return new Response(
|
||||||
@@ -266,7 +263,7 @@ export async function PUT(context: APIContext) {
|
|||||||
const [updatedProvider] = await db
|
const [updatedProvider] = await db
|
||||||
.update(ssoProviders)
|
.update(ssoProviders)
|
||||||
.set({
|
.set({
|
||||||
issuer: cleanIssuer,
|
issuer: effectiveIssuer,
|
||||||
domain: domain || existingProvider.domain,
|
domain: domain || existingProvider.domain,
|
||||||
oidcConfig: JSON.stringify(storedOidcConfig),
|
oidcConfig: JSON.stringify(storedOidcConfig),
|
||||||
organizationId: organizationId !== undefined ? organizationId : existingProvider.organizationId,
|
organizationId: organizationId !== undefined ? organizationId : existingProvider.organizationId,
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
configId: config.id,
|
configId: config.id,
|
||||||
name: repo.name,
|
name: repo.name,
|
||||||
fullName: repo.fullName,
|
fullName: repo.fullName,
|
||||||
|
normalizedFullName: repo.fullName.toLowerCase(),
|
||||||
url: repo.url,
|
url: repo.url,
|
||||||
cloneUrl: repo.cloneUrl,
|
cloneUrl: repo.cloneUrl,
|
||||||
owner: repo.owner,
|
owner: repo.owner,
|
||||||
@@ -97,6 +98,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
userId,
|
userId,
|
||||||
configId: config.id,
|
configId: config.id,
|
||||||
name: org.name,
|
name: org.name,
|
||||||
|
normalizedName: org.name.toLowerCase(),
|
||||||
avatarUrl: org.avatarUrl,
|
avatarUrl: org.avatarUrl,
|
||||||
membershipRole: org.membershipRole,
|
membershipRole: org.membershipRole,
|
||||||
isIncluded: false,
|
isIncluded: false,
|
||||||
@@ -113,22 +115,22 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
const [existingRepos, existingOrgs] = await Promise.all([
|
const [existingRepos, existingOrgs] = await Promise.all([
|
||||||
tx
|
tx
|
||||||
.select({ fullName: repositories.fullName })
|
.select({ normalizedFullName: repositories.normalizedFullName })
|
||||||
.from(repositories)
|
.from(repositories)
|
||||||
.where(eq(repositories.userId, userId)),
|
.where(eq(repositories.userId, userId)),
|
||||||
tx
|
tx
|
||||||
.select({ name: organizations.name })
|
.select({ normalizedName: organizations.normalizedName })
|
||||||
.from(organizations)
|
.from(organizations)
|
||||||
.where(eq(organizations.userId, userId)),
|
.where(eq(organizations.userId, userId)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const existingRepoNames = new Set(existingRepos.map((r) => r.fullName));
|
const existingRepoNames = new Set(existingRepos.map((r) => r.normalizedFullName));
|
||||||
const existingOrgNames = new Set(existingOrgs.map((o) => o.name));
|
const existingOrgNames = new Set(existingOrgs.map((o) => o.normalizedName));
|
||||||
|
|
||||||
insertedRepos = newRepos.filter(
|
insertedRepos = newRepos.filter(
|
||||||
(r) => !existingRepoNames.has(r.fullName)
|
(r) => !existingRepoNames.has(r.normalizedFullName)
|
||||||
);
|
);
|
||||||
insertedOrgs = newOrgs.filter((o) => !existingOrgNames.has(o.name));
|
insertedOrgs = newOrgs.filter((o) => !existingOrgNames.has(o.normalizedName));
|
||||||
|
|
||||||
// Batch insert repositories to avoid SQLite parameter limit (dynamic by column count)
|
// Batch insert repositories to avoid SQLite parameter limit (dynamic by column count)
|
||||||
const sample = newRepos[0];
|
const sample = newRepos[0];
|
||||||
@@ -140,7 +142,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
await tx
|
await tx
|
||||||
.insert(repositories)
|
.insert(repositories)
|
||||||
.values(batch)
|
.values(batch)
|
||||||
.onConflictDoNothing({ target: [repositories.userId, repositories.fullName] });
|
.onConflictDoNothing({ target: [repositories.userId, repositories.normalizedFullName] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { APIRoute } from "astro";
|
import type { APIRoute } from "astro";
|
||||||
import { Octokit } from "@octokit/rest";
|
|
||||||
import { configs, db, organizations, repositories } from "@/lib/db";
|
import { configs, db, organizations, repositories } from "@/lib/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { jsonResponse, createSecureErrorResponse } from "@/lib/utils";
|
import { jsonResponse, createSecureErrorResponse } from "@/lib/utils";
|
||||||
@@ -15,7 +14,7 @@ import { createGitHubClient } from "@/lib/github";
|
|||||||
export const POST: APIRoute = async ({ request }) => {
|
export const POST: APIRoute = async ({ request }) => {
|
||||||
try {
|
try {
|
||||||
const body: AddOrganizationApiRequest = await request.json();
|
const body: AddOrganizationApiRequest = await request.json();
|
||||||
const { role, org, userId } = body;
|
const { role, org, userId, force = false } = body;
|
||||||
|
|
||||||
if (!org || !userId || !role) {
|
if (!org || !userId || !role) {
|
||||||
return jsonResponse({
|
return jsonResponse({
|
||||||
@@ -24,21 +23,58 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if org already exists
|
const trimmedOrg = org.trim();
|
||||||
const existingOrg = await db
|
const normalizedOrg = trimmedOrg.toLowerCase();
|
||||||
|
|
||||||
|
// Check if org already exists (case-insensitive)
|
||||||
|
const [existingOrg] = await db
|
||||||
.select()
|
.select()
|
||||||
.from(organizations)
|
.from(organizations)
|
||||||
.where(
|
.where(
|
||||||
and(eq(organizations.name, org), eq(organizations.userId, userId))
|
and(
|
||||||
);
|
eq(organizations.userId, userId),
|
||||||
|
eq(organizations.normalizedName, normalizedOrg)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
if (existingOrg.length > 0) {
|
if (existingOrg && !force) {
|
||||||
return jsonResponse({
|
return jsonResponse({
|
||||||
data: {
|
data: {
|
||||||
success: false,
|
success: false,
|
||||||
error: "Organization already exists for this user",
|
error: "Organization already exists for this user",
|
||||||
},
|
},
|
||||||
status: 400,
|
status: 409,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingOrg && force) {
|
||||||
|
const [updatedOrg] = await db
|
||||||
|
.update(organizations)
|
||||||
|
.set({
|
||||||
|
membershipRole: role,
|
||||||
|
normalizedName: normalizedOrg,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(organizations.id, existingOrg.id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
const resPayload: AddOrganizationApiResponse = {
|
||||||
|
success: true,
|
||||||
|
organization: updatedOrg ?? existingOrg,
|
||||||
|
message: "Organization already exists; using existing record.",
|
||||||
|
};
|
||||||
|
|
||||||
|
return jsonResponse({ data: resPayload, status: 200 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingOrg) {
|
||||||
|
return jsonResponse({
|
||||||
|
data: {
|
||||||
|
success: false,
|
||||||
|
error: "Organization already exists for this user",
|
||||||
|
},
|
||||||
|
status: 409,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,17 +107,21 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
|
|
||||||
// Create authenticated Octokit instance with rate limit tracking
|
// Create authenticated Octokit instance with rate limit tracking
|
||||||
const githubUsername = decryptedConfig.githubConfig?.owner || undefined;
|
const githubUsername = decryptedConfig.githubConfig?.owner || undefined;
|
||||||
const octokit = createGitHubClient(decryptedConfig.githubConfig.token, userId, githubUsername);
|
const octokit = createGitHubClient(
|
||||||
|
decryptedConfig.githubConfig.token,
|
||||||
|
userId,
|
||||||
|
githubUsername
|
||||||
|
);
|
||||||
|
|
||||||
// Fetch org metadata
|
// Fetch org metadata
|
||||||
const { data: orgData } = await octokit.orgs.get({ org });
|
const { data: orgData } = await octokit.orgs.get({ org: trimmedOrg });
|
||||||
|
|
||||||
// Fetch repos based on config settings
|
// Fetch repos based on config settings
|
||||||
const allRepos = [];
|
const allRepos = [];
|
||||||
|
|
||||||
// Fetch all repos (public, private, and member) to show in UI
|
// Fetch all repos (public, private, and member) to show in UI
|
||||||
const publicRepos = await octokit.paginate(octokit.repos.listForOrg, {
|
const publicRepos = await octokit.paginate(octokit.repos.listForOrg, {
|
||||||
org,
|
org: trimmedOrg,
|
||||||
type: "public",
|
type: "public",
|
||||||
per_page: 100,
|
per_page: 100,
|
||||||
});
|
});
|
||||||
@@ -89,7 +129,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
|
|
||||||
// Always fetch private repos to show them in the UI
|
// Always fetch private repos to show them in the UI
|
||||||
const privateRepos = await octokit.paginate(octokit.repos.listForOrg, {
|
const privateRepos = await octokit.paginate(octokit.repos.listForOrg, {
|
||||||
org,
|
org: trimmedOrg,
|
||||||
type: "private",
|
type: "private",
|
||||||
per_page: 100,
|
per_page: 100,
|
||||||
});
|
});
|
||||||
@@ -97,7 +137,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
|
|
||||||
// Also fetch member repos (includes private repos the user has access to)
|
// Also fetch member repos (includes private repos the user has access to)
|
||||||
const memberRepos = await octokit.paginate(octokit.repos.listForOrg, {
|
const memberRepos = await octokit.paginate(octokit.repos.listForOrg, {
|
||||||
org,
|
org: trimmedOrg,
|
||||||
type: "member",
|
type: "member",
|
||||||
per_page: 100,
|
per_page: 100,
|
||||||
});
|
});
|
||||||
@@ -107,38 +147,44 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
allRepos.push(...uniqueMemberRepos);
|
allRepos.push(...uniqueMemberRepos);
|
||||||
|
|
||||||
// Insert repositories
|
// Insert repositories
|
||||||
const repoRecords = allRepos.map((repo) => ({
|
const repoRecords = allRepos.map((repo) => {
|
||||||
id: uuidv4(),
|
const normalizedOwner = repo.owner.login.trim().toLowerCase();
|
||||||
userId,
|
const normalizedRepoName = repo.name.trim().toLowerCase();
|
||||||
configId,
|
|
||||||
name: repo.name,
|
return {
|
||||||
fullName: repo.full_name,
|
id: uuidv4(),
|
||||||
url: repo.html_url,
|
userId,
|
||||||
cloneUrl: repo.clone_url ?? "",
|
configId,
|
||||||
owner: repo.owner.login,
|
name: repo.name,
|
||||||
organization:
|
fullName: repo.full_name,
|
||||||
repo.owner.type === "Organization" ? repo.owner.login : null,
|
normalizedFullName: `${normalizedOwner}/${normalizedRepoName}`,
|
||||||
mirroredLocation: "",
|
url: repo.html_url,
|
||||||
destinationOrg: null,
|
cloneUrl: repo.clone_url ?? "",
|
||||||
isPrivate: repo.private,
|
owner: repo.owner.login,
|
||||||
isForked: repo.fork,
|
organization:
|
||||||
forkedFrom: null,
|
repo.owner.type === "Organization" ? repo.owner.login : null,
|
||||||
hasIssues: repo.has_issues,
|
mirroredLocation: "",
|
||||||
isStarred: false,
|
destinationOrg: null,
|
||||||
isArchived: repo.archived,
|
isPrivate: repo.private,
|
||||||
size: repo.size,
|
isForked: repo.fork,
|
||||||
hasLFS: false,
|
forkedFrom: null,
|
||||||
hasSubmodules: false,
|
hasIssues: repo.has_issues,
|
||||||
language: repo.language ?? null,
|
isStarred: false,
|
||||||
description: repo.description ?? null,
|
isArchived: repo.archived,
|
||||||
defaultBranch: repo.default_branch ?? "main",
|
size: repo.size,
|
||||||
visibility: (repo.visibility ?? "public") as RepositoryVisibility,
|
hasLFS: false,
|
||||||
status: "imported" as RepoStatus,
|
hasSubmodules: false,
|
||||||
lastMirrored: null,
|
language: repo.language ?? null,
|
||||||
errorMessage: null,
|
description: repo.description ?? null,
|
||||||
createdAt: repo.created_at ? new Date(repo.created_at) : new Date(),
|
defaultBranch: repo.default_branch ?? "main",
|
||||||
updatedAt: repo.updated_at ? new Date(repo.updated_at) : new Date(),
|
visibility: (repo.visibility ?? "public") as RepositoryVisibility,
|
||||||
}));
|
status: "imported" as RepoStatus,
|
||||||
|
lastMirrored: null,
|
||||||
|
errorMessage: null,
|
||||||
|
createdAt: repo.created_at ? new Date(repo.created_at) : new Date(),
|
||||||
|
updatedAt: repo.updated_at ? new Date(repo.updated_at) : new Date(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// Batch insert repositories to avoid SQLite parameter limit
|
// Batch insert repositories to avoid SQLite parameter limit
|
||||||
// Compute batch size based on column count
|
// Compute batch size based on column count
|
||||||
@@ -150,7 +196,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
await db
|
await db
|
||||||
.insert(repositories)
|
.insert(repositories)
|
||||||
.values(batch)
|
.values(batch)
|
||||||
.onConflictDoNothing({ target: [repositories.userId, repositories.fullName] });
|
.onConflictDoNothing({ target: [repositories.userId, repositories.normalizedFullName] });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert organization metadata
|
// Insert organization metadata
|
||||||
@@ -159,6 +205,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
userId,
|
userId,
|
||||||
configId,
|
configId,
|
||||||
name: orgData.login,
|
name: orgData.login,
|
||||||
|
normalizedName: normalizedOrg,
|
||||||
avatarUrl: orgData.avatar_url,
|
avatarUrl: orgData.avatar_url,
|
||||||
membershipRole: role,
|
membershipRole: role,
|
||||||
isIncluded: false,
|
isIncluded: false,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { createMirrorJob } from "@/lib/helpers";
|
|||||||
export const POST: APIRoute = async ({ request }) => {
|
export const POST: APIRoute = async ({ request }) => {
|
||||||
try {
|
try {
|
||||||
const body: AddRepositoriesApiRequest = await request.json();
|
const body: AddRepositoriesApiRequest = await request.json();
|
||||||
const { owner, repo, userId } = body;
|
const { owner, repo, userId, force = false } = body;
|
||||||
|
|
||||||
if (!owner || !repo || !userId) {
|
if (!owner || !repo || !userId) {
|
||||||
return new Response(
|
return new Response(
|
||||||
@@ -27,26 +27,43 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const trimmedOwner = owner.trim();
|
||||||
|
const trimmedRepo = repo.trim();
|
||||||
|
|
||||||
|
if (!trimmedOwner || !trimmedRepo) {
|
||||||
|
return jsonResponse({
|
||||||
|
data: {
|
||||||
|
success: false,
|
||||||
|
error: "Missing owner, repo, or userId",
|
||||||
|
},
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedOwner = trimmedOwner.toLowerCase();
|
||||||
|
const normalizedRepo = trimmedRepo.toLowerCase();
|
||||||
|
const normalizedFullName = `${normalizedOwner}/${normalizedRepo}`;
|
||||||
|
|
||||||
// Check if repository with the same owner, name, and userId already exists
|
// Check if repository with the same owner, name, and userId already exists
|
||||||
const existingRepo = await db
|
const [existingRepo] = await db
|
||||||
.select()
|
.select()
|
||||||
.from(repositories)
|
.from(repositories)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(repositories.owner, owner),
|
eq(repositories.userId, userId),
|
||||||
eq(repositories.name, repo),
|
eq(repositories.normalizedFullName, normalizedFullName)
|
||||||
eq(repositories.userId, userId)
|
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
if (existingRepo.length > 0) {
|
if (existingRepo && !force) {
|
||||||
return jsonResponse({
|
return jsonResponse({
|
||||||
data: {
|
data: {
|
||||||
success: false,
|
success: false,
|
||||||
error:
|
error:
|
||||||
"Repository with this name and owner already exists for this user",
|
"Repository with this name and owner already exists for this user",
|
||||||
},
|
},
|
||||||
status: 400,
|
status: 409,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,14 +85,17 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
|
|
||||||
const octokit = new Octokit(); // No auth for public repos
|
const octokit = new Octokit(); // No auth for public repos
|
||||||
|
|
||||||
const { data: repoData } = await octokit.rest.repos.get({ owner, repo });
|
const { data: repoData } = await octokit.rest.repos.get({
|
||||||
|
owner: trimmedOwner,
|
||||||
|
repo: trimmedRepo,
|
||||||
|
});
|
||||||
|
|
||||||
const metadata = {
|
const baseMetadata = {
|
||||||
id: uuidv4(),
|
|
||||||
userId,
|
userId,
|
||||||
configId,
|
configId,
|
||||||
name: repoData.name,
|
name: repoData.name,
|
||||||
fullName: repoData.full_name,
|
fullName: repoData.full_name,
|
||||||
|
normalizedFullName,
|
||||||
url: repoData.html_url,
|
url: repoData.html_url,
|
||||||
cloneUrl: repoData.clone_url,
|
cloneUrl: repoData.clone_url,
|
||||||
owner: repoData.owner.login,
|
owner: repoData.owner.login,
|
||||||
@@ -94,6 +114,37 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
description: repoData.description ?? null,
|
description: repoData.description ?? null,
|
||||||
defaultBranch: repoData.default_branch,
|
defaultBranch: repoData.default_branch,
|
||||||
visibility: (repoData.visibility ?? "public") as RepositoryVisibility,
|
visibility: (repoData.visibility ?? "public") as RepositoryVisibility,
|
||||||
|
lastMirrored: existingRepo?.lastMirrored ?? null,
|
||||||
|
errorMessage: existingRepo?.errorMessage ?? null,
|
||||||
|
mirroredLocation: existingRepo?.mirroredLocation ?? "",
|
||||||
|
destinationOrg: existingRepo?.destinationOrg ?? null,
|
||||||
|
updatedAt: repoData.updated_at
|
||||||
|
? new Date(repoData.updated_at)
|
||||||
|
: new Date(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (existingRepo && force) {
|
||||||
|
const [updatedRepo] = await db
|
||||||
|
.update(repositories)
|
||||||
|
.set({
|
||||||
|
...baseMetadata,
|
||||||
|
normalizedFullName,
|
||||||
|
configId,
|
||||||
|
})
|
||||||
|
.where(eq(repositories.id, existingRepo.id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
const resPayload: AddRepositoriesApiResponse = {
|
||||||
|
success: true,
|
||||||
|
repository: updatedRepo ?? existingRepo,
|
||||||
|
message: "Repository already exists; metadata refreshed.",
|
||||||
|
};
|
||||||
|
|
||||||
|
return jsonResponse({ data: resPayload, status: 200 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = {
|
||||||
|
id: uuidv4(),
|
||||||
status: "imported" as Repository["status"],
|
status: "imported" as Repository["status"],
|
||||||
lastMirrored: null,
|
lastMirrored: null,
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
@@ -102,15 +153,13 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
createdAt: repoData.created_at
|
createdAt: repoData.created_at
|
||||||
? new Date(repoData.created_at)
|
? new Date(repoData.created_at)
|
||||||
: new Date(),
|
: new Date(),
|
||||||
updatedAt: repoData.updated_at
|
...baseMetadata,
|
||||||
? new Date(repoData.updated_at)
|
} satisfies Repository;
|
||||||
: new Date(),
|
|
||||||
};
|
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.insert(repositories)
|
.insert(repositories)
|
||||||
.values(metadata)
|
.values(metadata)
|
||||||
.onConflictDoNothing({ target: [repositories.userId, repositories.fullName] });
|
.onConflictDoNothing({ target: [repositories.userId, repositories.normalizedFullName] });
|
||||||
|
|
||||||
createMirrorJob({
|
createMirrorJob({
|
||||||
userId,
|
userId,
|
||||||
|
|||||||
@@ -81,11 +81,12 @@ export interface AddRepositoriesApiRequest {
|
|||||||
userId: string;
|
userId: string;
|
||||||
repo: string;
|
repo: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
|
force?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddRepositoriesApiResponse {
|
export interface AddRepositoriesApiResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
repository: Repository;
|
repository?: Repository;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,11 +45,12 @@ export interface AddOrganizationApiRequest {
|
|||||||
userId: string;
|
userId: string;
|
||||||
org: string;
|
org: string;
|
||||||
role: MembershipRole;
|
role: MembershipRole;
|
||||||
|
force?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddOrganizationApiResponse {
|
export interface AddOrganizationApiResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
organization: Organization;
|
organization?: Organization;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
www/public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
www/public/assets/hero_logo.webp
Normal file
|
After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
3
www/public/favicon.svg
Normal file
|
After Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 338 KiB After Width: | Height: | Size: 330 KiB |
@@ -22,7 +22,7 @@ export function Installation() {
|
|||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
title: "Clone the repository",
|
title: "Clone the repository",
|
||||||
command: "git clone https://github.com/RayLabsHQ/gitea-mirror.git\ncd gitea-mirror",
|
command: "git clone https://github.com/RayLabsHQ/gitea-mirror.git && cd gitea-mirror",
|
||||||
id: "docker-clone"
|
id: "docker-clone"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -166,4 +166,4 @@ export function Installation() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||