swadhinbiswas

[email protected]
2026-02-15 14:32:54 +0600 committed NaN years ago
719a1cb
commit 719a1cb
348 files changed
+43408 -2384
Showing 71 changed files+8094-679
This diff is too large and has been truncated. Displaying first 71 files.
.astro/settings.json.env.example.github/workflows/sync-roadmap-issues.yml.gitignoreREADME.mdbun.lockcli/bun.lockcli/package-lock.jsoncli/src/commands/repo.tscli/src/commands/runner/config.tscli/src/commands/stack/index.tscli/src/lib/stack-manager.tsdoc/feature_audit.mddoc/missing_implementation_tracker.mddoc/production_security_audit_2026-02-15.mddocs-site/astro.config.mjsdocs-site/package.jsondocs-site/public/architecture.svgdocs-site/public/assets/houston.webpdocs-site/public/logo-dark.pngdocs-site/public/logo-light.pngdocs-site/src/assets/houston.webpdocs-site/src/assets/logo-dark.pngdocs-site/src/assets/logo-light.pngdocs-site/src/components/Hero.astrodocs-site/src/components/StarlightHero.astrodocs-site/src/content.config.tsdocs-site/src/content/docs/index.mdxdocs-site/src/custom.cssdocs-site/src/worker.tsdocs-site/wrangler.jsoncdrizzle/0000_abandoned_king_bedlam.sqldrizzle/0001_faithful_randall.sqldrizzle/0002_third_cable.sqldrizzle/0003_married_cyclops.sqlgenerate-roadmap-issues.mjsgithub-issues-checklist.mdgithub-roadmap-issues.jsonnotimplemented.mdpackage.jsonscripts/backup.tsscripts/clean-corrupted-s3-repos.tsscripts/create-admin.tsscripts/create_github_issues_from_checklist.shscripts/debug-env.tsscripts/empty-r3-bucket.cjsscripts/fix_astro_class.pyscripts/generate-roadmap-issues.mjsscripts/list-s3-bucket.cjsscripts/manual-sync.tsscripts/publish-cli.shscripts/reset-database.cjsscripts/test-git-server.tsscripts/test-storage-config.tsscripts/verify-env-config.tsscripts/verify-ratelimit.tsscripts/worker.tssrc/components/admin/AdminDashboard.tsxsrc/components/admin/AlertBanner.tsxsrc/components/admin/DashboardStats.tsxsrc/components/admin/QuickStatsRow.tsxsrc/components/admin/SystemStatus.tsxsrc/components/dashboard/StackCard.astrosrc/components/git/InteractiveRebase.tsxsrc/components/home/HeroSection.tsxsrc/components/insights/InsightsCharts.tsxsrc/components/issues/IssueDetail.tsxsrc/components/issues/IssuesList.tsxsrc/components/kanban/Card.tsxsrc/components/kanban/Column.tsxsrc/components/kanban/KanbanBoard.tsx
▼.astro/settings.json
+1-1
1
1
{
2
2
"_variables": {
3
- "lastUpdateCheck": 1769015135620
3
+ "lastUpdateCheck": 1770408834603
4
4
}
5
5
}
6
6
▼.env.example
+9-0
52
52
# This secret protects git hooks from unauthorized access
53
53
# Generate with: openssl rand -hex 32
54
54
INTERNAL_HOOK_SECRET=change-this-internal-hook-secret-in-production
55
+# CRITICAL: Secret for cron endpoints (gc, mirror sync, etc.)
56
+# Generate with: openssl rand -hex 32
57
+CRON_SECRET=change-this-cron-secret-in-production
58
+# CRITICAL: Encryption key for sensitive config blobs (AI provider keys, etc.)
59
+# Generate with: openssl rand -hex 32
60
+AI_CONFIG_ENCRYPTION_KEY=change-this-ai-config-encryption-key-in-production
55
61
56
62
# OAuth Providers (optional)
57
63
OAUTH_GOOGLE_CLIENT_ID=
100
106
RUNNER_ARTIFACTS_PATH=./data/runner/artifacts
101
107
RUNNER_CONCURRENT_JOBS=2
102
108
RUNNER_TIMEOUT=3600
109
+# CRITICAL: Secret shared between server and runners. Must be the same.
110
+# Generate with: openssl rand -hex 32
111
+RUNNER_SECRET=change-this-runner-secret-in-production
103
112
104
113
# Email Configuration (optional)
105
114
SMTP_HOST=
106
115
▼.github/workflows/sync-roadmap-issues.yml
+100-0
1
+name: Sync Roadmap Issues
2
+
3
+on:
4
+ workflow_dispatch:
5
+ push:
6
+ paths:
7
+ - github-roadmap-issues.json
8
+ - .github/workflows/sync-roadmap-issues.yml
9
+
10
+permissions:
11
+ contents: read
12
+ issues: write
13
+
14
+jobs:
15
+ sync:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - name: Checkout
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Create Missing Roadmap Issues
22
+ uses: actions/github-script@v7
23
+ with:
24
+ script: |
25
+ const fs = require('node:fs');
26
+ const path = require('node:path');
27
+
28
+ const issuesPath = path.join(process.env.GITHUB_WORKSPACE, 'github-roadmap-issues.json');
29
+ const payload = JSON.parse(fs.readFileSync(issuesPath, 'utf8'));
30
+
31
+ const owner = context.repo.owner;
32
+ const repo = context.repo.repo;
33
+
34
+ const labelColors = {
35
+ 'roadmap/ws1': '0e8a16',
36
+ 'roadmap/ws2': '1d76db',
37
+ 'roadmap/ws3': 'b60205',
38
+ 'roadmap/ws4': '5319e7',
39
+ 'roadmap/ws5': 'fbca04',
40
+ 'roadmap/ws6': '0052cc',
41
+ 'status/partial': 'fbca04',
42
+ 'status/missing': 'd93f0b',
43
+ 'priority/high': 'b60205',
44
+ 'priority/medium': 'fbca04',
45
+ 'priority/low': '0e8a16',
46
+ 'docs-required': '0052cc',
47
+ };
48
+
49
+ const ensureLabel = async (name) => {
50
+ try {
51
+ await github.rest.issues.getLabel({ owner, repo, name });
52
+ } catch (error) {
53
+ if (error.status !== 404) throw error;
54
+ await github.rest.issues.createLabel({
55
+ owner,
56
+ repo,
57
+ name,
58
+ color: labelColors[name] || '6e7781',
59
+ });
60
+ }
61
+ };
62
+
63
+ const existingIssues = await github.paginate(github.rest.issues.listForRepo, {
64
+ owner,
65
+ repo,
66
+ state: 'all',
67
+ per_page: 100,
68
+ });
69
+
70
+ const existingTitleSet = new Set(
71
+ existingIssues
72
+ .filter((issue) => !issue.pull_request)
73
+ .map((issue) => issue.title)
74
+ );
75
+
76
+ let created = 0;
77
+ let skipped = 0;
78
+
79
+ for (const item of payload) {
80
+ if (existingTitleSet.has(item.title)) {
81
+ skipped += 1;
82
+ continue;
83
+ }
84
+
85
+ for (const label of item.labels) {
86
+ await ensureLabel(label);
87
+ }
88
+
89
+ await github.rest.issues.create({
90
+ owner,
91
+ repo,
92
+ title: item.title,
93
+ body: item.body,
94
+ labels: item.labels,
95
+ });
96
+
97
+ created += 1;
98
+ }
99
+
100
+ core.notice(`Roadmap issue sync complete. Created: ${created}, Skipped existing: ${skipped}`);
0
101
▼.gitignore
+10-1
34
34
tmp/
35
35
temp/
36
36
*.tmp
37
+*_debug.txt
38
+*_output.txt
39
+*_report.txt
40
+lint_output*
41
+lint_report*
42
+cookies.txt
43
+fix_get_calls.sh
44
+
45
+# Artifacts
46
+pci/
37
47
38
48
# SSH keys (generated)
39
49
*.pem
49
59
# Monaco Editor cache
50
60
.monaco/
51
61
.vercel
52
-scripts/
53
62
▼README.md
+15-0
611
611
- Add to merge queue
612
612
- View full PR
613
613
614
+#### Discord & Microsoft Teams
615
+
616
+OpenCodeHub also supports incoming webhooks for Discord and Microsoft Teams.
617
+
618
+**Setup:**
619
+1. **Repository → Settings → Webhooks**
620
+2. Create a new webhook
621
+3. Select **Discord** or **Microsoft Teams** as the type
622
+4. Paste your webhook URL
623
+5. Select triggers (PR events, Push events, etc.)
624
+
614
625
---
615
626
616
627
## 💡 Workflow Examples
740
751
SITE_URL=https://your-domain.com # Must be HTTPS in production
741
752
PORT=3000
742
753
HOST=0.0.0.0
754
+
755
+# CI/CD Runner Security (CRITICAL)
756
+# Must match the secret used by your runners
757
+RUNNER_SECRET=<run: openssl rand -hex 32>
743
758
```
744
759
745
760
### Database Configuration
746
761
▼bun.lock
+534-37
11
11
"@astrojs/tailwind": "^5.1.0",
12
12
"@astrojs/vercel": "^7.7.0",
13
13
"@aws-sdk/client-s3": "^3.948.0",
14
+ "@aws-sdk/lib-storage": "^3.972.0",
14
15
"@aws-sdk/s3-request-presigner": "^3.948.0",
16
+ "@azure/storage-blob": "^12.30.0",
17
+ "@dnd-kit/core": "^6.3.1",
18
+ "@dnd-kit/sortable": "^10.0.0",
19
+ "@dnd-kit/utilities": "^3.2.2",
20
+ "@google-cloud/storage": "^7.18.0",
15
21
"@libsql/client": "^0.15.15",
16
22
"@monaco-editor/react": "^4.6.0",
17
23
"@opentelemetry/auto-instrumentations-node": "^0.67.2",
20
26
"@radix-ui/react-accordion": "^1.2.0",
21
27
"@radix-ui/react-alert-dialog": "^1.1.1",
22
28
"@radix-ui/react-avatar": "^1.1.0",
23
- "@radix-ui/react-checkbox": "^1.1.1",
29
+ "@radix-ui/react-checkbox": "^1.3.3",
24
30
"@radix-ui/react-collapsible": "^1.1.0",
25
31
"@radix-ui/react-context-menu": "^2.2.1",
26
32
"@radix-ui/react-dialog": "^1.1.1",
32
38
"@radix-ui/react-popover": "^1.1.1",
33
39
"@radix-ui/react-progress": "^1.1.0",
34
40
"@radix-ui/react-scroll-area": "^1.1.0",
35
- "@radix-ui/react-select": "^2.1.1",
41
+ "@radix-ui/react-select": "^2.2.6",
36
42
"@radix-ui/react-separator": "^1.1.0",
37
43
"@radix-ui/react-slider": "^1.2.0",
38
44
"@radix-ui/react-slot": "^1.1.0",
43
49
"@radix-ui/react-toggle-group": "^1.1.0",
44
50
"@radix-ui/react-tooltip": "^1.1.2",
45
51
"@types/nodemailer": "^7.0.4",
52
+ "@types/qrcode": "^1.5.6",
46
53
"@upstash/redis": "^1.36.0",
54
+ "arctic": "^3.7.0",
47
55
"astro": "^4.11.0",
56
+ "basic-ftp": "^5.1.0",
48
57
"bcryptjs": "^2.4.3",
49
58
"better-sqlite3": "^11.1.2",
50
59
"bullmq": "^5.65.1",
52
61
"clsx": "^2.1.1",
53
62
"cmdk": "^1.1.1",
54
63
"cobe": "^0.6.5",
64
+ "date-fns": "^4.1.0",
55
65
"dockerode": "^4.0.2",
56
66
"drizzle-orm": "^0.45.1",
57
67
"framer-motion": "^11.18.2",
58
68
"github-markdown-css": "^5.8.1",
59
69
"googleapis": "^169.0.0",
60
- "ioredis": "^5.8.2",
70
+ "graphql": "^16.12.0",
71
+ "graphql-yoga": "^5.18.0",
72
+ "ioredis": "^5.9.2",
73
+ "isomorphic-dompurify": "^2.35.0",
61
74
"isomorphic-git": "^1.25.10",
62
75
"jose": "^5.6.3",
63
76
"js-yaml": "^4.1.0",
64
77
"lucide-react": "^0.400.0",
65
78
"marked": "^17.0.1",
79
+ "micromatch": "^4.0.8",
66
80
"monaco-editor": "^0.50.0",
67
81
"mysql2": "^3.15.3",
68
82
"nanoid": "^5.0.7",
69
83
"nodegit": "^0.28.0-alpha.27",
70
84
"nodemailer": "^7.0.12",
85
+ "openai": "^6.16.0",
86
+ "openid-client": "^6.8.1",
71
87
"openpgp": "^6.3.0",
72
88
"otplib": "^12.0.1",
73
89
"pg": "^8.16.3",
75
91
"pino-loki": "^3.0.0",
76
92
"postgres": "^3.4.7",
77
93
"prom-client": "^15.1.3",
94
+ "qrcode": "^1.5.4",
78
95
"react": "^18.3.1",
79
96
"react-dom": "^18.3.1",
80
97
"react-globe.gl": "^2.37.0",
98
+ "recharts": "^3.7.0",
81
99
"rehype-sanitize": "^6.0.0",
82
100
"rehype-stringify": "^10.0.1",
83
101
"remark-gfm": "^4.0.1",
84
102
"remark-parse": "^11.0.0",
85
103
"remark-rehype": "^11.1.2",
86
104
"simple-git": "^3.25.0",
105
+ "sonner": "^2.0.7",
87
106
"ssh2": "^1.15.0",
88
107
"tailwind-merge": "^2.6.0",
89
108
"tailwindcss-animate": "^1.0.7",
90
109
"three": "^0.182.0",
91
110
"unified": "^11.0.5",
111
+ "unist-util-visit": "^5.1.0",
92
112
"zod": "^3.23.8",
93
113
"zustand": "^4.5.4",
94
114
},
100
120
"@types/better-sqlite3": "^7.6.11",
101
121
"@types/dockerode": "^3.3.29",
102
122
"@types/js-yaml": "^4.0.9",
123
+ "@types/micromatch": "^4.0.10",
103
124
"@types/node": "^20.14.9",
104
125
"@types/pg": "^8.16.0",
105
126
"@types/react": "^18.3.3",
117
138
},
118
139
},
119
140
"packages": {
141
+ "@acemir/cssom": ["@acemir/[email protected]", "", {}, "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA=="],
142
+
120
143
"@alloc/quick-lru": ["@alloc/[email protected]", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
121
144
145
+ "@asamuzakjp/css-color": ["@asamuzakjp/[email protected]", "", { "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-color-parser": "^3.1.0", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", "lru-cache": "^11.2.4" } }, "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ=="],
146
+
147
+ "@asamuzakjp/dom-selector": ["@asamuzakjp/[email protected]", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.2.4" } }, "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg=="],
148
+
149
+ "@asamuzakjp/nwsapi": ["@asamuzakjp/[email protected]", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="],
150
+
122
151
"@astrojs/check": ["@astrojs/[email protected]", "", { "dependencies": { "@astrojs/language-server": "^2.16.1", "chokidar": "^4.0.1", "kleur": "^4.1.5", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": "^5.0.0" }, "bin": { "astro-check": "bin/astro-check.js" } }, "sha512-jlaEu5SxvSgmfGIFfNgcn5/f+29H61NJzEMfAZ82Xopr4XBchXB1GVlcJsE+elUlsYSbXlptZLX+JMG3b/wZEA=="],
123
152
124
153
"@astrojs/compiler": ["@astrojs/[email protected]", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="],
181
210
182
211
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.948.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-MvYQlXVoJyfF3/SmnNzOVEtANRAiJIObEUYYyjTqKZTmcRIVVky0tPuG26XnB8LmTYgtESwJIZJj/Eyyc9WURQ=="],
183
212
213
+ "@aws-sdk/lib-storage": ["@aws-sdk/[email protected]", "", { "dependencies": { "@smithy/abort-controller": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.10", "@smithy/smithy-client": "^4.10.11", "buffer": "5.6.0", "events": "3.3.0", "stream-browserify": "3.0.0", "tslib": "^2.6.2" }, "peerDependencies": { "@aws-sdk/client-s3": "3.974.0" } }, "sha512-6okXc+jsVUszk04A+Xx01J/ThygiisBK0Y2sE8T+sPLKKSplnr8VAusud3Fn8w9D9RmYBEWFx8E6Mg+vLZ5J0A=="],
214
+
184
215
"@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/util-arn-parser": "3.893.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-XLSVVfAorUxZh6dzF+HTOp4R1B5EQcdpGcPliWr0KUj2jukgjZEcqbBmjyMF/p9bmyQsONX80iURF1HLAlW0qg=="],
185
216
186
217
"@aws-sdk/middleware-expect-continue": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Eb4ELAC23bEQLJmUMYnPWcjD3FZIsmz2svDiXEcxRkQU9r7NRID7pM7C5NPH94wOfiCk0b2Y8rVyFXW0lGQwbA=="],
231
262
232
263
"@axosoft/nan": ["@axosoft/[email protected]", "", {}, "sha512-C4xrZ5HQoWwoq/WZnKDhf/Jd/czIaa0gsjHV792qUp7b7H+4Xtw1Um10BiiU/zrgmGhQuA92dedU2/rGvKErNg=="],
233
264
265
+ "@azure/abort-controller": ["@azure/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
266
+
267
+ "@azure/core-auth": ["@azure/[email protected]", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" } }, "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg=="],
268
+
269
+ "@azure/core-client": ["@azure/[email protected]", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "tslib": "^2.6.2" } }, "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w=="],
270
+
271
+ "@azure/core-http-compat": ["@azure/[email protected]", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-client": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0" } }, "sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g=="],
272
+
273
+ "@azure/core-lro": ["@azure/[email protected]", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-util": "^1.2.0", "@azure/logger": "^1.0.0", "tslib": "^2.6.2" } }, "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw=="],
274
+
275
+ "@azure/core-paging": ["@azure/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA=="],
276
+
277
+ "@azure/core-rest-pipeline": ["@azure/[email protected]", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg=="],
278
+
279
+ "@azure/core-tracing": ["@azure/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="],
280
+
281
+ "@azure/core-util": ["@azure/[email protected]", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A=="],
282
+
283
+ "@azure/core-xml": ["@azure/[email protected]", "", { "dependencies": { "fast-xml-parser": "^5.0.7", "tslib": "^2.8.1" } }, "sha512-D/sdlJBMJfx7gqoj66PKVmhDDaU6TKA49ptcolxdas29X7AfvLTmfAGLjAcIMBK7UZ2o4lygHIqVckOlQU3xWw=="],
284
+
285
+ "@azure/logger": ["@azure/[email protected]", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
286
+
287
+ "@azure/storage-blob": ["@azure/[email protected]", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.3", "@azure/core-http-compat": "^2.2.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.6.2", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/core-xml": "^1.4.5", "@azure/logger": "^1.1.4", "@azure/storage-common": "^12.2.0", "events": "^3.0.0", "tslib": "^2.8.1" } }, "sha512-peDCR8blSqhsAKDbpSP/o55S4sheNwSrblvCaHUZ5xUI73XA7ieUGGwrONgD/Fng0EoDe1VOa3fAQ7+WGB3Ocg=="],
288
+
289
+ "@azure/storage-common": ["@azure/[email protected]", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-http-compat": "^2.2.0", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.1.4", "events": "^3.3.0", "tslib": "^2.8.1" } }, "sha512-YZLxiJ3vBAAnFbG3TFuAMUlxZRexjQX5JDQxOkFGb6e2TpoxH3xyHI6idsMe/QrWtj41U/KoqBxlayzhS+LlwA=="],
290
+
234
291
"@babel/code-frame": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
235
292
236
293
"@babel/compat-data": ["@babel/[email protected]", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
279
336
280
337
"@balena/dockerignore": ["@balena/[email protected]", "", {}, "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="],
281
338
339
+ "@csstools/color-helpers": ["@csstools/[email protected]", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="],
340
+
341
+ "@csstools/css-calc": ["@csstools/[email protected]", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="],
342
+
343
+ "@csstools/css-color-parser": ["@csstools/[email protected]", "", { "dependencies": { "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA=="],
344
+
345
+ "@csstools/css-parser-algorithms": ["@csstools/[email protected]", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="],
346
+
347
+ "@csstools/css-syntax-patches-for-csstree": ["@csstools/[email protected]", "", {}, "sha512-g0Kw9W3vjx5BEBAF8c5Fm2NcB/Fs8jJXh85aXqwEXiL+tqtOut07TWgyaGzAAfTM+gKckrrncyeGEZPcaRgm2Q=="],
348
+
349
+ "@csstools/css-tokenizer": ["@csstools/[email protected]", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="],
350
+
282
351
"@dimforge/rapier3d-compat": ["@dimforge/[email protected]", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="],
283
352
353
+ "@dnd-kit/accessibility": ["@dnd-kit/[email protected]", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw=="],
354
+
355
+ "@dnd-kit/core": ["@dnd-kit/[email protected]", "", { "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ=="],
356
+
357
+ "@dnd-kit/sortable": ["@dnd-kit/[email protected]", "", { "dependencies": { "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@dnd-kit/core": "^6.3.0", "react": ">=16.8.0" } }, "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg=="],
358
+
359
+ "@dnd-kit/utilities": ["@dnd-kit/[email protected]", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="],
360
+
284
361
"@drizzle-team/brocli": ["@drizzle-team/[email protected]", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
285
362
286
363
"@emmetio/abbreviation": ["@emmetio/[email protected]", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="],
299
376
300
377
"@emnapi/runtime": ["@emnapi/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="],
301
378
379
+ "@envelop/core": ["@envelop/[email protected]", "", { "dependencies": { "@envelop/instrumentation": "^1.0.0", "@envelop/types": "^5.2.1", "@whatwg-node/promise-helpers": "^1.2.4", "tslib": "^2.5.0" } }, "sha512-nsU1EyJQAStaKHR1ZkB/ug9XBm+WPTliYtdedbJ/L1ykrp7dbbn0srqBeDnZ2mbZVp4hH3d0Fy+Og9OgPWZx+g=="],
380
+
381
+ "@envelop/instrumentation": ["@envelop/[email protected]", "", { "dependencies": { "@whatwg-node/promise-helpers": "^1.2.1", "tslib": "^2.5.0" } }, "sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw=="],
382
+
383
+ "@envelop/types": ["@envelop/[email protected]", "", { "dependencies": { "@whatwg-node/promise-helpers": "^1.0.0", "tslib": "^2.5.0" } }, "sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg=="],
384
+
302
385
"@esbuild-kit/core-utils": ["@esbuild-kit/[email protected]", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
303
386
304
387
"@esbuild-kit/esm-loader": ["@esbuild-kit/[email protected]", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
355
438
356
439
"@esbuild/win32-x64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
357
440
441
+ "@exodus/bytes": ["@exodus/[email protected]", "", { "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" }, "optionalPeers": ["@noble/hashes"] }, "sha512-lagqsvnk09NKogQaN/XrtlWeUF8SRhT12odMvbTIIaVObqzwAogL6jhR4DAp0gPuKoM1AOVrKUshJpRdpMFrww=="],
442
+
358
443
"@faker-js/faker": ["@faker-js/[email protected]", "", {}, "sha512-C3mrr3b5dRVlKPJdfrAXS8+dq+rq8Qm5SNRazca0JKgw1HQERFmrVb0towvMmw5uu8hHKNiQasMaR/tydf3Zsg=="],
359
444
445
+ "@fastify/busboy": ["@fastify/[email protected]", "", {}, "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA=="],
446
+
360
447
"@floating-ui/core": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
361
448
362
449
"@floating-ui/dom": ["@floating-ui/[email protected]", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="],
365
452
366
453
"@floating-ui/utils": ["@floating-ui/[email protected]", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
367
454
455
+ "@google-cloud/paginator": ["@google-cloud/[email protected]", "", { "dependencies": { "arrify": "^2.0.0", "extend": "^3.0.2" } }, "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg=="],
456
+
457
+ "@google-cloud/projectify": ["@google-cloud/[email protected]", "", {}, "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA=="],
458
+
459
+ "@google-cloud/promisify": ["@google-cloud/[email protected]", "", {}, "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g=="],
460
+
461
+ "@google-cloud/storage": ["@google-cloud/[email protected]", "", { "dependencies": { "@google-cloud/paginator": "^5.0.0", "@google-cloud/projectify": "^4.0.0", "@google-cloud/promisify": "<4.1.0", "abort-controller": "^3.0.0", "async-retry": "^1.3.3", "duplexify": "^4.1.3", "fast-xml-parser": "^4.4.1", "gaxios": "^6.0.2", "google-auth-library": "^9.6.3", "html-entities": "^2.5.2", "mime": "^3.0.0", "p-limit": "^3.0.1", "retry-request": "^7.0.0", "teeny-request": "^9.0.0", "uuid": "^8.0.0" } }, "sha512-r3ZwDMiz4nwW6R922Z1pwpePxyRwE5GdevYX63hRmAQUkUQJcBH/79EnQPDv5cOv1mFBgevdNWQfi3tie3dHrQ=="],
462
+
463
+ "@graphql-tools/executor": ["@graphql-tools/[email protected]", "", { "dependencies": { "@graphql-tools/utils": "^11.0.0", "@graphql-typed-document-node/core": "^3.2.0", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/promise-helpers": "^1.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-n94Qcu875Mji9GQ52n5UbgOTxlgvFJicBPYD+FRks9HKIQpdNPjkkrKZUYNG51XKa+bf03rxNflm4+wXhoHHrA=="],
464
+
465
+ "@graphql-tools/merge": ["@graphql-tools/[email protected]", "", { "dependencies": { "@graphql-tools/utils": "^11.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ=="],
466
+
467
+ "@graphql-tools/schema": ["@graphql-tools/[email protected]", "", { "dependencies": { "@graphql-tools/merge": "^9.1.7", "@graphql-tools/utils": "^11.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ=="],
468
+
469
+ "@graphql-tools/utils": ["@graphql-tools/[email protected]", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q=="],
470
+
471
+ "@graphql-typed-document-node/core": ["@graphql-typed-document-node/[email protected]", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
472
+
473
+ "@graphql-yoga/logger": ["@graphql-yoga/[email protected]", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-Nv0BoDGLMg9QBKy9cIswQ3/6aKaKjlTh87x3GiBg2Z4RrjyrM48DvOOK0pJh1C1At+b0mUIM67cwZcFTDLN4sA=="],
474
+
475
+ "@graphql-yoga/subscription": ["@graphql-yoga/[email protected]", "", { "dependencies": { "@graphql-yoga/typed-event-target": "^3.0.2", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/events": "^0.1.0", "tslib": "^2.8.1" } }, "sha512-oCMWOqFs6QV96/NZRt/ZhTQvzjkGB4YohBOpKM4jH/lDT4qb7Lex/aGCxpi/JD9njw3zBBtMqxbaC22+tFHVvw=="],
476
+
477
+ "@graphql-yoga/typed-event-target": ["@graphql-yoga/[email protected]", "", { "dependencies": { "@repeaterjs/repeater": "^3.0.4", "tslib": "^2.8.1" } }, "sha512-ZpJxMqB+Qfe3rp6uszCQoag4nSw42icURnBRfFYSOmTgEeOe4rD0vYlbA8spvCu2TlCesNTlEN9BLWtQqLxabA=="],
478
+
368
479
"@grpc/grpc-js": ["@grpc/[email protected]", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-QzVUtEFyu05UNx2xr0fCQmStUO17uVQhGNowtxs00IgTZT6/W2PBLfUkj30s0FKJ29VtTa3ArVNIhNP6akQhqA=="],
369
480
370
481
"@grpc/proto-loader": ["@grpc/[email protected]", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="],
407
518
408
519
"@img/sharp-win32-x64": ["@img/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
409
520
410
- "@ioredis/commands": ["@ioredis/[email protected]", "", {}, "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ=="],
521
+ "@ioredis/commands": ["@ioredis/[email protected]", "", {}, "sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow=="],
411
522
412
523
"@isaacs/cliui": ["@isaacs/[email protected]", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
413
524
643
754
644
755
"@opentelemetry/sql-common": ["@opentelemetry/[email protected]", "", { "dependencies": { "@opentelemetry/core": "^2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0" } }, "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ=="],
645
756
757
+ "@oslojs/asn1": ["@oslojs/[email protected]", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
758
+
759
+ "@oslojs/binary": ["@oslojs/[email protected]", "", {}, "sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ=="],
760
+
761
+ "@oslojs/crypto": ["@oslojs/[email protected]", "", { "dependencies": { "@oslojs/asn1": "1.0.0", "@oslojs/binary": "1.0.0" } }, "sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ=="],
762
+
646
763
"@oslojs/encoding": ["@oslojs/[email protected]", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="],
647
764
765
+ "@oslojs/jwt": ["@oslojs/[email protected]", "", { "dependencies": { "@oslojs/encoding": "0.4.1" } }, "sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg=="],
766
+
648
767
"@otplib/core": ["@otplib/[email protected]", "", {}, "sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA=="],
649
768
650
769
"@otplib/plugin-crypto": ["@otplib/[email protected]", "", { "dependencies": { "@otplib/core": "^12.0.1" } }, "sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g=="],
787
906
788
907
"@radix-ui/rect": ["@radix-ui/[email protected]", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
789
908
909
+ "@reduxjs/toolkit": ["@reduxjs/[email protected]", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="],
910
+
911
+ "@repeaterjs/repeater": ["@repeaterjs/[email protected]", "", {}, "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA=="],
912
+
790
913
"@rolldown/pluginutils": ["@rolldown/[email protected]", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
791
914
792
915
"@rollup/pluginutils": ["@rollup/[email protected]", "", { "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=="],
853
976
854
977
"@sindresorhus/is": ["@sindresorhus/[email protected]", "", {}, "sha512-rO92VvpgMc3kfiTjGT52LEtJ8Yc5kCWhZjLQ3LwlA4pSgPpQO7bVpYXParOD8Jwf+cVQECJo3yP/4I8aZtUQTQ=="],
855
978
856
- "@smithy/abort-controller": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA=="],
979
+ "@smithy/abort-controller": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw=="],
857
980
858
981
"@smithy/chunked-blob-reader": ["@smithy/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA=="],
859
982
957
1080
958
1081
"@standard-schema/spec": ["@standard-schema/[email protected]", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
959
1082
1083
+ "@standard-schema/utils": ["@standard-schema/[email protected]", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
1084
+
960
1085
"@tailwindcss/typography": ["@tailwindcss/[email protected]", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="],
961
1086
1087
+ "@tootallnate/once": ["@tootallnate/[email protected]", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="],
1088
+
962
1089
"@turf/boolean-point-in-polygon": ["@turf/[email protected]", "", { "dependencies": { "@turf/helpers": "7.3.1", "@turf/invariant": "7.3.1", "@types/geojson": "^7946.0.10", "point-in-polygon-hao": "^1.1.0", "tslib": "^2.8.1" } }, "sha512-BUPW63vE43LctwkgannjmEFTX1KFR/18SS7WzFahJWK1ZoP0s1jrfxGX+pi0BH/3Dd9mA71hkGKDDnj1Ndcz0g=="],
963
1090
964
1091
"@turf/helpers": ["@turf/[email protected]", "", { "dependencies": { "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-zkL34JVhi5XhsuMEO0MUTIIFEJ8yiW1InMu4hu/oRqamlY4mMoZql0viEmH6Dafh/p+zOl8OYvMJ3Vm3rFshgg=="],
981
1108
982
1109
"@types/better-sqlite3": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA=="],
983
1110
1111
+ "@types/braces": ["@types/[email protected]", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="],
1112
+
984
1113
"@types/bunyan": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ=="],
985
1114
1115
+ "@types/caseless": ["@types/[email protected]", "", {}, "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg=="],
1116
+
986
1117
"@types/chai": ["@types/[email protected]", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
987
1118
988
1119
"@types/connect": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="],
989
1120
990
1121
"@types/cookie": ["@types/[email protected]", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
991
1122
1123
+ "@types/d3-array": ["@types/[email protected]", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
1124
+
1125
+ "@types/d3-color": ["@types/[email protected]", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
1126
+
1127
+ "@types/d3-ease": ["@types/[email protected]", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
1128
+
1129
+ "@types/d3-interpolate": ["@types/[email protected]", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
1130
+
1131
+ "@types/d3-path": ["@types/[email protected]", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
1132
+
1133
+ "@types/d3-scale": ["@types/[email protected]", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
1134
+
1135
+ "@types/d3-shape": ["@types/[email protected]", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="],
1136
+
1137
+ "@types/d3-time": ["@types/[email protected]", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
1138
+
1139
+ "@types/d3-timer": ["@types/[email protected]", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
1140
+
992
1141
"@types/debug": ["@types/[email protected]", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
993
1142
994
1143
"@types/deep-eql": ["@types/[email protected]", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
1011
1160
1012
1161
"@types/memcached": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg=="],
1013
1162
1163
+ "@types/micromatch": ["@types/[email protected]", "", { "dependencies": { "@types/braces": "*" } }, "sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ=="],
1164
+
1014
1165
"@types/ms": ["@types/[email protected]", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
1015
1166
1016
1167
"@types/mysql": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA=="],
1029
1180
1030
1181
"@types/prop-types": ["@types/[email protected]", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
1031
1182
1183
+ "@types/qrcode": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw=="],
1184
+
1032
1185
"@types/react": ["@types/[email protected]", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w=="],
1033
1186
1034
1187
"@types/react-dom": ["@types/[email protected]", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="],
1035
1188
1189
+ "@types/request": ["@types/[email protected]", "", { "dependencies": { "@types/caseless": "*", "@types/node": "*", "@types/tough-cookie": "*", "form-data": "^2.5.5" } }, "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg=="],
1190
+
1036
1191
"@types/ssh2": ["@types/[email protected]", "", { "dependencies": { "@types/node": "^18.11.18" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="],
1037
1192
1038
1193
"@types/stats.js": ["@types/[email protected]", "", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="],
1041
1196
1042
1197
"@types/three": ["@types/[email protected]", "", { "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": ">=0.5.17", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.22.0" } }, "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q=="],
1043
1198
1199
+ "@types/tough-cookie": ["@types/[email protected]", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="],
1200
+
1201
+ "@types/trusted-types": ["@types/[email protected]", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
1202
+
1044
1203
"@types/unist": ["@types/[email protected]", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
1045
1204
1205
+ "@types/use-sync-external-store": ["@types/[email protected]", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="],
1206
+
1046
1207
"@types/webxr": ["@types/[email protected]", "", {}, "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg=="],
1047
1208
1048
1209
"@types/ws": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
1049
1210
1211
+ "@typespec/ts-http-runtime": ["@typespec/[email protected]", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg=="],
1212
+
1050
1213
"@ungap/structured-clone": ["@ungap/[email protected]", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
1051
1214
1052
1215
"@upstash/redis": ["@upstash/[email protected]", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-9zN2UV9QJGPnXfWU3yZBLVQaqqENDh7g+Y4J2vJuSxBCi9FQ0aUOtaXlzuFhnsiZvCqM+eS27ic+tgmkWUsfOg=="],
1091
1254
1092
1255
"@webgpu/types": ["@webgpu/[email protected]", "", {}, "sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA=="],
1093
1256
1257
+ "@whatwg-node/disposablestack": ["@whatwg-node/[email protected]", "", { "dependencies": { "@whatwg-node/promise-helpers": "^1.0.0", "tslib": "^2.6.3" } }, "sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw=="],
1258
+
1259
+ "@whatwg-node/events": ["@whatwg-node/[email protected]", "", { "dependencies": { "tslib": "^2.6.3" } }, "sha512-ApcWxkrs1WmEMS2CaLLFUEem/49erT3sxIVjpzU5f6zmVcnijtDSrhoK2zVobOIikZJdH63jdAXOrvjf6eOUNQ=="],
1260
+
1261
+ "@whatwg-node/fetch": ["@whatwg-node/[email protected]", "", { "dependencies": { "@whatwg-node/node-fetch": "^0.8.3", "urlpattern-polyfill": "^10.0.0" } }, "sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q=="],
1262
+
1263
+ "@whatwg-node/node-fetch": ["@whatwg-node/[email protected]", "", { "dependencies": { "@fastify/busboy": "^3.1.1", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/promise-helpers": "^1.3.2", "tslib": "^2.6.3" } }, "sha512-4xzCl/zphPqlp9tASLVeUhB5+WJHbuWGYpfoC2q1qh5dw0AqZBW7L27V5roxYWijPxj4sspRAAoOH3d2ztaHUQ=="],
1264
+
1265
+ "@whatwg-node/promise-helpers": ["@whatwg-node/[email protected]", "", { "dependencies": { "tslib": "^2.6.3" } }, "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA=="],
1266
+
1267
+ "@whatwg-node/server": ["@whatwg-node/[email protected]", "", { "dependencies": { "@envelop/instrumentation": "^1.0.0", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/fetch": "^0.10.13", "@whatwg-node/promise-helpers": "^1.3.2", "tslib": "^2.6.3" } }, "sha512-kMwLlxUbduttIgaPdSkmEarFpP+mSY8FEm+QWMBRJwxOHWkri+cxd8KZHO9EMrB9vgUuz+5WEaCawaL5wGVoXg=="],
1268
+
1094
1269
"abbrev": ["[email protected]", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="],
1095
1270
1096
1271
"abort-controller": ["[email protected]", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
1117
1292
1118
1293
"anymatch": ["[email protected]", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
1119
1294
1295
+ "arctic": ["[email protected]", "", { "dependencies": { "@oslojs/crypto": "1.0.1", "@oslojs/encoding": "1.1.0", "@oslojs/jwt": "0.2.0" } }, "sha512-ZMQ+f6VazDgUJOd+qNV+H7GohNSYal1mVjm5kEaZfE2Ifb7Ss70w+Q7xpJC87qZDkMZIXYf0pTIYZA0OPasSbw=="],
1296
+
1120
1297
"arg": ["[email protected]", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
1121
1298
1122
1299
"argparse": ["[email protected]", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
1127
1304
1128
1305
"array-iterate": ["[email protected]", "", {}, "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg=="],
1129
1306
1307
+ "arrify": ["[email protected]", "", {}, "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="],
1308
+
1130
1309
"asn1": ["[email protected]", "", { "dependencies": { "safer-buffer": "~2.1.0" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="],
1131
1310
1132
1311
"assertion-error": ["[email protected]", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
1135
1314
1136
1315
"async-lock": ["[email protected]", "", {}, "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ=="],
1137
1316
1317
+ "async-retry": ["[email protected]", "", { "dependencies": { "retry": "0.13.1" } }, "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw=="],
1318
+
1138
1319
"async-sema": ["[email protected]", "", {}, "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg=="],
1139
1320
1321
+ "asynckit": ["[email protected]", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
1322
+
1140
1323
"atomic-sleep": ["[email protected]", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
1141
1324
1142
1325
"autoprefixer": ["[email protected]", "", { "dependencies": { "browserslist": "^4.27.0", "caniuse-lite": "^1.0.30001754", "fraction.js": "^5.3.4", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg=="],
1171
1354
1172
1355
"baseline-browser-mapping": ["[email protected]", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg=="],
1173
1356
1357
+ "basic-ftp": ["[email protected]", "", {}, "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw=="],
1358
+
1174
1359
"bcrypt-pbkdf": ["[email protected]", "", { "dependencies": { "tweetnacl": "^0.14.3" } }, "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w=="],
1175
1360
1176
1361
"bcryptjs": ["[email protected]", "", {}, "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="],
1177
1362
1178
1363
"better-sqlite3": ["[email protected]", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ=="],
1179
1364
1365
+ "bidi-js": ["[email protected]", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="],
1366
+
1180
1367
"bignumber.js": ["[email protected]", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
1181
1368
1182
1369
"binary-extensions": ["[email protected]", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
1197
1384
1198
1385
"browserslist": ["[email protected]", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
1199
1386
1200
- "buffer": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
1387
+ "buffer": ["[email protected]", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" } }, "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw=="],
1201
1388
1202
1389
"buffer-equal-constant-time": ["[email protected]", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
1203
1390
1277
1464
1278
1465
"colorette": ["[email protected]", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
1279
1466
1467
+ "combined-stream": ["[email protected]", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
1468
+
1280
1469
"comma-separated-tokens": ["[email protected]", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
1281
1470
1282
1471
"commander": ["[email protected]", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
1297
1486
1298
1487
"cron-parser": ["[email protected]", "", { "dependencies": { "luxon": "^3.2.1" } }, "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q=="],
1299
1488
1489
+ "cross-inspect": ["[email protected]", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A=="],
1490
+
1300
1491
"cross-spawn": ["[email protected]", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
1301
1492
1493
+ "css-tree": ["[email protected]", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="],
1494
+
1302
1495
"cssesc": ["[email protected]", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
1303
1496
1497
+ "cssstyle": ["[email protected]", "", { "dependencies": { "@asamuzakjp/css-color": "^4.1.1", "@csstools/css-syntax-patches-for-csstree": "^1.0.21", "css-tree": "^3.1.0", "lru-cache": "^11.2.4" } }, "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ=="],
1498
+
1304
1499
"csstype": ["[email protected]", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
1305
1500
1306
1501
"d3-array": ["[email protected]", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
1309
1504
1310
1505
"d3-delaunay": ["[email protected]", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="],
1311
1506
1507
+ "d3-ease": ["[email protected]", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
1508
+
1312
1509
"d3-format": ["[email protected]", "", {}, "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="],
1313
1510
1314
1511
"d3-geo": ["[email protected]", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="],
1319
1516
1320
1517
"d3-octree": ["[email protected]", "", {}, "sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A=="],
1321
1518
1519
+ "d3-path": ["[email protected]", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
1520
+
1322
1521
"d3-scale": ["[email protected]", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
1323
1522
1324
1523
"d3-scale-chromatic": ["[email protected]", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="],
1325
1524
1326
1525
"d3-selection": ["[email protected]", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="],
1327
1526
1527
+ "d3-shape": ["[email protected]", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
1528
+
1328
1529
"d3-time": ["[email protected]", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
1329
1530
1330
1531
"d3-time-format": ["[email protected]", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
1331
1532
1533
+ "d3-timer": ["[email protected]", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
1534
+
1332
1535
"d3-tricontour": ["[email protected]", "", { "dependencies": { "d3-delaunay": "6", "d3-scale": "4" } }, "sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ=="],
1333
1536
1334
1537
"data-bind-mapper": ["[email protected]", "", { "dependencies": { "accessor-fn": "1" } }, "sha512-QmU3lyEnbENQPo0M1F9BMu4s6cqNNp8iJA+b/HP2sSb7pf3dxwF3+EP1eO69rwBfH9kFJ1apmzrtogAmVt2/Xw=="],
1335
1538
1336
1539
"data-uri-to-buffer": ["[email protected]", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
1337
1540
1541
+ "data-urls": ["[email protected]", "", { "dependencies": { "whatwg-mimetype": "^5.0.0", "whatwg-url": "^15.1.0" } }, "sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ=="],
1542
+
1543
+ "date-fns": ["[email protected]", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="],
1544
+
1338
1545
"dateformat": ["[email protected]", "", {}, "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="],
1339
1546
1340
1547
"debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
1341
1548
1549
+ "decamelize": ["[email protected]", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
1550
+
1551
+ "decimal.js": ["[email protected]", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="],
1552
+
1553
+ "decimal.js-light": ["[email protected]", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="],
1554
+
1342
1555
"decode-named-character-reference": ["[email protected]", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
1343
1556
1344
1557
"decompress-response": ["[email protected]", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
1349
1562
1350
1563
"delaunator": ["[email protected]", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="],
1351
1564
1565
+ "delayed-stream": ["[email protected]", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
1566
+
1352
1567
"denque": ["[email protected]", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
1353
1568
1354
1569
"depd": ["[email protected]", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
1373
1588
1374
1589
"diff3": ["[email protected]", "", {}, "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g=="],
1375
1590
1591
+ "dijkstrajs": ["[email protected]", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="],
1592
+
1376
1593
"dlv": ["[email protected]", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
1377
1594
1378
1595
"docker-modem": ["[email protected]", "", { "dependencies": { "debug": "^4.1.1", "readable-stream": "^3.5.0", "split-ca": "^1.0.1", "ssh2": "^1.15.0" } }, "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ=="],
1379
1596
1380
1597
"dockerode": ["[email protected]", "", { "dependencies": { "@balena/dockerignore": "^1.0.2", "@grpc/grpc-js": "^1.11.1", "@grpc/proto-loader": "^0.7.13", "docker-modem": "^5.0.6", "protobufjs": "^7.3.2", "tar-fs": "^2.1.4", "uuid": "^10.0.0" } }, "sha512-iND4mcOWhPaCNh54WmK/KoSb35AFqPAUWFMffTQcp52uQt36b5uNwEJTSXntJZBbeGad72Crbi/hvDIv6us/6Q=="],
1381
1598
1599
+ "dompurify": ["[email protected]", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q=="],
1600
+
1382
1601
"drizzle-kit": ["[email protected]", "", { "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-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg=="],
1383
1602
1384
1603
"drizzle-orm": ["[email protected]", "", { "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-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA=="],
1387
1606
1388
1607
"dunder-proto": ["[email protected]", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
1389
1608
1609
+ "duplexify": ["[email protected]", "", { "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", "stream-shift": "^1.0.2" } }, "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA=="],
1610
+
1390
1611
"earcut": ["[email protected]", "", {}, "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ=="],
1391
1612
1392
1613
"eastasianwidth": ["[email protected]", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
1423
1644
1424
1645
"es-object-atoms": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
1425
1646
1647
+ "es-set-tostringtag": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
1648
+
1649
+ "es-toolkit": ["[email protected]", "", {}, "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg=="],
1650
+
1426
1651
"esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
1427
1652
1428
1653
"esbuild-register": ["[email protected]", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
1469
1694
1470
1695
"fast-uri": ["[email protected]", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
1471
1696
1472
- "fast-xml-parser": ["[email protected]", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
1697
+ "fast-xml-parser": ["[email protected]", "", { "dependencies": { "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig=="],
1473
1698
1474
1699
"fastq": ["[email protected]", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
1475
1700
1497
1722
1498
1723
"foreground-child": ["[email protected]", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
1499
1724
1725
+ "form-data": ["[email protected]", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" } }, "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A=="],
1726
+
1500
1727
"form-data-encoder": ["[email protected]", "", {}, "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw=="],
1501
1728
1502
1729
"formdata-polyfill": ["[email protected]", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
1523
1750
1524
1751
"function-bind": ["[email protected]", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
1525
1752
1526
- "gaxios": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
1753
+ "gaxios": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
1527
1754
1528
1755
"gcp-metadata": ["[email protected]", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="],
1529
1756
1557
1784
1558
1785
"globe.gl": ["[email protected]", "", { "dependencies": { "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", "kapsule": "^1.16", "three": ">=0.154 <1", "three-globe": "^2.45", "three-render-objects": "^1.40" } }, "sha512-fjkLHVBrnbESkUgklTd4UbcGLciu4nIl49IIi1hclLjI6MU3ASu6JYmf/K5qwPf7I+tNOauQRr4i5Y28JTtHQg=="],
1559
1786
1560
- "google-auth-library": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="],
1787
+ "google-auth-library": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^6.1.1", "gcp-metadata": "^6.1.0", "gtoken": "^7.0.0", "jws": "^4.0.0" } }, "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng=="],
1561
1788
1562
1789
"google-logging-utils": ["[email protected]", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
1563
1790
1571
1798
1572
1799
"graceful-fs": ["[email protected]", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
1573
1800
1801
+ "graphql": ["[email protected]", "", {}, "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ=="],
1802
+
1803
+ "graphql-yoga": ["[email protected]", "", { "dependencies": { "@envelop/core": "^5.3.0", "@envelop/instrumentation": "^1.0.0", "@graphql-tools/executor": "^1.5.0", "@graphql-tools/schema": "^10.0.11", "@graphql-tools/utils": "^10.11.0", "@graphql-yoga/logger": "^2.0.1", "@graphql-yoga/subscription": "^5.0.5", "@whatwg-node/fetch": "^0.10.6", "@whatwg-node/promise-helpers": "^1.3.2", "@whatwg-node/server": "^0.10.14", "lru-cache": "^10.0.0", "tslib": "^2.8.1" }, "peerDependencies": { "graphql": "^15.2.0 || ^16.0.0" } }, "sha512-xFt1DVXS1BZ3AvjnawAGc5OYieSe56WuQuyk3iEpBwJ3QDZJWQGLmU9z/L5NUZ+pUcyprsz/bOwkYIV96fXt/g=="],
1804
+
1574
1805
"gray-matter": ["[email protected]", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
1575
1806
1576
- "gtoken": ["[email protected]", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
1807
+ "gtoken": ["[email protected]", "", { "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw=="],
1577
1808
1578
1809
"h3-js": ["[email protected]", "", {}, "sha512-DvJh07MhGgY2KcC4OeZc8SSyA+ZXpdvoh6uCzGpoKvWtZxJB+g6VXXC1+eWYkaMIsLz7J/ErhOalHCpcs1KYog=="],
1579
1810
1609
1840
1610
1841
"help-me": ["[email protected]", "", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="],
1611
1842
1843
+ "html-encoding-sniffer": ["[email protected]", "", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="],
1844
+
1845
+ "html-entities": ["[email protected]", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="],
1846
+
1612
1847
"html-escaper": ["[email protected]", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="],
1613
1848
1614
1849
"html-void-elements": ["[email protected]", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
1629
1864
1630
1865
"ignore": ["[email protected]", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
1631
1866
1867
+ "immer": ["[email protected]", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="],
1868
+
1632
1869
"import-in-the-middle": ["[email protected]", "", { "dependencies": { "acorn": "^8.14.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } }, "sha512-yNZhyQYqXpkT0AKq3F3KLasUSK4fHvebNH5hOsKQw2dhGSALvQ4U0BqUc5suziKvydO5u5hgN2hy1RJaho8U5A=="],
1633
1870
1634
1871
"import-meta-resolve": ["[email protected]", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="],
1645
1882
1646
1883
"internmap": ["[email protected]", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
1647
1884
1648
- "ioredis": ["[email protected]", "", { "dependencies": { "@ioredis/commands": "1.4.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q=="],
1885
+ "ioredis": ["[email protected]", "", { "dependencies": { "@ioredis/commands": "1.5.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-tAAg/72/VxOUW7RQSX1pIxJVucYKcjFjfvj60L57jrZpYCHC3XN0WCQ3sNYL4Gmvv+7GPvTAjc+KSdeNuE8oWQ=="],
1649
1886
1650
1887
"ip-address": ["[email protected]", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
1651
1888
1675
1912
1676
1913
"is-plain-obj": ["[email protected]", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
1677
1914
1915
+ "is-potential-custom-element-name": ["[email protected]", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="],
1916
+
1678
1917
"is-property": ["[email protected]", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
1679
1918
1680
1919
"is-stream": ["[email protected]", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
1689
1928
1690
1929
"isexe": ["[email protected]", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
1691
1930
1931
+ "isomorphic-dompurify": ["[email protected]", "", { "dependencies": { "dompurify": "^3.3.1", "jsdom": "^27.4.0" } }, "sha512-a9+LQqylQCU8f1zmsYmg2tfrbdY2YS/Hc+xntcq/mDI2MY3Q108nq8K23BWDIg6YGC5JsUMC15fj2ZMqCzt/+A=="],
1932
+
1692
1933
"isomorphic-git": ["[email protected]", "", { "dependencies": { "async-lock": "^1.4.1", "clean-git-ref": "^2.0.1", "crc-32": "^1.2.0", "diff3": "0.0.3", "ignore": "^5.1.4", "minimisted": "^2.0.0", "pako": "^1.0.10", "pify": "^4.0.1", "readable-stream": "^4.0.0", "sha.js": "^2.4.12", "simple-get": "^4.0.1" }, "bin": { "isogit": "cli.cjs" } }, "sha512-22tU165ptowHYoDEwYJy5EKRzpHiuLMliaR01fH9ZwaUj1z/IqE++tGpjw/pD6eCWoxiOp6TPWX434aJ9zA4Lg=="],
1693
1934
1694
1935
"jackspeak": ["[email protected]", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
1707
1948
1708
1949
"js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
1709
1950
1951
+ "jsdom": ["[email protected]", "", { "dependencies": { "@acemir/cssom": "^0.9.28", "@asamuzakjp/dom-selector": "^6.7.6", "@exodus/bytes": "^1.6.0", "cssstyle": "^5.3.4", "data-urls": "^6.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.0", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^15.1.0", "ws": "^8.18.3", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ=="],
1952
+
1710
1953
"jsesc": ["[email protected]", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
1711
1954
1712
1955
"json-bigint": ["[email protected]", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
1761
2004
1762
2005
"lowercase-keys": ["[email protected]", "", {}, "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="],
1763
2006
1764
- "lru-cache": ["[email protected]", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
2007
+ "lru-cache": ["[email protected]", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
1765
2008
1766
2009
"lru.min": ["[email protected]", "", {}, "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q=="],
1767
2010
1807
2050
1808
2051
"mdast-util-to-string": ["[email protected]", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
1809
2052
2053
+ "mdn-data": ["[email protected]", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="],
2054
+
1810
2055
"merge2": ["[email protected]", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
1811
2056
1812
2057
"meshoptimizer": ["[email protected]", "", {}, "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg=="],
1869
2114
1870
2115
"micromatch": ["[email protected]", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
1871
2116
1872
- "mime": ["[email protected]", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
2117
+ "mime": ["[email protected]", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
2118
+
2119
+ "mime-db": ["[email protected]", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
2120
+
2121
+ "mime-types": ["[email protected]", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
1873
2122
1874
2123
"mimic-function": ["[email protected]", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
1875
2124
1939
2188
1940
2189
"node-domexception": ["[email protected]", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
1941
2190
1942
- "node-fetch": ["[email protected]", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
2191
+ "node-fetch": ["[email protected]", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
1943
2192
1944
2193
"node-gyp": ["[email protected]", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^14.0.3", "nopt": "^8.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "tar": "^7.4.3", "tinyglobby": "^0.2.12", "which": "^5.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ=="],
1945
2194
1961
2210
1962
2211
"normalize-url": ["[email protected]", "", {}, "sha512-X06Mfd/5aKsRHc0O0J5CUedwnPmnDtLF2+nq+KN9KSDlJHkPuh0JUviWjEWMe0SW/9TDdSLVPuk7L5gGTIA1/w=="],
1963
2212
2213
+ "oauth4webapi": ["[email protected]", "", {}, "sha512-pQ5BsX3QRTgnt5HxgHwgunIRaDXBdkT23tf8dfzmtTIL2LTpdmxgbpbBm0VgFWAIDlezQvQCTgnVIUmHupXHxw=="],
2214
+
1964
2215
"object-assign": ["[email protected]", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
1965
2216
1966
2217
"object-hash": ["[email protected]", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
1979
2230
1980
2231
"oniguruma-to-es": ["[email protected]", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
1981
2232
2233
+ "openai": ["[email protected]", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-fZ1uBqjFUjXzbGc35fFtYKEOxd20kd9fDpFeqWtsOZWiubY8CZ1NAlXHW3iathaFvqmNtCWMIsosCuyeI7Joxg=="],
2234
+
2235
+ "openid-client": ["[email protected]", "", { "dependencies": { "jose": "^6.1.0", "oauth4webapi": "^3.8.2" } }, "sha512-VoYT6enBo6Vj2j3Q5Ec0AezS+9YGzQo1f5Xc42lreMGlfP4ljiXPKVDvCADh+XHCV/bqPu/wWSiCVXbJKvrODw=="],
2236
+
1982
2237
"openpgp": ["[email protected]", "", {}, "sha512-pLzCU8IgyKXPSO11eeharQkQ4GzOKNWhXq79pQarIRZEMt1/ssyr+MIuWBv1mNoenJLg04gvPx+fi4gcKZ4bag=="],
1983
2238
1984
2239
"ora": ["[email protected]", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="],
1987
2242
1988
2243
"p-cancelable": ["[email protected]", "", {}, "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg=="],
1989
2244
1990
- "p-limit": ["[email protected]", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="],
2245
+ "p-limit": ["[email protected]", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
1991
2246
1992
2247
"p-locate": ["[email protected]", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
1993
2248
2005
2260
2006
2261
"parse-latin": ["[email protected]", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="],
2007
2262
2008
- "parse5": ["[email protected]", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
2263
+ "parse5": ["[email protected]", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="],
2009
2264
2010
2265
"path-browserify": ["[email protected]", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
2011
2266
2063
2318
2064
2319
"playwright-core": ["[email protected]", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ=="],
2065
2320
2321
+ "pngjs": ["[email protected]", "", {}, "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="],
2322
+
2066
2323
"point-in-polygon-hao": ["[email protected]", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ=="],
2067
2324
2068
2325
"polished": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.17.8" } }, "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA=="],
2125
2382
2126
2383
"pump": ["[email protected]", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
2127
2384
2385
+ "punycode": ["[email protected]", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
2386
+
2387
+ "qrcode": ["[email protected]", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
2388
+
2128
2389
"qs": ["[email protected]", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
2129
2390
2130
2391
"queue-microtask": ["[email protected]", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
2147
2408
2148
2409
"react-kapsule": ["[email protected]", "", { "dependencies": { "jerrypick": "^1.1.1" }, "peerDependencies": { "react": ">=16.13.1" } }, "sha512-kifAF4ZPD77qZKc4CKLmozq6GY1sBzPEJTIJb0wWFK6HsePJatK3jXplZn2eeAt3x67CDozgi7/rO8fNQ/AL7A=="],
2149
2410
2411
+ "react-redux": ["[email protected]", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="],
2412
+
2150
2413
"react-refresh": ["[email protected]", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
2151
2414
2152
2415
"react-remove-scroll": ["[email protected]", "", { "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-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
2163
2426
2164
2427
"real-require": ["[email protected]", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
2165
2428
2429
+ "recharts": ["[email protected]", "", { "dependencies": { "@reduxjs/toolkit": "1.x.x || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-l2VCsy3XXeraxIID9fx23eCb6iCBsxUQDnE8tWm6DFdszVAO7WVY/ChAD9wVit01y6B2PMupYiMmQwhgPHc9Ew=="],
2430
+
2166
2431
"redis-errors": ["[email protected]", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="],
2167
2432
2168
2433
"redis-parser": ["[email protected]", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="],
2169
2434
2435
+ "redux": ["[email protected]", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="],
2436
+
2437
+ "redux-thunk": ["[email protected]", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="],
2438
+
2170
2439
"regex": ["[email protected]", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="],
2171
2440
2172
2441
"regex-recursion": ["[email protected]", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="],
2201
2470
2202
2471
"require-in-the-middle": ["[email protected]", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3" } }, "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ=="],
2203
2472
2473
+ "require-main-filename": ["[email protected]", "", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="],
2474
+
2475
+ "reselect": ["[email protected]", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="],
2476
+
2204
2477
"resolve": ["[email protected]", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
2205
2478
2206
2479
"resolve-alpn": ["[email protected]", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="],
2221
2494
2222
2495
"retext-stringify": ["[email protected]", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" } }, "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA=="],
2223
2496
2224
- "retry": ["[email protected]", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
2497
+ "retry": ["[email protected]", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
2498
+
2499
+ "retry-request": ["[email protected]", "", { "dependencies": { "@types/request": "^2.48.8", "extend": "^3.0.2", "teeny-request": "^9.0.0" } }, "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w=="],
2225
2500
2226
2501
"reusify": ["[email protected]", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
2227
2502
2239
2514
2240
2515
"safer-buffer": ["[email protected]", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
2241
2516
2517
+ "saxes": ["[email protected]", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="],
2518
+
2242
2519
"scheduler": ["[email protected]", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
2243
2520
2244
2521
"section-matter": ["[email protected]", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="],
2253
2530
2254
2531
"server-destroy": ["[email protected]", "", {}, "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="],
2255
2532
2533
+ "set-blocking": ["[email protected]", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
2534
+
2256
2535
"set-function-length": ["[email protected]", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
2257
2536
2258
2537
"setprototypeof": ["[email protected]", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
2299
2578
2300
2579
"sonic-boom": ["[email protected]", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww=="],
2301
2580
2581
+ "sonner": ["[email protected]", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="],
2582
+
2302
2583
"source-map": ["[email protected]", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
2303
2584
2304
2585
"source-map-js": ["[email protected]", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
2331
2612
2332
2613
"stdin-discarder": ["[email protected]", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="],
2333
2614
2615
+ "stream-browserify": ["[email protected]", "", { "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" } }, "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA=="],
2616
+
2617
+ "stream-events": ["[email protected]", "", { "dependencies": { "stubs": "^3.0.0" } }, "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg=="],
2618
+
2619
+ "stream-shift": ["[email protected]", "", {}, "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="],
2620
+
2334
2621
"streamx": ["[email protected]", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="],
2335
2622
2336
2623
"string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
2351
2638
2352
2639
"strip-json-comments": ["[email protected]", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="],
2353
2640
2354
- "strnum": ["[email protected]", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
2641
+ "strnum": ["[email protected]", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="],
2642
+
2643
+ "stubs": ["[email protected]", "", {}, "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw=="],
2355
2644
2356
2645
"sucrase": ["[email protected]", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
2357
2646
2358
2647
"supports-preserve-symlinks-flag": ["[email protected]", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
2359
2648
2649
+ "symbol-tree": ["[email protected]", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
2650
+
2360
2651
"tailwind-merge": ["[email protected]", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="],
2361
2652
2362
2653
"tailwindcss": ["[email protected]", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="],
2371
2662
2372
2663
"tdigest": ["[email protected]", "", { "dependencies": { "bintrees": "1.0.2" } }, "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA=="],
2373
2664
2665
+ "teeny-request": ["[email protected]", "", { "dependencies": { "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "node-fetch": "^2.6.9", "stream-events": "^1.0.5", "uuid": "^9.0.0" } }, "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g=="],
2666
+
2374
2667
"text-decoder": ["[email protected]", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="],
2375
2668
2376
2669
"thenify": ["[email protected]", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
2393
2686
2394
2687
"three-slippy-map-globe": ["[email protected]", "", { "dependencies": { "d3-geo": "1 - 3", "d3-octree": "^1.1", "d3-scale": "1 - 4" }, "peerDependencies": { "three": ">=0.154" } }, "sha512-am8A4PP38AfTdrhXBDucwPRHLTbBl93yhpjIs56K1TLs9VuUWzg68oim4Dibs9QC1riXbj5SoBp/okA1VN9eYg=="],
2395
2688
2689
+ "tiny-invariant": ["[email protected]", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
2690
+
2396
2691
"tinybench": ["[email protected]", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
2397
2692
2398
2693
"tinycolor2": ["[email protected]", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
2403
2698
2404
2699
"tinyrainbow": ["[email protected]", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="],
2405
2700
2701
+ "tldts": ["[email protected]", "", { "dependencies": { "tldts-core": "^7.0.19" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA=="],
2702
+
2703
+ "tldts-core": ["[email protected]", "", {}, "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A=="],
2704
+
2406
2705
"to-buffer": ["[email protected]", "", { "dependencies": { "isarray": "^2.0.5", "safe-buffer": "^5.2.1", "typed-array-buffer": "^1.0.3" } }, "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw=="],
2407
2706
2408
2707
"to-regex-range": ["[email protected]", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
2409
2708
2410
2709
"toidentifier": ["[email protected]", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
2411
2710
2412
- "tr46": ["[email protected]", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
2711
+ "tough-cookie": ["[email protected]", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="],
2712
+
2713
+ "tr46": ["[email protected]", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="],
2413
2714
2414
2715
"trim-lines": ["[email protected]", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
2415
2716
2459
2760
2460
2761
"unist-util-stringify-position": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
2461
2762
2462
- "unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
2763
+ "unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
2463
2764
2464
2765
"unist-util-visit-children": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA=="],
2465
2766
2471
2772
2472
2773
"url-template": ["[email protected]", "", {}, "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="],
2473
2774
2775
+ "urlpattern-polyfill": ["[email protected]", "", {}, "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw=="],
2776
+
2474
2777
"use-callback-ref": ["[email protected]", "", { "dependencies": { "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-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
2475
2778
2476
2779
"use-sidecar": ["[email protected]", "", { "dependencies": { "detect-node-es": "^1.1.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-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
2479
2782
2480
2783
"util-deprecate": ["[email protected]", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
2481
2784
2482
- "uuid": ["[email protected]", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
2785
+ "uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
2483
2786
2484
2787
"vfile": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
2485
2788
2487
2790
2488
2791
"vfile-message": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
2489
2792
2793
+ "victory-vendor": ["[email protected]", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="],
2794
+
2490
2795
"vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ=="],
2491
2796
2492
2797
"vitefu": ["[email protected]", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
2527
2832
2528
2833
"vscode-uri": ["[email protected]", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
2529
2834
2835
+ "w3c-xmlserializer": ["[email protected]", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
2836
+
2530
2837
"web-namespaces": ["[email protected]", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
2531
2838
2532
2839
"web-streams-polyfill": ["[email protected]", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
2533
2840
2534
2841
"web-vitals": ["[email protected]", "", {}, "sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg=="],
2535
2842
2536
- "webidl-conversions": ["[email protected]", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
2843
+ "webidl-conversions": ["[email protected]", "", {}, "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ=="],
2537
2844
2538
- "whatwg-url": ["[email protected]", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
2845
+ "whatwg-mimetype": ["[email protected]", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
2846
+
2847
+ "whatwg-url": ["[email protected]", "", { "dependencies": { "tr46": "^6.0.0", "webidl-conversions": "^8.0.0" } }, "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g=="],
2539
2848
2540
2849
"which": ["[email protected]", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="],
2541
2850
2851
+ "which-module": ["[email protected]", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="],
2852
+
2542
2853
"which-pm": ["[email protected]", "", { "dependencies": { "load-yaml-file": "^0.2.0" } }, "sha512-v2JrMq0waAI4ju1xU5x3blsxBBMgdgZve580iYMN5frDaLGjbA24fok7wKCsya8KLVO19Ju4XDc5+zTZCJkQfg=="],
2543
2854
2544
2855
"which-pm-runs": ["[email protected]", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="],
2557
2868
2558
2869
"ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
2559
2870
2871
+ "xml-name-validator": ["[email protected]", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="],
2872
+
2873
+ "xmlchars": ["[email protected]", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="],
2874
+
2560
2875
"xtend": ["[email protected]", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
2561
2876
2562
2877
"xxhash-wasm": ["[email protected]", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
2573
2888
2574
2889
"yargs-parser": ["[email protected]", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
2575
2890
2576
- "yocto-queue": ["[email protected]", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
2891
+ "yocto-queue": ["[email protected]", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
2577
2892
2578
2893
"zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
2579
2894
2585
2900
2586
2901
"zwitch": ["[email protected]", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
2587
2902
2903
+ "@asamuzakjp/css-color/lru-cache": ["[email protected]", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="],
2904
+
2905
+ "@asamuzakjp/dom-selector/lru-cache": ["[email protected]", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="],
2906
+
2588
2907
"@astrojs/check/chokidar": ["[email protected]", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
2589
2908
2909
+ "@astrojs/markdown-remark/unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
2910
+
2590
2911
"@astrojs/react/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
2591
2912
2592
2913
"@astrojs/tailwind/postcss-load-config": ["[email protected]", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="],
2665
2986
2666
2987
"@aws-sdk/client-sesv2/@smithy/util-retry": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg=="],
2667
2988
2989
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/core": "^3.21.1", "@smithy/middleware-serde": "^4.2.9", "@smithy/node-config-provider": "^4.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-/WqsrycweGGfb9sSzME4CrsuayjJF6BueBmkKlcbeU5q18OhxRrvvKlmfw3tpDsK5ilx2XUJvoukwxHB0nHs/Q=="],
2990
+
2991
+ "@aws-sdk/lib-storage/@smithy/smithy-client": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/core": "^3.21.1", "@smithy/middleware-endpoint": "^4.4.11", "@smithy/middleware-stack": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" } }, "sha512-VKO/HKoQ5OrSHW6AJUmEnUKeXI1/5LfCwO9cwyao7CmLvGnZeM1i36Lyful3LK1XU7HwTVieTqO1y2C/6t3qtA=="],
2992
+
2993
+ "@aws-sdk/xml-builder/fast-xml-parser": ["[email protected]", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
2994
+
2995
+ "@azure/core-xml/fast-xml-parser": ["[email protected]", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
2996
+
2668
2997
"@babel/core/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
2669
2998
2999
+ "@babel/helper-compilation-targets/lru-cache": ["[email protected]", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
3000
+
2670
3001
"@babel/helper-compilation-targets/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
2671
3002
2672
3003
"@esbuild-kit/core-utils/esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
2673
3004
3005
+ "@graphql-tools/executor/@graphql-tools/utils": ["@graphql-tools/[email protected]", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA=="],
3006
+
3007
+ "@graphql-tools/merge/@graphql-tools/utils": ["@graphql-tools/[email protected]", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA=="],
3008
+
3009
+ "@graphql-tools/schema/@graphql-tools/utils": ["@graphql-tools/[email protected]", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA=="],
3010
+
2674
3011
"@grpc/grpc-js/@grpc/proto-loader": ["@grpc/[email protected]", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="],
2675
3012
2676
3013
"@isaacs/cliui/string-width": ["[email protected]", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
2677
3014
2678
3015
"@isaacs/cliui/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
2679
3016
2680
- "@mapbox/node-pre-gyp/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2681
-
2682
- "@mapbox/node-pre-gyp/node-fetch": ["[email protected]", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
3017
+ "@libsql/hrana-client/node-fetch": ["[email protected]", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
2683
3018
2684
- "@npmcli/agent/lru-cache": ["[email protected]", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
3019
+ "@mapbox/node-pre-gyp/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2685
3020
2686
3021
"@opentelemetry/instrumentation-pg/@types/pg": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ=="],
2687
3022
3023
+ "@oslojs/jwt/@oslojs/encoding": ["@oslojs/[email protected]", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],
3024
+
2688
3025
"@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/[email protected]", "", { "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=="],
2689
3026
2690
3027
"@radix-ui/react-avatar/@radix-ui/react-context": ["@radix-ui/[email protected]", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="],
2713
3050
2714
3051
"@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/[email protected]", "", { "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=="],
2715
3052
3053
+ "@reduxjs/toolkit/immer": ["[email protected]", "", {}, "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q=="],
3054
+
2716
3055
"@rollup/pluginutils/estree-walker": ["[email protected]", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
2717
3056
3057
+ "@smithy/abort-controller/@smithy/types": ["@smithy/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw=="],
3058
+
3059
+ "@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA=="],
3060
+
3061
+ "@smithy/util-waiter/@smithy/abort-controller": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA=="],
3062
+
2718
3063
"@types/ssh2/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
2719
3064
2720
3065
"@vercel/nft/estree-walker": ["[email protected]", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
2723
3068
2724
3069
"astro/esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
2725
3070
3071
+ "astro/p-limit": ["[email protected]", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="],
3072
+
2726
3073
"astro/tinyexec": ["[email protected]", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
2727
3074
3075
+ "astro/unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
3076
+
2728
3077
"astro/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
2729
3078
2730
3079
"bl/buffer": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
2733
3082
2734
3083
"boxen/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
2735
3084
2736
- "cacache/glob": ["[email protected]", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
3085
+ "bullmq/ioredis": ["[email protected]", "", { "dependencies": { "@ioredis/commands": "1.4.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q=="],
2737
3086
2738
- "cacache/lru-cache": ["[email protected]", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
3087
+ "bullmq/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
3088
+
3089
+ "cacache/glob": ["[email protected]", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
2739
3090
2740
3091
"cacheable-request/mimic-response": ["[email protected]", "", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="],
2741
3092
2751
3102
2752
3103
"cross-spawn/which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
2753
3104
3105
+ "cssstyle/lru-cache": ["[email protected]", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="],
3106
+
3107
+ "data-urls/whatwg-mimetype": ["[email protected]", "", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="],
3108
+
2754
3109
"docker-modem/readable-stream": ["[email protected]", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
2755
3110
2756
3111
"dockerode/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
2757
3112
3113
+ "duplexify/readable-stream": ["[email protected]", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
3114
+
2758
3115
"encoding/iconv-lite": ["[email protected]", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
2759
3116
2760
3117
"fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
2761
3118
2762
- "gcp-metadata/gaxios": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
3119
+ "gaxios/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
2763
3120
2764
3121
"gcp-metadata/google-logging-utils": ["[email protected]", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
2765
3122
2767
3124
2768
3125
"globe.gl/@tweenjs/tween.js": ["@tweenjs/[email protected]", "", {}, "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="],
2769
3126
2770
- "google-auth-library/gcp-metadata": ["[email protected]", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
3127
+ "googleapis/google-auth-library": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="],
3128
+
3129
+ "googleapis-common/gaxios": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
3130
+
3131
+ "googleapis-common/google-auth-library": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.0.0", "gcp-metadata": "^8.0.0", "google-logging-utils": "^1.0.0", "gtoken": "^8.0.0", "jws": "^4.0.0" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="],
2771
3132
2772
3133
"got/decompress-response": ["[email protected]", "", { "dependencies": { "mimic-response": "^4.0.0" } }, "sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q=="],
2773
3134
2774
3135
"gray-matter/js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
2775
3136
3137
+ "hast-util-from-html/parse5": ["[email protected]", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
3138
+
3139
+ "hast-util-raw/parse5": ["[email protected]", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
3140
+
3141
+ "hast-util-raw/unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
3142
+
2776
3143
"load-yaml-file/js-yaml": ["[email protected]", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
2777
3144
2778
3145
"log-symbols/is-unicode-supported": ["[email protected]", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="],
2779
3146
2780
- "lru-cache/yallist": ["[email protected]", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
3147
+ "mdast-util-definitions/unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
3148
+
3149
+ "mdast-util-to-hast/unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
3150
+
3151
+ "mdast-util-to-markdown/unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
2781
3152
2782
3153
"micromatch/picomatch": ["[email protected]", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
2783
3154
2787
3158
2788
3159
"minipass-sized/minipass": ["[email protected]", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
2789
3160
3161
+ "node-fetch/whatwg-url": ["[email protected]", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
3162
+
2790
3163
"node-gyp-build-optional-packages/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2791
3164
2792
3165
"nodegit/tar-fs": ["[email protected]", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="],
2793
3166
3167
+ "openid-client/jose": ["[email protected]", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
3168
+
2794
3169
"ora/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
2795
3170
2796
3171
"p-locate/p-limit": ["[email protected]", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
2797
3172
2798
- "path-scurry/lru-cache": ["[email protected]", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
2799
-
2800
3173
"pino/pino-abstract-transport": ["[email protected]", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="],
2801
3174
2802
3175
"postcss/nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
2805
3178
2806
3179
"prebuild-install/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2807
3180
3181
+ "promise-retry/retry": ["[email protected]", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
3182
+
2808
3183
"prompts/kleur": ["[email protected]", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
2809
3184
3185
+ "qrcode/yargs": ["[email protected]", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
3186
+
2810
3187
"rc/strip-json-comments": ["[email protected]", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
2811
3188
2812
3189
"read-cache/pify": ["[email protected]", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
2813
3190
3191
+ "readable-stream/buffer": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
3192
+
2814
3193
"readdirp/picomatch": ["[email protected]", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
2815
3194
3195
+ "remark-smartypants/unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
3196
+
3197
+ "retext-smartypants/unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
3198
+
2816
3199
"rimraf/glob": ["[email protected]", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
2817
3200
2818
3201
"rollup/fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
2819
3202
2820
3203
"send/debug": ["[email protected]", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
2821
3204
3205
+ "send/mime": ["[email protected]", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
3206
+
2822
3207
"sharp/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
2823
3208
3209
+ "stream-browserify/readable-stream": ["[email protected]", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
3210
+
2824
3211
"string-width/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
2825
3212
2826
3213
"string-width-cjs/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
2833
3220
2834
3221
"tar-stream/readable-stream": ["[email protected]", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
2835
3222
3223
+ "teeny-request/http-proxy-agent": ["[email protected]", "", { "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } }, "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w=="],
3224
+
3225
+ "teeny-request/https-proxy-agent": ["[email protected]", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
3226
+
3227
+ "teeny-request/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
3228
+
2836
3229
"three-globe/@tweenjs/tween.js": ["@tweenjs/[email protected]", "", {}, "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="],
2837
3230
2838
3231
"three-render-objects/@tweenjs/tween.js": ["@tweenjs/[email protected]", "", {}, "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="],
2839
3232
3233
+ "unist-util-remove-position/unist-util-visit": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
3234
+
2840
3235
"vite/fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
2841
3236
2842
3237
"vscode-json-languageservice/jsonc-parser": ["[email protected]", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
2967
3362
2968
3363
"@aws-sdk/client-sesv2/@smithy/util-retry/@smithy/service-error-classification": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.11.0" } }, "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA=="],
2969
3364
3365
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.9", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-NUH8R4O6FkN8HKMojzbGg/5pNjsfTjlMmeFclyPfPaXXUrbr5TzhWgbf7t92wfrpCHRgpjyz7ffASIS3wX28aA=="],
3366
+
3367
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ=="],
3368
+
3369
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/node-config-provider": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg=="],
3370
+
3371
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg=="],
3372
+
3373
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/types": ["@smithy/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw=="],
3374
+
3375
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA=="],
3376
+
3377
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/util-middleware": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A=="],
3378
+
3379
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/core": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.9", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-NUH8R4O6FkN8HKMojzbGg/5pNjsfTjlMmeFclyPfPaXXUrbr5TzhWgbf7t92wfrpCHRgpjyz7ffASIS3wX28aA=="],
3380
+
3381
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA=="],
3382
+
3383
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/protocol-http": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ=="],
3384
+
3385
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/types": ["@smithy/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw=="],
3386
+
3387
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g=="],
3388
+
3389
+ "@aws-sdk/xml-builder/fast-xml-parser/strnum": ["[email protected]", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
3390
+
3391
+ "@azure/core-xml/fast-xml-parser/strnum": ["[email protected]", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
3392
+
3393
+ "@babel/helper-compilation-targets/lru-cache/yallist": ["[email protected]", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
3394
+
2970
3395
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
2971
3396
2972
3397
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
3061
3486
3062
3487
"astro/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
3063
3488
3489
+ "astro/p-limit/yocto-queue": ["[email protected]", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
3490
+
3064
3491
"astro/vite/fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
3065
3492
3066
3493
"boxen/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
3067
3494
3495
+ "bullmq/ioredis/@ioredis/commands": ["@ioredis/[email protected]", "", {}, "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ=="],
3496
+
3068
3497
"cacache/glob/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
3069
3498
3070
3499
"cliui/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
3073
3502
3074
3503
"cross-spawn/which/isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
3075
3504
3076
- "gcp-metadata/gaxios/node-fetch": ["[email protected]", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
3505
+ "googleapis-common/gaxios/node-fetch": ["[email protected]", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
3506
+
3507
+ "googleapis-common/google-auth-library/gcp-metadata": ["[email protected]", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
3508
+
3509
+ "googleapis-common/google-auth-library/gtoken": ["[email protected]", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
3510
+
3511
+ "googleapis/google-auth-library/gaxios": ["[email protected]", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="],
3512
+
3513
+ "googleapis/google-auth-library/gcp-metadata": ["[email protected]", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
3077
3514
3078
- "gcp-metadata/gaxios/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
3515
+ "googleapis/google-auth-library/gtoken": ["[email protected]", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="],
3079
3516
3080
3517
"got/decompress-response/mimic-response": ["[email protected]", "", {}, "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="],
3081
3518
3089
3526
3090
3527
"minipass-sized/minipass/yallist": ["[email protected]", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
3091
3528
3529
+ "node-fetch/whatwg-url/tr46": ["[email protected]", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
3530
+
3531
+ "node-fetch/whatwg-url/webidl-conversions": ["[email protected]", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
3532
+
3092
3533
"nodegit/tar-fs/tar-stream": ["[email protected]", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
3093
3534
3094
3535
"ora/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
3095
3536
3537
+ "qrcode/yargs/cliui": ["[email protected]", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
3538
+
3539
+ "qrcode/yargs/y18n": ["[email protected]", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="],
3540
+
3541
+ "qrcode/yargs/yargs-parser": ["[email protected]", "", { "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ=="],
3542
+
3096
3543
"rimraf/glob/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
3097
3544
3098
3545
"send/debug/ms": ["[email protected]", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
3101
3548
3102
3549
"string-width/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
3103
3550
3551
+ "teeny-request/http-proxy-agent/agent-base": ["[email protected]", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
3552
+
3553
+ "teeny-request/https-proxy-agent/agent-base": ["[email protected]", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
3554
+
3104
3555
"widest-line/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
3105
3556
3106
3557
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
3159
3610
3160
3611
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/[email protected]", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
3161
3612
3613
+ "@aws-sdk/client-sesv2/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["[email protected]", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
3614
+
3162
3615
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
3163
3616
3164
3617
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-sDwtDnBSszUIbzbOORGh5gmXGl9aK25+BHb4gb1aVlqB+nNL2+IUEJA62+CE55lXSH8qXF90paivjK8tOHTwPA=="],
3175
3628
3176
3629
"@aws-sdk/client-sesv2/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
3177
3630
3631
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/protocol-http": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ=="],
3632
+
3633
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g=="],
3634
+
3635
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/middleware-serde/@smithy/protocol-http": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ=="],
3636
+
3637
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w=="],
3638
+
3639
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA=="],
3640
+
3641
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/core/@smithy/middleware-serde": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ=="],
3642
+
3643
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/core/@smithy/util-middleware": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A=="],
3644
+
3645
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/querystring-builder": "^4.2.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA=="],
3646
+
3647
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/abort-controller": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/querystring-builder": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg=="],
3648
+
3178
3649
"cacache/glob/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
3179
3650
3651
+ "googleapis/google-auth-library/gaxios/node-fetch": ["[email protected]", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
3652
+
3653
+ "qrcode/yargs/cliui/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
3654
+
3655
+ "qrcode/yargs/cliui/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
3656
+
3657
+ "qrcode/yargs/yargs-parser/camelcase": ["[email protected]", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
3658
+
3180
3659
"rimraf/glob/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
3181
3660
3661
+ "@aws-sdk/client-sesv2/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["[email protected]", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
3662
+
3182
3663
"@aws-sdk/client-sesv2/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/[email protected]", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw=="],
3664
+
3665
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/querystring-builder": "^4.2.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA=="],
3666
+
3667
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/abort-controller": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/querystring-builder": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg=="],
3668
+
3669
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw=="],
3670
+
3671
+ "@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw=="],
3672
+
3673
+ "qrcode/yargs/cliui/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
3674
+
3675
+ "qrcode/yargs/cliui/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
3676
+
3677
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw=="],
3678
+
3679
+ "@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/[email protected]", "", { "dependencies": { "@smithy/types": "^4.12.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw=="],
3183
3680
}
3184
3681
}
3185
3682
▼cli/bun.lock
+85-5
5
5
"": {
6
6
"name": "opencodehub-cli",
7
7
"dependencies": {
8
+ "boxen": "^7.1.1",
8
9
"chalk": "^5.3.0",
10
+ "cli-progress": "^3.12.0",
11
+ "cli-table3": "^0.6.5",
9
12
"commander": "^12.0.0",
10
13
"conf": "^12.0.0",
14
+ "figlet": "^1.7.0",
15
+ "gradient-string": "^2.0.2",
11
16
"inquirer": "^9.2.0",
12
17
"ora": "^8.0.0",
13
18
"simple-git": "^3.25.0",
14
19
},
15
20
"devDependencies": {
21
+ "@types/cli-progress": "^3.11.6",
22
+ "@types/figlet": "^1.7.0",
23
+ "@types/gradient-string": "^1.1.6",
16
24
"@types/inquirer": "^9.0.9",
17
25
"@types/node": "^20.14.9",
18
26
"tsx": "^4.15.0",
22
30
},
23
31
},
24
32
"packages": {
33
+ "@colors/colors": ["@colors/[email protected]", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="],
34
+
25
35
"@esbuild/aix-ppc64": ["@esbuild/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="],
26
36
27
37
"@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="],
138
148
139
149
"@types/chai": ["@types/[email protected]", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
140
150
151
+ "@types/cli-progress": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA=="],
152
+
141
153
"@types/deep-eql": ["@types/[email protected]", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
142
154
143
155
"@types/estree": ["@types/[email protected]", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
144
156
157
+ "@types/figlet": ["@types/[email protected]", "", {}, "sha512-KwrT7p/8Eo3Op/HBSIwGXOsTZKYiM9NpWRBJ5sVjWP/SmlS+oxxRvJht/FNAtliJvja44N3ul1yATgohnVBV0Q=="],
158
+
159
+ "@types/gradient-string": ["@types/[email protected]", "", { "dependencies": { "@types/tinycolor2": "*" } }, "sha512-LkaYxluY4G5wR1M4AKQUal2q61Di1yVVCw42ImFTuaIoQVgmV0WP1xUaLB8zwb47mp82vWTpePI9JmrjEnJ7nQ=="],
160
+
145
161
"@types/inquirer": ["@types/[email protected]", "", { "dependencies": { "@types/through": "*", "rxjs": "^7.2.0" } }, "sha512-/mWx5136gts2Z2e5izdoRCo46lPp5TMs9R15GTSsgg/XnZyxDWVqoVU3R9lWnccKpqwsJLvRoxbCjoJtZB7DSw=="],
146
162
147
163
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="],
148
164
149
165
"@types/through": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ=="],
150
166
167
+ "@types/tinycolor2": ["@types/[email protected]", "", {}, "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw=="],
168
+
151
169
"@vitest/expect": ["@vitest/[email protected]", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.17", "@vitest/utils": "4.0.17", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-mEoqP3RqhKlbmUmntNDDCJeTDavDR+fVYkSOw8qRwJFaW/0/5zA9zFeTrHqNtcmwh6j26yMmwx2PqUDPzt5ZAQ=="],
152
170
153
171
"@vitest/mocker": ["@vitest/[email protected]", "", { "dependencies": { "@vitest/spy": "4.0.17", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw"] }, "sha512-+ZtQhLA3lDh1tI2wxe3yMsGzbp7uuJSWBM1iTIKCbppWTSBN09PUC+L+fyNlQApQoR+Ps8twt2pbSSXg2fQVEQ=="],
166
184
167
185
"ajv-formats": ["[email protected]", "", { "dependencies": { "ajv": "^8.0.0" }, "peerDependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="],
168
186
187
+ "ansi-align": ["[email protected]", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
188
+
169
189
"ansi-escapes": ["[email protected]", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
170
190
171
191
"ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
172
192
173
- "ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
193
+ "ansi-styles": ["[email protected]", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
174
194
175
195
"assertion-error": ["[email protected]", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
176
196
180
200
181
201
"bl": ["[email protected]", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
182
202
203
+ "boxen": ["[email protected]", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^7.0.1", "chalk": "^5.2.0", "cli-boxes": "^3.0.0", "string-width": "^5.1.2", "type-fest": "^2.13.0", "widest-line": "^4.0.1", "wrap-ansi": "^8.1.0" } }, "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog=="],
204
+
183
205
"buffer": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
184
206
207
+ "camelcase": ["[email protected]", "", {}, "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw=="],
208
+
185
209
"chai": ["[email protected]", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
186
210
187
211
"chalk": ["[email protected]", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
188
212
189
213
"chardet": ["[email protected]", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="],
190
214
215
+ "cli-boxes": ["[email protected]", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
216
+
191
217
"cli-cursor": ["[email protected]", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
192
218
219
+ "cli-progress": ["[email protected]", "", { "dependencies": { "string-width": "^4.2.3" } }, "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A=="],
220
+
193
221
"cli-spinners": ["[email protected]", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
194
222
223
+ "cli-table3": ["[email protected]", "", { "dependencies": { "string-width": "^4.2.0" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ=="],
224
+
195
225
"cli-width": ["[email protected]", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="],
196
226
197
227
"clone": ["[email protected]", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="],
212
242
213
243
"dot-prop": ["[email protected]", "", { "dependencies": { "type-fest": "^3.8.0" } }, "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ=="],
214
244
215
- "emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
245
+ "eastasianwidth": ["[email protected]", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
246
+
247
+ "emoji-regex": ["[email protected]", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
216
248
217
249
"env-paths": ["[email protected]", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="],
218
250
230
262
231
263
"fdir": ["[email protected]", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
232
264
265
+ "figlet": ["[email protected]", "", { "dependencies": { "commander": "^14.0.0" }, "bin": { "figlet": "bin/index.js" } }, "sha512-aktIwEZZ6Gp9AWdMXW4YCi0J2Ahuxo67fNJRUIWD81w8pQ0t9TS8FFpbl27ChlTLF06VkwjDesZSzEVzN75rzA=="],
266
+
233
267
"fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
234
268
235
269
"get-east-asian-width": ["[email protected]", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
236
270
237
271
"get-tsconfig": ["[email protected]", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
238
272
273
+ "gradient-string": ["[email protected]", "", { "dependencies": { "chalk": "^4.1.2", "tinygradient": "^1.1.5" } }, "sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw=="],
274
+
239
275
"has-flag": ["[email protected]", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
240
276
241
277
"iconv-lite": ["[email protected]", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
318
354
319
355
"stdin-discarder": ["[email protected]", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="],
320
356
321
- "string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
357
+ "string-width": ["[email protected]", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
322
358
323
359
"string_decoder": ["[email protected]", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
324
360
332
368
333
369
"tinybench": ["[email protected]", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
334
370
371
+ "tinycolor2": ["[email protected]", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
372
+
335
373
"tinyexec": ["[email protected]", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
336
374
337
375
"tinyglobby": ["[email protected]", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
338
376
377
+ "tinygradient": ["[email protected]", "", { "dependencies": { "@types/tinycolor2": "^1.4.0", "tinycolor2": "^1.0.0" } }, "sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw=="],
378
+
339
379
"tinyrainbow": ["[email protected]", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="],
340
380
341
381
"tslib": ["[email protected]", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
342
382
343
383
"tsx": ["[email protected]", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": "dist/cli.mjs" }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="],
344
384
345
- "type-fest": ["[email protected]", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="],
385
+ "type-fest": ["[email protected]", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="],
346
386
347
387
"typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
348
388
362
402
363
403
"why-is-node-running": ["[email protected]", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": "cli.js" }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
364
404
365
- "wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
405
+ "widest-line": ["[email protected]", "", { "dependencies": { "string-width": "^5.0.1" } }, "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig=="],
406
+
407
+ "wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
366
408
367
409
"yoctocolors-cjs": ["[email protected]", "", {}, "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw=="],
368
410
411
+ "ansi-align/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
412
+
369
413
"ansi-escapes/type-fest": ["[email protected]", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
370
414
415
+ "cli-progress/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
416
+
417
+ "cli-table3/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
418
+
419
+ "dot-prop/type-fest": ["[email protected]", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="],
420
+
421
+ "figlet/commander": ["[email protected]", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
422
+
423
+ "gradient-string/chalk": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
424
+
371
425
"inquirer/ora": ["[email protected]", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="],
372
426
427
+ "inquirer/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
428
+
429
+ "inquirer/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
430
+
373
431
"log-symbols/is-unicode-supported": ["[email protected]", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="],
374
432
375
433
"ora/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
376
434
377
435
"ora/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
378
436
437
+ "string-width/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
438
+
439
+ "wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
440
+
441
+ "ansi-align/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
442
+
443
+ "cli-progress/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
444
+
445
+ "cli-table3/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
446
+
447
+ "gradient-string/chalk/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
448
+
379
449
"inquirer/ora/chalk": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
380
450
381
451
"inquirer/ora/cli-cursor": ["[email protected]", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="],
386
456
387
457
"inquirer/ora/log-symbols": ["[email protected]", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
388
458
459
+ "inquirer/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
460
+
461
+ "inquirer/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
462
+
389
463
"ora/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
390
464
391
465
"ora/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
392
466
467
+ "string-width/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
468
+
469
+ "wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
470
+
471
+ "inquirer/ora/chalk/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
472
+
393
473
"inquirer/ora/cli-cursor/restore-cursor": ["[email protected]", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="],
394
474
395
475
"inquirer/ora/cli-cursor/restore-cursor/onetime": ["[email protected]", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
396
476
▼cli/package-lock.json
+2-2
1
1
{
2
2
"name": "opencodehub-cli",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
"lockfileVersion": 3,
5
5
"requires": true,
6
6
"packages": {
7
7
"": {
8
8
"name": "opencodehub-cli",
9
- "version": "1.0.1",
9
+ "version": "1.1.0",
10
10
"license": "MIT",
11
11
"dependencies": {
12
12
"boxen": "^7.1.1",
13
13
▼cli/src/commands/repo.ts
+1-1
401
401
402
402
try {
403
403
const result = await postWithAuth<{ data: RepoInfo }>("/api/repos", {
404
- name: options.name,
404
+ name: options.name.toLowerCase(),
405
405
description: options.description,
406
406
visibility: options.visibility || "public",
407
407
readme: options.init !== false,
408
408
▼cli/src/commands/runner/config.ts
+58-0
1
+
2
+import { Command } from 'commander';
3
+import fs from 'node:fs/promises';
4
+import path from 'node:path';
5
+import inquirer from 'inquirer';
6
+import chalk from 'chalk';
7
+
8
+export const runnerConfigCommand = new Command('config')
9
+ .description('Configure a self-hosted runner interactively')
10
+ .option('-t, --token <token>', 'Runner registration token')
11
+ .option('-u, --url <url>', 'OpenCodeHub API URL')
12
+ .action(async (options) => {
13
+ console.log(chalk.bold.blue('OpenCodeHub Runner Configuration'));
14
+ console.log('Run this command in the directory where you want to run the runner.\n');
15
+
16
+ const answers = await inquirer.prompt([
17
+ {
18
+ type: 'input',
19
+ name: 'url',
20
+ message: 'OpenCodeHub API URL:',
21
+ default: options.url || 'http://localhost:3000',
22
+ validate: (input) => input.startsWith('http') ? true : 'Must be a valid URL'
23
+ },
24
+ {
25
+ type: 'password',
26
+ name: 'token',
27
+ message: 'Registration Token:',
28
+ default: options.token,
29
+ validate: (input) => input ? true : 'Token is required'
30
+ },
31
+ {
32
+ type: 'input',
33
+ name: 'name',
34
+ message: 'Runner Name:',
35
+ default: `runner-${Math.random().toString(36).substring(7)}`
36
+ }
37
+ ]);
38
+
39
+ const envContent = `
40
+# OpenCodeHub Runner Configuration
41
+API_URL=${answers.url}
42
+RUNNER_TOKEN=${answers.token}
43
+RUNNER_NAME=${answers.name}
44
+`.trim();
45
+
46
+ const envPath = path.join(process.cwd(), '.env.runner'); // Use distinct file or .env
47
+ // Check if .env exists, maybe append? For safety, let's write to .env.runner and instruct user.
48
+
49
+ // Actually, let's just write to .env so `bun run runner:start` picks it up automatically if in proper dir,
50
+ // or strictly for the runner execution context.
51
+ // Assuming user runs this in the runner source dir or deployment dir.
52
+
53
+ await fs.writeFile('.env', envContent);
54
+
55
+ console.log(chalk.green('\n✓ Configuration saved to .env'));
56
+ console.log('\nYou can now start the runner with:');
57
+ console.log(chalk.cyan('bun run runner:start'));
58
+ });
0
59
▼cli/src/commands/stack/index.ts
+111-212
1
+
1
2
/**
2
3
* Stack Commands
3
4
* Manage stacked PRs from the CLI
14
15
import { applyTlsConfig } from "../../lib/api.js";
15
16
import { getConfig } from "../../lib/config.js";
16
17
import { getRepoInfoFromGit } from "../../lib/git.js";
18
+import {
19
+ setParentBranch,
20
+ getParentBranch,
21
+ getStackTopology,
22
+ flattenStack,
23
+ recursiveRebase
24
+} from "../../lib/stack-manager.js";
17
25
18
26
const git = simpleGit();
19
27
49
57
50
58
try {
51
59
// Get current branch
52
- const currentBranch = await git.revparse(["--abbrev-ref", "HEAD"]);
60
+ const currentBranch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
53
61
54
62
// Create new branch
55
63
const newBranch = `stack/${name}`;
56
64
await git.checkoutLocalBranch(newBranch);
57
65
66
+ // Save parent metadata
67
+ await setParentBranch(git, newBranch, currentBranch);
68
+
58
69
spinner.succeed(
59
- `Created branch ${chalk.green(newBranch)} (stacked on ${chalk.cyan(currentBranch.trim())})`,
70
+ `Created branch ${chalk.green(newBranch)} (stacked on ${chalk.cyan(currentBranch)})`,
60
71
);
61
72
62
73
console.log("\n" + chalk.dim("Next steps:"));
63
74
console.log(chalk.dim(" • Make your changes"));
64
75
console.log(
65
76
chalk.dim(" • Run ") +
66
- chalk.cyan("och stack submit") +
67
- chalk.dim(" to push and create PRs"),
77
+ chalk.cyan("och stack submit") +
78
+ chalk.dim(" to push and create PRs"),
68
79
);
69
80
} catch (error) {
70
81
spinner.fail("Failed to create branch");
92
103
const spinner = ora("Analyzing stack...").start();
93
104
94
105
try {
95
- // Get all stack branches
96
- const branches = await git.branchLocal();
97
- const currentBranch = (
98
- await git.revparse(["--abbrev-ref", "HEAD"])
99
- ).trim();
100
- const stackBranches = branches.all.filter((b) => b.startsWith("stack/"));
101
-
102
- if (stackBranches.length === 0) {
106
+ // Get stack topology
107
+ const roots = await getStackTopology(git);
108
+ const allNodes = flattenStack(roots);
109
+
110
+ // Filter for branches starting with 'stack/' or just use the topology?
111
+ // Let's use topology but only push branches that we manage.
112
+ // Assuming user is on a branch in the stack, we probably want to submit *this* stack.
113
+ const currentBranch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
114
+
115
+ // Filter nodes related to current branch? existing logic submitted ALL 'stack/' branches.
116
+ // Let's stick to submitting topologically sorted list of 'stack/' branches.
117
+ const sortedStackBranches = allNodes
118
+ .map(n => n.branch)
119
+ .filter(b => b.startsWith("stack/"));
120
+
121
+ if (sortedStackBranches.length === 0) {
103
122
spinner.fail("No stack branches found");
104
123
console.log(
105
124
chalk.dim("Run ") +
106
- chalk.cyan("och stack create <name>") +
107
- chalk.dim(" to start a stack."),
125
+ chalk.cyan("och stack create <name>") +
126
+ chalk.dim(" to start a stack."),
108
127
);
109
128
process.exit(1);
110
129
}
132
151
};
133
152
134
153
try {
135
- for (const branch of stackBranches) {
154
+ for (const branch of sortedStackBranches) {
136
155
try {
137
156
console.log(chalk.dim(` Pushing ${branch} to origin...`));
138
157
const result = spawnSync(
167
186
spinner.text = "Creating PRs...";
168
187
169
188
// Build branch metadata for PR creation
170
- const branchData = stackBranches.map((branch, index) => {
189
+ const branchData = [];
190
+ for (const branch of sortedStackBranches) {
171
191
const name = branch.replace("stack/", "");
172
- const parentBranch = index === 0 ? "main" : stackBranches[index - 1];
192
+ const parent = await getParentBranch(git, branch) || "main";
173
193
174
- return {
194
+ branchData.push({
175
195
name: branch,
176
196
title: options.message
177
197
? `${options.message}: ${name}`
178
198
: `[Stack] ${name}`,
179
- description: `Part of stacked PR workflow.\n\nBranch: \`${branch}\`\nBased on: \`${parentBranch}\``,
180
- parentBranch,
181
- };
182
- });
199
+ description: `Part of stacked PR workflow.\n\nBranch: \`${branch}\`\nBased on: \`${parent}\``,
200
+ parentBranch: parent,
201
+ });
202
+ }
183
203
184
204
// Create PRs via API
185
205
try {
229
249
console.log(chalk.dim(" • Get reviews on your PRs"));
230
250
console.log(
231
251
chalk.dim(" • Run ") +
232
- chalk.cyan("och stack sync") +
233
- chalk.dim(" to keep in sync"),
252
+ chalk.cyan("och stack sync") +
253
+ chalk.dim(" to keep in sync"),
234
254
);
235
255
} catch (error) {
236
256
spinner.fail("Failed to submit stack");
243
263
244
264
// View current stack
245
265
stackCommands
246
- .command("view")
266
+ .command("log")
247
267
.alias("ls")
248
- .alias("log")
249
- .description("View current stack visualization (like gt log)")
268
+ .alias("view")
269
+ .description("Visual stack graph")
250
270
.action(async () => {
251
271
try {
252
- const currentBranch = await git.revparse(["--abbrev-ref", "HEAD"]);
253
- const branches = await git.branchLocal();
254
-
255
- console.log(chalk.bold("\n📚 Current Stack\n"));
256
-
257
- // Find stack branches
258
- const stackBranches = branches.all.filter((b) => b.startsWith("stack/"));
259
-
260
- if (stackBranches.length === 0) {
261
- console.log(chalk.dim(" No stack branches found."));
262
- console.log(
263
- chalk.dim(" Run ") +
264
- chalk.cyan("och stack create <name>") +
265
- chalk.dim(" to start a stack."),
266
- );
267
- return;
268
- }
269
-
270
- // Display stack
271
- console.log(
272
- chalk.dim(" ┌─ ") + chalk.green("main") + chalk.dim(" (base)"),
273
- );
274
-
275
- for (let i = 0; i < stackBranches.length; i++) {
276
- const branch = stackBranches[i];
277
- const isCurrent = currentBranch.trim() === branch;
278
- const prefix = i === stackBranches.length - 1 ? "└─" : "├─";
279
-
280
- if (isCurrent) {
281
- console.log(chalk.dim(" │"));
282
- console.log(
283
- chalk.dim(` ${prefix} `) +
284
- chalk.yellow.bold("● " + branch) +
285
- chalk.yellow(" (current)"),
286
- );
287
- } else {
288
- console.log(chalk.dim(" │"));
289
- console.log(chalk.dim(` ${prefix} `) + chalk.cyan("○ " + branch));
290
- }
291
- }
292
-
293
- console.log("");
294
- } catch (error) {
295
- console.error(chalk.red("Failed to view stack"));
296
- process.exit(1);
297
- }
298
- });
299
-
300
-// Sync stack with remote
301
-stackCommands
302
- .command("sync")
303
- .description("Bidirectional sync with remote")
304
- .option("-p, --push", "Push local changes to remote")
305
- .option("-l, --pull", "Pull remote changes to local")
306
- .option("-f, --force", "Force push (use with caution)")
307
- .action(async (options) => {
308
- const spinner = ora("Syncing stack...").start();
309
-
310
- try {
311
- // Fetch latest
312
- spinner.text = "Fetching from remote...";
313
- await git.fetch();
272
+ const roots = await getStackTopology(git);
314
273
315
- const branches = await git.branchLocal();
316
- const stackBranches = branches.all.filter((b) => b.startsWith("stack/"));
274
+ console.log(chalk.bold("\n📚 Stack Topology\n"));
317
275
318
- if (stackBranches.length === 0) {
319
- spinner.info("No stack branches found");
276
+ if (roots.length === 0) {
277
+ console.log(chalk.dim("No stack found."));
320
278
return;
321
279
}
322
280
323
- const currentBranch = await git.revparse(["--abbrev-ref", "HEAD"]);
281
+ const printNode = (node: any, prefix: string, isLast: boolean, isRoot: boolean) => {
282
+ const marker = isRoot ? "" : (isLast ? "└─" : "├─");
283
+ const branchColor = node.isCurrent ? chalk.green.bold : chalk.cyan;
284
+ const status = node.isCurrent ? chalk.yellow(" (current)") : "";
324
285
325
- // Default: do both push and pull if neither specified
326
- const doPush = options.push || (!options.push && !options.pull);
327
- const doPull = options.pull || (!options.push && !options.pull);
286
+ console.log(`${prefix}${marker} ${branchColor(node.branch)}${status}`);
328
287
329
- let syncedCount = 0;
330
- const errors: string[] = [];
331
-
332
- for (const branch of stackBranches) {
333
- spinner.text = `Syncing ${branch}...`;
334
-
335
- try {
336
- await git.checkout(branch);
337
-
338
- if (doPull) {
339
- // Pull with rebase
340
- try {
341
- await git.pull(["--rebase", "origin", branch]);
342
- } catch (e) {
343
- // Branch might not exist on remote yet
344
- }
345
- }
288
+ const newPrefix = isRoot ? "" : (prefix + (isLast ? " " : "│ "));
346
289
347
- if (doPush) {
348
- const pushArgs = ["-u", "origin", branch];
349
- if (options.force) {
350
- pushArgs.unshift("--force-with-lease");
351
- }
352
- await git.push(pushArgs);
353
- }
290
+ for (let i = 0; i < node.children.length; i++) {
291
+ printNode(node.children[i], newPrefix, i === node.children.length - 1, false);
292
+ }
293
+ };
354
294
355
- syncedCount++;
356
- } catch (error) {
357
- errors.push(
358
- `${branch}: ${error instanceof Error ? error.message : "Unknown error"}`,
359
- );
295
+ for (const root of roots) {
296
+ // If root has children or is interesting (e.g. main)
297
+ if (root.children.length > 0 || root.branch === 'main') {
298
+ printNode(root, "", true, true);
299
+ console.log("");
360
300
}
361
301
}
362
302
363
- // Return to original branch
364
- await git.checkout(currentBranch.trim());
365
-
366
- if (errors.length > 0) {
367
- spinner.warn(`Synced ${syncedCount}/${stackBranches.length} branches`);
368
- console.log(chalk.yellow("\nIssues:"));
369
- errors.forEach((e) => console.log(chalk.dim(` • ${e}`)));
370
- } else {
371
- spinner.succeed(`Synced ${syncedCount} branches successfully`);
372
- }
373
303
} catch (error) {
374
- spinner.fail("Failed to sync stack");
375
- console.error(
376
- chalk.red(error instanceof Error ? error.message : "Unknown error"),
377
- );
378
- console.log(chalk.dim("\nIf there are conflicts, resolve them and run:"));
379
- console.log(chalk.cyan(" git rebase --continue"));
304
+ console.error(chalk.red("Failed to view stack"), error);
380
305
process.exit(1);
381
306
}
382
307
});
383
308
384
-// Stack status
309
+// Sync stack (Recursive Rebase)
385
310
stackCommands
386
- .command("status")
387
- .alias("st")
388
- .description("Show sync status for all stack branches")
311
+ .command("sync")
312
+ .description("Recursively rebase stack on updated main")
389
313
.action(async () => {
390
- const spinner = ora("Checking status...").start();
314
+ const spinner = ora("Syncing stack...").start();
391
315
392
316
try {
317
+ // 1. Update main
318
+ spinner.text = "Fetching origin/main...";
393
319
await git.fetch();
320
+ try {
321
+ await git.checkout("main");
322
+ await git.pull();
323
+ } catch (e) {
324
+ // Maybe main doesn't exist locally or something
325
+ }
394
326
395
- const branches = await git.branchLocal();
396
- const stackBranches = branches.all.filter((b) => b.startsWith("stack/"));
397
- const currentBranch = await git.revparse(["--abbrev-ref", "HEAD"]);
327
+ // 2. Refresh topology
328
+ const roots = await getStackTopology(git);
329
+ const flatStack = flattenStack(roots);
398
330
399
- spinner.stop();
331
+ // Filter out main itself from loop
332
+ const stackBranches = flatStack.filter(n => n.branch !== 'main');
400
333
401
334
if (stackBranches.length === 0) {
402
- console.log(chalk.dim("\nNo stack branches found."));
335
+ spinner.info("No stack branches to sync.");
403
336
return;
404
337
}
405
338
406
- console.log(chalk.bold("\n📊 Stack Status\n"));
339
+ let syncedCount = 0;
407
340
408
- for (const branch of stackBranches) {
409
- const isCurrent = currentBranch.trim() === branch;
341
+ // 3. Recursive Rebase
342
+ // We iterate breadth-first or topologically (flattenStack is DFS preorder usually, which ensures parents processed before children)
343
+ // Actually flattenStack implementation above is DFS preorder. Parent -> Child -> ...
344
+ for (const node of stackBranches) {
345
+ const parentBranch = node.parent;
346
+ if (!parentBranch) continue;
410
347
411
- // Get ahead/behind
412
- let ahead = 0;
413
- let behind = 0;
414
- let hasRemote = true;
348
+ spinner.text = `Rebasing ${node.branch} onto ${parentBranch}...`;
415
349
416
350
try {
417
- const log = await git.raw([
418
- "rev-list",
419
- "--left-right",
420
- "--count",
421
- `${branch}...origin/${branch}`,
422
- ]);
423
- [ahead, behind] = log.trim().split("\t").map(Number);
424
- } catch {
425
- hasRemote = false;
426
- }
427
-
428
- // Build status line
429
- let status = "";
430
- if (!hasRemote) {
431
- status = chalk.yellow("⚠ not pushed");
432
- } else if (ahead > 0 && behind > 0) {
433
- status = chalk.red(`↑${ahead} ↓${behind} diverged`);
434
- } else if (ahead > 0) {
435
- status = chalk.cyan(`↑${ahead} ahead`);
436
- } else if (behind > 0) {
437
- status = chalk.magenta(`↓${behind} behind`);
438
- } else {
439
- status = chalk.green("✓ synced");
351
+ // We rebase 'node.branch' onto 'parentBranch'
352
+ // Since we processed 'parentBranch' already (if it was in stack), 'parentBranch' tip is updated.
353
+ // So 'git rebase parentBranch' works perfectly.
354
+ await git.checkout(node.branch);
355
+ await git.rebase([parentBranch]);
356
+ syncedCount++;
357
+ } catch (e) {
358
+ spinner.fail(`Conflict rebasing ${node.branch} onto ${parentBranch}`);
359
+ console.log(chalk.yellow("\nResolve conflicts manually, then continue with:"));
360
+ console.log(chalk.cyan(` git rebase --continue`));
361
+ // We have to stop here because children depend on this
362
+ process.exit(1);
440
363
}
441
-
442
- const marker = isCurrent ? chalk.yellow("● ") : chalk.dim("○ ");
443
- console.log(
444
- ` ${marker}${isCurrent ? chalk.bold(branch) : branch} ${status}`,
445
- );
446
364
}
447
365
448
- console.log("");
449
- } catch (error) {
450
- spinner.fail("Failed to get status");
451
- console.error(
452
- chalk.red(error instanceof Error ? error.message : "Unknown error"),
453
- );
454
- process.exit(1);
455
- }
456
- });
366
+ // 4. Force Push updates (Optional, or explicit?)
367
+ // Graphite defaults to not pushing unless requested or 'gt submit'.
368
+ // But 'och stack sync' implies syncing WITH remote often.
369
+ // Let's keep it local only for safety, user updates remote via 'och stack submit'.
457
370
458
-// Rebase entire stack
459
-stackCommands
460
- .command("rebase")
461
- .description("Rebase entire stack on base branch")
462
- .option("-b, --base <branch>", "Base branch to rebase on", "main")
463
- .action(async (options) => {
464
- const spinner = ora(`Rebasing stack on ${options.base}...`).start();
371
+ spinner.succeed(`Recursively rebased ${syncedCount} branches. Run 'och stack submit' to push updates.`);
465
372
466
- try {
467
- // Fetch latest
468
- await git.fetch();
469
-
470
- // Rebase
471
- await git.rebase([options.base]);
472
-
473
- spinner.succeed(`Stack rebased on ${chalk.green(options.base)}`);
474
373
} catch (error) {
475
- spinner.fail("Failed to rebase stack");
374
+ spinner.fail("Failed to sync stack");
476
375
console.error(
477
376
chalk.red(error instanceof Error ? error.message : "Unknown error"),
478
377
);
479
378
▼cli/src/lib/stack-manager.ts
+135-0
1
+
2
+import { SimpleGit } from "simple-git";
3
+import chalk from "chalk";
4
+
5
+export interface StackNode {
6
+ branch: string;
7
+ parent: string | null;
8
+ children: StackNode[];
9
+ isCurrent: boolean;
10
+}
11
+
12
+export const PARENT_CONFIG_KEY = "och.parent";
13
+
14
+/**
15
+ * Set the parent branch for stack tracking
16
+ */
17
+export async function setParentBranch(git: SimpleGit, branch: string, parent: string) {
18
+ await git.raw(["config", `branch.${branch}.${PARENT_CONFIG_KEY}`, parent]);
19
+}
20
+
21
+/**
22
+ * Get the explicitly configured parent for a branch
23
+ */
24
+export async function getParentBranch(git: SimpleGit, branch: string): Promise<string | null> {
25
+ try {
26
+ const parent = await git.raw(["config", `branch.${branch}.${PARENT_CONFIG_KEY}`]);
27
+ return parent.trim();
28
+ } catch {
29
+ return null;
30
+ }
31
+}
32
+
33
+/**
34
+ * Build a tree of stack branches based on config
35
+ */
36
+export async function getStackTopology(git: SimpleGit): Promise<StackNode[]> {
37
+ const branches = await git.branchLocal();
38
+ const currentBranch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
39
+
40
+ const nodes: Map<string, StackNode> = new Map();
41
+ const allBranches = branches.all;
42
+
43
+ // Initialize nodes
44
+ for (const branch of allBranches) {
45
+ nodes.set(branch, {
46
+ branch,
47
+ parent: null,
48
+ children: [],
49
+ isCurrent: branch === currentBranch
50
+ });
51
+ }
52
+
53
+ // Link parents
54
+ for (const branch of allBranches) {
55
+ const parent = await getParentBranch(git, branch);
56
+ if (parent && nodes.has(parent)) {
57
+ const node = nodes.get(branch)!;
58
+ node.parent = parent;
59
+ nodes.get(parent)!.children.push(node);
60
+ }
61
+ }
62
+
63
+ // Find roots (branches with no tracked parent or parent not in map)
64
+ // We assume 'main' or trunk is a root if it has children, or any branch that has children but no parent config.
65
+ // Actually, we usually want to find the stack roots.
66
+ // A stack root is a branch that:
67
+ // 1. Is 'main' (or base)
68
+ // 2. OR has no parent config
69
+
70
+ const roots: StackNode[] = [];
71
+ for (const node of nodes.values()) {
72
+ if (!node.parent) {
73
+ roots.push(node);
74
+ }
75
+ }
76
+
77
+ return roots;
78
+}
79
+
80
+/**
81
+ * Recursively rebase a branch and its children
82
+ */
83
+export async function recursiveRebase(
84
+ git: SimpleGit,
85
+ branch: string,
86
+ newParentTip: string,
87
+ dryRun = false
88
+): Promise<void> {
89
+ // 1. Rebase this branch
90
+ console.log(chalk.dim(`Rebasing ${branch} onto ${newParentTip}...`));
91
+
92
+ if (!dryRun) {
93
+ // Checkout
94
+ await git.checkout(branch);
95
+
96
+ // We use --onto to transplant: git rebase --onto new_parent_tip old_parent_tip branch
97
+ // But we need to know the OLD parent tip.
98
+ // If we just track "parent branch name", we can use `git rebase parent_branch`.
99
+ // Git rebase automatically finds the merge base.
100
+ // If the parent branch was just updated (rebased itself), `git rebase parent` works recursively IF we do it in order.
101
+
102
+ try {
103
+ await git.rebase([newParentTip]);
104
+ } catch (e) {
105
+ throw new Error(`Conflict rebasing ${branch} onto ${newParentTip}. Resolve manually.`);
106
+ }
107
+ }
108
+
109
+ // 2. Update children
110
+ // To do this, we need the topology.
111
+ // This function might need to be part of a larger loop or take the tree.
112
+ // Let's assume the caller manages the order (topological sort).
113
+}
114
+
115
+/**
116
+ * Get a flat list of branches in topological order (Parent -> Child -> Grandchild)
117
+ */
118
+export function flattenStack(nodes: StackNode[]): StackNode[] {
119
+ const result: StackNode[] = [];
120
+
121
+ function traverse(node: StackNode) {
122
+ result.push(node);
123
+ for (const child of node.children) {
124
+ traverse(child);
125
+ }
126
+ }
127
+
128
+ for (const node of nodes) {
129
+ // Only traverse if it looks like a stack root or has children relevant to us
130
+ // For simplicity, traverse all roots
131
+ traverse(node);
132
+ }
133
+
134
+ return result;
135
+}
0
136
▼doc/feature_audit.md
+262-0
1
+# OpenCodeHub Feature Audit & Implementation Plan
2
+**Audit Date:** February 6, 2026
3
+**Total Features Analyzed:** 122
4
+**Status:** ✅ Implemented: 47 | ⚠️ Partial: 29 | ❌ Missing: 46
5
+
6
+## Feature Matrix Summary
7
+
8
+| Category | Implemented | Partial | Missing | Total |
9
+| :--- | :---: | :---: | :---: | :---: |
10
+| **Repository & Git** | 8 | 3 | 1 | 12 |
11
+| **Pull Requests** | 6 | 5 | 4 | 15 |
12
+| **Code Review** | 4 | 3 | 3 | 10 |
13
+| **Issues & Planning** | 4 | 2 | 4 | 10 |
14
+| **CI/CD & Automation** | 4 | 3 | 1 | 8 |
15
+| **Third-Party Integrations** | 2 | 1 | 17 | 20 |
16
+| **Dependency Awareness** | 1 | 1 | 3 | 5 |
17
+| **Security** | 4 | 4 | 4 | 12 |
18
+| **Analytics & Insights** | 5 | 1 | 2 | 8 |
19
+| **Notifications** | 6 | 1 | 1 | 8 |
20
+| **Interfaces & Extensibility** | 5 | 1 | 1 | 7 |
21
+| **Self-Hosted** | 4 | 2 | 1 | 7 |
22
+
23
+---
24
+
25
+## 1. Repository & Git
26
+| Feature | Status | Implementation Notes |
27
+| :--- | :---: | :--- |
28
+| Full Git protocol (SSH/HTTPS) | ✅ | `ssh.ts`, `git-server.ts` fully implemented |
29
+| Repository hosting | ✅ | Multiple storage adapters (S3, R2, local, GDrive) |
30
+| Repository mirroring | ⚠️ | Schema exists, background sync incomplete |
31
+| Forks & pull-based workflows | ✅ | `fork.ts` API, UI implemented |
32
+| Monorepo support | ⚠️ | Basic support, no advanced path scoping |
33
+| Git LFS | ✅ | `lfs.ts` with batch API (upload/download) |
34
+| Submodules | ✅ | Native Git support, no special handling needed |
35
+| Branch protection rules | ✅ | Schema + UI + API in place |
36
+| Repository templates | ⚠️ | Not fully implemented |
37
+| Repository Wiki | ✅ | Implemented (Phase 8) |
38
+| File-level permissions | ❌ | Not implemented |
39
+| Commit signing (GPG) | ✅ | GPG key management UI exists |
40
+| Commit signing (SSH) | ✅ | SSH key management UI exists |
41
+
42
+## 2. Pull Requests
43
+| Feature | Status | Implementation Notes |
44
+| :--- | :---: | :--- |
45
+| Native stacked PRs | ✅ | `stacks.ts` - create, add, reorder, visualize |
46
+| PR dependency graphs (DAG) | ✅ | `getStackVisualization()` implemented |
47
+| Cross-PR dependency detection | ⚠️ | Basic via stacks, no automatic detection |
48
+| Stack rebase & auto-update | ⚠️ | Partial - manual rebase exists |
49
+| Stack-level approvals | ⚠️ | Basic - individual PR approvals only |
50
+| Bulk merge (merge stacks) | ⚠️ | Via merge queue, not explicit bulk action |
51
+| Custom PR states | ❌ | Only standard states (open/closed/merged/draft) |
52
+| Required reviewers per state | ❌ | Not implemented |
53
+| PR merge queues | ✅ | `merge-queue.ts` (590+ lines) |
54
+| Conflict detection before merge | ✅ | `mergeable` field in PR schema |
55
+| Auto-merge rules | ⚠️ | Basic via automations, incomplete |
56
+| Draft PRs | ✅ | Schema supports `isDraft` field |
57
+| PR labels | ✅ | Full implementation with schema |
58
+| PR assignees | ✅ | Full implementation with schema |
59
+| PR checks | ❌ | Schema exists, CI integration incomplete |
60
+
61
+## 3. Code Review
62
+| Feature | Status | Implementation Notes |
63
+| :--- | :---: | :--- |
64
+| Inline code comments | ✅ | `pullRequestComments` schema with line/position |
65
+| Threaded discussions | ✅ | Reply threading via `replyToId` |
66
+| Suggested changes | ❌ | Not implemented |
67
+| Batch comments | ⚠️ | Schema supports, UI may be incomplete |
68
+| Code owner enforcement | ⚠️ | TODO in `automations.ts`, CODEOWNERS not implemented |
69
+| Review templates | ❌ | Not implemented |
70
+| Required approval policies | ✅ | Via branch protection rules |
71
+| Partial file approvals | ❌ | Not implemented |
72
+| Multi-reviewer rules | ⚠️ | Basic - required reviewer count only |
73
+| AI code review | ✅ | `ai-review.ts` with OpenAI/Anthropic |
74
+
75
+## 4. Issues & Planning
76
+| Feature | Status | Implementation Notes |
77
+| :--- | :---: | :--- |
78
+| Issue tracking | ✅ | Full schema + UI for issues |
79
+| Epics and sub-tasks | ✅ | Implemented (Phase 8) |
80
+| Custom issue fields | ❌ | Not implemented |
81
+| Issue workflows | ❌ | Not implemented |
82
+| Milestones & roadmaps | ✅ | `milestones` schema + UI |
83
+| Kanban boards | ✅ | Implemented |
84
+| PR ↔ Issue linking | ⚠️ | Basic via text parsing only |
85
+| Cross-repo issues | ❌ | Not implemented |
86
+| Labels | ✅ | Full implementation |
87
+| Assignees | ✅ | Full implementation |
88
+
89
+## 5. CI/CD & Automation
90
+| Feature | Status | Implementation Notes |
91
+| :--- | :---: | :--- |
92
+| Native CI pipeline support | ✅ | `pipeline.ts` - 1066 lines, GitHub Actions compatible |
93
+| External CI integration | ⚠️ | Webhook based, no official integrations |
94
+| Self-hosted runners | ✅ | `runner/` with Docker executor |
95
+| Secrets management | ✅ | Schema + UI for repo/org secrets |
96
+| Merge checks & gates | ⚠️ | Via branch protection, incomplete CI gates |
97
+| Automation rules engine | ⚠️ | `automations.ts` exists, has TODOs |
98
+| Webhooks | ✅ | Full implementation with UI |
99
+| Workflow templates | ❌ | Not implemented |
100
+
101
+## 6. Third-Party Integrations
102
+### 6.1 Code Quality & Coverage
103
+| Feature | Status | Notes |
104
+| :--- | :---: | :--- |
105
+| Codecov | ❌ | Not implemented |
106
+| Coveralls | ❌ | Not implemented |
107
+| SonarQube | ❌ | Not implemented |
108
+| Snyk | ⚠️ | Trivy-based scanning exists |
109
+
110
+### 6.2 CI Providers
111
+| Feature | Status | Notes |
112
+| :--- | :---: | :--- |
113
+| GitHub Actions | ✅ | Compatible workflow format |
114
+| GitLab CI | ❌ | Not supported |
115
+| CircleCI | ❌ | Not supported |
116
+| Buildkite | ❌ | Not supported |
117
+| Jenkins | ❌ | Not supported |
118
+
119
+### 6.3 Issue Tracking
120
+| Feature | Status | Notes |
121
+| :--- | :---: | :--- |
122
+| Jira | ⚠️ | Basic integration implemented (`jira.ts`) |
123
+| Linear | ❌ | Not implemented |
124
+| Trello | ❌ | Not implemented |
125
+| ClickUp | ❌ | Not implemented |
126
+
127
+### 6.4 Chat & Notifications
128
+| Feature | Status | Notes |
129
+| :--- | :---: | :--- |
130
+| Slack | ✅ | `slack-notifications.ts` + schema |
131
+| Microsoft Teams | ❌ | Not implemented |
132
+| Discord | ❌ | Not implemented |
133
+| Email notifications | ⚠️ | `email.ts` exists, needs enhancement |
134
+
135
+### 6.5 Cloud & Infrastructure
136
+| Feature | Status | Notes |
137
+| :--- | :---: | :--- |
138
+| AWS | ❌ | No direct integration |
139
+| Google Cloud | ❌ | No direct integration |
140
+| Microsoft Azure | ❌ | No direct integration |
141
+| Kubernetes-native | ❌ | Docker only currently |
142
+| Terraform/IaC hooks | ❌ | Not implemented |
143
+
144
+## 7. Dependency & Impact Awareness
145
+| Feature | Status | Implementation Notes |
146
+| :--- | :---: | :--- |
147
+| PR dependency visualization | ✅ | Stack visualization in `stacks.ts` |
148
+| Cross-repo change sets | ❌ | Not implemented |
149
+| Breaking-change detection | ❌ | Not implemented |
150
+| Database migration detection | ❌ | Not implemented |
151
+| API change awareness | ⚠️ | AI review can detect, no dedicated system |
152
+
153
+## 8. Security
154
+| Feature | Status | Implementation Notes |
155
+| :--- | :---: | :--- |
156
+| Role-based access control (RBAC) | ⚠️ | Custom roles (`custom_roles` table) added, UI pending |
157
+| Organization & team management | ✅ | Schema + UI implemented |
158
+| SSO (OIDC / SAML) | ✅ | `oidc.ts` implements OIDC fully, SAML missing |
159
+| MFA | ✅ | TOTP implemented (`src/pages/api/user/settings/2fa.ts`) |
160
+| Secret scanning | ⚠️ | TODO in codebase |
161
+| Dependency vulnerability scanning | ✅ | Trivy integration in `security.ts` |
162
+| License compliance scanning | ❌ | Not implemented |
163
+| Audit logs | ✅ | `audit.astro` + schema |
164
+| Session management | ✅ | JWT + session handling |
165
+| Rate limiting | ✅ | Implemented with Redis backend (`src/middleware/rate-limit.ts`) |
166
+| CSRF protection | ✅ | Implemented |
167
+| Input validation | ✅ | Zod schemas throughout |
168
+
169
+## 9. Analytics & Insights
170
+| Feature | Status | Implementation Notes |
171
+| :--- | :---: | :--- |
172
+| PR cycle time analytics | ✅ | `developer-metrics.ts` |
173
+| Review latency tracking | ✅ | Metrics schema exists |
174
+| Merge frequency metrics | ⚠️ | Basic tracking |
175
+| Developer workload insights | ⚠️ | Basic - needs enhancement |
176
+| Hotspot file detection | ❌ | Not implemented |
177
+| Delivery performance dashboards | ✅ | `insights.astro` pages |
178
+| Export metrics | ❌ | Not implemented |
179
+| Custom dashboards | ❌ | Not implemented |
180
+
181
+## 10. Notifications & Collaboration
182
+| Feature | Status | Implementation Notes |
183
+| :--- | :---: | :--- |
184
+| Smart notifications | ⚠️ | Basic notifications, no AI prioritization |
185
+| Blocking alerts | ⚠️ | Basic via inbox sections |
186
+| Daily/weekly digests | ❌ | Not implemented |
187
+| Mentions & subscriptions | ✅ | Implemented |
188
+| PR-level discussions | ✅ | Full threaded comments |
189
+| Activity feeds | ✅ | Dashboard activity feed |
190
+| Inbox sections | ✅ | `inbox-sections.ts` |
191
+| Real-time updates | ✅ | `realtime.ts` with SSE |
192
+
193
+## 11. Interfaces & Extensibility
194
+| Feature | Status | Implementation Notes |
195
+| :--- | :---: | :--- |
196
+| Web UI | ✅ | Astro + React, full implementation |
197
+| REST API | ✅ | Extensive API under `/api/` |
198
+| GraphQL API | ✅ | `graphql.ts` exists (Yoga based) |
199
+| CLI | ✅ | `cli/` package - 21+ commands |
200
+| Plugin/extension system | ⚠️ | `plugins.ts` infrastructure exists, but basic |
201
+| Webhook events | ✅ | Full implementation |
202
+| API documentation | ⚠️ | OpenAPI spec generator exists (`openapi.json.ts`) |
203
+
204
+## 12. Self-Hosted & Deployment
205
+| Feature | Status | Implementation Notes |
206
+| :--- | :---: | :--- |
207
+| Docker deployment | ✅ | Dockerfile + docker-compose |
208
+| Kubernetes deployment | ⚠️ | No official Helm chart |
209
+| Horizontal scaling | ⚠️ | Merge queue/rate limiting not distributed-safe |
210
+| Backup & restore tools | ❌ | Not implemented |
211
+| Config-as-code | ✅ | Environment variables |
212
+| Offline/air-gapped mode | ❌ | Not tested/documented |
213
+| Multi-tenant mode | ✅ | Organization-based multi-tenancy |
214
+
215
+---
216
+
217
+# Implementation Plan
218
+
219
+## Phase 1: Critical Security & Stability (Weeks 1-4)
220
+> **CAUTION**: These issues must be fixed before any production use
221
+
222
+| Task | Priority | Effort | Files Involved |
223
+| :--- | :--- | :--- | :--- |
224
+| **Fix runner auth bypass** | 🔴 Critical | 2h | `runner-auth.ts` |
225
+| **Add executor resource limits & timeouts** | 🔴 Critical | 4h | `executor.ts` |
226
+| **Distributed rate limiting (Redis)** | 🔴 Critical | 8h | New middleware |
227
+| **Distributed merge queue locks** | 🔴 Critical | 8h | `merge-queue.ts` |
228
+| **Complete TODO items** | 🟠 High | 16h | Multiple files |
229
+
230
+## Phase 2: Core Feature Completion (Weeks 5-12)
231
+| Task | Priority | Effort | Dependencies |
232
+| :--- | :--- | :--- | :--- |
233
+| **SSO (OIDC) integration** | 🔴 Critical | 24h | None |
234
+| **MFA/2FA implementation** | 🔴 Critical | 16h | TOTP via otplib |
235
+| **CODEOWNERS enforcement** | 🟠 High | 16h | `automations.ts` |
236
+| **Suggested changes in reviews** | 🟠 High | 12h | PR comments |
237
+| **Kanban board UI** | 🟡 Medium | 20h | Issues system |
238
+| **PR auto-merge rules** | 🟡 Medium | 12h | `automations.ts` |
239
+| **Custom PR states** | 🟡 Medium | 8h | Schema + UI |
240
+| **Required reviewers per state** | 🟡 Medium | 8h | Branch protection |
241
+
242
+## Phase 3: Integrations & Extensions (Weeks 13-20)
243
+| Task | Priority | Effort | Dependencies |
244
+| :--- | :--- | :--- | :--- |
245
+| **GraphQL API layer** | 🟠 High | 40h | REST API |
246
+| **Microsoft Teams integration** | 🟡 Medium | 16h | Slack pattern |
247
+| **Discord integration** | 🟡 Medium | 12h | Slack pattern |
248
+| **Jira integration** | 🟡 Medium | 24h | Webhooks |
249
+| **API documentation (OpenAPI)** | 🟡 Medium | 16h | REST endpoints |
250
+| **Plugin system enhancement** | 🟡 Medium | 24h | `plugins.ts` |
251
+
252
+## Phase 4: Advanced Features (Weeks 21-32)
253
+| Task | Priority | Effort | Dependencies |
254
+| :--- | :--- | :--- | :--- |
255
+| **Epics & sub-tasks** | 🟡 Medium | 24h | Issues |
256
+| **Custom issue fields** | 🟡 Medium | 20h | Schema |
257
+| **Issue workflows** | 🟡 Medium | 32h | State machine |
258
+| **Cross-repo support** | 🟡 Medium | 40h | Architecture |
259
+| **License compliance scanning** | 🟡 Medium | 16h | Security scan |
260
+| **Kubernetes Helm chart** | 🟡 Medium | 16h | Docker |
261
+| **Backup & restore tools** | 🟡 Medium | 24h | DB operations |
262
+| **API breaking change detection** | 🔵 Low | 40h | AI/AST analysis |
0
263
▼doc/missing_implementation_tracker.md
+74-0
1
+# Missing / Not-Implemented Tracker
2
+
3
+Date: 2026-02-15
4
+Scope: `src/`, `cli/`, `packages/`, and existing audit docs.
5
+
6
+## Summary
7
+- Explicit `not implemented` runtime blockers: 15
8
+- Explicit `TODO` implementation gaps: 5
9
+- Partial/placeholder implementations verified in code: 7
10
+- Existing broad feature audit available at `doc/feature_audit.md` (last updated 2026-02-06)
11
+
12
+## A) Runtime Blockers (Explicit Not Implemented)
13
+
14
+| Status | Area | Evidence |
15
+|---|---|---|
16
+| Missing | DB adapter: `mysql` | `src/db/adapter/index.ts:23` |
17
+| Missing | DB adapter: `mongodb` | `src/db/adapter/index.ts:25` |
18
+| Missing | DB adapter: `turso` | `src/db/adapter/index.ts:27` |
19
+| Missing | DB adapter: `planetscale` | `src/db/adapter/index.ts:30` |
20
+| Missing | DB adapter: `redis` | `src/db/adapter/index.ts:32` |
21
+| Missing | DB adapter: `firestore` | `src/db/adapter/index.ts:34` |
22
+| Missing | DB adapter: `dynamodb` | `src/db/adapter/index.ts:37` |
23
+| Missing | DB adapter: `neo4j` | `src/db/adapter/index.ts:40` |
24
+| Missing | DB adapter: `cockroachdb` | `src/db/adapter/index.ts:42` |
25
+| Missing | DB adapter: `cassandra` | `src/db/adapter/index.ts:44` |
26
+| Missing | DB adapter: `scylladb` | `src/db/adapter/index.ts:47` |
27
+| Missing | DB adapter: `surrealdb` | `src/db/adapter/index.ts:50` |
28
+| Missing | DB adapter: `tidb` | `src/db/adapter/index.ts:53` |
29
+| Missing | DB adapter: `mariadb` | `src/db/adapter/index.ts:56` |
30
+| Missing | AI provider: `anthropic` | `src/lib/ai/index.ts:15` |
31
+
32
+## B) TODO Gaps (Declared but Incomplete)
33
+
34
+| Status | Area | Evidence |
35
+|---|---|---|
36
+| Partial | Admin storage config test action | `src/pages/api/admin/config/storage.ts:68` |
37
+| Partial | Issue detail page assignees mapping | `src/pages/[owner]/[repo]/issues/[number].astro:80` |
38
+| Partial | Issue detail page labels mapping | `src/pages/[owner]/[repo]/issues/[number].astro:81` |
39
+| Partial | Issue creation permission check | `src/pages/[owner]/[repo]/issues/new.astro:39` |
40
+| Partial | Team-based reviewer requirements in PR state updates | `src/pages/api/repos/[owner]/[repo]/pulls/[number]/index.ts:140` |
41
+
42
+## C) Placeholder / Mock Implementations
43
+
44
+| Status | Area | Evidence |
45
+|---|---|---|
46
+| Partial | Automation builder UI uses placeholder alerts instead of full builder flow | `src/pages/settings/automations.astro:454` |
47
+| Partial | File-based PR dependency detection logs not implemented and returns empty conflicts | `src/lib/pr-dependencies.ts:93` |
48
+| Partial | GraphQL stargazer count returns constant `0` because stars table path is not implemented | `src/lib/graphql/resolvers.ts:212` |
49
+| Partial | Notification digests are placeholder HTML with `itemCount: 0` | `src/lib/chat-notifications.ts:554` |
50
+| Partial | SonarQube trigger creates pending report instead of running scanner | `src/lib/code-quality.ts:330` |
51
+| Partial | Snyk scan path currently simulates result with empty issue list | `src/lib/code-quality.ts:437` |
52
+| Partial | Admin metrics page has simplified mock user growth (`Promise.resolve([])`) | `src/pages/admin/_metrics-disabled.txt:57` |
53
+
54
+## D) Existing Broad Audit (High-Level)
55
+
56
+`doc/feature_audit.md` already tracks feature-level implementation state across 122 features:
57
+- Implemented: 47
58
+- Partial: 29
59
+- Missing: 46
60
+
61
+Use that document for roadmap-level planning and this tracker for code-level execution items.
62
+
63
+## Suggested Next Implementation Order
64
+
65
+1. Close hard runtime blockers first:
66
+ - Choose supported DB drivers and remove/guard unsupported ones (`src/db/adapter/index.ts`).
67
+ - Implement or disable `anthropic` provider path in `src/lib/ai/index.ts`.
68
+2. Resolve TODOs that affect correctness/security:
69
+ - Issue create permission check.
70
+ - Team reviewer enforcement in PR state transitions.
71
+3. Replace placeholders with production paths:
72
+ - Automations builder UI flow.
73
+ - Digest generation from real activity data.
74
+ - PR file dependency detection.
0
75
▼doc/production_security_audit_2026-02-15.md
+82-0
1
+# Production Security Audit
2
+
3
+Date: 2026-02-15
4
+Repository: OpenCodeHub
5
+Scope: `src/`, `cli/`, API routes, auth flow, secrets handling, and dependency audit attempt.
6
+
7
+## Executive Summary
8
+
9
+Critical security issues were present and have been patched in this pass:
10
+- Removed hardcoded JWT fallback secret and made `JWT_SECRET` mandatory.
11
+- Enforced admin access on previously exposed admin dashboard and admin stats endpoint.
12
+- Enforced admin access on realtime connection stats endpoint.
13
+- Secured internal git hook endpoints by requiring `INTERNAL_HOOK_SECRET` and rejecting unauthenticated calls.
14
+- Removed insecure fallback for cron auth (`CRON_SECRET`) and fail-closed on missing config.
15
+- Removed fixed runner fallback secret and switched to required production secret + ephemeral dev secret.
16
+- Prevented API key leakage in `/api/user/ai-config` GET responses (masked metadata only).
17
+- Added encryption-at-rest for user AI provider API keys.
18
+- Removed auth callback localhost fallbacks in production-sensitive redirect paths.
19
+- Added regression unit tests for secret handling and strict site URL behavior.
20
+
21
+## Fixed Findings
22
+
23
+| Severity | Finding | Status | Evidence |
24
+|---|---|---|---|
25
+| Critical | Hardcoded JWT fallback secret | Fixed | `src/lib/auth.ts` |
26
+| Critical | Admin dashboard route lacked auth guard | Fixed | `src/pages/admin/index.astro` |
27
+| High | `/api/admin/stats` did not enforce admin auth | Fixed | `src/pages/api/admin/stats.ts` |
28
+| High | `/api/realtime/events` stats endpoint exposed to any logged-in user | Fixed | `src/pages/api/realtime/events.ts` |
29
+| Critical | `/api/internal/hooks/pre-receive` had no shared-secret auth | Fixed | `src/pages/api/internal/hooks/pre-receive.ts` |
30
+| High | `/api/internal/hooks/post-receive` accepted weak default secret fallback | Fixed | `src/pages/api/internal/hooks/post-receive.ts` |
31
+| High | Cron GC endpoint accepted weak default secret fallback | Fixed | `src/pages/api/cron/gc.ts` |
32
+| High | Git hook installer embedded weak fallback secret | Fixed | `src/lib/git.ts` |
33
+| Medium | Runner auth used fixed fallback secret in dev path | Fixed | `src/lib/runner-auth.ts` |
34
+| Medium | Env validation did not require `CRON_SECRET`/`RUNNER_SECRET` | Fixed | `src/lib/env-validation.ts` |
35
+| High | `/api/user/ai-config` could return raw API keys | Fixed | `src/pages/api/user/ai-config.ts` |
36
+| High | AI provider API keys were stored as plain text JSON | Fixed | `src/lib/ai-config.ts`, `src/pages/api/user/ai-config.ts` |
37
+| Medium | Auth callback routes used localhost fallback when `SITE_URL` missing | Fixed | `src/pages/api/auth/github/callback.ts`, `src/pages/api/auth/sso/callback.ts`, `src/pages/api/auth/sso/[provider].ts`, `src/lib/site-url.ts` |
38
+| Medium | Missing regression tests for new secret/auth controls | Fixed | `tests/unit/ai-config.test.ts`, `tests/unit/site-url.test.ts`, `tests/unit/runner-auth.test.ts` |
39
+
40
+## Remaining Risks (Not Fully Resolved)
41
+
42
+| Severity | Risk | Evidence | Recommendation |
43
+|---|---|---|---|
44
+| Medium | CDN-hosted scripts are used directly (supply-chain risk) | e.g. Monaco/Alpine CDN script includes in pages | Pin versions with SRI hashes or self-host critical assets. |
45
+| Medium | Production readiness currently blocked by pre-existing lint/type issues unrelated to this patch | `npm run lint` output (many existing errors) | Clean type/lint baseline and enforce CI gates before release. |
46
+| Medium | Security test suite currently failing from test setup/schema issues | `tests/security.test.ts` (`db.execute is not a function`) | Repair test harness and add regression tests for newly fixed auth/secret controls. |
47
+
48
+## Dependency Vulnerability Scan
49
+
50
+Dependency vulnerability audit could not be completed due network resolution failure:
51
+- `npm audit --omit=dev --json` failed with `getaddrinfo EAI_AGAIN registry.npmjs.org`.
52
+
53
+Action required when network is available:
54
+1. Run `npm audit --omit=dev` in repo root.
55
+2. Run `npm audit --omit=dev` in `cli/`.
56
+3. Triage and patch high/critical advisories first.
57
+
58
+## Validation Notes
59
+
60
+Checks performed in this pass:
61
+- Static grep scan for hardcoded secrets/defaults and auth bypass patterns.
62
+- Manual review of critical auth, admin, internal hook, and cron endpoints.
63
+- Attempted test/lint verification (`npm run test -- tests/security.test.ts`, `npm run lint`).
64
+- Added and executed focused regression tests for this hardening set:
65
+ - `tests/unit/ai-config.test.ts`
66
+ - `tests/unit/site-url.test.ts`
67
+ - `tests/unit/runner-auth.test.ts`
68
+
69
+Limitations:
70
+- Full dependency CVE scan blocked by offline registry access.
71
+- Existing repository-wide type/lint failures prevent clean full-build security validation.
72
+
73
+## Next Production Hardening Steps
74
+
75
+1. Expand encrypted-at-rest handling to additional sensitive config domains (beyond AI keys).
76
+2. Repair security tests and add broader regression coverage for:
77
+ - admin route/API authorization
78
+ - internal hook secret verification
79
+ - cron secret verification
80
+ - secret redaction in config APIs
81
+3. Re-run dependency CVE audits and patch high/critical packages.
82
+4. Add CI security gates (lint/typecheck/tests + secret scan + audit threshold).
0
83
▼docs-site/astro.config.mjs
+6-2
8
8
starlight({
9
9
title: "OpenCodeHub Docs",
10
10
logo: {
11
- light: "../../public/logo-light.png",
12
- dark: "../../public/logo-dark.png",
11
+ light: "./src/assets/logo-light.png",
12
+ dark: "./src/assets/logo-dark.png",
13
13
replacesTitle: false,
14
14
},
15
+ customCss: ["./src/custom.css"],
16
+ components: {
17
+ Hero: "./src/components/StarlightHero.astro",
18
+ },
15
19
social: [
16
20
{
17
21
icon: "github",
18
22
▼docs-site/package.json
+2-1
7
7
"start": "astro dev",
8
8
"build": "astro build",
9
9
"preview": "astro preview",
10
+ "deploy": "wrangler deploy",
10
11
"astro": "astro"
11
12
},
12
13
"dependencies": {
14
15
"astro": "^5.6.1",
15
16
"sharp": "^0.34.2"
16
17
}
17
-}
18
+}
18
19
▼docs-site/public/architecture.svg
+89-0
1
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 500">
2
+ <defs>
3
+ <style>
4
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&amp;display=swap');
5
+ </style>
6
+ <!-- Gradients -->
7
+ <linearGradient id="grad-app" x1="0%" y1="0%" x2="100%" y2="100%">
8
+ <stop offset="0%" style="stop-color:#0f172a;stop-opacity:1" />
9
+ <stop offset="100%" style="stop-color:#334155;stop-opacity:1" />
10
+ </linearGradient>
11
+ <linearGradient id="grad-db" x1="0%" y1="0%" x2="0%" y2="100%">
12
+ <stop offset="0%" style="stop-color:#f59e0b;stop-opacity:1" />
13
+ <stop offset="100%" style="stop-color:#d97706;stop-opacity:1" />
14
+ </linearGradient>
15
+ <linearGradient id="grad-git" x1="0%" y1="0%" x2="100%" y2="100%">
16
+ <stop offset="0%" style="stop-color:#ef4444;stop-opacity:1" />
17
+ <stop offset="100%" style="stop-color:#b91c1c;stop-opacity:1" />
18
+ </linearGradient>
19
+ <linearGradient id="grad-user" x1="0%" y1="0%" x2="100%" y2="100%">
20
+ <stop offset="0%" style="stop-color:#3b82f6;stop-opacity:1" />
21
+ <stop offset="100%" style="stop-color:#1d4ed8;stop-opacity:1" />
22
+ </linearGradient>
23
+
24
+ <filter id="shadow">
25
+ <feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#000" flood-opacity="0.2"/>
26
+ </filter>
27
+ </defs>
28
+
29
+ <!-- Title -->
30
+ <text x="400" y="40" font-family="Inter, sans-serif" font-size="24" font-weight="700" text-anchor="middle" fill="#1e293b">System Architecture</text>
31
+
32
+ <!-- Central Hub: OpenCodeHub Server -->
33
+ <g transform="translate(250, 150)">
34
+ <rect x="0" y="0" width="300" height="200" rx="16" fill="url(#grad-app)" filter="url(#shadow)"/>
35
+ <text x="150" y="40" font-family="Inter, sans-serif" font-size="20" font-weight="700" text-anchor="middle" fill="white">OpenCodeHub</text>
36
+
37
+ <!-- Internal Components -->
38
+ <g transform="translate(30, 70)">
39
+ <!-- Next.js -->
40
+ <rect x="0" y="0" width="100" height="40" rx="6" fill="#1e293b" stroke="#475569" stroke-width="1"/>
41
+ <text x="50" y="25" font-family="Inter, sans-serif" font-size="14" text-anchor="middle" fill="#e2e8f0">Next.js App</text>
42
+ </g>
43
+
44
+ <g transform="translate(170, 70)">
45
+ <!-- Git Service -->
46
+ <rect x="0" y="0" width="100" height="40" rx="6" fill="#1e293b" stroke="#475569" stroke-width="1"/>
47
+ <text x="50" y="25" font-family="Inter, sans-serif" font-size="14" text-anchor="middle" fill="#e2e8f0">Git Service</text>
48
+ </g>
49
+
50
+ <g transform="translate(100, 130)">
51
+ <!-- API -->
52
+ <rect x="0" y="0" width="100" height="40" rx="6" fill="#1e293b" stroke="#475569" stroke-width="1"/>
53
+ <text x="50" y="25" font-family="Inter, sans-serif" font-size="14" text-anchor="middle" fill="#e2e8f0">tRPC API</text>
54
+ </g>
55
+ </g>
56
+
57
+ <!-- Database Node (Bottom) -->
58
+ <g transform="translate(350, 400)">
59
+ <path d="M0,20 Q50,-10 100,20 L100,80 Q50,110 0,80 Z" fill="url(#grad-db)" filter="url(#shadow)"/>
60
+ <path d="M0,20 Q50,50 100,20" fill="none" stroke="#b45309" stroke-width="2" opacity="0.5"/>
61
+ <text x="50" y="60" font-family="Inter, sans-serif" font-size="16" font-weight="600" text-anchor="middle" fill="white">Database</text>
62
+ </g>
63
+
64
+ <!-- Users (Left) -->
65
+ <g transform="translate(50, 200)">
66
+ <circle cx="50" cy="50" r="40" fill="url(#grad-user)" filter="url(#shadow)"/>
67
+ <text x="50" y="110" font-family="Inter, sans-serif" font-size="16" font-weight="600" text-anchor="middle" fill="#0f172a">Developers</text>
68
+ <text x="50" y="130" font-family="Inter, sans-serif" font-size="12" text-anchor="middle" fill="#64748b">CLI &amp; Web</text>
69
+ </g>
70
+
71
+ <!-- Repositories (Right) -->
72
+ <g transform="translate(650, 200)">
73
+ <rect x="0" y="10" width="80" height="90" rx="4" fill="url(#grad-git)" filter="url(#shadow)"/>
74
+ <path d="M10,10 L70,10 L70,30 L10,30 Z" fill="#fff" opacity="0.2"/>
75
+ <text x="40" y="60" font-family="Inter, sans-serif" font-size="16" font-weight="600" text-anchor="middle" fill="white">Repos</text>
76
+ <text x="40" y="120" font-family="Inter, sans-serif" font-size="12" text-anchor="middle" fill="#64748b">Raw Git Data</text>
77
+ </g>
78
+
79
+ <!-- Connections -->
80
+ <!-- User to App -->
81
+ <path d="M150 250 L240 250" stroke="#94a3b8" stroke-width="2" stroke-dasharray="4"/>
82
+
83
+ <!-- App to DB -->
84
+ <path d="M400 350 L400 390" stroke="#94a3b8" stroke-width="2"/>
85
+
86
+ <!-- App to Repos -->
87
+ <path d="M550 250 L640 250" stroke="#94a3b8" stroke-width="2"/>
88
+
89
+</svg>
0
90
▼docs-site/public/assets/houston.webp
+0-0
Binary file not shown.
▼docs-site/public/logo-dark.png
+0-0
Binary file not shown.
▼docs-site/public/logo-light.png
+0-0
Binary file not shown.
▼docs-site/src/assets/houston.webp
+0-0
Binary file not shown.
▼docs-site/src/assets/logo-dark.png
+0-0
Binary file not shown.
▼docs-site/src/assets/logo-light.png
+0-0
Binary file not shown.
▼docs-site/src/components/Hero.astro
+534-0
1
+---
2
+const { title, tagline } = Astro.props;
3
+---
4
+
5
+<div class="hero-container" data-och-hero>
6
+ <div class="hero-background">
7
+ <div class="gradient-orb orb-1"></div>
8
+ <div class="gradient-orb orb-2"></div>
9
+ <div class="gradient-orb orb-3"></div>
10
+ <div class="stars"></div>
11
+ <svg
12
+ class="bg-grid"
13
+ viewBox="0 0 1200 600"
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ aria-hidden="true"
16
+ focusable="false"
17
+ >
18
+ <defs>
19
+ <linearGradient id="ochGrid" x1="0" y1="0" x2="1" y2="1">
20
+ <stop offset="0" stop-color="#8be9fd" stop-opacity="0.22"></stop>
21
+ <stop offset="0.5" stop-color="#bd93f9" stop-opacity="0.18"></stop>
22
+ <stop offset="1" stop-color="#ff79c6" stop-opacity="0.14"></stop>
23
+ </linearGradient>
24
+ <pattern id="grid" width="48" height="48" patternUnits="userSpaceOnUse">
25
+ <path
26
+ d="M 48 0 L 0 0 0 48"
27
+ fill="none"
28
+ stroke="url(#ochGrid)"
29
+ stroke-width="1"></path>
30
+ </pattern>
31
+ </defs>
32
+ <rect width="1200" height="600" fill="url(#grid)" opacity="0.35"></rect>
33
+ </svg>
34
+ </div>
35
+
36
+ <div class="hero-content">
37
+ <div class="hero-text">
38
+ <h1 class="hero-title">
39
+ <span class="title-line fade-in-up" style="animation-delay: 0.1s;"
40
+ >{title ?? "OpenCodeHub"}</span
41
+ >
42
+ <span
43
+ class="title-line fade-in-up gradient-text"
44
+ style="animation-delay: 0.3s;">Documentation</span
45
+ >
46
+ </h1>
47
+
48
+ <p class="hero-tagline fade-in-up" style="animation-delay: 0.5s;">
49
+ {tagline}
50
+ </p>
51
+
52
+ <div class="hero-actions fade-in-up" style="animation-delay: 0.7s;">
53
+ <a href="/getting-started/installation/" class="hero-button primary">
54
+ <span>Get Started</span>
55
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
56
+ <path
57
+ d="M6 12L10 8L6 4"
58
+ stroke="currentColor"
59
+ stroke-width="2"
60
+ stroke-linecap="round"
61
+ stroke-linejoin="round"></path>
62
+ </svg>
63
+ </a>
64
+ <a
65
+ href="https://github.com/swadhinbiswas/OpencodeHub"
66
+ class="hero-button secondary"
67
+ target="_blank"
68
+ rel="noopener"
69
+ >
70
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
71
+ <path
72
+ d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
73
+ ></path>
74
+ </svg>
75
+ <span>View on GitHub</span>
76
+ </a>
77
+ </div>
78
+ </div>
79
+
80
+ <div class="hero-visual fade-in-up" style="animation-delay: 0.9s;">
81
+ <div class="mascot-container">
82
+ <img
83
+ src="/logo-light.png"
84
+ alt="OpenCodeHub Logo"
85
+ class="mascot floating logo-light"
86
+ />
87
+ <img
88
+ src="/logo-dark.png"
89
+ alt="OpenCodeHub Logo"
90
+ class="mascot floating logo-dark"
91
+ />
92
+ <svg
93
+ class="logo-spark"
94
+ viewBox="0 0 320 320"
95
+ xmlns="http://www.w3.org/2000/svg"
96
+ aria-hidden="true"
97
+ focusable="false"
98
+ >
99
+ <path
100
+ d="M44 196 C88 120, 132 232, 176 156 S264 184, 276 124"
101
+ fill="none"
102
+ stroke="currentColor"
103
+ stroke-width="3"
104
+ stroke-linecap="round"
105
+ stroke-linejoin="round"></path>
106
+ <path
107
+ d="M62 116 L86 116 M74 104 L74 128"
108
+ fill="none"
109
+ stroke="currentColor"
110
+ stroke-width="3"
111
+ stroke-linecap="round"></path>
112
+ <path
113
+ d="M248 216 L272 216 M260 204 L260 228"
114
+ fill="none"
115
+ stroke="currentColor"
116
+ stroke-width="3"
117
+ stroke-linecap="round"></path>
118
+ </svg>
119
+ <div class="glow-effect"></div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+</div>
124
+
125
+<script>
126
+ (() => {
127
+ const heroEl = document.querySelector("[data-och-hero]");
128
+ if (!(heroEl instanceof HTMLElement)) return;
129
+
130
+ const reduce = window.matchMedia?.(
131
+ "(prefers-reduced-motion: reduce)",
132
+ )?.matches;
133
+ if (reduce) return;
134
+
135
+ const setVars = (clientX: number, clientY: number) => {
136
+ const r = heroEl.getBoundingClientRect();
137
+ const x = (clientX - r.left) / r.width;
138
+ const y = (clientY - r.top) / r.height;
139
+ heroEl.style.setProperty("--mx", String((x - 0.5) * 2));
140
+ heroEl.style.setProperty("--my", String((y - 0.5) * 2));
141
+ };
142
+
143
+ const onMove = (e: MouseEvent | TouchEvent) => {
144
+ if ("touches" in e && e.touches?.[0])
145
+ setVars(e.touches[0].clientX, e.touches[0].clientY);
146
+ else setVars((e as MouseEvent).clientX, (e as MouseEvent).clientY);
147
+ };
148
+
149
+ const onLeave = () => {
150
+ heroEl.style.setProperty("--mx", "0");
151
+ heroEl.style.setProperty("--my", "0");
152
+ };
153
+
154
+ heroEl.addEventListener("mousemove", onMove, { passive: true });
155
+ heroEl.addEventListener("touchmove", onMove, { passive: true });
156
+ heroEl.addEventListener("mouseleave", onLeave, { passive: true });
157
+ })();
158
+</script>
159
+
160
+<style>
161
+ .hero-container {
162
+ position: relative;
163
+ min-height: 70vh;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ overflow: hidden;
168
+ padding: 4rem 2rem;
169
+ }
170
+
171
+ .hero-background {
172
+ position: absolute;
173
+ inset: 0;
174
+ overflow: hidden;
175
+ z-index: 0;
176
+ }
177
+
178
+ .bg-grid {
179
+ position: absolute;
180
+ inset: -20% -20% -20% -20%;
181
+ width: 140%;
182
+ height: 140%;
183
+ transform: translate(calc(var(--mx, 0) * -10px), calc(var(--my, 0) * -10px))
184
+ rotate(-2deg);
185
+ transition: transform 120ms ease;
186
+ opacity: 0.3;
187
+ pointer-events: none;
188
+ }
189
+
190
+ .gradient-orb {
191
+ position: absolute;
192
+ border-radius: 50%;
193
+ filter: blur(80px);
194
+ opacity: 0.3;
195
+ animation: float 20s ease-in-out infinite;
196
+ }
197
+
198
+ .orb-1 {
199
+ width: 500px;
200
+ height: 500px;
201
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
202
+ top: -10%;
203
+ left: -10%;
204
+ animation-delay: 0s;
205
+ transform: translate(calc(var(--mx, 0) * 16px), calc(var(--my, 0) * 10px));
206
+ }
207
+
208
+ .orb-2 {
209
+ width: 400px;
210
+ height: 400px;
211
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
212
+ top: 40%;
213
+ right: -5%;
214
+ animation-delay: -7s;
215
+ transform: translate(calc(var(--mx, 0) * -12px), calc(var(--my, 0) * 14px));
216
+ }
217
+
218
+ .orb-3 {
219
+ width: 350px;
220
+ height: 350px;
221
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
222
+ bottom: -10%;
223
+ left: 30%;
224
+ animation-delay: -14s;
225
+ transform: translate(calc(var(--mx, 0) * 10px), calc(var(--my, 0) * -14px));
226
+ }
227
+
228
+ .stars {
229
+ position: absolute;
230
+ inset: 0;
231
+ background-image:
232
+ radial-gradient(2px 2px at 20% 30%, white, transparent),
233
+ radial-gradient(2px 2px at 60% 70%, white, transparent),
234
+ radial-gradient(1px 1px at 50% 50%, white, transparent),
235
+ radial-gradient(1px 1px at 80% 10%, white, transparent),
236
+ radial-gradient(2px 2px at 90% 60%, white, transparent),
237
+ radial-gradient(1px 1px at 33% 80%, white, transparent),
238
+ radial-gradient(1px 1px at 70% 25%, white, transparent);
239
+ background-size: 200% 200%;
240
+ animation: twinkle 8s ease-in-out infinite;
241
+ opacity: 0.5;
242
+ }
243
+
244
+ .hero-content {
245
+ position: relative;
246
+ z-index: 1;
247
+ max-width: 1200px;
248
+ width: 100%;
249
+ display: grid;
250
+ grid-template-columns: 1fr 1fr;
251
+ gap: 4rem;
252
+ align-items: center;
253
+ }
254
+
255
+ .hero-text {
256
+ display: flex;
257
+ flex-direction: column;
258
+ gap: 2rem;
259
+ }
260
+
261
+ .hero-title {
262
+ font-size: clamp(2.5rem, 6vw, 4.5rem);
263
+ font-weight: 800;
264
+ line-height: 1.1;
265
+ margin: 0;
266
+ display: flex;
267
+ flex-direction: column;
268
+ gap: 0.5rem;
269
+ }
270
+
271
+ .title-line {
272
+ display: block;
273
+ }
274
+
275
+ .gradient-text {
276
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
277
+ background-size: 200% 200%;
278
+ -webkit-background-clip: text;
279
+ -webkit-text-fill-color: transparent;
280
+ background-clip: text;
281
+ animation: gradient-shift 8s ease infinite;
282
+ }
283
+
284
+ .hero-tagline {
285
+ font-size: clamp(1rem, 2vw, 1.25rem);
286
+ line-height: 1.6;
287
+ color: var(--sl-color-gray-2);
288
+ margin: 0;
289
+ max-width: 600px;
290
+ }
291
+
292
+ .hero-actions {
293
+ display: flex;
294
+ gap: 1rem;
295
+ flex-wrap: wrap;
296
+ }
297
+
298
+ .hero-button {
299
+ display: inline-flex;
300
+ align-items: center;
301
+ gap: 0.5rem;
302
+ padding: 0.875rem 1.75rem;
303
+ border-radius: 12px;
304
+ font-weight: 600;
305
+ font-size: 1rem;
306
+ text-decoration: none;
307
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
308
+ position: relative;
309
+ overflow: hidden;
310
+ }
311
+
312
+ .hero-button::before {
313
+ content: "";
314
+ position: absolute;
315
+ inset: 0;
316
+ border-radius: inherit;
317
+ padding: 2px;
318
+ background: linear-gradient(135deg, #667eea, #764ba2, #f093fb);
319
+ -webkit-mask:
320
+ linear-gradient(#fff 0 0) content-box,
321
+ linear-gradient(#fff 0 0);
322
+ -webkit-mask-composite: xor;
323
+ mask-composite: exclude;
324
+ opacity: 0;
325
+ transition: opacity 0.3s;
326
+ }
327
+
328
+ .hero-button.primary {
329
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
330
+ color: white;
331
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
332
+ }
333
+
334
+ .hero-button.primary:hover {
335
+ transform: translateY(-2px);
336
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.6);
337
+ }
338
+
339
+ .hero-button.secondary {
340
+ background: var(--sl-color-bg-nav);
341
+ color: var(--sl-color-white);
342
+ border: 2px solid var(--sl-color-gray-5);
343
+ }
344
+
345
+ .hero-button.secondary:hover {
346
+ transform: translateY(-2px);
347
+ border-color: var(--sl-color-gray-4);
348
+ background: var(--sl-color-gray-6);
349
+ }
350
+
351
+ .hero-button.secondary:hover::before {
352
+ opacity: 1;
353
+ }
354
+
355
+ .hero-visual {
356
+ display: flex;
357
+ justify-content: center;
358
+ align-items: center;
359
+ }
360
+
361
+ .mascot-container {
362
+ position: relative;
363
+ width: 100%;
364
+ max-width: 400px;
365
+ aspect-ratio: 1;
366
+ }
367
+
368
+ .mascot {
369
+ width: 100%;
370
+ height: 100%;
371
+ object-fit: contain;
372
+ filter: drop-shadow(0 20px 40px rgba(102, 126, 234, 0.3));
373
+ cursor: pointer;
374
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
375
+ }
376
+
377
+ .logo-light {
378
+ display: block;
379
+ }
380
+
381
+ .logo-dark {
382
+ display: none;
383
+ }
384
+
385
+ html[data-theme="dark"] .logo-light {
386
+ display: none;
387
+ }
388
+
389
+ html[data-theme="dark"] .logo-dark {
390
+ display: block;
391
+ }
392
+
393
+ .mascot:hover {
394
+ transform: scale(1.05) rotate(5deg);
395
+ }
396
+
397
+ .glow-effect {
398
+ position: absolute;
399
+ inset: 10%;
400
+ background: radial-gradient(
401
+ circle,
402
+ rgba(102, 126, 234, 0.3) 0%,
403
+ transparent 70%
404
+ );
405
+ border-radius: 50%;
406
+ filter: blur(40px);
407
+ animation: pulse 3s ease-in-out infinite;
408
+ pointer-events: none;
409
+ }
410
+
411
+ .logo-spark {
412
+ position: absolute;
413
+ inset: -10%;
414
+ width: 120%;
415
+ height: 120%;
416
+ color: color-mix(in srgb, #8be9fd 60%, #bd93f9);
417
+ opacity: 0.55;
418
+ pointer-events: none;
419
+ transform: translate(calc(var(--mx, 0) * 10px), calc(var(--my, 0) * 10px));
420
+ transition: transform 120ms ease;
421
+ }
422
+
423
+ .logo-spark path {
424
+ stroke-dasharray: 220;
425
+ stroke-dashoffset: 220;
426
+ animation: draw 1.6s ease forwards 0.7s;
427
+ }
428
+
429
+ @keyframes draw {
430
+ to {
431
+ stroke-dashoffset: 0;
432
+ }
433
+ }
434
+
435
+ /* Animations */
436
+ @keyframes float {
437
+ 0%,
438
+ 100% {
439
+ transform: translate(0, 0) rotate(0deg);
440
+ }
441
+ 33% {
442
+ transform: translate(30px, -30px) rotate(5deg);
443
+ }
444
+ 66% {
445
+ transform: translate(-20px, 20px) rotate(-5deg);
446
+ }
447
+ }
448
+
449
+ @keyframes floating {
450
+ 0%,
451
+ 100% {
452
+ transform: translateY(0);
453
+ }
454
+ 50% {
455
+ transform: translateY(-20px);
456
+ }
457
+ }
458
+
459
+ @keyframes gradient-shift {
460
+ 0%,
461
+ 100% {
462
+ background-position: 0% 50%;
463
+ }
464
+ 50% {
465
+ background-position: 100% 50%;
466
+ }
467
+ }
468
+
469
+ @keyframes twinkle {
470
+ 0%,
471
+ 100% {
472
+ opacity: 0.3;
473
+ }
474
+ 50% {
475
+ opacity: 0.8;
476
+ }
477
+ }
478
+
479
+ @keyframes pulse {
480
+ 0%,
481
+ 100% {
482
+ opacity: 0.3;
483
+ transform: scale(1);
484
+ }
485
+ 50% {
486
+ opacity: 0.6;
487
+ transform: scale(1.1);
488
+ }
489
+ }
490
+
491
+ @keyframes fade-in-up {
492
+ from {
493
+ opacity: 0;
494
+ transform: translateY(30px);
495
+ }
496
+ to {
497
+ opacity: 1;
498
+ transform: translateY(0);
499
+ }
500
+ }
501
+
502
+ .fade-in-up {
503
+ animation: fade-in-up 0.8s cubic-bezier(0.4, 0, 0.2, 1) both;
504
+ }
505
+
506
+ .floating {
507
+ animation: floating 6s ease-in-out infinite;
508
+ }
509
+
510
+ @media (max-width: 768px) {
511
+ .hero-content {
512
+ grid-template-columns: 1fr;
513
+ gap: 2rem;
514
+ text-align: center;
515
+ }
516
+
517
+ .hero-actions {
518
+ justify-content: center;
519
+ }
520
+
521
+ .hero-visual {
522
+ order: -1;
523
+ }
524
+
525
+ .mascot-container {
526
+ max-width: 250px;
527
+ }
528
+
529
+ .hero-tagline {
530
+ margin-left: auto;
531
+ margin-right: auto;
532
+ }
533
+ }
534
+</style>
0
535
▼docs-site/src/components/StarlightHero.astro
+565-0
1
+---
2
+// Custom Starlight Hero (used when frontmatter `hero` exists)
3
+const { data } = Astro.locals.starlightRoute.entry;
4
+const { title = data.title, tagline } = data.hero || {};
5
+
6
+const actions = [
7
+ {
8
+ href: "/getting-started/installation/",
9
+ text: "Get Started",
10
+ variant: "primary",
11
+ },
12
+ {
13
+ href: "https://github.com/swadhinbiswas/OpencodeHub",
14
+ text: "View on GitHub",
15
+ variant: "secondary",
16
+ external: true,
17
+ },
18
+];
19
+---
20
+
21
+<section class="och-hero" data-och-hero>
22
+ <div class="och-bg" aria-hidden="true">
23
+ <div class="orb orb-1"></div>
24
+ <div class="orb orb-2"></div>
25
+ <div class="orb orb-3"></div>
26
+
27
+ <svg class="grid" viewBox="0 0 1200 600" xmlns="http://www.w3.org/2000/svg">
28
+ <defs>
29
+ <linearGradient id="ochGrid" x1="0" y1="0" x2="1" y2="1">
30
+ <stop offset="0" stop-color="#8be9fd" stop-opacity="0.20"></stop>
31
+ <stop offset="0.5" stop-color="#bd93f9" stop-opacity="0.16"></stop>
32
+ <stop offset="1" stop-color="#ff79c6" stop-opacity="0.12"></stop>
33
+ </linearGradient>
34
+ <pattern
35
+ id="gridPattern"
36
+ width="44"
37
+ height="44"
38
+ patternUnits="userSpaceOnUse"
39
+ >
40
+ <path
41
+ d="M 44 0 L 0 0 0 44"
42
+ fill="none"
43
+ stroke="url(#ochGrid)"
44
+ stroke-width="1"></path>
45
+ </pattern>
46
+ </defs>
47
+ <rect width="1200" height="600" fill="url(#gridPattern)" opacity="0.35"
48
+ ></rect>
49
+ </svg>
50
+
51
+ <svg class="spark" viewBox="0 0 380 180" xmlns="http://www.w3.org/2000/svg">
52
+ <path
53
+ d="M20 120 C70 40, 120 160, 170 90 S280 120, 350 60"
54
+ fill="none"
55
+ stroke="currentColor"
56
+ stroke-width="3"
57
+ stroke-linecap="round"></path>
58
+ <circle cx="20" cy="120" r="5"></circle>
59
+ </svg>
60
+ </div>
61
+
62
+ <div class="och-inner">
63
+ <div class="copy">
64
+ <div class="badges" aria-label="Highlights">
65
+ <span class="badge">Stacked PRs</span>
66
+ <span class="badge">AI Review</span>
67
+ <span class="badge">Merge Queue</span>
68
+ </div>
69
+
70
+ <h1 class="title">
71
+ <span class="title-top" set:html={title} />
72
+ <span class="title-bottom">Documentation</span>
73
+ </h1>
74
+
75
+ {tagline && <p class="tagline" set:html={tagline} />}
76
+
77
+ <div class="actions">
78
+ {
79
+ actions.map((a) => (
80
+ <a
81
+ class={`btn ${a.variant}`}
82
+ href={a.href}
83
+ target={a.external ? "_blank" : undefined}
84
+ rel={a.external ? "noopener" : undefined}
85
+ >
86
+ <span>{a.text}</span>
87
+ {a.variant === "primary" && (
88
+ <svg
89
+ width="16"
90
+ height="16"
91
+ viewBox="0 0 16 16"
92
+ fill="none"
93
+ aria-hidden="true"
94
+ >
95
+ <path
96
+ d="M6 12L10 8L6 4"
97
+ stroke="currentColor"
98
+ stroke-width="2"
99
+ stroke-linecap="round"
100
+ stroke-linejoin="round"
101
+ />
102
+ </svg>
103
+ )}
104
+ {a.variant === "secondary" && (
105
+ <svg
106
+ width="16"
107
+ height="16"
108
+ viewBox="0 0 24 24"
109
+ fill="currentColor"
110
+ aria-hidden="true"
111
+ >
112
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
113
+ </svg>
114
+ )}
115
+ </a>
116
+ ))
117
+ }
118
+ </div>
119
+ </div>
120
+
121
+ <div class="visual" aria-hidden="true">
122
+ <div class="logo-wrap">
123
+ <img src="/logo-light.png" alt="" class="logo logo-light" />
124
+ <img src="/logo-dark.png" alt="" class="logo logo-dark" />
125
+
126
+ <svg
127
+ class="ring"
128
+ viewBox="0 0 240 240"
129
+ xmlns="http://www.w3.org/2000/svg"
130
+ >
131
+ <defs>
132
+ <linearGradient id="ringGrad" x1="0" y1="0" x2="1" y2="1">
133
+ <stop offset="0" stop-color="#8be9fd"></stop>
134
+ <stop offset="0.5" stop-color="#bd93f9"></stop>
135
+ <stop offset="1" stop-color="#ff79c6"></stop>
136
+ </linearGradient>
137
+ </defs>
138
+ <circle
139
+ cx="120"
140
+ cy="120"
141
+ r="92"
142
+ fill="none"
143
+ stroke="url(#ringGrad)"
144
+ stroke-width="3"
145
+ stroke-dasharray="10 10"
146
+ opacity="0.65"></circle>
147
+ </svg>
148
+
149
+ <div class="glow"></div>
150
+ </div>
151
+ </div>
152
+ </div>
153
+</section>
154
+
155
+<script>
156
+ (() => {
157
+ const el = document.querySelector("[data-och-hero]");
158
+ if (!(el instanceof HTMLElement)) return;
159
+
160
+ const reduce = window.matchMedia?.(
161
+ "(prefers-reduced-motion: reduce)",
162
+ )?.matches;
163
+ if (reduce) return;
164
+
165
+ const setVars = (clientX: number, clientY: number) => {
166
+ const r = el.getBoundingClientRect();
167
+ const x = (clientX - r.left) / r.width;
168
+ const y = (clientY - r.top) / r.height;
169
+ el.style.setProperty("--mx", String((x - 0.5) * 2));
170
+ el.style.setProperty("--my", String((y - 0.5) * 2));
171
+ };
172
+
173
+ const onMove = (e: MouseEvent | TouchEvent) => {
174
+ if (e instanceof TouchEvent) {
175
+ const touch = e.touches?.[0];
176
+ if (touch) setVars(touch.clientX, touch.clientY);
177
+ return;
178
+ }
179
+ setVars(e.clientX, e.clientY);
180
+ };
181
+
182
+ const onLeave = () => {
183
+ el.style.setProperty("--mx", "0");
184
+ el.style.setProperty("--my", "0");
185
+ };
186
+
187
+ el.addEventListener("mousemove", onMove, { passive: true });
188
+ el.addEventListener("touchmove", onMove, { passive: true });
189
+ el.addEventListener("mouseleave", onLeave, { passive: true });
190
+ })();
191
+</script>
192
+
193
+<style>
194
+ @layer starlight.core {
195
+ .och-hero {
196
+ --mx: 0;
197
+ --my: 0;
198
+ position: relative;
199
+ padding-block: clamp(2.25rem, 4vw, 4rem);
200
+ margin: 0;
201
+ }
202
+
203
+ .och-inner {
204
+ position: relative;
205
+ display: grid;
206
+ grid-template-columns: 1.2fr 0.8fr;
207
+ gap: clamp(1.5rem, 3vw, 3rem);
208
+ align-items: center;
209
+ border: 1px solid
210
+ color-mix(in srgb, var(--sl-color-gray-5) 70%, transparent);
211
+ background: color-mix(in srgb, var(--sl-color-bg) 92%, transparent);
212
+ border-radius: 22px;
213
+ padding: clamp(1.25rem, 3vw, 2.25rem);
214
+ overflow: hidden;
215
+ box-shadow: 0 20px 70px rgba(0, 0, 0, 0.35);
216
+ }
217
+
218
+ .och-bg {
219
+ position: absolute;
220
+ inset: 0;
221
+ pointer-events: none;
222
+ overflow: hidden;
223
+ }
224
+
225
+ .orb {
226
+ position: absolute;
227
+ border-radius: 999px;
228
+ filter: blur(90px);
229
+ opacity: 0.35;
230
+ transform: translate(calc(var(--mx) * 14px), calc(var(--my) * 14px));
231
+ transition: transform 120ms ease;
232
+ animation: floaty 18s ease-in-out infinite;
233
+ }
234
+
235
+ .orb-1 {
236
+ width: 520px;
237
+ height: 520px;
238
+ left: -220px;
239
+ top: -240px;
240
+ background:
241
+ radial-gradient(circle at 30% 30%, #8be9fd, transparent 60%),
242
+ radial-gradient(circle at 70% 70%, #2dd4bf, transparent 55%);
243
+ }
244
+
245
+ .orb-2 {
246
+ width: 480px;
247
+ height: 480px;
248
+ right: -220px;
249
+ top: 30px;
250
+ animation-delay: -6s;
251
+ background:
252
+ radial-gradient(circle at 35% 30%, #bd93f9, transparent 60%),
253
+ radial-gradient(circle at 65% 70%, #ff79c6, transparent 55%);
254
+ }
255
+
256
+ .orb-3 {
257
+ width: 420px;
258
+ height: 420px;
259
+ left: 40%;
260
+ bottom: -250px;
261
+ animation-delay: -12s;
262
+ background:
263
+ radial-gradient(circle at 30% 30%, #2dd4bf, transparent 60%),
264
+ radial-gradient(circle at 70% 70%, #bd93f9, transparent 55%);
265
+ }
266
+
267
+ .grid {
268
+ position: absolute;
269
+ inset: -25% -25% -25% -25%;
270
+ width: 150%;
271
+ height: 150%;
272
+ opacity: 0.28;
273
+ transform: translate(calc(var(--mx) * -10px), calc(var(--my) * -10px))
274
+ rotate(-2deg);
275
+ transition: transform 120ms ease;
276
+ }
277
+
278
+ .spark {
279
+ position: absolute;
280
+ left: 6%;
281
+ bottom: 14%;
282
+ width: min(520px, 60%);
283
+ color: color-mix(in srgb, #8be9fd 60%, #bd93f9);
284
+ opacity: 0.5;
285
+ }
286
+
287
+ .spark path {
288
+ stroke-dasharray: 450;
289
+ stroke-dashoffset: 450;
290
+ animation: draw 1.6s ease forwards;
291
+ }
292
+
293
+ .spark circle {
294
+ fill: currentColor;
295
+ opacity: 0.9;
296
+ animation: dot 2.8s ease-in-out infinite;
297
+ }
298
+
299
+ .copy {
300
+ display: flex;
301
+ flex-direction: column;
302
+ gap: 1rem;
303
+ z-index: 1;
304
+ }
305
+
306
+ .badges {
307
+ display: flex;
308
+ gap: 0.5rem;
309
+ flex-wrap: wrap;
310
+ }
311
+
312
+ .badge {
313
+ font-size: 0.85rem;
314
+ padding: 0.35rem 0.6rem;
315
+ border-radius: 999px;
316
+ border: 1px solid
317
+ color-mix(in srgb, var(--sl-color-gray-5) 70%, transparent);
318
+ background: color-mix(in srgb, var(--sl-color-bg) 86%, transparent);
319
+ color: var(--sl-color-gray-2);
320
+ }
321
+
322
+ .title {
323
+ margin: 0;
324
+ line-height: 1.05;
325
+ font-weight: 800;
326
+ letter-spacing: -0.02em;
327
+ font-size: clamp(2.2rem, 4vw, 3.6rem);
328
+ }
329
+
330
+ .title-bottom {
331
+ display: block;
332
+ margin-top: 0.35rem;
333
+ background: linear-gradient(
334
+ 135deg,
335
+ #8be9fd 0%,
336
+ #bd93f9 45%,
337
+ #ff79c6 100%
338
+ );
339
+ -webkit-background-clip: text;
340
+ background-clip: text;
341
+ color: transparent;
342
+ background-size: 200% 200%;
343
+ animation: shift 8s ease infinite;
344
+ }
345
+
346
+ .tagline {
347
+ margin: 0;
348
+ max-width: 58ch;
349
+ font-size: 1.08rem;
350
+ color: var(--sl-color-gray-2);
351
+ line-height: 1.65;
352
+ }
353
+
354
+ .actions {
355
+ display: flex;
356
+ gap: 0.75rem;
357
+ flex-wrap: wrap;
358
+ margin-top: 0.25rem;
359
+ }
360
+
361
+ .btn {
362
+ display: inline-flex;
363
+ gap: 0.55rem;
364
+ align-items: center;
365
+ padding: 0.8rem 1.1rem;
366
+ border-radius: 12px;
367
+ font-weight: 700;
368
+ text-decoration: none;
369
+ border: 1px solid transparent;
370
+ transition:
371
+ transform 180ms ease,
372
+ box-shadow 180ms ease,
373
+ background 180ms ease,
374
+ border-color 180ms ease;
375
+ will-change: transform;
376
+ }
377
+
378
+ .btn.primary {
379
+ background: linear-gradient(
380
+ 135deg,
381
+ #8be9fd 0%,
382
+ #bd93f9 50%,
383
+ #ff79c6 100%
384
+ );
385
+ color: #0b1020;
386
+ box-shadow: 0 14px 40px rgba(139, 233, 253, 0.18);
387
+ }
388
+
389
+ .btn.primary:hover {
390
+ transform: translateY(-2px);
391
+ box-shadow: 0 18px 60px rgba(189, 147, 249, 0.25);
392
+ }
393
+
394
+ .btn.secondary {
395
+ background: color-mix(in srgb, var(--sl-color-bg) 84%, transparent);
396
+ border-color: color-mix(in srgb, var(--sl-color-gray-5) 70%, transparent);
397
+ color: var(--sl-color-white);
398
+ }
399
+
400
+ .btn.secondary:hover {
401
+ transform: translateY(-2px);
402
+ border-color: color-mix(in srgb, #8be9fd 40%, var(--sl-color-gray-5));
403
+ box-shadow: 0 14px 50px rgba(0, 0, 0, 0.25);
404
+ }
405
+
406
+ .visual {
407
+ display: flex;
408
+ justify-content: center;
409
+ align-items: center;
410
+ z-index: 1;
411
+ }
412
+
413
+ .logo-wrap {
414
+ position: relative;
415
+ width: min(360px, 100%);
416
+ aspect-ratio: 16 / 9;
417
+ display: grid;
418
+ place-items: center;
419
+ transform: translate(calc(var(--mx) * 6px), calc(var(--my) * 6px));
420
+ transition: transform 120ms ease;
421
+ }
422
+
423
+ .logo {
424
+ width: 100%;
425
+ height: auto;
426
+ object-fit: contain;
427
+ filter: drop-shadow(0 24px 60px rgba(0, 0, 0, 0.35));
428
+ animation: floatLogo 6.2s ease-in-out infinite;
429
+ }
430
+
431
+ .logo-light {
432
+ display: block;
433
+ }
434
+
435
+ .logo-dark {
436
+ display: none;
437
+ }
438
+
439
+ html[data-theme="dark"] .logo-light {
440
+ display: none;
441
+ }
442
+
443
+ html[data-theme="dark"] .logo-dark {
444
+ display: block;
445
+ }
446
+
447
+ .ring {
448
+ position: absolute;
449
+ inset: -10%;
450
+ width: 120%;
451
+ height: 120%;
452
+ opacity: 0.6;
453
+ animation: spin 18s linear infinite;
454
+ filter: blur(0.2px);
455
+ }
456
+
457
+ .glow {
458
+ position: absolute;
459
+ inset: 18%;
460
+ border-radius: 999px;
461
+ background: radial-gradient(
462
+ circle,
463
+ rgba(139, 233, 253, 0.22),
464
+ transparent 65%
465
+ );
466
+ filter: blur(40px);
467
+ animation: pulse 3.2s ease-in-out infinite;
468
+ }
469
+
470
+ @media (max-width: 900px) {
471
+ .och-inner {
472
+ grid-template-columns: 1fr;
473
+ text-align: center;
474
+ }
475
+ .actions {
476
+ justify-content: center;
477
+ }
478
+ .badges {
479
+ justify-content: center;
480
+ }
481
+ .spark {
482
+ left: 10%;
483
+ bottom: 6%;
484
+ }
485
+ }
486
+
487
+ @media (prefers-reduced-motion: reduce) {
488
+ .orb,
489
+ .ring,
490
+ .logo,
491
+ .spark path,
492
+ .spark circle {
493
+ animation: none !important;
494
+ }
495
+ }
496
+
497
+ @keyframes floaty {
498
+ 0%,
499
+ 100% {
500
+ transform: translate(calc(var(--mx) * 14px), calc(var(--my) * 14px))
501
+ translate(0, 0);
502
+ }
503
+ 50% {
504
+ transform: translate(calc(var(--mx) * 14px), calc(var(--my) * 14px))
505
+ translate(18px, -14px);
506
+ }
507
+ }
508
+
509
+ @keyframes shift {
510
+ 0%,
511
+ 100% {
512
+ background-position: 0% 50%;
513
+ }
514
+ 50% {
515
+ background-position: 100% 50%;
516
+ }
517
+ }
518
+
519
+ @keyframes spin {
520
+ to {
521
+ transform: rotate(360deg);
522
+ }
523
+ }
524
+
525
+ @keyframes pulse {
526
+ 0%,
527
+ 100% {
528
+ opacity: 0.35;
529
+ transform: scale(1);
530
+ }
531
+ 50% {
532
+ opacity: 0.65;
533
+ transform: scale(1.08);
534
+ }
535
+ }
536
+
537
+ @keyframes draw {
538
+ to {
539
+ stroke-dashoffset: 0;
540
+ }
541
+ }
542
+
543
+ @keyframes dot {
544
+ 0%,
545
+ 100% {
546
+ transform: translateX(0);
547
+ opacity: 0.7;
548
+ }
549
+ 50% {
550
+ transform: translateX(320px);
551
+ opacity: 1;
552
+ }
553
+ }
554
+
555
+ @keyframes floatLogo {
556
+ 0%,
557
+ 100% {
558
+ transform: translateY(0);
559
+ }
560
+ 50% {
561
+ transform: translateY(-14px);
562
+ }
563
+ }
564
+ }
565
+</style>
0
566
▼docs-site/src/content.config.ts
+4-4
1
-import { defineCollection } from 'astro:content';
2
-import { docsLoader } from '@astrojs/starlight/loaders';
3
-import { docsSchema } from '@astrojs/starlight/schema';
1
+import { docsLoader } from "@astrojs/starlight/loaders";
2
+import { docsSchema } from "@astrojs/starlight/schema";
3
+import { defineCollection } from "astro:content";
4
4
5
5
export const collections = {
6
- docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
6
+ docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
7
7
};
8
8
▼docs-site/src/content/docs/index.mdx
+65-88
1
1
---
2
2
title: OpenCodeHub Documentation
3
3
description: The open-source, AI-native Git server with Stacked PRs and Smart Merge Queues.
4
-template: splash
5
4
hero:
5
+ title: OpenCodeHub
6
6
tagline: The fully self-hosted GitHub alternative with SSH hosting, CI/CD pipelines, and a modern developer experience.
7
- image:
8
- file: ../../assets/houston.webp
9
- actions:
10
- - text: Get Started
11
- link: /getting-started/installation/
12
- icon: right-arrow
13
- - text: View on GitHub
14
- link: https://github.com/swadhinbiswas/OpencodeHub
15
- icon: external
16
- variant: minimal
17
7
---
18
8
19
-import { Card, CardGrid, Tabs, TabItem } from '@astrojs/starlight/components';
9
+import { Card, CardGrid } from "@astrojs/starlight/components";
20
10
21
11
## Why OpenCodeHub?
22
12
23
13
<CardGrid stagger>
24
- <Card title="🚀 Stacked PRs" icon="rocket">
25
- Break large features into small, dependent steps. Ship faster without the review bottleneck.
26
- </Card>
27
- <Card title="🤖 AI Code Review" icon="star">
28
- Built-in LLM agents (GPT-4/Claude) review your code for bugs and security issues automatically.
29
- </Card>
30
- <Card title="🔀 Smart Merge Queue" icon="random">
31
- Automate your merging strategy. Ensure `main` never breaks with speculative CI runs.
32
- </Card>
33
- <Card title="🏠 Fully Self-Hosted" icon="laptop">
34
- Keep your code on your servers. No "per-seat" pricing. No vendor lock-in.
35
- </Card>
14
+ <Card title="🚀 Stacked PRs" icon="rocket">
15
+ Break large features into small, dependent steps. Ship faster without the
16
+ review bottleneck.
17
+ </Card>
18
+ <Card title="🤖 AI Code Review" icon="star">
19
+ Built-in LLM agents (GPT-4/Claude) review your code for bugs and security
20
+ issues automatically.
21
+ </Card>
22
+ <Card title="🔀 Smart Merge Queue" icon="random">
23
+ Automate your merging strategy. Ensure `main` never breaks with speculative
24
+ CI runs.
25
+ </Card>
26
+ <Card title="🏠 Fully Self-Hosted" icon="laptop">
27
+ Keep your code on your servers. No "per-seat" pricing. No vendor lock-in.
28
+ </Card>
36
29
</CardGrid>
37
30
38
31
## Architecture Overview
39
32
40
-```
41
-┌─────────────────────────────────────────────────────────────────┐
42
-│ OpenCodeHub │
43
-│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
44
-│ │ Astro │ │ Git │ │ CLI │ │
45
-│ │ Web UI │ │ Server │ │ (och) │ │
46
-│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
47
-│ └────────────────┼────────────────┘ │
48
-│ ▼ │
49
-│ ┌───────────────────────┐ │
50
-│ │ REST/GraphQL API │ │
51
-│ └───────────┬───────────┘ │
52
-│ ▼ │
53
-│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
54
-│ │ PostgreSQL │ │ Redis │ │ Storage │ │
55
-│ │ (Turso) │ │ (optional) │ │ (S3/Local) │ │
56
-│ └─────────────┘ └─────────────┘ └─────────────┘ │
57
-└─────────────────────────────────────────────────────────────────┘
58
-```
33
+![OpenCodeHub Architecture](/architecture.svg)
34
+
35
+OpenCodeHub is built on a modern, scalable architecture designed for performance and flexibility.
59
36
60
37
## Deploy Anywhere
61
38
62
39
<CardGrid>
63
- <Card title="🐳 Docker" icon="add-document">
64
- The recommended way to deploy. Full Docker Compose setup with PostgreSQL and Redis.
65
- [Deploy with Docker →](/administration/deploy-docker/)
66
- </Card>
67
- <Card title="🦭 Podman" icon="add-document">
68
- Rootless containers with native systemd integration. Ideal for security-focused deployments.
69
- [Deploy with Podman →](/administration/deploy-podman/)
70
- </Card>
71
- <Card title="☁️ Cloudflare" icon="add-document">
72
- Use Cloudflare Tunnel + R2 for zero-port exposure and S3-compatible storage.
73
- [Deploy with Cloudflare →](/administration/deploy-cloudflare/)
74
- </Card>
75
- <Card title="🌐 Nginx" icon="add-document">
76
- Production-ready reverse proxy config with SSL and Git-specific optimizations.
77
- [Configure Nginx →](/administration/deploy-nginx/)
78
- </Card>
40
+ <Card title="🐳 Docker" icon="add-document">
41
+ The recommended way to deploy. Full Docker Compose setup with PostgreSQL and
42
+ Redis. [Deploy with Docker →](/administration/deploy-docker/)
43
+ </Card>
44
+ <Card title="🦭 Podman" icon="add-document">
45
+ Rootless containers with native systemd integration. Ideal for
46
+ security-focused deployments. [Deploy with Podman
47
+ →](/administration/deploy-podman/)
48
+ </Card>
49
+ <Card title="☁️ Cloudflare" icon="add-document">
50
+ Use Cloudflare Tunnel + R2 for zero-port exposure and S3-compatible storage.
51
+ [Deploy with Cloudflare →](/administration/deploy-cloudflare/)
52
+ </Card>
53
+ <Card title="🌐 Nginx" icon="add-document">
54
+ Production-ready reverse proxy config with SSL and Git-specific
55
+ optimizations. [Configure Nginx →](/administration/deploy-nginx/)
56
+ </Card>
79
57
</CardGrid>
80
58
81
59
## Tech Stack
82
60
83
-| Layer | Technology |
84
-|-------|------------|
61
+| Layer | Technology |
62
+| ------------ | ------------------------------------- |
85
63
| **Frontend** | Astro, React, Tailwind CSS, shadcn/ui |
86
-| **Backend** | Node.js, Hono (API), simple-git |
87
-| **Database** | PostgreSQL, Turso (LibSQL), SQLite |
88
-| **Storage** | Local disk, S3, R2, Google Drive |
89
-| **Auth** | JWT, 2FA, OAuth (GitHub, Google) |
90
-| **CI/CD** | GitHub Actions compatible runners |
64
+| **Backend** | Node.js, Hono (API), simple-git |
65
+| **Database** | PostgreSQL, Turso (LibSQL), SQLite |
66
+| **Storage** | Local disk, S3, R2, Google Drive |
67
+| **Auth** | JWT, 2FA, OAuth (GitHub, Google) |
68
+| **CI/CD** | GitHub Actions compatible runners |
91
69
92
70
## Quick Links
93
71
94
72
<CardGrid>
95
- <Card title="Installation" icon="add-document">
96
- Get OpenCodeHub running in minutes with Docker or Node.js.
97
- [Read the guide →](/getting-started/installation/)
98
- </Card>
99
- <Card title="CLI Reference" icon="seti:shell">
100
- Learn the `och` CLI commands for managing stacks and repositories.
101
- [View commands →](/reference/cli-commands/)
102
- </Card>
103
- <Card title="API Reference" icon="open-book">
104
- Integrate with OpenCodeHub using the REST API.
105
- [Explore the API →](/api/rest-api/)
106
- </Card>
107
- <Card title="Contributing" icon="github">
108
- Help make OpenCodeHub better. Set up your local dev environment.
109
- [Start contributing →](/development/contributing/)
110
- </Card>
73
+ <Card title="Installation" icon="add-document">
74
+ Get OpenCodeHub running in minutes with Docker or Node.js. [Read the guide
75
+ →](/getting-started/installation/)
76
+ </Card>
77
+ <Card title="CLI Reference" icon="seti:shell">
78
+ Learn the `och` CLI commands for managing stacks and repositories. [View
79
+ commands →](/reference/cli-commands/)
80
+ </Card>
81
+ <Card title="API Reference" icon="open-book">
82
+ Integrate with OpenCodeHub using the REST API. [Explore the API
83
+ →](/api/rest-api/)
84
+ </Card>
85
+ <Card title="Contributing" icon="github">
86
+ Help make OpenCodeHub better. Set up your local dev environment. [Start
87
+ contributing →](/development/contributing/)
88
+ </Card>
111
89
</CardGrid>
112
90
113
91
## Storage Options
116
94
For best performance, use **local SSD storage** in production. Object storage (S3/R2) introduces latency. See [Storage Adapters](/guides/storage-adapters/#️-performance-considerations) for details.
117
95
:::
118
96
119
-| Storage Type | Performance | Cost | Best For |
120
-|--------------|-------------|------|----------|
121
-| **Local SSD** | ⚡ Fastest | $$ | Production, self-hosted |
122
-| **S3/R2** | 🐢 Slow | $ | Backup, serverless |
123
-| **Google Drive** | 🐢 Slow | Free | Personal projects |
124
-
97
+| Storage Type | Performance | Cost | Best For |
98
+| ---------------- | ----------- | ---- | ----------------------- |
99
+| **Local SSD** | ⚡ Fastest | $$ | Production, self-hosted |
100
+| **S3/R2** | 🐢 Slow | $ | Backup, serverless |
101
+| **Google Drive** | 🐢 Slow | Free | Personal projects |
125
102
▼docs-site/src/custom.css
+73-0
1
+/* OpenCodeHub Docs – Starlight theme polish */
2
+
3
+@import url("https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&display=swap");
4
+
5
+:root {
6
+ /* Prefer Nerd Font if the user has it installed; fall back to Fira Code webfont. */
7
+ --sl-font:
8
+ "FiraCode Nerd Font", "FiraCode NF", "Fira Code", var(--sl-font-system);
9
+ --sl-font-mono:
10
+ "FiraCode Nerd Font", "FiraCode NF", "Fira Code", var(--sl-font-system-mono);
11
+}
12
+
13
+:root {
14
+ --och-accent-1: #2dd4bf;
15
+ --och-accent-2: #8be9fd;
16
+ --och-accent-3: #bd93f9;
17
+ --och-accent-4: #ff79c6;
18
+
19
+ --och-hero-surface: color-mix(in srgb, var(--sl-color-bg) 92%, transparent);
20
+}
21
+
22
+/* Slightly richer accent + nicer gradients */
23
+:root {
24
+ --sl-color-accent: var(--och-accent-1);
25
+ --sl-color-accent-high: color-mix(in srgb, var(--och-accent-1) 70%, white);
26
+}
27
+
28
+/* Link polish */
29
+.sl-markdown-content a {
30
+ text-underline-offset: 3px;
31
+ text-decoration-thickness: 1.5px;
32
+}
33
+
34
+.sl-markdown-content a:hover {
35
+ text-decoration-thickness: 2px;
36
+}
37
+
38
+/* Card hover polish (home page grids, etc.) */
39
+.sl-card {
40
+ transition:
41
+ transform 180ms ease,
42
+ box-shadow 180ms ease,
43
+ border-color 180ms ease;
44
+}
45
+
46
+.sl-card:hover {
47
+ transform: translateY(-2px);
48
+ border-color: color-mix(
49
+ in srgb,
50
+ var(--och-accent-2) 40%,
51
+ var(--sl-color-gray-5)
52
+ );
53
+ box-shadow: 0 18px 50px rgba(0, 0, 0, 0.25);
54
+}
55
+
56
+/* Softer nav background and nicer focus rings */
57
+:root {
58
+ --sl-color-bg-nav: color-mix(in srgb, var(--sl-color-bg) 92%, black);
59
+}
60
+
61
+:focus-visible {
62
+ outline: 2px solid color-mix(in srgb, var(--och-accent-2) 65%, white);
63
+ outline-offset: 3px;
64
+}
65
+
66
+@media (prefers-reduced-motion: reduce) {
67
+ * {
68
+ animation-duration: 0.001ms !important;
69
+ animation-iteration-count: 1 !important;
70
+ transition-duration: 0.001ms !important;
71
+ scroll-behavior: auto !important;
72
+ }
73
+}
0
74
▼docs-site/src/worker.ts
+10-0
1
+export interface Env {
2
+ ASSETS: Fetcher;
3
+}
4
+
5
+export default {
6
+ async fetch(request: Request, env: Env): Promise<Response> {
7
+ // Delegate all requests to the static asset handler.
8
+ return env.ASSETS.fetch(request);
9
+ },
10
+};
0
11
▼docs-site/wrangler.jsonc
+15-0
1
+{
2
+ // Cloudflare Workers config for deploying this Astro build as static assets.
3
+ // This makes `npx wrangler deploy` work without extra CLI arguments.
4
+ "name": "opencodehub-docs",
5
+ "compatibility_date": "2026-01-22",
6
+
7
+ // Minimal worker that forwards requests to the uploaded static assets.
8
+ "main": "src/worker.ts",
9
+
10
+ // Upload `dist/` as Workers static assets.
11
+ "assets": {
12
+ "directory": "./dist",
13
+ "binding": "ASSETS",
14
+ },
15
+}
0
16
▼drizzle/0000_abandoned_king_bedlam.sql
+36-0
1
+ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "ai_config" text;
2
+
3
+CREATE TABLE IF NOT EXISTS "organizations" (
4
+ "id" text PRIMARY KEY NOT NULL,
5
+ "name" text NOT NULL,
6
+ "display_name" text,
7
+ "description" text,
8
+ "email" text,
9
+ "location" text,
10
+ "website" text,
11
+ "avatar_url" text,
12
+ "is_verified" boolean DEFAULT false,
13
+ "created_at" timestamp DEFAULT now() NOT NULL,
14
+ "updated_at" timestamp DEFAULT now() NOT NULL,
15
+ CONSTRAINT "organizations_name_unique" UNIQUE("name")
16
+);
17
+
18
+CREATE TABLE IF NOT EXISTS "organization_members" (
19
+ "organization_id" text NOT NULL,
20
+ "user_id" text NOT NULL,
21
+ "role" text DEFAULT 'member' NOT NULL,
22
+ "created_at" timestamp DEFAULT now() NOT NULL,
23
+ CONSTRAINT "organization_members_organization_id_user_id_pk" PRIMARY KEY("organization_id","user_id")
24
+);
25
+
26
+DO $$ BEGIN
27
+ ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;
28
+EXCEPTION
29
+ WHEN duplicate_object THEN null;
30
+END $$;
31
+
32
+DO $$ BEGIN
33
+ ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
34
+EXCEPTION
35
+ WHEN duplicate_object THEN null;
36
+END $$;
0
37
▼drizzle/0001_faithful_randall.sql
+130-0
1
+CREATE TABLE "project_cards" (
2
+ "id" text PRIMARY KEY NOT NULL,
3
+ "column_id" text NOT NULL,
4
+ "content_id" text,
5
+ "content_type" text,
6
+ "note" text,
7
+ "order" integer NOT NULL,
8
+ "created_at" timestamp DEFAULT now() NOT NULL,
9
+ "updated_at" timestamp DEFAULT now() NOT NULL,
10
+ "creator_id" text NOT NULL
11
+);
12
+--> statement-breakpoint
13
+CREATE TABLE "project_columns" (
14
+ "id" text PRIMARY KEY NOT NULL,
15
+ "project_id" text NOT NULL,
16
+ "name" text NOT NULL,
17
+ "order" integer NOT NULL,
18
+ "created_at" timestamp DEFAULT now() NOT NULL,
19
+ "updated_at" timestamp DEFAULT now() NOT NULL
20
+);
21
+--> statement-breakpoint
22
+CREATE TABLE "projects" (
23
+ "id" text PRIMARY KEY NOT NULL,
24
+ "repository_id" text NOT NULL,
25
+ "name" text NOT NULL,
26
+ "description" text,
27
+ "number" integer NOT NULL,
28
+ "created_at" timestamp DEFAULT now() NOT NULL,
29
+ "updated_at" timestamp DEFAULT now() NOT NULL,
30
+ "creator_id" text NOT NULL
31
+);
32
+--> statement-breakpoint
33
+CREATE TABLE "merge_queue" (
34
+ "id" text PRIMARY KEY NOT NULL,
35
+ "repository_id" text NOT NULL,
36
+ "pull_request_id" text NOT NULL,
37
+ "stack_id" text,
38
+ "status" text DEFAULT 'pending' NOT NULL,
39
+ "priority" integer DEFAULT 0,
40
+ "position" integer DEFAULT 0,
41
+ "ci_status" text DEFAULT 'pending',
42
+ "merge_method" text DEFAULT 'merge',
43
+ "delete_on_merge" boolean DEFAULT true,
44
+ "execution_branch" text,
45
+ "attempt_count" integer DEFAULT 0,
46
+ "last_attempt_at" timestamp,
47
+ "added_by_id" text,
48
+ "added_at" timestamp DEFAULT now() NOT NULL,
49
+ "queued_at" timestamp DEFAULT now() NOT NULL,
50
+ "started_at" timestamp,
51
+ "completed_at" timestamp,
52
+ "failure_reason" text
53
+);
54
+--> statement-breakpoint
55
+CREATE TABLE "merge_queue_speculative_runs" (
56
+ "id" text PRIMARY KEY NOT NULL,
57
+ "repository_id" text NOT NULL,
58
+ "pull_request_ids" text NOT NULL,
59
+ "branch_name" text NOT NULL,
60
+ "base_branch" text NOT NULL,
61
+ "status" text DEFAULT 'pending' NOT NULL,
62
+ "ci_url" text,
63
+ "created_at" timestamp DEFAULT now() NOT NULL,
64
+ "started_at" timestamp,
65
+ "completed_at" timestamp,
66
+ "failure_reason" text,
67
+ "commit_sha" text
68
+);
69
+--> statement-breakpoint
70
+CREATE TABLE "sso_configs" (
71
+ "id" text PRIMARY KEY NOT NULL,
72
+ "name" text NOT NULL,
73
+ "type" text DEFAULT 'oidc' NOT NULL,
74
+ "issuer" text NOT NULL,
75
+ "client_id" text NOT NULL,
76
+ "client_secret" text NOT NULL,
77
+ "scopes" text DEFAULT 'openid,profile,email',
78
+ "authorization_url" text,
79
+ "token_url" text,
80
+ "userinfo_url" text,
81
+ "jwks_uri" text,
82
+ "enabled" boolean DEFAULT false,
83
+ "auto_create_users" boolean DEFAULT true,
84
+ "allowed_domains" text,
85
+ "default_role" text DEFAULT 'member',
86
+ "organization_id" text,
87
+ "created_at" timestamp DEFAULT now() NOT NULL,
88
+ "updated_at" timestamp DEFAULT now() NOT NULL,
89
+ "created_by_id" text
90
+);
91
+--> statement-breakpoint
92
+ALTER TABLE "scheduled_workflows" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
93
+ALTER TABLE "workflow_artifacts" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
94
+ALTER TABLE "workflow_jobs" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
95
+ALTER TABLE "workflow_logs" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
96
+ALTER TABLE "workflow_runs" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
97
+ALTER TABLE "workflow_secrets" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
98
+ALTER TABLE "workflow_steps" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
99
+ALTER TABLE "workflow_variables" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
100
+ALTER TABLE "workflows" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
101
+ALTER TABLE "merge_queue_items" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
102
+DROP TABLE "scheduled_workflows" CASCADE;--> statement-breakpoint
103
+DROP TABLE "workflow_artifacts" CASCADE;--> statement-breakpoint
104
+DROP TABLE "workflow_jobs" CASCADE;--> statement-breakpoint
105
+DROP TABLE "workflow_logs" CASCADE;--> statement-breakpoint
106
+DROP TABLE "workflow_runs" CASCADE;--> statement-breakpoint
107
+DROP TABLE "workflow_secrets" CASCADE;--> statement-breakpoint
108
+DROP TABLE "workflow_steps" CASCADE;--> statement-breakpoint
109
+DROP TABLE "workflow_variables" CASCADE;--> statement-breakpoint
110
+DROP TABLE "workflows" CASCADE;--> statement-breakpoint
111
+DROP TABLE "merge_queue_items" CASCADE;--> statement-breakpoint
112
+ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_content" text;--> statement-breakpoint
113
+ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_applied" boolean DEFAULT false;--> statement-breakpoint
114
+ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_applied_by_id" text;--> statement-breakpoint
115
+ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_applied_at" timestamp;--> statement-breakpoint
116
+ALTER TABLE "pull_request_comments" ADD COLUMN "suggestion_commit_sha" text;--> statement-breakpoint
117
+ALTER TABLE "webhooks" ADD COLUMN "provider" text DEFAULT 'generic';--> statement-breakpoint
118
+ALTER TABLE "webhooks" ADD COLUMN "name" text;--> statement-breakpoint
119
+ALTER TABLE "webhooks" ADD COLUMN "enabled" boolean DEFAULT true NOT NULL;--> statement-breakpoint
120
+ALTER TABLE "project_cards" ADD CONSTRAINT "project_cards_column_id_project_columns_id_fk" FOREIGN KEY ("column_id") REFERENCES "public"."project_columns"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
121
+ALTER TABLE "project_cards" ADD CONSTRAINT "project_cards_creator_id_users_id_fk" FOREIGN KEY ("creator_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
122
+ALTER TABLE "project_columns" ADD CONSTRAINT "project_columns_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
123
+ALTER TABLE "projects" ADD CONSTRAINT "projects_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
124
+ALTER TABLE "projects" ADD CONSTRAINT "projects_creator_id_users_id_fk" FOREIGN KEY ("creator_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
125
+ALTER TABLE "merge_queue" ADD CONSTRAINT "merge_queue_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
126
+ALTER TABLE "merge_queue" ADD CONSTRAINT "merge_queue_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
127
+ALTER TABLE "merge_queue" ADD CONSTRAINT "merge_queue_added_by_id_users_id_fk" FOREIGN KEY ("added_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
128
+ALTER TABLE "merge_queue_speculative_runs" ADD CONSTRAINT "merge_queue_speculative_runs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
129
+ALTER TABLE "sso_configs" ADD CONSTRAINT "sso_configs_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
130
+ALTER TABLE "pull_request_comments" ADD CONSTRAINT "pull_request_comments_suggestion_applied_by_id_users_id_fk" FOREIGN KEY ("suggestion_applied_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
0
131
▼drizzle/0002_third_cable.sql
+153-0
1
+CREATE TABLE "scheduled_workflows" (
2
+ "id" text PRIMARY KEY NOT NULL,
3
+ "workflow_id" text NOT NULL,
4
+ "cron_expression" text NOT NULL,
5
+ "timezone" text DEFAULT 'UTC',
6
+ "is_enabled" boolean DEFAULT true,
7
+ "last_run_at" timestamp,
8
+ "next_run_at" timestamp,
9
+ "created_at" timestamp DEFAULT now() NOT NULL
10
+);
11
+--> statement-breakpoint
12
+CREATE TABLE "workflow_artifacts" (
13
+ "id" text PRIMARY KEY NOT NULL,
14
+ "run_id" text NOT NULL,
15
+ "job_id" text,
16
+ "name" text NOT NULL,
17
+ "size_bytes" integer NOT NULL,
18
+ "storage_path" text NOT NULL,
19
+ "mime_type" text,
20
+ "download_count" integer DEFAULT 0,
21
+ "expires_at" timestamp,
22
+ "created_at" timestamp DEFAULT now() NOT NULL
23
+);
24
+--> statement-breakpoint
25
+CREATE TABLE "workflow_jobs" (
26
+ "id" text PRIMARY KEY NOT NULL,
27
+ "run_id" text NOT NULL,
28
+ "name" text NOT NULL,
29
+ "status" text DEFAULT 'queued' NOT NULL,
30
+ "conclusion" text,
31
+ "runner_id" text,
32
+ "runner_name" text,
33
+ "runner_group_id" text,
34
+ "runner_group_name" text,
35
+ "container_id" text,
36
+ "container_image" text,
37
+ "needs" text,
38
+ "environment" text,
39
+ "environment_url" text,
40
+ "matrix" text,
41
+ "started_at" timestamp,
42
+ "completed_at" timestamp,
43
+ "created_at" timestamp DEFAULT now() NOT NULL,
44
+ "updated_at" timestamp DEFAULT now() NOT NULL
45
+);
46
+--> statement-breakpoint
47
+CREATE TABLE "workflow_logs" (
48
+ "id" text PRIMARY KEY NOT NULL,
49
+ "job_id" text NOT NULL,
50
+ "step_id" text,
51
+ "log_level" text DEFAULT 'info' NOT NULL,
52
+ "message" text NOT NULL,
53
+ "timestamp" text NOT NULL,
54
+ "line_number" integer,
55
+ "created_at" timestamp DEFAULT now() NOT NULL
56
+);
57
+--> statement-breakpoint
58
+CREATE TABLE "workflow_runs" (
59
+ "id" text PRIMARY KEY NOT NULL,
60
+ "workflow_id" text NOT NULL,
61
+ "repository_id" text NOT NULL,
62
+ "run_number" integer NOT NULL,
63
+ "run_attempt" integer DEFAULT 1 NOT NULL,
64
+ "name" text NOT NULL,
65
+ "display_title" text,
66
+ "status" text DEFAULT 'queued' NOT NULL,
67
+ "conclusion" text,
68
+ "event" text NOT NULL,
69
+ "head_branch" text,
70
+ "head_sha" text NOT NULL,
71
+ "base_branch" text,
72
+ "base_sha" text,
73
+ "pull_request_id" text,
74
+ "triggered_by_id" text,
75
+ "started_at" timestamp,
76
+ "completed_at" timestamp,
77
+ "workflow_config" text,
78
+ "created_at" timestamp DEFAULT now() NOT NULL,
79
+ "updated_at" timestamp DEFAULT now() NOT NULL
80
+);
81
+--> statement-breakpoint
82
+CREATE TABLE "workflow_secrets" (
83
+ "id" text PRIMARY KEY NOT NULL,
84
+ "repository_id" text NOT NULL,
85
+ "name" text NOT NULL,
86
+ "encrypted_value" text NOT NULL,
87
+ "environment" text,
88
+ "created_by_id" text,
89
+ "created_at" timestamp DEFAULT now() NOT NULL,
90
+ "updated_at" timestamp DEFAULT now() NOT NULL
91
+);
92
+--> statement-breakpoint
93
+CREATE TABLE "workflow_steps" (
94
+ "id" text PRIMARY KEY NOT NULL,
95
+ "job_id" text NOT NULL,
96
+ "number" integer NOT NULL,
97
+ "name" text NOT NULL,
98
+ "status" text DEFAULT 'queued' NOT NULL,
99
+ "conclusion" text,
100
+ "uses" text,
101
+ "run" text,
102
+ "shell" text,
103
+ "working_directory" text,
104
+ "env" text,
105
+ "with" text,
106
+ "if" text,
107
+ "continue_on_error" boolean DEFAULT false,
108
+ "timeout_minutes" integer,
109
+ "started_at" timestamp,
110
+ "completed_at" timestamp,
111
+ "created_at" timestamp DEFAULT now() NOT NULL
112
+);
113
+--> statement-breakpoint
114
+CREATE TABLE "workflow_variables" (
115
+ "id" text PRIMARY KEY NOT NULL,
116
+ "repository_id" text NOT NULL,
117
+ "name" text NOT NULL,
118
+ "value" text NOT NULL,
119
+ "environment" text,
120
+ "created_by_id" text,
121
+ "created_at" timestamp DEFAULT now() NOT NULL,
122
+ "updated_at" timestamp DEFAULT now() NOT NULL
123
+);
124
+--> statement-breakpoint
125
+CREATE TABLE "workflows" (
126
+ "id" text PRIMARY KEY NOT NULL,
127
+ "repository_id" text NOT NULL,
128
+ "name" text NOT NULL,
129
+ "path" text NOT NULL,
130
+ "state" text DEFAULT 'active' NOT NULL,
131
+ "badge_url" text,
132
+ "created_at" timestamp DEFAULT now() NOT NULL,
133
+ "updated_at" timestamp DEFAULT now() NOT NULL
134
+);
135
+--> statement-breakpoint
136
+ALTER TABLE "issues" ADD COLUMN "type" text DEFAULT 'issue' NOT NULL;--> statement-breakpoint
137
+ALTER TABLE "issues" ADD COLUMN "parent_id" text;--> statement-breakpoint
138
+ALTER TABLE "scheduled_workflows" ADD CONSTRAINT "scheduled_workflows_workflow_id_workflows_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
139
+ALTER TABLE "workflow_artifacts" ADD CONSTRAINT "workflow_artifacts_run_id_workflow_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "public"."workflow_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
140
+ALTER TABLE "workflow_artifacts" ADD CONSTRAINT "workflow_artifacts_job_id_workflow_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."workflow_jobs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
141
+ALTER TABLE "workflow_jobs" ADD CONSTRAINT "workflow_jobs_run_id_workflow_runs_id_fk" FOREIGN KEY ("run_id") REFERENCES "public"."workflow_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
142
+ALTER TABLE "workflow_logs" ADD CONSTRAINT "workflow_logs_job_id_workflow_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."workflow_jobs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
143
+ALTER TABLE "workflow_logs" ADD CONSTRAINT "workflow_logs_step_id_workflow_steps_id_fk" FOREIGN KEY ("step_id") REFERENCES "public"."workflow_steps"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
144
+ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_workflow_id_workflows_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "public"."workflows"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
145
+ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
146
+ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
147
+ALTER TABLE "workflow_runs" ADD CONSTRAINT "workflow_runs_triggered_by_id_users_id_fk" FOREIGN KEY ("triggered_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
148
+ALTER TABLE "workflow_secrets" ADD CONSTRAINT "workflow_secrets_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
149
+ALTER TABLE "workflow_secrets" ADD CONSTRAINT "workflow_secrets_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
150
+ALTER TABLE "workflow_steps" ADD CONSTRAINT "workflow_steps_job_id_workflow_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."workflow_jobs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
151
+ALTER TABLE "workflow_variables" ADD CONSTRAINT "workflow_variables_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
152
+ALTER TABLE "workflow_variables" ADD CONSTRAINT "workflow_variables_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
153
+ALTER TABLE "workflows" ADD CONSTRAINT "workflows_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;
0
154
▼drizzle/0003_married_cyclops.sql
+590-0
1
+CREATE TABLE "team_members" (
2
+ "team_id" text NOT NULL,
3
+ "user_id" text NOT NULL,
4
+ "role" text DEFAULT 'member' NOT NULL,
5
+ "created_at" timestamp DEFAULT now() NOT NULL,
6
+ CONSTRAINT "team_members_team_id_user_id_pk" PRIMARY KEY("team_id","user_id")
7
+);
8
+--> statement-breakpoint
9
+CREATE TABLE "teams" (
10
+ "id" text PRIMARY KEY NOT NULL,
11
+ "organization_id" text NOT NULL,
12
+ "name" text NOT NULL,
13
+ "slug" text NOT NULL,
14
+ "description" text,
15
+ "privacy" text DEFAULT 'visible',
16
+ "created_at" timestamp DEFAULT now() NOT NULL,
17
+ "updated_at" timestamp DEFAULT now() NOT NULL
18
+);
19
+--> statement-breakpoint
20
+CREATE TABLE "repository_path_permissions" (
21
+ "id" text PRIMARY KEY NOT NULL,
22
+ "repository_id" text NOT NULL,
23
+ "path_pattern" text NOT NULL,
24
+ "user_id" text,
25
+ "team_id" text,
26
+ "permission" text DEFAULT 'write' NOT NULL,
27
+ "require_approval" text DEFAULT 'false',
28
+ "created_at" timestamp DEFAULT now() NOT NULL,
29
+ "updated_at" timestamp DEFAULT now() NOT NULL
30
+);
31
+--> statement-breakpoint
32
+CREATE TABLE "pr_state_definitions" (
33
+ "id" text PRIMARY KEY NOT NULL,
34
+ "repository_id" text NOT NULL,
35
+ "name" text NOT NULL,
36
+ "display_name" text NOT NULL,
37
+ "description" text,
38
+ "color" text DEFAULT '#6B7280' NOT NULL,
39
+ "icon" text,
40
+ "order" integer DEFAULT 0 NOT NULL,
41
+ "is_default" boolean DEFAULT false,
42
+ "is_final" boolean DEFAULT false,
43
+ "allow_merge" boolean DEFAULT false,
44
+ "created_at" timestamp DEFAULT now() NOT NULL,
45
+ "updated_at" timestamp DEFAULT now() NOT NULL
46
+);
47
+--> statement-breakpoint
48
+CREATE TABLE "pr_state_reviewers" (
49
+ "id" text PRIMARY KEY NOT NULL,
50
+ "state_definition_id" text NOT NULL,
51
+ "user_id" text,
52
+ "team_id" text,
53
+ "required_count" integer DEFAULT 1,
54
+ "created_at" timestamp DEFAULT now() NOT NULL
55
+);
56
+--> statement-breakpoint
57
+CREATE TABLE "pr_state_transitions" (
58
+ "id" text PRIMARY KEY NOT NULL,
59
+ "pull_request_id" text NOT NULL,
60
+ "from_state" text,
61
+ "to_state" text NOT NULL,
62
+ "changed_by_id" text NOT NULL,
63
+ "reason" text,
64
+ "created_at" timestamp DEFAULT now() NOT NULL
65
+);
66
+--> statement-breakpoint
67
+CREATE TABLE "breaking_changes" (
68
+ "id" text PRIMARY KEY NOT NULL,
69
+ "pull_request_id" text NOT NULL,
70
+ "change_type" text NOT NULL,
71
+ "severity" text NOT NULL,
72
+ "description" text NOT NULL,
73
+ "affected_files" jsonb,
74
+ "suggested_action" text,
75
+ "acknowledged" boolean DEFAULT false,
76
+ "created_at" timestamp DEFAULT now() NOT NULL
77
+);
78
+--> statement-breakpoint
79
+CREATE TABLE "change_set_items" (
80
+ "id" text PRIMARY KEY NOT NULL,
81
+ "change_set_id" text NOT NULL,
82
+ "pull_request_id" text,
83
+ "repository_id" text NOT NULL,
84
+ "order" integer DEFAULT 0,
85
+ "created_at" timestamp DEFAULT now() NOT NULL
86
+);
87
+--> statement-breakpoint
88
+CREATE TABLE "change_sets" (
89
+ "id" text PRIMARY KEY NOT NULL,
90
+ "name" text NOT NULL,
91
+ "description" text,
92
+ "created_by_id" text NOT NULL,
93
+ "status" text DEFAULT 'draft' NOT NULL,
94
+ "created_at" timestamp DEFAULT now() NOT NULL,
95
+ "updated_at" timestamp DEFAULT now() NOT NULL
96
+);
97
+--> statement-breakpoint
98
+CREATE TABLE "chat_integrations" (
99
+ "id" text PRIMARY KEY NOT NULL,
100
+ "organization_id" text,
101
+ "repository_id" text,
102
+ "provider" text NOT NULL,
103
+ "name" text NOT NULL,
104
+ "webhook_url" text,
105
+ "api_token" text,
106
+ "channel_id" text,
107
+ "is_enabled" boolean DEFAULT true,
108
+ "events" jsonb DEFAULT '[]'::jsonb,
109
+ "created_at" timestamp DEFAULT now() NOT NULL,
110
+ "updated_at" timestamp DEFAULT now() NOT NULL
111
+);
112
+--> statement-breakpoint
113
+CREATE TABLE "cloud_configs" (
114
+ "id" text PRIMARY KEY NOT NULL,
115
+ "repository_id" text,
116
+ "organization_id" text,
117
+ "provider" text NOT NULL,
118
+ "name" text NOT NULL,
119
+ "region" text,
120
+ "credentials" jsonb,
121
+ "settings" jsonb,
122
+ "is_enabled" boolean DEFAULT true,
123
+ "created_at" timestamp DEFAULT now() NOT NULL,
124
+ "updated_at" timestamp DEFAULT now() NOT NULL
125
+);
126
+--> statement-breakpoint
127
+CREATE TABLE "code_quality_configs" (
128
+ "id" text PRIMARY KEY NOT NULL,
129
+ "repository_id" text NOT NULL,
130
+ "provider" text NOT NULL,
131
+ "project_key" text,
132
+ "api_token" text,
133
+ "server_url" text,
134
+ "webhook_secret" text,
135
+ "is_enabled" boolean DEFAULT true,
136
+ "report_on_pr" boolean DEFAULT true,
137
+ "block_on_fail" boolean DEFAULT false,
138
+ "min_coverage" real,
139
+ "created_at" timestamp DEFAULT now() NOT NULL,
140
+ "updated_at" timestamp DEFAULT now() NOT NULL
141
+);
142
+--> statement-breakpoint
143
+CREATE TABLE "code_quality_issues" (
144
+ "id" text PRIMARY KEY NOT NULL,
145
+ "repository_id" text NOT NULL,
146
+ "pull_request_id" text,
147
+ "commit_sha" text NOT NULL,
148
+ "provider" text NOT NULL,
149
+ "issue_type" text NOT NULL,
150
+ "severity" text NOT NULL,
151
+ "message" text NOT NULL,
152
+ "file" text,
153
+ "line" integer,
154
+ "rule" text,
155
+ "effort" text,
156
+ "status" text DEFAULT 'open',
157
+ "created_at" timestamp DEFAULT now() NOT NULL
158
+);
159
+--> statement-breakpoint
160
+CREATE TABLE "coverage_reports" (
161
+ "id" text PRIMARY KEY NOT NULL,
162
+ "repository_id" text NOT NULL,
163
+ "pull_request_id" text,
164
+ "commit_sha" text NOT NULL,
165
+ "provider" text NOT NULL,
166
+ "coverage" real NOT NULL,
167
+ "lines_covered" integer,
168
+ "lines_total" integer,
169
+ "branch_coverage" real,
170
+ "delta" real,
171
+ "status" text NOT NULL,
172
+ "report_url" text,
173
+ "created_at" timestamp DEFAULT now() NOT NULL
174
+);
175
+--> statement-breakpoint
176
+CREATE TABLE "cross_repo_issue_links" (
177
+ "id" text PRIMARY KEY NOT NULL,
178
+ "source_issue_id" text NOT NULL,
179
+ "target_issue_id" text NOT NULL,
180
+ "link_type" text NOT NULL,
181
+ "created_by_id" text NOT NULL,
182
+ "created_at" timestamp DEFAULT now() NOT NULL
183
+);
184
+--> statement-breakpoint
185
+CREATE TABLE "custom_dashboards" (
186
+ "id" text PRIMARY KEY NOT NULL,
187
+ "user_id" text NOT NULL,
188
+ "name" text NOT NULL,
189
+ "description" text,
190
+ "is_public" boolean DEFAULT false,
191
+ "layout" jsonb,
192
+ "created_at" timestamp DEFAULT now() NOT NULL,
193
+ "updated_at" timestamp DEFAULT now() NOT NULL
194
+);
195
+--> statement-breakpoint
196
+CREATE TABLE "custom_field_definitions" (
197
+ "id" text PRIMARY KEY NOT NULL,
198
+ "repository_id" text NOT NULL,
199
+ "name" text NOT NULL,
200
+ "field_type" text NOT NULL,
201
+ "description" text,
202
+ "is_required" boolean DEFAULT false,
203
+ "options" text,
204
+ "default_value" text,
205
+ "display_order" integer DEFAULT 0,
206
+ "created_at" timestamp DEFAULT now() NOT NULL,
207
+ "updated_at" timestamp DEFAULT now() NOT NULL
208
+);
209
+--> statement-breakpoint
210
+CREATE TABLE "custom_field_values" (
211
+ "id" text PRIMARY KEY NOT NULL,
212
+ "issue_id" text NOT NULL,
213
+ "field_id" text NOT NULL,
214
+ "value" text,
215
+ "created_at" timestamp DEFAULT now() NOT NULL,
216
+ "updated_at" timestamp DEFAULT now() NOT NULL
217
+);
218
+--> statement-breakpoint
219
+CREATE TABLE "dashboard_widgets" (
220
+ "id" text PRIMARY KEY NOT NULL,
221
+ "dashboard_id" text NOT NULL,
222
+ "widget_type" text NOT NULL,
223
+ "title" text NOT NULL,
224
+ "config" jsonb,
225
+ "position" jsonb,
226
+ "created_at" timestamp DEFAULT now() NOT NULL
227
+);
228
+--> statement-breakpoint
229
+CREATE TABLE "cloud_deployments" (
230
+ "id" text PRIMARY KEY NOT NULL,
231
+ "config_id" text NOT NULL,
232
+ "pull_request_id" text,
233
+ "commit_sha" text NOT NULL,
234
+ "environment" text NOT NULL,
235
+ "status" text NOT NULL,
236
+ "url" text,
237
+ "logs" text,
238
+ "started_at" timestamp,
239
+ "completed_at" timestamp,
240
+ "created_at" timestamp DEFAULT now() NOT NULL
241
+);
242
+--> statement-breakpoint
243
+CREATE TABLE "email_settings" (
244
+ "id" text PRIMARY KEY NOT NULL,
245
+ "organization_id" text,
246
+ "smtp_host" text,
247
+ "smtp_port" text,
248
+ "smtp_user" text,
249
+ "smtp_pass" text,
250
+ "from_address" text,
251
+ "from_name" text,
252
+ "is_enabled" boolean DEFAULT true,
253
+ "created_at" timestamp DEFAULT now() NOT NULL
254
+);
255
+--> statement-breakpoint
256
+CREATE TABLE "external_builds" (
257
+ "id" text PRIMARY KEY NOT NULL,
258
+ "config_id" text NOT NULL,
259
+ "pull_request_id" text,
260
+ "external_build_id" text NOT NULL,
261
+ "build_number" text,
262
+ "status" text NOT NULL,
263
+ "url" text,
264
+ "started_at" timestamp,
265
+ "completed_at" timestamp,
266
+ "created_at" timestamp DEFAULT now() NOT NULL
267
+);
268
+--> statement-breakpoint
269
+CREATE TABLE "external_ci_configs" (
270
+ "id" text PRIMARY KEY NOT NULL,
271
+ "repository_id" text NOT NULL,
272
+ "provider" text NOT NULL,
273
+ "name" text NOT NULL,
274
+ "base_url" text NOT NULL,
275
+ "api_token" text,
276
+ "project_id" text,
277
+ "webhook_secret" text,
278
+ "is_enabled" boolean DEFAULT true,
279
+ "sync_status" boolean DEFAULT true,
280
+ "created_at" timestamp DEFAULT now() NOT NULL,
281
+ "updated_at" timestamp DEFAULT now() NOT NULL
282
+);
283
+--> statement-breakpoint
284
+CREATE TABLE "file_approvals" (
285
+ "id" text PRIMARY KEY NOT NULL,
286
+ "pull_request_id" text NOT NULL,
287
+ "path" text NOT NULL,
288
+ "approved_by_id" text NOT NULL,
289
+ "approved_at" timestamp DEFAULT now() NOT NULL,
290
+ "commit_sha" text NOT NULL,
291
+ "comment" text,
292
+ "created_at" timestamp DEFAULT now() NOT NULL,
293
+ "updated_at" timestamp DEFAULT now() NOT NULL
294
+);
295
+--> statement-breakpoint
296
+CREATE TABLE "file_hotspots" (
297
+ "id" text PRIMARY KEY NOT NULL,
298
+ "repository_id" text NOT NULL,
299
+ "file_path" text NOT NULL,
300
+ "change_count" integer DEFAULT 0,
301
+ "bug_count" integer DEFAULT 0,
302
+ "review_count" integer DEFAULT 0,
303
+ "complexity_score" real,
304
+ "last_modified" timestamp,
305
+ "calculated_at" timestamp DEFAULT now() NOT NULL
306
+);
307
+--> statement-breakpoint
308
+CREATE TABLE "ip_allow_lists" (
309
+ "id" text PRIMARY KEY NOT NULL,
310
+ "organization_id" text NOT NULL,
311
+ "name" text NOT NULL,
312
+ "cidr_block" text NOT NULL,
313
+ "description" text,
314
+ "is_enabled" boolean DEFAULT true,
315
+ "created_at" timestamp DEFAULT now() NOT NULL,
316
+ "created_by_id" text NOT NULL
317
+);
318
+--> statement-breakpoint
319
+CREATE TABLE "issue_tracker_configs" (
320
+ "id" text PRIMARY KEY NOT NULL,
321
+ "repository_id" text NOT NULL,
322
+ "provider" text NOT NULL,
323
+ "name" text NOT NULL,
324
+ "api_url" text,
325
+ "api_token" text,
326
+ "project_key" text,
327
+ "webhook_secret" text,
328
+ "is_enabled" boolean DEFAULT true,
329
+ "sync_to_external" boolean DEFAULT true,
330
+ "sync_from_external" boolean DEFAULT true,
331
+ "created_at" timestamp DEFAULT now() NOT NULL,
332
+ "updated_at" timestamp DEFAULT now() NOT NULL
333
+);
334
+--> statement-breakpoint
335
+CREATE TABLE "issue_tracker_links" (
336
+ "id" text PRIMARY KEY NOT NULL,
337
+ "config_id" text NOT NULL,
338
+ "local_issue_id" text,
339
+ "external_id" text NOT NULL,
340
+ "external_key" text,
341
+ "external_url" text,
342
+ "last_sync_at" timestamp,
343
+ "created_at" timestamp DEFAULT now() NOT NULL
344
+);
345
+--> statement-breakpoint
346
+CREATE TABLE "license_scans" (
347
+ "id" text PRIMARY KEY NOT NULL,
348
+ "repository_id" text NOT NULL,
349
+ "commit_sha" text NOT NULL,
350
+ "package_name" text NOT NULL,
351
+ "package_version" text,
352
+ "license" text NOT NULL,
353
+ "license_type" text NOT NULL,
354
+ "is_compliant" boolean NOT NULL,
355
+ "policy_violation" text,
356
+ "created_at" timestamp DEFAULT now() NOT NULL
357
+);
358
+--> statement-breakpoint
359
+CREATE TABLE "merge_gates" (
360
+ "id" text PRIMARY KEY NOT NULL,
361
+ "repository_id" text NOT NULL,
362
+ "name" text NOT NULL,
363
+ "description" text,
364
+ "gate_type" text NOT NULL,
365
+ "config" text,
366
+ "condition_script" text,
367
+ "is_enabled" boolean DEFAULT true,
368
+ "order" integer DEFAULT 0,
369
+ "created_at" timestamp DEFAULT now() NOT NULL
370
+);
371
+--> statement-breakpoint
372
+CREATE TABLE "metric_snapshots" (
373
+ "id" text PRIMARY KEY NOT NULL,
374
+ "repository_id" text,
375
+ "organization_id" text,
376
+ "metric_type" text NOT NULL,
377
+ "value" real NOT NULL,
378
+ "dimensions" jsonb,
379
+ "timestamp" timestamp DEFAULT now() NOT NULL
380
+);
381
+--> statement-breakpoint
382
+CREATE TABLE "migration_detections" (
383
+ "id" text PRIMARY KEY NOT NULL,
384
+ "pull_request_id" text NOT NULL,
385
+ "migration_type" text NOT NULL,
386
+ "tool" text,
387
+ "files" jsonb,
388
+ "is_reversible" boolean,
389
+ "requires_downtime" boolean,
390
+ "notes" text,
391
+ "created_at" timestamp DEFAULT now() NOT NULL
392
+);
393
+--> statement-breakpoint
394
+CREATE TABLE "pr_issue_links" (
395
+ "id" text PRIMARY KEY NOT NULL,
396
+ "pull_request_id" text NOT NULL,
397
+ "issue_id" text NOT NULL,
398
+ "link_type" text NOT NULL,
399
+ "created_by_id" text NOT NULL,
400
+ "created_at" timestamp DEFAULT now() NOT NULL
401
+);
402
+--> statement-breakpoint
403
+CREATE TABLE "rate_limit_logs" (
404
+ "id" text PRIMARY KEY NOT NULL,
405
+ "rule_id" text NOT NULL,
406
+ "key" text NOT NULL,
407
+ "request_count" integer NOT NULL,
408
+ "window_start" timestamp NOT NULL,
409
+ "blocked" boolean DEFAULT false
410
+);
411
+--> statement-breakpoint
412
+CREATE TABLE "rate_limit_rules" (
413
+ "id" text PRIMARY KEY NOT NULL,
414
+ "name" text NOT NULL,
415
+ "path" text NOT NULL,
416
+ "method" text,
417
+ "window_ms" integer NOT NULL,
418
+ "max_requests" integer NOT NULL,
419
+ "key_type" text DEFAULT 'ip',
420
+ "is_enabled" boolean DEFAULT true,
421
+ "created_at" timestamp DEFAULT now() NOT NULL
422
+);
423
+--> statement-breakpoint
424
+CREATE TABLE "repository_workflows" (
425
+ "id" text PRIMARY KEY NOT NULL,
426
+ "repository_id" text NOT NULL,
427
+ "name" text NOT NULL,
428
+ "path" text NOT NULL,
429
+ "content" text NOT NULL,
430
+ "template_id" text,
431
+ "is_enabled" boolean DEFAULT true,
432
+ "created_at" timestamp DEFAULT now() NOT NULL,
433
+ "updated_at" timestamp DEFAULT now() NOT NULL
434
+);
435
+--> statement-breakpoint
436
+CREATE TABLE "required_status_checks" (
437
+ "id" text PRIMARY KEY NOT NULL,
438
+ "repository_id" text NOT NULL,
439
+ "branch" text NOT NULL,
440
+ "check_name" text NOT NULL,
441
+ "is_required" boolean DEFAULT true,
442
+ "strict_mode" boolean DEFAULT true,
443
+ "created_at" timestamp DEFAULT now() NOT NULL
444
+);
445
+--> statement-breakpoint
446
+CREATE TABLE "review_templates" (
447
+ "id" text PRIMARY KEY NOT NULL,
448
+ "repository_id" text NOT NULL,
449
+ "organization_id" text,
450
+ "name" text NOT NULL,
451
+ "description" text,
452
+ "category" text,
453
+ "content" text NOT NULL,
454
+ "is_default" boolean DEFAULT false NOT NULL,
455
+ "created_by_id" text NOT NULL,
456
+ "created_at" timestamp DEFAULT now() NOT NULL,
457
+ "updated_at" timestamp DEFAULT now() NOT NULL
458
+);
459
+--> statement-breakpoint
460
+CREATE TABLE "saml_configs" (
461
+ "id" text PRIMARY KEY NOT NULL,
462
+ "organization_id" text NOT NULL,
463
+ "entity_id" text NOT NULL,
464
+ "sso_url" text NOT NULL,
465
+ "certificate" text NOT NULL,
466
+ "signature_algorithm" text DEFAULT 'RSA-SHA256',
467
+ "digest_algorithm" text DEFAULT 'SHA256',
468
+ "is_enabled" boolean DEFAULT true,
469
+ "created_at" timestamp DEFAULT now() NOT NULL,
470
+ "updated_at" timestamp DEFAULT now() NOT NULL
471
+);
472
+--> statement-breakpoint
473
+CREATE TABLE "secret_scan_results" (
474
+ "id" text PRIMARY KEY NOT NULL,
475
+ "repository_id" text NOT NULL,
476
+ "commit_sha" text NOT NULL,
477
+ "secret_type" text NOT NULL,
478
+ "file" text NOT NULL,
479
+ "line" integer,
480
+ "snippet" text,
481
+ "severity" text NOT NULL,
482
+ "status" text DEFAULT 'open',
483
+ "resolved_at" timestamp,
484
+ "resolved_by_id" text,
485
+ "created_at" timestamp DEFAULT now() NOT NULL
486
+);
487
+--> statement-breakpoint
488
+CREATE TABLE "workflow_states" (
489
+ "id" text PRIMARY KEY NOT NULL,
490
+ "repository_id" text NOT NULL,
491
+ "name" text NOT NULL,
492
+ "category" text NOT NULL,
493
+ "color" text DEFAULT '#6b7280',
494
+ "icon" text,
495
+ "display_order" integer DEFAULT 0,
496
+ "is_default" boolean DEFAULT false,
497
+ "is_closed_state" boolean DEFAULT false,
498
+ "created_at" timestamp DEFAULT now() NOT NULL
499
+);
500
+--> statement-breakpoint
501
+CREATE TABLE "workflow_templates" (
502
+ "id" text PRIMARY KEY NOT NULL,
503
+ "name" text NOT NULL,
504
+ "description" text,
505
+ "category" text NOT NULL,
506
+ "language" text,
507
+ "content" text NOT NULL,
508
+ "is_official" boolean DEFAULT false,
509
+ "is_public" boolean DEFAULT true,
510
+ "created_by_id" text,
511
+ "downloads" text DEFAULT '0',
512
+ "created_at" timestamp DEFAULT now() NOT NULL,
513
+ "updated_at" timestamp DEFAULT now() NOT NULL
514
+);
515
+--> statement-breakpoint
516
+CREATE TABLE "workflow_transitions" (
517
+ "id" text PRIMARY KEY NOT NULL,
518
+ "repository_id" text NOT NULL,
519
+ "from_state_id" text,
520
+ "to_state_id" text NOT NULL,
521
+ "requires_comment" boolean DEFAULT false,
522
+ "created_at" timestamp DEFAULT now() NOT NULL
523
+);
524
+--> statement-breakpoint
525
+ALTER TABLE "deploy_keys" ALTER COLUMN "read_only" SET NOT NULL;--> statement-breakpoint
526
+ALTER TABLE "deploy_keys" ADD COLUMN "fingerprint" text NOT NULL;--> statement-breakpoint
527
+ALTER TABLE "deploy_keys" ADD COLUMN "last_used_at" timestamp;--> statement-breakpoint
528
+ALTER TABLE "repositories" ADD COLUMN "is_template" boolean DEFAULT false;--> statement-breakpoint
529
+ALTER TABLE "repositories" ADD COLUMN "last_mirror_sync_at" timestamp;--> statement-breakpoint
530
+ALTER TABLE "repositories" ADD COLUMN "mirror_sync_status" text;--> statement-breakpoint
531
+ALTER TABLE "pull_request_checks" ADD COLUMN "completed_at" timestamp;--> statement-breakpoint
532
+ALTER TABLE "pull_request_checks" ADD COLUMN "created_at" timestamp DEFAULT now() NOT NULL;--> statement-breakpoint
533
+ALTER TABLE "pull_requests" ADD COLUMN "auto_merge_enabled_by_id" text;--> statement-breakpoint
534
+ALTER TABLE "pull_requests" ADD COLUMN "auto_merge_enabled_at" timestamp;--> statement-breakpoint
535
+ALTER TABLE "pull_requests" ADD COLUMN "custom_state" text;--> statement-breakpoint
536
+ALTER TABLE "pull_requests" ADD COLUMN "custom_state_changed_at" timestamp;--> statement-breakpoint
537
+ALTER TABLE "audit_logs" ADD COLUMN "organization_id" text;--> statement-breakpoint
538
+ALTER TABLE "team_members" ADD CONSTRAINT "team_members_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
539
+ALTER TABLE "team_members" ADD CONSTRAINT "team_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
540
+ALTER TABLE "teams" ADD CONSTRAINT "teams_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
541
+ALTER TABLE "repository_path_permissions" ADD CONSTRAINT "repository_path_permissions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
542
+ALTER TABLE "repository_path_permissions" ADD CONSTRAINT "repository_path_permissions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
543
+ALTER TABLE "repository_path_permissions" ADD CONSTRAINT "repository_path_permissions_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
544
+ALTER TABLE "pr_state_definitions" ADD CONSTRAINT "pr_state_definitions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
545
+ALTER TABLE "pr_state_reviewers" ADD CONSTRAINT "pr_state_reviewers_state_definition_id_pr_state_definitions_id_fk" FOREIGN KEY ("state_definition_id") REFERENCES "public"."pr_state_definitions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
546
+ALTER TABLE "pr_state_reviewers" ADD CONSTRAINT "pr_state_reviewers_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
547
+ALTER TABLE "pr_state_reviewers" ADD CONSTRAINT "pr_state_reviewers_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
548
+ALTER TABLE "pr_state_transitions" ADD CONSTRAINT "pr_state_transitions_changed_by_id_users_id_fk" FOREIGN KEY ("changed_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
549
+ALTER TABLE "breaking_changes" ADD CONSTRAINT "breaking_changes_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
550
+ALTER TABLE "change_set_items" ADD CONSTRAINT "change_set_items_change_set_id_change_sets_id_fk" FOREIGN KEY ("change_set_id") REFERENCES "public"."change_sets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
551
+ALTER TABLE "change_set_items" ADD CONSTRAINT "change_set_items_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
552
+ALTER TABLE "change_set_items" ADD CONSTRAINT "change_set_items_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
553
+ALTER TABLE "change_sets" ADD CONSTRAINT "change_sets_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
554
+ALTER TABLE "cloud_configs" ADD CONSTRAINT "cloud_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
555
+ALTER TABLE "code_quality_configs" ADD CONSTRAINT "code_quality_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
556
+ALTER TABLE "code_quality_issues" ADD CONSTRAINT "code_quality_issues_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
557
+ALTER TABLE "coverage_reports" ADD CONSTRAINT "coverage_reports_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
558
+ALTER TABLE "cross_repo_issue_links" ADD CONSTRAINT "cross_repo_issue_links_source_issue_id_issues_id_fk" FOREIGN KEY ("source_issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
559
+ALTER TABLE "cross_repo_issue_links" ADD CONSTRAINT "cross_repo_issue_links_target_issue_id_issues_id_fk" FOREIGN KEY ("target_issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
560
+ALTER TABLE "custom_dashboards" ADD CONSTRAINT "custom_dashboards_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
561
+ALTER TABLE "custom_field_definitions" ADD CONSTRAINT "custom_field_definitions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
562
+ALTER TABLE "custom_field_values" ADD CONSTRAINT "custom_field_values_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
563
+ALTER TABLE "custom_field_values" ADD CONSTRAINT "custom_field_values_field_id_custom_field_definitions_id_fk" FOREIGN KEY ("field_id") REFERENCES "public"."custom_field_definitions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
564
+ALTER TABLE "dashboard_widgets" ADD CONSTRAINT "dashboard_widgets_dashboard_id_custom_dashboards_id_fk" FOREIGN KEY ("dashboard_id") REFERENCES "public"."custom_dashboards"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
565
+ALTER TABLE "cloud_deployments" ADD CONSTRAINT "cloud_deployments_config_id_cloud_configs_id_fk" FOREIGN KEY ("config_id") REFERENCES "public"."cloud_configs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
566
+ALTER TABLE "external_builds" ADD CONSTRAINT "external_builds_config_id_external_ci_configs_id_fk" FOREIGN KEY ("config_id") REFERENCES "public"."external_ci_configs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
567
+ALTER TABLE "external_ci_configs" ADD CONSTRAINT "external_ci_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
568
+ALTER TABLE "file_approvals" ADD CONSTRAINT "file_approvals_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
569
+ALTER TABLE "file_approvals" ADD CONSTRAINT "file_approvals_approved_by_id_users_id_fk" FOREIGN KEY ("approved_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
570
+ALTER TABLE "file_hotspots" ADD CONSTRAINT "file_hotspots_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
571
+ALTER TABLE "issue_tracker_configs" ADD CONSTRAINT "issue_tracker_configs_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
572
+ALTER TABLE "issue_tracker_links" ADD CONSTRAINT "issue_tracker_links_config_id_issue_tracker_configs_id_fk" FOREIGN KEY ("config_id") REFERENCES "public"."issue_tracker_configs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
573
+ALTER TABLE "issue_tracker_links" ADD CONSTRAINT "issue_tracker_links_local_issue_id_issues_id_fk" FOREIGN KEY ("local_issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
574
+ALTER TABLE "license_scans" ADD CONSTRAINT "license_scans_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
575
+ALTER TABLE "merge_gates" ADD CONSTRAINT "merge_gates_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
576
+ALTER TABLE "metric_snapshots" ADD CONSTRAINT "metric_snapshots_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
577
+ALTER TABLE "migration_detections" ADD CONSTRAINT "migration_detections_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
578
+ALTER TABLE "pr_issue_links" ADD CONSTRAINT "pr_issue_links_pull_request_id_pull_requests_id_fk" FOREIGN KEY ("pull_request_id") REFERENCES "public"."pull_requests"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
579
+ALTER TABLE "pr_issue_links" ADD CONSTRAINT "pr_issue_links_issue_id_issues_id_fk" FOREIGN KEY ("issue_id") REFERENCES "public"."issues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
580
+ALTER TABLE "rate_limit_logs" ADD CONSTRAINT "rate_limit_logs_rule_id_rate_limit_rules_id_fk" FOREIGN KEY ("rule_id") REFERENCES "public"."rate_limit_rules"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
581
+ALTER TABLE "repository_workflows" ADD CONSTRAINT "repository_workflows_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
582
+ALTER TABLE "repository_workflows" ADD CONSTRAINT "repository_workflows_template_id_workflow_templates_id_fk" FOREIGN KEY ("template_id") REFERENCES "public"."workflow_templates"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
583
+ALTER TABLE "required_status_checks" ADD CONSTRAINT "required_status_checks_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
584
+ALTER TABLE "review_templates" ADD CONSTRAINT "review_templates_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
585
+ALTER TABLE "secret_scan_results" ADD CONSTRAINT "secret_scan_results_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
586
+ALTER TABLE "workflow_states" ADD CONSTRAINT "workflow_states_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
587
+ALTER TABLE "workflow_transitions" ADD CONSTRAINT "workflow_transitions_repository_id_repositories_id_fk" FOREIGN KEY ("repository_id") REFERENCES "public"."repositories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
588
+ALTER TABLE "workflow_transitions" ADD CONSTRAINT "workflow_transitions_from_state_id_workflow_states_id_fk" FOREIGN KEY ("from_state_id") REFERENCES "public"."workflow_states"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
589
+ALTER TABLE "workflow_transitions" ADD CONSTRAINT "workflow_transitions_to_state_id_workflow_states_id_fk" FOREIGN KEY ("to_state_id") REFERENCES "public"."workflow_states"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
590
+ALTER TABLE "pull_requests" ADD CONSTRAINT "pull_requests_auto_merge_enabled_by_id_users_id_fk" FOREIGN KEY ("auto_merge_enabled_by_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
0
591
▼generate-roadmap-issues.mjs
+80-0
1
+#!/usr/bin/env node
2
+import fs from 'node:fs';
3
+import path from 'node:path';
4
+
5
+const repoRoot = process.cwd();
6
+const checklistPath = path.join(repoRoot, 'github-issues-checklist.md');
7
+const outputPath = path.join(repoRoot, 'github-roadmap-issues.json');
8
+
9
+const source = fs.readFileSync(checklistPath, 'utf8');
10
+const lines = source.split(/\r?\n/);
11
+
12
+const issueLine = /^- \[ \] (G\d{3}) \| \[(Partial|Missing)\] (.+?) \| WS: `(WS\d+)` \| Phase: `(\d+)` \| Priority: `(low|medium|high)` \| Labels: `([^`]+)`$/;
13
+
14
+const issues = [];
15
+
16
+for (const line of lines) {
17
+ const match = line.match(issueLine);
18
+ if (!match) continue;
19
+
20
+ const [, id, status, feature, ws, phase, priority, labelCsv] = match;
21
+ const labels = labelCsv.split(',').map((label) => label.trim()).filter(Boolean);
22
+
23
+ const title = `[${id}] ${feature}`;
24
+ const body = [
25
+ `<!-- roadmap-id:${id} -->`,
26
+ '',
27
+ `## Metadata`,
28
+ `- Gap ID: ${id}`,
29
+ `- Status: ${status}`,
30
+ `- Workstream: ${ws}`,
31
+ `- Phase: ${phase}`,
32
+ `- Priority: ${priority}`,
33
+ '',
34
+ '## Problem',
35
+ 'Current state and gap from feature audit.',
36
+ '',
37
+ '## Scope',
38
+ '- Backend/API changes',
39
+ '- UI/UX changes',
40
+ '- Data model/migrations',
41
+ '- Docs updates (`docs/` + `docs-site/src/content/docs/`)',
42
+ '',
43
+ '## Acceptance Criteria',
44
+ '- [ ] Feature behavior complete',
45
+ '- [ ] Authorization/security checks in place',
46
+ '- [ ] Unit/integration/e2e tests added',
47
+ '- [ ] `lint` + `typecheck` + tests pass',
48
+ '- [ ] Docs updated in both doc trees',
49
+ '',
50
+ '## Technical Notes',
51
+ 'Relevant files/modules, rollout/rollback plan, migration details.',
52
+ '',
53
+ '## Source',
54
+ '- Checklist: `github-issues-checklist.md`',
55
+ '- Roadmap: `notimplemented.md`',
56
+ ].join('\n');
57
+
58
+ issues.push({
59
+ id,
60
+ status: status.toLowerCase(),
61
+ feature,
62
+ ws,
63
+ phase: Number(phase),
64
+ priority,
65
+ labels,
66
+ title,
67
+ body,
68
+ });
69
+}
70
+
71
+if (issues.length === 0) {
72
+ throw new Error('No issue rows parsed from github-issues-checklist.md');
73
+}
74
+
75
+issues.sort((a, b) => a.id.localeCompare(b.id));
76
+
77
+fs.mkdirSync(path.dirname(outputPath), { recursive: true });
78
+fs.writeFileSync(outputPath, `${JSON.stringify(issues, null, 2)}\n`, 'utf8');
79
+
80
+console.log(`Generated ${issues.length} issues -> ${path.relative(repoRoot, outputPath)}`);
0
81
▼github-issues-checklist.md
+117-0
1
+# GitHub Issue Checklist: Partial + Missing Gaps
2
+
3
+Generated: 2026-02-15
4
+Source: `doc/feature_audit.md`
5
+
6
+Total gaps: **67** (Partial: **29**, Missing: **38**)
7
+
8
+## How To Use
9
+1. Create one GitHub issue per checklist line.
10
+2. Use the line text as the issue title and metadata.
11
+3. Apply listed labels exactly and assign an owner.
12
+4. Add links to code paths + docs updates in `docs/` and `docs-site/src/content/docs/`.
13
+
14
+## Issue Body Template
15
+```md
16
+## Problem
17
+Current state and gap from feature audit.
18
+
19
+## Scope
20
+- Backend/API changes
21
+- UI/UX changes
22
+- Data model/migrations
23
+- Docs updates (`docs/` + `docs-site/src/content/docs/`)
24
+
25
+## Acceptance Criteria
26
+- [ ] Feature behavior complete
27
+- [ ] Authorization/security checks in place
28
+- [ ] Unit/integration/e2e tests added
29
+- [ ] `lint` + `typecheck` + tests pass
30
+- [ ] Docs updated in both doc trees
31
+
32
+## Technical Notes
33
+Relevant files/modules, rollout/rollback plan, migration details.
34
+```
35
+
36
+## WS1 Core Collaboration
37
+
38
+- [ ] G001 | [Partial] Monorepo support | WS: `WS1` | Phase: `1` | Priority: `medium` | Labels: `roadmap/ws1,status/partial,phase/1,priority/medium,docs-required`
39
+- [ ] G002 | [Partial] Repository templates | WS: `WS1` | Phase: `1` | Priority: `medium` | Labels: `roadmap/ws1,status/partial,phase/1,priority/medium,docs-required`
40
+- [ ] G003 | [Partial] Cross-PR dependency detection | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/partial,phase/1,priority/high,docs-required`
41
+- [ ] G004 | [Partial] Stack rebase & auto-update | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/partial,phase/1,priority/high,docs-required`
42
+- [ ] G005 | [Partial] Stack-level approvals | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/partial,phase/1,priority/high,docs-required`
43
+- [ ] G006 | [Partial] Bulk merge (merge stacks) | WS: `WS1` | Phase: `1` | Priority: `medium` | Labels: `roadmap/ws1,status/partial,phase/1,priority/medium,docs-required`
44
+- [ ] G007 | [Partial] Auto-merge rules | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/partial,phase/1,priority/high,docs-required`
45
+- [ ] G008 | [Partial] Batch comments | WS: `WS1` | Phase: `1` | Priority: `medium` | Labels: `roadmap/ws1,status/partial,phase/1,priority/medium,docs-required`
46
+- [ ] G009 | [Partial] Code owner enforcement | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/partial,phase/1,priority/high,docs-required`
47
+- [ ] G010 | [Partial] Multi-reviewer rules | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/partial,phase/1,priority/high,docs-required`
48
+- [ ] G011 | [Partial] PR ↔ Issue linking | WS: `WS1` | Phase: `1` | Priority: `medium` | Labels: `roadmap/ws1,status/partial,phase/1,priority/medium,docs-required`
49
+- [ ] G012 | [Missing] File-level permissions | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/missing,phase/1,priority/high,docs-required`
50
+- [ ] G013 | [Missing] Custom PR states | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/missing,phase/1,priority/high,docs-required`
51
+- [ ] G014 | [Missing] Required reviewers per state | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/missing,phase/1,priority/high,docs-required`
52
+- [ ] G015 | [Missing] PR checks | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/missing,phase/1,priority/high,docs-required`
53
+- [ ] G016 | [Missing] Suggested changes | WS: `WS1` | Phase: `1` | Priority: `medium` | Labels: `roadmap/ws1,status/missing,phase/1,priority/medium,docs-required`
54
+- [ ] G017 | [Missing] Review templates | WS: `WS1` | Phase: `1` | Priority: `medium` | Labels: `roadmap/ws1,status/missing,phase/1,priority/medium,docs-required`
55
+- [ ] G018 | [Missing] Partial file approvals | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/missing,phase/1,priority/high,docs-required`
56
+- [ ] G019 | [Missing] Custom issue fields | WS: `WS1` | Phase: `1` | Priority: `medium` | Labels: `roadmap/ws1,status/missing,phase/1,priority/medium,docs-required`
57
+- [ ] G020 | [Missing] Issue workflows | WS: `WS1` | Phase: `1` | Priority: `high` | Labels: `roadmap/ws1,status/missing,phase/1,priority/high,docs-required`
58
+- [ ] G021 | [Missing] Cross-repo issues | WS: `WS1` | Phase: `1` | Priority: `medium` | Labels: `roadmap/ws1,status/missing,phase/1,priority/medium,docs-required`
59
+
60
+## WS2 CI/CD + Integrations
61
+
62
+- [ ] G022 | [Partial] External CI integration | WS: `WS2` | Phase: `2` | Priority: `high` | Labels: `roadmap/ws2,status/partial,phase/2,priority/high,docs-required`
63
+- [ ] G023 | [Partial] Merge checks & gates | WS: `WS2` | Phase: `2` | Priority: `high` | Labels: `roadmap/ws2,status/partial,phase/2,priority/high,docs-required`
64
+- [ ] G024 | [Partial] Automation rules engine | WS: `WS2` | Phase: `2` | Priority: `high` | Labels: `roadmap/ws2,status/partial,phase/2,priority/high,docs-required`
65
+- [ ] G025 | [Partial] Snyk | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/partial,phase/2,priority/medium,docs-required`
66
+- [ ] G026 | [Partial] Jira | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/partial,phase/2,priority/medium,docs-required`
67
+- [ ] G027 | [Missing] Workflow templates | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
68
+- [ ] G028 | [Missing] Codecov | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
69
+- [ ] G029 | [Missing] Coveralls | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
70
+- [ ] G030 | [Missing] SonarQube | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
71
+- [ ] G031 | [Missing] GitLab CI | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
72
+- [ ] G032 | [Missing] CircleCI | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
73
+- [ ] G033 | [Missing] Buildkite | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
74
+- [ ] G034 | [Missing] Jenkins | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
75
+- [ ] G035 | [Missing] Linear | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
76
+- [ ] G036 | [Missing] Trello | WS: `WS2` | Phase: `2` | Priority: `low` | Labels: `roadmap/ws2,status/missing,phase/2,priority/low,docs-required`
77
+- [ ] G037 | [Missing] ClickUp | WS: `WS2` | Phase: `2` | Priority: `low` | Labels: `roadmap/ws2,status/missing,phase/2,priority/low,docs-required`
78
+- [ ] G038 | [Missing] Microsoft Teams | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
79
+- [ ] G039 | [Missing] Discord | WS: `WS2` | Phase: `2` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/2,priority/medium,docs-required`
80
+- [ ] G040 | [Missing] AWS | WS: `WS2` | Phase: `5` | Priority: `low` | Labels: `roadmap/ws2,status/missing,phase/5,priority/low,docs-required`
81
+- [ ] G041 | [Missing] Google Cloud | WS: `WS2` | Phase: `5` | Priority: `low` | Labels: `roadmap/ws2,status/missing,phase/5,priority/low,docs-required`
82
+- [ ] G042 | [Missing] Microsoft Azure | WS: `WS2` | Phase: `5` | Priority: `low` | Labels: `roadmap/ws2,status/missing,phase/5,priority/low,docs-required`
83
+- [ ] G043 | [Missing] Terraform/IaC hooks | WS: `WS2` | Phase: `5` | Priority: `medium` | Labels: `roadmap/ws2,status/missing,phase/5,priority/medium,docs-required`
84
+
85
+## WS3 Security + Compliance
86
+
87
+- [ ] G044 | [Partial] API change awareness | WS: `WS3` | Phase: `3` | Priority: `medium` | Labels: `roadmap/ws3,status/partial,phase/3,priority/medium,docs-required`
88
+- [ ] G045 | [Partial] Role-based access control (RBAC) | WS: `WS3` | Phase: `3` | Priority: `high` | Labels: `roadmap/ws3,status/partial,phase/3,priority/high,docs-required`
89
+- [ ] G046 | [Partial] Secret scanning | WS: `WS3` | Phase: `3` | Priority: `high` | Labels: `roadmap/ws3,status/partial,phase/3,priority/high,docs-required`
90
+- [ ] G047 | [Missing] Cross-repo change sets | WS: `WS3` | Phase: `3` | Priority: `medium` | Labels: `roadmap/ws3,status/missing,phase/3,priority/medium,docs-required`
91
+- [ ] G048 | [Missing] Breaking-change detection | WS: `WS3` | Phase: `3` | Priority: `medium` | Labels: `roadmap/ws3,status/missing,phase/3,priority/medium,docs-required`
92
+- [ ] G049 | [Missing] Database migration detection | WS: `WS3` | Phase: `3` | Priority: `medium` | Labels: `roadmap/ws3,status/missing,phase/3,priority/medium,docs-required`
93
+- [ ] G050 | [Missing] License compliance scanning | WS: `WS3` | Phase: `3` | Priority: `medium` | Labels: `roadmap/ws3,status/missing,phase/3,priority/medium,docs-required`
94
+
95
+## WS4 Analytics + Notifications + Extensibility
96
+
97
+- [ ] G051 | [Partial] Email notifications | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/partial,phase/4,priority/medium,docs-required`
98
+- [ ] G052 | [Partial] Merge frequency metrics | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/partial,phase/4,priority/medium,docs-required`
99
+- [ ] G053 | [Partial] Developer workload insights | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/partial,phase/4,priority/medium,docs-required`
100
+- [ ] G054 | [Partial] Smart notifications | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/partial,phase/4,priority/medium,docs-required`
101
+- [ ] G055 | [Partial] Blocking alerts | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/partial,phase/4,priority/medium,docs-required`
102
+- [ ] G056 | [Partial] Plugin/extension system | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/partial,phase/4,priority/medium,docs-required`
103
+- [ ] G057 | [Partial] API documentation | WS: `WS4` | Phase: `4` | Priority: `high` | Labels: `roadmap/ws4,status/partial,phase/4,priority/high,docs-required`
104
+- [ ] G058 | [Missing] Hotspot file detection | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/missing,phase/4,priority/medium,docs-required`
105
+- [ ] G059 | [Missing] Export metrics | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/missing,phase/4,priority/medium,docs-required`
106
+- [ ] G060 | [Missing] Custom dashboards | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/missing,phase/4,priority/medium,docs-required`
107
+- [ ] G061 | [Missing] Daily/weekly digests | WS: `WS4` | Phase: `4` | Priority: `medium` | Labels: `roadmap/ws4,status/missing,phase/4,priority/medium,docs-required`
108
+
109
+## WS5 Platform + Self-host + Scalability
110
+
111
+- [ ] G062 | [Partial] Repository mirroring | WS: `WS5` | Phase: `5` | Priority: `high` | Labels: `roadmap/ws5,status/partial,phase/5,priority/high,docs-required`
112
+- [ ] G063 | [Partial] Kubernetes deployment | WS: `WS5` | Phase: `5` | Priority: `medium` | Labels: `roadmap/ws5,status/partial,phase/5,priority/medium,docs-required`
113
+- [ ] G064 | [Partial] Horizontal scaling | WS: `WS5` | Phase: `5` | Priority: `high` | Labels: `roadmap/ws5,status/partial,phase/5,priority/high,docs-required`
114
+- [ ] G065 | [Missing] Kubernetes-native | WS: `WS5` | Phase: `5` | Priority: `medium` | Labels: `roadmap/ws5,status/missing,phase/5,priority/medium,docs-required`
115
+- [ ] G066 | [Missing] Backup & restore tools | WS: `WS5` | Phase: `5` | Priority: `high` | Labels: `roadmap/ws5,status/missing,phase/5,priority/high,docs-required`
116
+- [ ] G067 | [Missing] Offline/air-gapped mode | WS: `WS5` | Phase: `5` | Priority: `medium` | Labels: `roadmap/ws5,status/missing,phase/5,priority/medium,docs-required`
117
+
0
118
▼github-roadmap-issues.json
+1141-0
1
+[
2
+ {
3
+ "id": "G001",
4
+ "status": "partial",
5
+ "feature": "Monorepo support",
6
+ "ws": "WS1",
7
+ "phase": 1,
8
+ "priority": "medium",
9
+ "labels": [
10
+ "roadmap/ws1",
11
+ "status/partial",
12
+ "phase/1",
13
+ "priority/medium",
14
+ "docs-required"
15
+ ],
16
+ "title": "[G001] Monorepo support",
17
+ "body": "<!-- roadmap-id:G001 -->\n\n## Metadata\n- Gap ID: G001\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
18
+ },
19
+ {
20
+ "id": "G002",
21
+ "status": "partial",
22
+ "feature": "Repository templates",
23
+ "ws": "WS1",
24
+ "phase": 1,
25
+ "priority": "medium",
26
+ "labels": [
27
+ "roadmap/ws1",
28
+ "status/partial",
29
+ "phase/1",
30
+ "priority/medium",
31
+ "docs-required"
32
+ ],
33
+ "title": "[G002] Repository templates",
34
+ "body": "<!-- roadmap-id:G002 -->\n\n## Metadata\n- Gap ID: G002\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
35
+ },
36
+ {
37
+ "id": "G003",
38
+ "status": "partial",
39
+ "feature": "Cross-PR dependency detection",
40
+ "ws": "WS1",
41
+ "phase": 1,
42
+ "priority": "high",
43
+ "labels": [
44
+ "roadmap/ws1",
45
+ "status/partial",
46
+ "phase/1",
47
+ "priority/high",
48
+ "docs-required"
49
+ ],
50
+ "title": "[G003] Cross-PR dependency detection",
51
+ "body": "<!-- roadmap-id:G003 -->\n\n## Metadata\n- Gap ID: G003\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
52
+ },
53
+ {
54
+ "id": "G004",
55
+ "status": "partial",
56
+ "feature": "Stack rebase & auto-update",
57
+ "ws": "WS1",
58
+ "phase": 1,
59
+ "priority": "high",
60
+ "labels": [
61
+ "roadmap/ws1",
62
+ "status/partial",
63
+ "phase/1",
64
+ "priority/high",
65
+ "docs-required"
66
+ ],
67
+ "title": "[G004] Stack rebase & auto-update",
68
+ "body": "<!-- roadmap-id:G004 -->\n\n## Metadata\n- Gap ID: G004\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
69
+ },
70
+ {
71
+ "id": "G005",
72
+ "status": "partial",
73
+ "feature": "Stack-level approvals",
74
+ "ws": "WS1",
75
+ "phase": 1,
76
+ "priority": "high",
77
+ "labels": [
78
+ "roadmap/ws1",
79
+ "status/partial",
80
+ "phase/1",
81
+ "priority/high",
82
+ "docs-required"
83
+ ],
84
+ "title": "[G005] Stack-level approvals",
85
+ "body": "<!-- roadmap-id:G005 -->\n\n## Metadata\n- Gap ID: G005\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
86
+ },
87
+ {
88
+ "id": "G006",
89
+ "status": "partial",
90
+ "feature": "Bulk merge (merge stacks)",
91
+ "ws": "WS1",
92
+ "phase": 1,
93
+ "priority": "medium",
94
+ "labels": [
95
+ "roadmap/ws1",
96
+ "status/partial",
97
+ "phase/1",
98
+ "priority/medium",
99
+ "docs-required"
100
+ ],
101
+ "title": "[G006] Bulk merge (merge stacks)",
102
+ "body": "<!-- roadmap-id:G006 -->\n\n## Metadata\n- Gap ID: G006\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
103
+ },
104
+ {
105
+ "id": "G007",
106
+ "status": "partial",
107
+ "feature": "Auto-merge rules",
108
+ "ws": "WS1",
109
+ "phase": 1,
110
+ "priority": "high",
111
+ "labels": [
112
+ "roadmap/ws1",
113
+ "status/partial",
114
+ "phase/1",
115
+ "priority/high",
116
+ "docs-required"
117
+ ],
118
+ "title": "[G007] Auto-merge rules",
119
+ "body": "<!-- roadmap-id:G007 -->\n\n## Metadata\n- Gap ID: G007\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
120
+ },
121
+ {
122
+ "id": "G008",
123
+ "status": "partial",
124
+ "feature": "Batch comments",
125
+ "ws": "WS1",
126
+ "phase": 1,
127
+ "priority": "medium",
128
+ "labels": [
129
+ "roadmap/ws1",
130
+ "status/partial",
131
+ "phase/1",
132
+ "priority/medium",
133
+ "docs-required"
134
+ ],
135
+ "title": "[G008] Batch comments",
136
+ "body": "<!-- roadmap-id:G008 -->\n\n## Metadata\n- Gap ID: G008\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
137
+ },
138
+ {
139
+ "id": "G009",
140
+ "status": "partial",
141
+ "feature": "Code owner enforcement",
142
+ "ws": "WS1",
143
+ "phase": 1,
144
+ "priority": "high",
145
+ "labels": [
146
+ "roadmap/ws1",
147
+ "status/partial",
148
+ "phase/1",
149
+ "priority/high",
150
+ "docs-required"
151
+ ],
152
+ "title": "[G009] Code owner enforcement",
153
+ "body": "<!-- roadmap-id:G009 -->\n\n## Metadata\n- Gap ID: G009\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
154
+ },
155
+ {
156
+ "id": "G010",
157
+ "status": "partial",
158
+ "feature": "Multi-reviewer rules",
159
+ "ws": "WS1",
160
+ "phase": 1,
161
+ "priority": "high",
162
+ "labels": [
163
+ "roadmap/ws1",
164
+ "status/partial",
165
+ "phase/1",
166
+ "priority/high",
167
+ "docs-required"
168
+ ],
169
+ "title": "[G010] Multi-reviewer rules",
170
+ "body": "<!-- roadmap-id:G010 -->\n\n## Metadata\n- Gap ID: G010\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
171
+ },
172
+ {
173
+ "id": "G011",
174
+ "status": "partial",
175
+ "feature": "PR ↔ Issue linking",
176
+ "ws": "WS1",
177
+ "phase": 1,
178
+ "priority": "medium",
179
+ "labels": [
180
+ "roadmap/ws1",
181
+ "status/partial",
182
+ "phase/1",
183
+ "priority/medium",
184
+ "docs-required"
185
+ ],
186
+ "title": "[G011] PR ↔ Issue linking",
187
+ "body": "<!-- roadmap-id:G011 -->\n\n## Metadata\n- Gap ID: G011\n- Status: Partial\n- Workstream: WS1\n- Phase: 1\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
188
+ },
189
+ {
190
+ "id": "G012",
191
+ "status": "missing",
192
+ "feature": "File-level permissions",
193
+ "ws": "WS1",
194
+ "phase": 1,
195
+ "priority": "high",
196
+ "labels": [
197
+ "roadmap/ws1",
198
+ "status/missing",
199
+ "phase/1",
200
+ "priority/high",
201
+ "docs-required"
202
+ ],
203
+ "title": "[G012] File-level permissions",
204
+ "body": "<!-- roadmap-id:G012 -->\n\n## Metadata\n- Gap ID: G012\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
205
+ },
206
+ {
207
+ "id": "G013",
208
+ "status": "missing",
209
+ "feature": "Custom PR states",
210
+ "ws": "WS1",
211
+ "phase": 1,
212
+ "priority": "high",
213
+ "labels": [
214
+ "roadmap/ws1",
215
+ "status/missing",
216
+ "phase/1",
217
+ "priority/high",
218
+ "docs-required"
219
+ ],
220
+ "title": "[G013] Custom PR states",
221
+ "body": "<!-- roadmap-id:G013 -->\n\n## Metadata\n- Gap ID: G013\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
222
+ },
223
+ {
224
+ "id": "G014",
225
+ "status": "missing",
226
+ "feature": "Required reviewers per state",
227
+ "ws": "WS1",
228
+ "phase": 1,
229
+ "priority": "high",
230
+ "labels": [
231
+ "roadmap/ws1",
232
+ "status/missing",
233
+ "phase/1",
234
+ "priority/high",
235
+ "docs-required"
236
+ ],
237
+ "title": "[G014] Required reviewers per state",
238
+ "body": "<!-- roadmap-id:G014 -->\n\n## Metadata\n- Gap ID: G014\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
239
+ },
240
+ {
241
+ "id": "G015",
242
+ "status": "missing",
243
+ "feature": "PR checks",
244
+ "ws": "WS1",
245
+ "phase": 1,
246
+ "priority": "high",
247
+ "labels": [
248
+ "roadmap/ws1",
249
+ "status/missing",
250
+ "phase/1",
251
+ "priority/high",
252
+ "docs-required"
253
+ ],
254
+ "title": "[G015] PR checks",
255
+ "body": "<!-- roadmap-id:G015 -->\n\n## Metadata\n- Gap ID: G015\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
256
+ },
257
+ {
258
+ "id": "G016",
259
+ "status": "missing",
260
+ "feature": "Suggested changes",
261
+ "ws": "WS1",
262
+ "phase": 1,
263
+ "priority": "medium",
264
+ "labels": [
265
+ "roadmap/ws1",
266
+ "status/missing",
267
+ "phase/1",
268
+ "priority/medium",
269
+ "docs-required"
270
+ ],
271
+ "title": "[G016] Suggested changes",
272
+ "body": "<!-- roadmap-id:G016 -->\n\n## Metadata\n- Gap ID: G016\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
273
+ },
274
+ {
275
+ "id": "G017",
276
+ "status": "missing",
277
+ "feature": "Review templates",
278
+ "ws": "WS1",
279
+ "phase": 1,
280
+ "priority": "medium",
281
+ "labels": [
282
+ "roadmap/ws1",
283
+ "status/missing",
284
+ "phase/1",
285
+ "priority/medium",
286
+ "docs-required"
287
+ ],
288
+ "title": "[G017] Review templates",
289
+ "body": "<!-- roadmap-id:G017 -->\n\n## Metadata\n- Gap ID: G017\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
290
+ },
291
+ {
292
+ "id": "G018",
293
+ "status": "missing",
294
+ "feature": "Partial file approvals",
295
+ "ws": "WS1",
296
+ "phase": 1,
297
+ "priority": "high",
298
+ "labels": [
299
+ "roadmap/ws1",
300
+ "status/missing",
301
+ "phase/1",
302
+ "priority/high",
303
+ "docs-required"
304
+ ],
305
+ "title": "[G018] Partial file approvals",
306
+ "body": "<!-- roadmap-id:G018 -->\n\n## Metadata\n- Gap ID: G018\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
307
+ },
308
+ {
309
+ "id": "G019",
310
+ "status": "missing",
311
+ "feature": "Custom issue fields",
312
+ "ws": "WS1",
313
+ "phase": 1,
314
+ "priority": "medium",
315
+ "labels": [
316
+ "roadmap/ws1",
317
+ "status/missing",
318
+ "phase/1",
319
+ "priority/medium",
320
+ "docs-required"
321
+ ],
322
+ "title": "[G019] Custom issue fields",
323
+ "body": "<!-- roadmap-id:G019 -->\n\n## Metadata\n- Gap ID: G019\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
324
+ },
325
+ {
326
+ "id": "G020",
327
+ "status": "missing",
328
+ "feature": "Issue workflows",
329
+ "ws": "WS1",
330
+ "phase": 1,
331
+ "priority": "high",
332
+ "labels": [
333
+ "roadmap/ws1",
334
+ "status/missing",
335
+ "phase/1",
336
+ "priority/high",
337
+ "docs-required"
338
+ ],
339
+ "title": "[G020] Issue workflows",
340
+ "body": "<!-- roadmap-id:G020 -->\n\n## Metadata\n- Gap ID: G020\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
341
+ },
342
+ {
343
+ "id": "G021",
344
+ "status": "missing",
345
+ "feature": "Cross-repo issues",
346
+ "ws": "WS1",
347
+ "phase": 1,
348
+ "priority": "medium",
349
+ "labels": [
350
+ "roadmap/ws1",
351
+ "status/missing",
352
+ "phase/1",
353
+ "priority/medium",
354
+ "docs-required"
355
+ ],
356
+ "title": "[G021] Cross-repo issues",
357
+ "body": "<!-- roadmap-id:G021 -->\n\n## Metadata\n- Gap ID: G021\n- Status: Missing\n- Workstream: WS1\n- Phase: 1\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
358
+ },
359
+ {
360
+ "id": "G022",
361
+ "status": "partial",
362
+ "feature": "External CI integration",
363
+ "ws": "WS2",
364
+ "phase": 2,
365
+ "priority": "high",
366
+ "labels": [
367
+ "roadmap/ws2",
368
+ "status/partial",
369
+ "phase/2",
370
+ "priority/high",
371
+ "docs-required"
372
+ ],
373
+ "title": "[G022] External CI integration",
374
+ "body": "<!-- roadmap-id:G022 -->\n\n## Metadata\n- Gap ID: G022\n- Status: Partial\n- Workstream: WS2\n- Phase: 2\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
375
+ },
376
+ {
377
+ "id": "G023",
378
+ "status": "partial",
379
+ "feature": "Merge checks & gates",
380
+ "ws": "WS2",
381
+ "phase": 2,
382
+ "priority": "high",
383
+ "labels": [
384
+ "roadmap/ws2",
385
+ "status/partial",
386
+ "phase/2",
387
+ "priority/high",
388
+ "docs-required"
389
+ ],
390
+ "title": "[G023] Merge checks & gates",
391
+ "body": "<!-- roadmap-id:G023 -->\n\n## Metadata\n- Gap ID: G023\n- Status: Partial\n- Workstream: WS2\n- Phase: 2\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
392
+ },
393
+ {
394
+ "id": "G024",
395
+ "status": "partial",
396
+ "feature": "Automation rules engine",
397
+ "ws": "WS2",
398
+ "phase": 2,
399
+ "priority": "high",
400
+ "labels": [
401
+ "roadmap/ws2",
402
+ "status/partial",
403
+ "phase/2",
404
+ "priority/high",
405
+ "docs-required"
406
+ ],
407
+ "title": "[G024] Automation rules engine",
408
+ "body": "<!-- roadmap-id:G024 -->\n\n## Metadata\n- Gap ID: G024\n- Status: Partial\n- Workstream: WS2\n- Phase: 2\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
409
+ },
410
+ {
411
+ "id": "G025",
412
+ "status": "partial",
413
+ "feature": "Snyk",
414
+ "ws": "WS2",
415
+ "phase": 2,
416
+ "priority": "medium",
417
+ "labels": [
418
+ "roadmap/ws2",
419
+ "status/partial",
420
+ "phase/2",
421
+ "priority/medium",
422
+ "docs-required"
423
+ ],
424
+ "title": "[G025] Snyk",
425
+ "body": "<!-- roadmap-id:G025 -->\n\n## Metadata\n- Gap ID: G025\n- Status: Partial\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
426
+ },
427
+ {
428
+ "id": "G026",
429
+ "status": "partial",
430
+ "feature": "Jira",
431
+ "ws": "WS2",
432
+ "phase": 2,
433
+ "priority": "medium",
434
+ "labels": [
435
+ "roadmap/ws2",
436
+ "status/partial",
437
+ "phase/2",
438
+ "priority/medium",
439
+ "docs-required"
440
+ ],
441
+ "title": "[G026] Jira",
442
+ "body": "<!-- roadmap-id:G026 -->\n\n## Metadata\n- Gap ID: G026\n- Status: Partial\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
443
+ },
444
+ {
445
+ "id": "G027",
446
+ "status": "missing",
447
+ "feature": "Workflow templates",
448
+ "ws": "WS2",
449
+ "phase": 2,
450
+ "priority": "medium",
451
+ "labels": [
452
+ "roadmap/ws2",
453
+ "status/missing",
454
+ "phase/2",
455
+ "priority/medium",
456
+ "docs-required"
457
+ ],
458
+ "title": "[G027] Workflow templates",
459
+ "body": "<!-- roadmap-id:G027 -->\n\n## Metadata\n- Gap ID: G027\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
460
+ },
461
+ {
462
+ "id": "G028",
463
+ "status": "missing",
464
+ "feature": "Codecov",
465
+ "ws": "WS2",
466
+ "phase": 2,
467
+ "priority": "medium",
468
+ "labels": [
469
+ "roadmap/ws2",
470
+ "status/missing",
471
+ "phase/2",
472
+ "priority/medium",
473
+ "docs-required"
474
+ ],
475
+ "title": "[G028] Codecov",
476
+ "body": "<!-- roadmap-id:G028 -->\n\n## Metadata\n- Gap ID: G028\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
477
+ },
478
+ {
479
+ "id": "G029",
480
+ "status": "missing",
481
+ "feature": "Coveralls",
482
+ "ws": "WS2",
483
+ "phase": 2,
484
+ "priority": "medium",
485
+ "labels": [
486
+ "roadmap/ws2",
487
+ "status/missing",
488
+ "phase/2",
489
+ "priority/medium",
490
+ "docs-required"
491
+ ],
492
+ "title": "[G029] Coveralls",
493
+ "body": "<!-- roadmap-id:G029 -->\n\n## Metadata\n- Gap ID: G029\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
494
+ },
495
+ {
496
+ "id": "G030",
497
+ "status": "missing",
498
+ "feature": "SonarQube",
499
+ "ws": "WS2",
500
+ "phase": 2,
501
+ "priority": "medium",
502
+ "labels": [
503
+ "roadmap/ws2",
504
+ "status/missing",
505
+ "phase/2",
506
+ "priority/medium",
507
+ "docs-required"
508
+ ],
509
+ "title": "[G030] SonarQube",
510
+ "body": "<!-- roadmap-id:G030 -->\n\n## Metadata\n- Gap ID: G030\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
511
+ },
512
+ {
513
+ "id": "G031",
514
+ "status": "missing",
515
+ "feature": "GitLab CI",
516
+ "ws": "WS2",
517
+ "phase": 2,
518
+ "priority": "medium",
519
+ "labels": [
520
+ "roadmap/ws2",
521
+ "status/missing",
522
+ "phase/2",
523
+ "priority/medium",
524
+ "docs-required"
525
+ ],
526
+ "title": "[G031] GitLab CI",
527
+ "body": "<!-- roadmap-id:G031 -->\n\n## Metadata\n- Gap ID: G031\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
528
+ },
529
+ {
530
+ "id": "G032",
531
+ "status": "missing",
532
+ "feature": "CircleCI",
533
+ "ws": "WS2",
534
+ "phase": 2,
535
+ "priority": "medium",
536
+ "labels": [
537
+ "roadmap/ws2",
538
+ "status/missing",
539
+ "phase/2",
540
+ "priority/medium",
541
+ "docs-required"
542
+ ],
543
+ "title": "[G032] CircleCI",
544
+ "body": "<!-- roadmap-id:G032 -->\n\n## Metadata\n- Gap ID: G032\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
545
+ },
546
+ {
547
+ "id": "G033",
548
+ "status": "missing",
549
+ "feature": "Buildkite",
550
+ "ws": "WS2",
551
+ "phase": 2,
552
+ "priority": "medium",
553
+ "labels": [
554
+ "roadmap/ws2",
555
+ "status/missing",
556
+ "phase/2",
557
+ "priority/medium",
558
+ "docs-required"
559
+ ],
560
+ "title": "[G033] Buildkite",
561
+ "body": "<!-- roadmap-id:G033 -->\n\n## Metadata\n- Gap ID: G033\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
562
+ },
563
+ {
564
+ "id": "G034",
565
+ "status": "missing",
566
+ "feature": "Jenkins",
567
+ "ws": "WS2",
568
+ "phase": 2,
569
+ "priority": "medium",
570
+ "labels": [
571
+ "roadmap/ws2",
572
+ "status/missing",
573
+ "phase/2",
574
+ "priority/medium",
575
+ "docs-required"
576
+ ],
577
+ "title": "[G034] Jenkins",
578
+ "body": "<!-- roadmap-id:G034 -->\n\n## Metadata\n- Gap ID: G034\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
579
+ },
580
+ {
581
+ "id": "G035",
582
+ "status": "missing",
583
+ "feature": "Linear",
584
+ "ws": "WS2",
585
+ "phase": 2,
586
+ "priority": "medium",
587
+ "labels": [
588
+ "roadmap/ws2",
589
+ "status/missing",
590
+ "phase/2",
591
+ "priority/medium",
592
+ "docs-required"
593
+ ],
594
+ "title": "[G035] Linear",
595
+ "body": "<!-- roadmap-id:G035 -->\n\n## Metadata\n- Gap ID: G035\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
596
+ },
597
+ {
598
+ "id": "G036",
599
+ "status": "missing",
600
+ "feature": "Trello",
601
+ "ws": "WS2",
602
+ "phase": 2,
603
+ "priority": "low",
604
+ "labels": [
605
+ "roadmap/ws2",
606
+ "status/missing",
607
+ "phase/2",
608
+ "priority/low",
609
+ "docs-required"
610
+ ],
611
+ "title": "[G036] Trello",
612
+ "body": "<!-- roadmap-id:G036 -->\n\n## Metadata\n- Gap ID: G036\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: low\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
613
+ },
614
+ {
615
+ "id": "G037",
616
+ "status": "missing",
617
+ "feature": "ClickUp",
618
+ "ws": "WS2",
619
+ "phase": 2,
620
+ "priority": "low",
621
+ "labels": [
622
+ "roadmap/ws2",
623
+ "status/missing",
624
+ "phase/2",
625
+ "priority/low",
626
+ "docs-required"
627
+ ],
628
+ "title": "[G037] ClickUp",
629
+ "body": "<!-- roadmap-id:G037 -->\n\n## Metadata\n- Gap ID: G037\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: low\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
630
+ },
631
+ {
632
+ "id": "G038",
633
+ "status": "missing",
634
+ "feature": "Microsoft Teams",
635
+ "ws": "WS2",
636
+ "phase": 2,
637
+ "priority": "medium",
638
+ "labels": [
639
+ "roadmap/ws2",
640
+ "status/missing",
641
+ "phase/2",
642
+ "priority/medium",
643
+ "docs-required"
644
+ ],
645
+ "title": "[G038] Microsoft Teams",
646
+ "body": "<!-- roadmap-id:G038 -->\n\n## Metadata\n- Gap ID: G038\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
647
+ },
648
+ {
649
+ "id": "G039",
650
+ "status": "missing",
651
+ "feature": "Discord",
652
+ "ws": "WS2",
653
+ "phase": 2,
654
+ "priority": "medium",
655
+ "labels": [
656
+ "roadmap/ws2",
657
+ "status/missing",
658
+ "phase/2",
659
+ "priority/medium",
660
+ "docs-required"
661
+ ],
662
+ "title": "[G039] Discord",
663
+ "body": "<!-- roadmap-id:G039 -->\n\n## Metadata\n- Gap ID: G039\n- Status: Missing\n- Workstream: WS2\n- Phase: 2\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
664
+ },
665
+ {
666
+ "id": "G040",
667
+ "status": "missing",
668
+ "feature": "AWS",
669
+ "ws": "WS2",
670
+ "phase": 5,
671
+ "priority": "low",
672
+ "labels": [
673
+ "roadmap/ws2",
674
+ "status/missing",
675
+ "phase/5",
676
+ "priority/low",
677
+ "docs-required"
678
+ ],
679
+ "title": "[G040] AWS",
680
+ "body": "<!-- roadmap-id:G040 -->\n\n## Metadata\n- Gap ID: G040\n- Status: Missing\n- Workstream: WS2\n- Phase: 5\n- Priority: low\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
681
+ },
682
+ {
683
+ "id": "G041",
684
+ "status": "missing",
685
+ "feature": "Google Cloud",
686
+ "ws": "WS2",
687
+ "phase": 5,
688
+ "priority": "low",
689
+ "labels": [
690
+ "roadmap/ws2",
691
+ "status/missing",
692
+ "phase/5",
693
+ "priority/low",
694
+ "docs-required"
695
+ ],
696
+ "title": "[G041] Google Cloud",
697
+ "body": "<!-- roadmap-id:G041 -->\n\n## Metadata\n- Gap ID: G041\n- Status: Missing\n- Workstream: WS2\n- Phase: 5\n- Priority: low\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
698
+ },
699
+ {
700
+ "id": "G042",
701
+ "status": "missing",
702
+ "feature": "Microsoft Azure",
703
+ "ws": "WS2",
704
+ "phase": 5,
705
+ "priority": "low",
706
+ "labels": [
707
+ "roadmap/ws2",
708
+ "status/missing",
709
+ "phase/5",
710
+ "priority/low",
711
+ "docs-required"
712
+ ],
713
+ "title": "[G042] Microsoft Azure",
714
+ "body": "<!-- roadmap-id:G042 -->\n\n## Metadata\n- Gap ID: G042\n- Status: Missing\n- Workstream: WS2\n- Phase: 5\n- Priority: low\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
715
+ },
716
+ {
717
+ "id": "G043",
718
+ "status": "missing",
719
+ "feature": "Terraform/IaC hooks",
720
+ "ws": "WS2",
721
+ "phase": 5,
722
+ "priority": "medium",
723
+ "labels": [
724
+ "roadmap/ws2",
725
+ "status/missing",
726
+ "phase/5",
727
+ "priority/medium",
728
+ "docs-required"
729
+ ],
730
+ "title": "[G043] Terraform/IaC hooks",
731
+ "body": "<!-- roadmap-id:G043 -->\n\n## Metadata\n- Gap ID: G043\n- Status: Missing\n- Workstream: WS2\n- Phase: 5\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
732
+ },
733
+ {
734
+ "id": "G044",
735
+ "status": "partial",
736
+ "feature": "API change awareness",
737
+ "ws": "WS3",
738
+ "phase": 3,
739
+ "priority": "medium",
740
+ "labels": [
741
+ "roadmap/ws3",
742
+ "status/partial",
743
+ "phase/3",
744
+ "priority/medium",
745
+ "docs-required"
746
+ ],
747
+ "title": "[G044] API change awareness",
748
+ "body": "<!-- roadmap-id:G044 -->\n\n## Metadata\n- Gap ID: G044\n- Status: Partial\n- Workstream: WS3\n- Phase: 3\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
749
+ },
750
+ {
751
+ "id": "G045",
752
+ "status": "partial",
753
+ "feature": "Role-based access control (RBAC)",
754
+ "ws": "WS3",
755
+ "phase": 3,
756
+ "priority": "high",
757
+ "labels": [
758
+ "roadmap/ws3",
759
+ "status/partial",
760
+ "phase/3",
761
+ "priority/high",
762
+ "docs-required"
763
+ ],
764
+ "title": "[G045] Role-based access control (RBAC)",
765
+ "body": "<!-- roadmap-id:G045 -->\n\n## Metadata\n- Gap ID: G045\n- Status: Partial\n- Workstream: WS3\n- Phase: 3\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
766
+ },
767
+ {
768
+ "id": "G046",
769
+ "status": "partial",
770
+ "feature": "Secret scanning",
771
+ "ws": "WS3",
772
+ "phase": 3,
773
+ "priority": "high",
774
+ "labels": [
775
+ "roadmap/ws3",
776
+ "status/partial",
777
+ "phase/3",
778
+ "priority/high",
779
+ "docs-required"
780
+ ],
781
+ "title": "[G046] Secret scanning",
782
+ "body": "<!-- roadmap-id:G046 -->\n\n## Metadata\n- Gap ID: G046\n- Status: Partial\n- Workstream: WS3\n- Phase: 3\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
783
+ },
784
+ {
785
+ "id": "G047",
786
+ "status": "missing",
787
+ "feature": "Cross-repo change sets",
788
+ "ws": "WS3",
789
+ "phase": 3,
790
+ "priority": "medium",
791
+ "labels": [
792
+ "roadmap/ws3",
793
+ "status/missing",
794
+ "phase/3",
795
+ "priority/medium",
796
+ "docs-required"
797
+ ],
798
+ "title": "[G047] Cross-repo change sets",
799
+ "body": "<!-- roadmap-id:G047 -->\n\n## Metadata\n- Gap ID: G047\n- Status: Missing\n- Workstream: WS3\n- Phase: 3\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
800
+ },
801
+ {
802
+ "id": "G048",
803
+ "status": "missing",
804
+ "feature": "Breaking-change detection",
805
+ "ws": "WS3",
806
+ "phase": 3,
807
+ "priority": "medium",
808
+ "labels": [
809
+ "roadmap/ws3",
810
+ "status/missing",
811
+ "phase/3",
812
+ "priority/medium",
813
+ "docs-required"
814
+ ],
815
+ "title": "[G048] Breaking-change detection",
816
+ "body": "<!-- roadmap-id:G048 -->\n\n## Metadata\n- Gap ID: G048\n- Status: Missing\n- Workstream: WS3\n- Phase: 3\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
817
+ },
818
+ {
819
+ "id": "G049",
820
+ "status": "missing",
821
+ "feature": "Database migration detection",
822
+ "ws": "WS3",
823
+ "phase": 3,
824
+ "priority": "medium",
825
+ "labels": [
826
+ "roadmap/ws3",
827
+ "status/missing",
828
+ "phase/3",
829
+ "priority/medium",
830
+ "docs-required"
831
+ ],
832
+ "title": "[G049] Database migration detection",
833
+ "body": "<!-- roadmap-id:G049 -->\n\n## Metadata\n- Gap ID: G049\n- Status: Missing\n- Workstream: WS3\n- Phase: 3\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
834
+ },
835
+ {
836
+ "id": "G050",
837
+ "status": "missing",
838
+ "feature": "License compliance scanning",
839
+ "ws": "WS3",
840
+ "phase": 3,
841
+ "priority": "medium",
842
+ "labels": [
843
+ "roadmap/ws3",
844
+ "status/missing",
845
+ "phase/3",
846
+ "priority/medium",
847
+ "docs-required"
848
+ ],
849
+ "title": "[G050] License compliance scanning",
850
+ "body": "<!-- roadmap-id:G050 -->\n\n## Metadata\n- Gap ID: G050\n- Status: Missing\n- Workstream: WS3\n- Phase: 3\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
851
+ },
852
+ {
853
+ "id": "G051",
854
+ "status": "partial",
855
+ "feature": "Email notifications",
856
+ "ws": "WS4",
857
+ "phase": 4,
858
+ "priority": "medium",
859
+ "labels": [
860
+ "roadmap/ws4",
861
+ "status/partial",
862
+ "phase/4",
863
+ "priority/medium",
864
+ "docs-required"
865
+ ],
866
+ "title": "[G051] Email notifications",
867
+ "body": "<!-- roadmap-id:G051 -->\n\n## Metadata\n- Gap ID: G051\n- Status: Partial\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
868
+ },
869
+ {
870
+ "id": "G052",
871
+ "status": "partial",
872
+ "feature": "Merge frequency metrics",
873
+ "ws": "WS4",
874
+ "phase": 4,
875
+ "priority": "medium",
876
+ "labels": [
877
+ "roadmap/ws4",
878
+ "status/partial",
879
+ "phase/4",
880
+ "priority/medium",
881
+ "docs-required"
882
+ ],
883
+ "title": "[G052] Merge frequency metrics",
884
+ "body": "<!-- roadmap-id:G052 -->\n\n## Metadata\n- Gap ID: G052\n- Status: Partial\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
885
+ },
886
+ {
887
+ "id": "G053",
888
+ "status": "partial",
889
+ "feature": "Developer workload insights",
890
+ "ws": "WS4",
891
+ "phase": 4,
892
+ "priority": "medium",
893
+ "labels": [
894
+ "roadmap/ws4",
895
+ "status/partial",
896
+ "phase/4",
897
+ "priority/medium",
898
+ "docs-required"
899
+ ],
900
+ "title": "[G053] Developer workload insights",
901
+ "body": "<!-- roadmap-id:G053 -->\n\n## Metadata\n- Gap ID: G053\n- Status: Partial\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
902
+ },
903
+ {
904
+ "id": "G054",
905
+ "status": "partial",
906
+ "feature": "Smart notifications",
907
+ "ws": "WS4",
908
+ "phase": 4,
909
+ "priority": "medium",
910
+ "labels": [
911
+ "roadmap/ws4",
912
+ "status/partial",
913
+ "phase/4",
914
+ "priority/medium",
915
+ "docs-required"
916
+ ],
917
+ "title": "[G054] Smart notifications",
918
+ "body": "<!-- roadmap-id:G054 -->\n\n## Metadata\n- Gap ID: G054\n- Status: Partial\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
919
+ },
920
+ {
921
+ "id": "G055",
922
+ "status": "partial",
923
+ "feature": "Blocking alerts",
924
+ "ws": "WS4",
925
+ "phase": 4,
926
+ "priority": "medium",
927
+ "labels": [
928
+ "roadmap/ws4",
929
+ "status/partial",
930
+ "phase/4",
931
+ "priority/medium",
932
+ "docs-required"
933
+ ],
934
+ "title": "[G055] Blocking alerts",
935
+ "body": "<!-- roadmap-id:G055 -->\n\n## Metadata\n- Gap ID: G055\n- Status: Partial\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
936
+ },
937
+ {
938
+ "id": "G056",
939
+ "status": "partial",
940
+ "feature": "Plugin/extension system",
941
+ "ws": "WS4",
942
+ "phase": 4,
943
+ "priority": "medium",
944
+ "labels": [
945
+ "roadmap/ws4",
946
+ "status/partial",
947
+ "phase/4",
948
+ "priority/medium",
949
+ "docs-required"
950
+ ],
951
+ "title": "[G056] Plugin/extension system",
952
+ "body": "<!-- roadmap-id:G056 -->\n\n## Metadata\n- Gap ID: G056\n- Status: Partial\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
953
+ },
954
+ {
955
+ "id": "G057",
956
+ "status": "partial",
957
+ "feature": "API documentation",
958
+ "ws": "WS4",
959
+ "phase": 4,
960
+ "priority": "high",
961
+ "labels": [
962
+ "roadmap/ws4",
963
+ "status/partial",
964
+ "phase/4",
965
+ "priority/high",
966
+ "docs-required"
967
+ ],
968
+ "title": "[G057] API documentation",
969
+ "body": "<!-- roadmap-id:G057 -->\n\n## Metadata\n- Gap ID: G057\n- Status: Partial\n- Workstream: WS4\n- Phase: 4\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
970
+ },
971
+ {
972
+ "id": "G058",
973
+ "status": "missing",
974
+ "feature": "Hotspot file detection",
975
+ "ws": "WS4",
976
+ "phase": 4,
977
+ "priority": "medium",
978
+ "labels": [
979
+ "roadmap/ws4",
980
+ "status/missing",
981
+ "phase/4",
982
+ "priority/medium",
983
+ "docs-required"
984
+ ],
985
+ "title": "[G058] Hotspot file detection",
986
+ "body": "<!-- roadmap-id:G058 -->\n\n## Metadata\n- Gap ID: G058\n- Status: Missing\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
987
+ },
988
+ {
989
+ "id": "G059",
990
+ "status": "missing",
991
+ "feature": "Export metrics",
992
+ "ws": "WS4",
993
+ "phase": 4,
994
+ "priority": "medium",
995
+ "labels": [
996
+ "roadmap/ws4",
997
+ "status/missing",
998
+ "phase/4",
999
+ "priority/medium",
1000
+ "docs-required"
1001
+ ],
1002
+ "title": "[G059] Export metrics",
1003
+ "body": "<!-- roadmap-id:G059 -->\n\n## Metadata\n- Gap ID: G059\n- Status: Missing\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
1004
+ },
1005
+ {
1006
+ "id": "G060",
1007
+ "status": "missing",
1008
+ "feature": "Custom dashboards",
1009
+ "ws": "WS4",
1010
+ "phase": 4,
1011
+ "priority": "medium",
1012
+ "labels": [
1013
+ "roadmap/ws4",
1014
+ "status/missing",
1015
+ "phase/4",
1016
+ "priority/medium",
1017
+ "docs-required"
1018
+ ],
1019
+ "title": "[G060] Custom dashboards",
1020
+ "body": "<!-- roadmap-id:G060 -->\n\n## Metadata\n- Gap ID: G060\n- Status: Missing\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
1021
+ },
1022
+ {
1023
+ "id": "G061",
1024
+ "status": "missing",
1025
+ "feature": "Daily/weekly digests",
1026
+ "ws": "WS4",
1027
+ "phase": 4,
1028
+ "priority": "medium",
1029
+ "labels": [
1030
+ "roadmap/ws4",
1031
+ "status/missing",
1032
+ "phase/4",
1033
+ "priority/medium",
1034
+ "docs-required"
1035
+ ],
1036
+ "title": "[G061] Daily/weekly digests",
1037
+ "body": "<!-- roadmap-id:G061 -->\n\n## Metadata\n- Gap ID: G061\n- Status: Missing\n- Workstream: WS4\n- Phase: 4\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
1038
+ },
1039
+ {
1040
+ "id": "G062",
1041
+ "status": "partial",
1042
+ "feature": "Repository mirroring",
1043
+ "ws": "WS5",
1044
+ "phase": 5,
1045
+ "priority": "high",
1046
+ "labels": [
1047
+ "roadmap/ws5",
1048
+ "status/partial",
1049
+ "phase/5",
1050
+ "priority/high",
1051
+ "docs-required"
1052
+ ],
1053
+ "title": "[G062] Repository mirroring",
1054
+ "body": "<!-- roadmap-id:G062 -->\n\n## Metadata\n- Gap ID: G062\n- Status: Partial\n- Workstream: WS5\n- Phase: 5\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
1055
+ },
1056
+ {
1057
+ "id": "G063",
1058
+ "status": "partial",
1059
+ "feature": "Kubernetes deployment",
1060
+ "ws": "WS5",
1061
+ "phase": 5,
1062
+ "priority": "medium",
1063
+ "labels": [
1064
+ "roadmap/ws5",
1065
+ "status/partial",
1066
+ "phase/5",
1067
+ "priority/medium",
1068
+ "docs-required"
1069
+ ],
1070
+ "title": "[G063] Kubernetes deployment",
1071
+ "body": "<!-- roadmap-id:G063 -->\n\n## Metadata\n- Gap ID: G063\n- Status: Partial\n- Workstream: WS5\n- Phase: 5\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
1072
+ },
1073
+ {
1074
+ "id": "G064",
1075
+ "status": "partial",
1076
+ "feature": "Horizontal scaling",
1077
+ "ws": "WS5",
1078
+ "phase": 5,
1079
+ "priority": "high",
1080
+ "labels": [
1081
+ "roadmap/ws5",
1082
+ "status/partial",
1083
+ "phase/5",
1084
+ "priority/high",
1085
+ "docs-required"
1086
+ ],
1087
+ "title": "[G064] Horizontal scaling",
1088
+ "body": "<!-- roadmap-id:G064 -->\n\n## Metadata\n- Gap ID: G064\n- Status: Partial\n- Workstream: WS5\n- Phase: 5\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
1089
+ },
1090
+ {
1091
+ "id": "G065",
1092
+ "status": "missing",
1093
+ "feature": "Kubernetes-native",
1094
+ "ws": "WS5",
1095
+ "phase": 5,
1096
+ "priority": "medium",
1097
+ "labels": [
1098
+ "roadmap/ws5",
1099
+ "status/missing",
1100
+ "phase/5",
1101
+ "priority/medium",
1102
+ "docs-required"
1103
+ ],
1104
+ "title": "[G065] Kubernetes-native",
1105
+ "body": "<!-- roadmap-id:G065 -->\n\n## Metadata\n- Gap ID: G065\n- Status: Missing\n- Workstream: WS5\n- Phase: 5\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
1106
+ },
1107
+ {
1108
+ "id": "G066",
1109
+ "status": "missing",
1110
+ "feature": "Backup & restore tools",
1111
+ "ws": "WS5",
1112
+ "phase": 5,
1113
+ "priority": "high",
1114
+ "labels": [
1115
+ "roadmap/ws5",
1116
+ "status/missing",
1117
+ "phase/5",
1118
+ "priority/high",
1119
+ "docs-required"
1120
+ ],
1121
+ "title": "[G066] Backup & restore tools",
1122
+ "body": "<!-- roadmap-id:G066 -->\n\n## Metadata\n- Gap ID: G066\n- Status: Missing\n- Workstream: WS5\n- Phase: 5\n- Priority: high\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
1123
+ },
1124
+ {
1125
+ "id": "G067",
1126
+ "status": "missing",
1127
+ "feature": "Offline/air-gapped mode",
1128
+ "ws": "WS5",
1129
+ "phase": 5,
1130
+ "priority": "medium",
1131
+ "labels": [
1132
+ "roadmap/ws5",
1133
+ "status/missing",
1134
+ "phase/5",
1135
+ "priority/medium",
1136
+ "docs-required"
1137
+ ],
1138
+ "title": "[G067] Offline/air-gapped mode",
1139
+ "body": "<!-- roadmap-id:G067 -->\n\n## Metadata\n- Gap ID: G067\n- Status: Missing\n- Workstream: WS5\n- Phase: 5\n- Priority: medium\n\n## Problem\nCurrent state and gap from feature audit.\n\n## Scope\n- Backend/API changes\n- UI/UX changes\n- Data model/migrations\n- Docs updates (`docs/` + `docs-site/src/content/docs/`)\n\n## Acceptance Criteria\n- [ ] Feature behavior complete\n- [ ] Authorization/security checks in place\n- [ ] Unit/integration/e2e tests added\n- [ ] `lint` + `typecheck` + tests pass\n- [ ] Docs updated in both doc trees\n\n## Technical Notes\nRelevant files/modules, rollout/rollback plan, migration details.\n\n## Source\n- Checklist: `github-issues-checklist.md`\n- Roadmap: `notimplemented.md`"
1140
+ }
1141
+]
0
1142
▼notimplemented.md
+368-0
1
+# OpenCodeHub Advanced Roadmap: Partial + Missing Features to Production
2
+
3
+Date: February 15, 2026
4
+Primary source baseline: `doc/feature_audit.md` (47 implemented, 29 partial, 46 missing)
5
+
6
+## 1. Goal And Exit Criteria
7
+
8
+## Goal
9
+Ship OpenCodeHub to a production-grade state by closing all partial (`⚠️`) and missing (`❌`) items from the current audit, while maintaining release quality, security, and documentation parity.
10
+
11
+## Production Exit Criteria (must all be true)
12
+- 100% of current `⚠️` and `❌` items are either implemented or intentionally de-scoped with approved rationale.
13
+- `npm run lint`, `npm run typecheck`, and full test suite are green in CI for default branch.
14
+- Security gates active in CI (secret scan, dependency audit threshold, auth/permission regression tests).
15
+- Operational readiness complete: backup/restore verified, horizontal scaling validated, deployment runbooks complete.
16
+- Documentation parity complete in both doc stacks:
17
+ - `docs/` (markdown docs)
18
+ - `docs-site/src/content/docs/` (Starlight docs)
19
+
20
+## 2. Master Inventory (From Current Audit)
21
+
22
+## Partial (29)
23
+- Repository mirroring
24
+- Monorepo advanced path scoping
25
+- Repository templates
26
+- Cross-PR dependency detection
27
+- Stack rebase & auto-update
28
+- Stack-level approvals
29
+- Bulk merge (stack-level)
30
+- Auto-merge rules
31
+- Batch comments
32
+- Code owner enforcement
33
+- Multi-reviewer rules
34
+- PR ↔ Issue linking
35
+- External CI integration
36
+- Merge checks & CI gates
37
+- Automation rules engine
38
+- Snyk integration depth
39
+- Jira integration depth
40
+- Email notifications hardening
41
+- API change awareness
42
+- RBAC UI/ops completion
43
+- Secret scanning completion
44
+- Merge frequency metrics
45
+- Developer workload insights
46
+- Smart notifications
47
+- Blocking alerts
48
+- Plugin system maturity
49
+- API documentation completeness
50
+- Kubernetes deployment packaging
51
+- Horizontal scaling hardening
52
+
53
+## Missing (46)
54
+- File-level permissions
55
+- Custom PR states
56
+- Required reviewers per PR state
57
+- PR checks completion
58
+- Suggested changes
59
+- Review templates
60
+- Partial file approvals
61
+- Custom issue fields
62
+- Issue workflows
63
+- Cross-repo issues
64
+- Workflow templates
65
+- Codecov
66
+- Coveralls
67
+- SonarQube
68
+- GitLab CI
69
+- CircleCI
70
+- Buildkite
71
+- Jenkins
72
+- Linear
73
+- Trello
74
+- ClickUp
75
+- Microsoft Teams
76
+- Discord
77
+- AWS integration
78
+- Google Cloud integration
79
+- Microsoft Azure integration
80
+- Kubernetes-native experience
81
+- Terraform/IaC hooks
82
+- Cross-repo change sets
83
+- Breaking-change detection
84
+- Database migration detection
85
+- License compliance scanning
86
+- Hotspot file detection
87
+- Export metrics
88
+- Custom dashboards
89
+- Daily/weekly digests
90
+- Backup & restore tools
91
+- Offline/air-gapped mode
92
+- plus remaining audit-listed missing items under integrations/deployment categories
93
+
94
+## 3. Delivery Model
95
+
96
+## Program Duration
97
+- 32 weeks total (8 months), starting February 16, 2026.
98
+
99
+## Workstream Model
100
+- WS1: Core Collaboration (PR/Review/Issues)
101
+- WS2: CI/CD + Automation + Integrations
102
+- WS3: Security + Compliance + Permissions
103
+- WS4: Analytics + Notifications + Extensibility
104
+- WS5: Platform + Self-host + Scalability
105
+- WS6: Docs + DX + Release Quality (cross-cutting)
106
+
107
+## Cadence
108
+- 2-week sprints
109
+- Every sprint must include:
110
+ - Implementation tasks
111
+ - Tests (unit/integration/e2e)
112
+ - Docs updates in both doc trees
113
+ - Security review checklist
114
+
115
+## 4. Phase Plan
116
+
117
+## Phase 0 (Weeks 1-2): Baseline Stabilization (mandatory before feature expansion)
118
+### Objectives
119
+- Establish a green CI baseline and strict quality gates.
120
+### Deliverables
121
+- Fix current lint/typecheck failures.
122
+- Stabilize failing security tests.
123
+- Add CI policy: no merge on failing lint/type/test.
124
+- Add security CI jobs: secret scanning, `npm audit` threshold, dependency policy.
125
+### Exit
126
+- All core checks green on default branch.
127
+
128
+## Phase 1 (Weeks 3-8): Core Collaboration Completion
129
+### Scope
130
+- Custom PR states
131
+- Required reviewers per state
132
+- Suggested changes
133
+- Review templates
134
+- Partial file approvals
135
+- Multi-reviewer rules completion
136
+- Code owner enforcement
137
+- Batch comments UI completion
138
+- PR checks completion
139
+- PR↔Issue linking completion
140
+- Custom issue fields
141
+- Issue workflows
142
+- Cross-repo issues
143
+### Dependencies
144
+- Schema migrations + policy engine updates
145
+- Permissions integration
146
+### Exit
147
+- End-to-end PR/review/issues flows fully implemented and test-covered.
148
+
149
+## Phase 2 (Weeks 9-14): CI/CD + Automation + External Integrations
150
+### Scope
151
+- Merge checks/gates completion
152
+- Automation rules engine completion
153
+- Workflow templates
154
+- External CI integrations (GitLab CI, CircleCI, Buildkite, Jenkins)
155
+- Code quality integrations (Codecov, Coveralls, SonarQube, Snyk deep integration)
156
+- Jira hardening + Linear/Trello/ClickUp
157
+### Exit
158
+- Complete CI ecosystem coverage and automation reliability.
159
+
160
+## Phase 3 (Weeks 15-20): Security, Compliance, Enterprise Controls
161
+### Scope
162
+- File-level permissions
163
+- Secret scanning completion
164
+- License compliance scanning
165
+- RBAC admin UX completion
166
+- API change awareness + breaking-change + migration detection
167
+- Teams/Discord integrations for security/event alerts
168
+### Exit
169
+- Security/compliance controls production-validated.
170
+
171
+## Phase 4 (Weeks 21-26): Analytics, Notification Intelligence, Extensibility
172
+### Scope
173
+- Merge frequency + workload insights completion
174
+- Hotspot file detection
175
+- Export metrics
176
+- Custom dashboards
177
+- Smart notifications + blocking alerts + daily/weekly digests
178
+- Plugin system maturation
179
+- API docs completeness (OpenAPI + examples + SDK parity)
180
+### Exit
181
+- Decision-quality analytics and notification intelligence ready.
182
+
183
+## Phase 5 (Weeks 27-32): Platform and Operational Readiness
184
+### Scope
185
+- Kubernetes-native deployment experience + Helm chart
186
+- Horizontal scaling hardening (merge queue/rate limit/distributed locking)
187
+- Backup & restore tooling + restore drill automation
188
+- Offline/air-gapped mode validation + docs
189
+- Cloud integrations (AWS/GCP/Azure) from MVP to stable baseline
190
+### Exit
191
+- Self-hosting and operations fully production-ready.
192
+
193
+## 5. Detailed Workstream Backlog With Acceptance Criteria
194
+
195
+## WS1 Core Collaboration
196
+- Implement custom PR states and state transitions.
197
+- Add per-state reviewer requirements (user/team).
198
+- Add suggested changes API + UI apply flow.
199
+- Complete file-level and template-based review workflows.
200
+- Complete issue workflows/custom fields/cross-repo issues.
201
+Acceptance criteria:
202
+- All new permissions enforced server-side.
203
+- Migration + rollback scripts tested.
204
+- E2E tests cover happy-path + authorization failures.
205
+
206
+## WS2 CI/CD + Integrations
207
+- Complete PR checks model and status propagation.
208
+- Build provider adapters for external CI and quality tools.
209
+- Complete automation engine actions/conditions/observability.
210
+Acceptance criteria:
211
+- Deterministic retry + dead-letter for failed webhooks/automation events.
212
+- Integration contract tests per provider.
213
+
214
+## WS3 Security + Compliance
215
+- Complete CODEOWNERS policy enforcement.
216
+- Harden secret scanning and compliance scanning.
217
+- Complete RBAC admin and audit flows.
218
+Acceptance criteria:
219
+- Security regression suite must pass for every PR.
220
+- All privileged APIs include authz tests.
221
+
222
+## WS4 Analytics + Notifications + Extensibility
223
+- Implement metrics exports and dashboard composer.
224
+- Build prioritization model for smart notifications.
225
+- Complete digest generation pipeline.
226
+- Mature plugin lifecycle model and isolation.
227
+Acceptance criteria:
228
+- Query performance SLOs defined and validated.
229
+- Notification relevance metrics tracked.
230
+
231
+## WS5 Platform + Ops
232
+- Helm chart and K8s operator-friendly configs.
233
+- Distributed locks and queue correctness under multi-instance load.
234
+- Automated backup/restore workflows.
235
+Acceptance criteria:
236
+- Load/stress test sign-off.
237
+- Recovery time objective (RTO) and recovery point objective (RPO) measured.
238
+
239
+## WS6 Docs + DX + Release Quality
240
+- Every feature PR must include docs changes and changelog entry.
241
+- API changes must update OpenAPI and examples.
242
+- Add operator runbooks and troubleshooting guides.
243
+Acceptance criteria:
244
+- Docs lints/build passes in CI.
245
+- No feature merges with stale docs.
246
+
247
+## 6. Documentation Rollout Plan (both docs stacks)
248
+
249
+## Documentation policy
250
+- Single source of truth for implementation status: `doc/feature_audit.md` and this roadmap file.
251
+- User-facing docs must be updated in both:
252
+ - `docs/`
253
+ - `docs-site/src/content/docs/`
254
+- Required in every feature PR:
255
+ - What changed
256
+ - Configuration required
257
+ - API/CLI changes
258
+ - Security implications
259
+ - Migration notes
260
+
261
+## Docs Update Matrix
262
+
263
+### Core Collaboration
264
+- Update:
265
+ - `docs/features/stacked-prs.md`
266
+ - `docs/features/merge-queue.md`
267
+ - `docs/features/ai-review.md`
268
+ - `docs/guides/team-workflows.md`
269
+ - `docs-site/src/content/docs/features/stacked-prs.md`
270
+ - `docs-site/src/content/docs/features/merge-queue.md`
271
+ - `docs-site/src/content/docs/features/ai-review.md`
272
+ - `docs-site/src/content/docs/guides/team-workflows.md`
273
+
274
+### CI/CD + Integrations
275
+- Update:
276
+ - `docs/development/testing.md`
277
+ - `docs/guides/webhooks.md`
278
+ - `docs/reference/cli-commands.md`
279
+ - `docs-site/src/content/docs/features/ci-actions.md`
280
+ - `docs-site/src/content/docs/guides/webhooks.md`
281
+ - `docs-site/src/content/docs/reference/cli-core-commands.md`
282
+
283
+### Security + Compliance
284
+- Update:
285
+ - `docs/administration/security.md`
286
+ - `docs/guides/branch-protection.md`
287
+ - `docs-site/src/content/docs/administration/security.md`
288
+ - `docs-site/src/content/docs/guides/branch-protection.md`
289
+
290
+### Analytics + Notifications
291
+- Update:
292
+ - `docs/administration/monitoring.md`
293
+ - `docs/features/merge-queue.md` (metrics sections)
294
+ - `docs-site/src/content/docs/features/developer-metrics.md`
295
+ - `docs-site/src/content/docs/features/notifications.md`
296
+ - `docs-site/src/content/docs/features/inbox.md`
297
+
298
+### Platform + Self-host
299
+- Update:
300
+ - `docs/administration/deployment.md`
301
+ - `docs/administration/configuration.md`
302
+ - `docs-site/src/content/docs/administration/deploy-docker.md`
303
+ - `docs-site/src/content/docs/administration/deploy-nginx.md`
304
+ - add new Helm/K8s docs under both trees
305
+
306
+## New docs files to add
307
+- `docs/development/release-gates.md`
308
+- `docs/administration/backup-restore.md`
309
+- `docs/administration/horizontal-scaling.md`
310
+- `docs/administration/air-gapped.md`
311
+- `docs/reference/api-versioning.md`
312
+- mirrored files in `docs-site/src/content/docs/...`
313
+
314
+## 7. Governance, Ownership, and Tracking
315
+
316
+## Ownership
317
+- Tech Lead: roadmap execution and dependency management.
318
+- Security Lead: threat model, authz reviews, compliance sign-off.
319
+- Docs Lead: parity across both docs stacks.
320
+- QA Lead: test strategy and release confidence.
321
+
322
+## Tracking
323
+- Create one epic per workstream and one issue per feature gap.
324
+- Labeling model:
325
+ - `roadmap/ws1` ... `roadmap/ws6`
326
+ - `status/partial`, `status/missing`, `security`, `docs-required`
327
+- Weekly KPI dashboard:
328
+ - Partial count remaining
329
+ - Missing count remaining
330
+ - CI pass rate
331
+ - Vulnerability count by severity
332
+ - Docs parity completion %
333
+
334
+## 8. Risk Register
335
+- Scope expansion before baseline stability is restored.
336
+- Integration vendor API drift.
337
+- Schema migration regressions.
338
+- Docs lagging behind feature implementation.
339
+- Multi-instance correctness bugs (queues/locks/rate-limit).
340
+
341
+Mitigation:
342
+- Enforce phase gates.
343
+- Contract tests for integrations.
344
+- Migration rehearsal in staging.
345
+- Docs-required check in PR template.
346
+- Chaos/load testing before final release.
347
+
348
+## 9. Immediate Next 14-Day Action Plan
349
+
350
+1. Finish Phase 0 baseline stabilization.
351
+2. Convert every `⚠️`/`❌` item into tracked issue with owner and estimate.
352
+3. Start Phase 1 with this order:
353
+ - Custom PR states
354
+ - Required reviewers per state
355
+ - CODEOWNERS enforcement
356
+ - Suggested changes
357
+ - Partial file approvals
358
+4. Enable docs parity workflow in CI for both docs trees.
359
+
360
+## 10. Definition Of Done Per Feature
361
+- Code complete + migrations + backfill scripts
362
+- Unit/integration/e2e tests
363
+- Security review completed
364
+- Performance impact measured
365
+- Docs updated in `docs/` and `docs-site/src/content/docs/`
366
+- Changelog updated
367
+- Rollback plan documented
368
+
0
369
▼package.json
+28-7
22
22
"db:push": "drizzle-kit push",
23
23
"db:studio": "drizzle-kit studio",
24
24
"git:init": "node scripts/init-git-server.js",
25
- "git:start": "tsx scripts/ssh-server.ts",
26
- "runner:start": "node src/runner/index.js",
25
+ "git:start": "bun scripts/ssh-server.ts",
26
+ "worker:start": "bun scripts/worker.ts",
27
+ "runner:start": "bun src/runner/index.ts",
27
28
"lint": "astro check",
28
29
"typecheck": "tsc --noEmit",
29
30
"test": "vitest run",
30
- "test:coverage": "vitest run --coverage"
31
+ "test:coverage": "vitest run --coverage",
32
+ "backup": "bun scripts/backup.ts"
31
33
},
32
34
"dependencies": {
33
35
"@astrojs/check": "^0.9.6",
38
40
"@aws-sdk/client-s3": "^3.948.0",
39
41
"@aws-sdk/lib-storage": "^3.972.0",
40
42
"@aws-sdk/s3-request-presigner": "^3.948.0",
43
+ "@azure/storage-blob": "^12.30.0",
44
+ "@dnd-kit/core": "^6.3.1",
45
+ "@dnd-kit/sortable": "^10.0.0",
46
+ "@dnd-kit/utilities": "^3.2.2",
47
+ "@google-cloud/storage": "^7.18.0",
41
48
"@libsql/client": "^0.15.15",
42
49
"@monaco-editor/react": "^4.6.0",
43
50
"@opentelemetry/auto-instrumentations-node": "^0.67.2",
46
53
"@radix-ui/react-accordion": "^1.2.0",
47
54
"@radix-ui/react-alert-dialog": "^1.1.1",
48
55
"@radix-ui/react-avatar": "^1.1.0",
49
- "@radix-ui/react-checkbox": "^1.1.1",
56
+ "@radix-ui/react-checkbox": "^1.3.3",
50
57
"@radix-ui/react-collapsible": "^1.1.0",
51
58
"@radix-ui/react-context-menu": "^2.2.1",
52
59
"@radix-ui/react-dialog": "^1.1.1",
58
65
"@radix-ui/react-popover": "^1.1.1",
59
66
"@radix-ui/react-progress": "^1.1.0",
60
67
"@radix-ui/react-scroll-area": "^1.1.0",
61
- "@radix-ui/react-select": "^2.1.1",
68
+ "@radix-ui/react-select": "^2.2.6",
62
69
"@radix-ui/react-separator": "^1.1.0",
63
70
"@radix-ui/react-slider": "^1.2.0",
64
71
"@radix-ui/react-slot": "^1.1.0",
69
76
"@radix-ui/react-toggle-group": "^1.1.0",
70
77
"@radix-ui/react-tooltip": "^1.1.2",
71
78
"@types/nodemailer": "^7.0.4",
79
+ "@types/qrcode": "^1.5.6",
72
80
"@upstash/redis": "^1.36.0",
81
+ "arctic": "^3.7.0",
73
82
"astro": "^4.11.0",
83
+ "basic-ftp": "^5.1.0",
74
84
"bcryptjs": "^2.4.3",
75
85
"better-sqlite3": "^11.1.2",
76
86
"bullmq": "^5.65.1",
78
88
"clsx": "^2.1.1",
79
89
"cmdk": "^1.1.1",
80
90
"cobe": "^0.6.5",
91
+ "date-fns": "^4.1.0",
81
92
"dockerode": "^4.0.2",
82
93
"drizzle-orm": "^0.45.1",
83
94
"framer-motion": "^11.18.2",
84
95
"github-markdown-css": "^5.8.1",
85
96
"googleapis": "^169.0.0",
86
- "ioredis": "^5.8.2",
97
+ "graphql": "^16.12.0",
98
+ "graphql-yoga": "^5.18.0",
99
+ "ioredis": "^5.9.2",
100
+ "isomorphic-dompurify": "^2.35.0",
87
101
"isomorphic-git": "^1.25.10",
88
102
"jose": "^5.6.3",
89
103
"js-yaml": "^4.1.0",
90
104
"lucide-react": "^0.400.0",
91
105
"marked": "^17.0.1",
106
+ "micromatch": "^4.0.8",
92
107
"monaco-editor": "^0.50.0",
93
108
"mysql2": "^3.15.3",
94
109
"nanoid": "^5.0.7",
95
110
"nodegit": "^0.28.0-alpha.27",
96
111
"nodemailer": "^7.0.12",
112
+ "openai": "^6.16.0",
113
+ "openid-client": "^6.8.1",
97
114
"openpgp": "^6.3.0",
98
115
"otplib": "^12.0.1",
99
116
"pg": "^8.16.3",
101
118
"pino-loki": "^3.0.0",
102
119
"postgres": "^3.4.7",
103
120
"prom-client": "^15.1.3",
121
+ "qrcode": "^1.5.4",
104
122
"react": "^18.3.1",
105
123
"react-dom": "^18.3.1",
106
124
"react-globe.gl": "^2.37.0",
125
+ "recharts": "^3.7.0",
107
126
"rehype-sanitize": "^6.0.0",
108
127
"rehype-stringify": "^10.0.1",
109
128
"remark-gfm": "^4.0.1",
116
135
"tailwindcss-animate": "^1.0.7",
117
136
"three": "^0.182.0",
118
137
"unified": "^11.0.5",
138
+ "unist-util-visit": "^5.1.0",
119
139
"zod": "^3.23.8",
120
140
"zustand": "^4.5.4"
121
141
},
127
147
"@types/better-sqlite3": "^7.6.11",
128
148
"@types/dockerode": "^3.3.29",
129
149
"@types/js-yaml": "^4.0.9",
150
+ "@types/micromatch": "^4.0.10",
130
151
"@types/node": "^20.14.9",
131
152
"@types/pg": "^8.16.0",
132
153
"@types/react": "^18.3.3",
144
165
"engines": {
145
166
"node": ">=20.0.0"
146
167
}
147
-}
168
+}
148
169
▼scripts/backup.ts
+77-0
1
+
2
+import { existsSync, mkdirSync, copyFileSync } from "fs";
3
+import { join, dirname } from "path";
4
+import { execSync } from "child_process";
5
+import { fileURLToPath } from "url";
6
+
7
+// Get root directory
8
+const __dirname = dirname(fileURLToPath(import.meta.url));
9
+const rootDir = join(__dirname, "..");
10
+const backupDir = join(rootDir, "backups");
11
+const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
12
+const targetDir = join(backupDir, `backup-${timestamp}`);
13
+
14
+// Ensure backup directory exists
15
+if (!existsSync(backupDir)) {
16
+ mkdirSync(backupDir);
17
+}
18
+mkdirSync(targetDir);
19
+
20
+console.log(`[Backup] Starting backup to ${targetDir}`);
21
+
22
+// 1. Backup Database
23
+const dbDriver = process.env.DATABASE_DRIVER || "sqlite";
24
+const dbUrl = process.env.DATABASE_URL || "data/opencodehub.db";
25
+
26
+console.log(`[Backup] Backing up database (${dbDriver})...`);
27
+
28
+try {
29
+ if (dbDriver === "sqlite") {
30
+ const dbPath = join(rootDir, dbUrl);
31
+ if (existsSync(dbPath)) {
32
+ copyFileSync(dbPath, join(targetDir, "database.sqlite"));
33
+ console.log(`[Backup] SQLite database copied.`);
34
+ } else {
35
+ console.warn(`[Backup] SQLite database not found at ${dbPath}`);
36
+ }
37
+ } else if (dbDriver === "postgres") {
38
+ // Requires pg_dump to be installed
39
+ const outputFile = join(targetDir, "dump.sql");
40
+ execSync(`pg_dump "${dbUrl}" > "${outputFile}"`);
41
+ console.log(`[Backup] Postgres dumped to dump.sql`);
42
+ } else {
43
+ console.log(`[Backup] Automatic backup not supported for driver: ${dbDriver}. Please check functionality manually.`);
44
+ }
45
+} catch (error) {
46
+ console.error(`[Backup] Database backup failed:`, error);
47
+}
48
+
49
+// 2. Backup Repositories
50
+// Assumes repos are in "repos" or "git-data" directory relative to root, or specified by config.
51
+// Default configuration often puts them in ./repos or similar.
52
+// We'll try to find the standard location or read from config if possible.
53
+// For now, we assume "./repos" or check environment GIT_STORAGE_PATH
54
+
55
+const repoPath = process.env.GIT_STORAGE_PATH ? join(rootDir, process.env.GIT_STORAGE_PATH) : join(rootDir, "repos");
56
+
57
+if (existsSync(repoPath)) {
58
+ console.log(`[Backup] Backing up repositories from ${repoPath}...`);
59
+ const repoArchive = join(targetDir, "repos.tar.gz");
60
+ try {
61
+ execSync(`tar -czf "${repoArchive}" -C "${dirname(repoPath)}" "${process.env.GIT_STORAGE_PATH || "repos"}"`);
62
+ console.log(`[Backup] Repositories archived.`);
63
+ } catch (error) {
64
+ console.error(`[Backup] Repository backup failed:`, error);
65
+ }
66
+} else {
67
+ console.warn(`[Backup] Repository directory not found at ${repoPath}`);
68
+}
69
+
70
+// 3. Backup Config / Env
71
+const envPath = join(rootDir, ".env");
72
+if (existsSync(envPath)) {
73
+ copyFileSync(envPath, join(targetDir, ".env.backup"));
74
+ console.log(`[Backup] Environment file copied.`);
75
+}
76
+
77
+console.log(`[Backup] Completed successfully at ${targetDir}`);
0
78
▼scripts/clean-corrupted-s3-repos.ts
+105-0
1
+import { getStorage } from '../src/lib/storage';
2
+
3
+/**
4
+ * This script identifies and optionally deletes corrupted repository structures in S3.
5
+ *
6
+ * Corrupted repos have the pattern: repos/swadhinbiswas/test.git/reposswadhinbiswas/...
7
+ * instead of: repos/swadhinbiswas/test.git/HEAD, refs/, objects/, etc.
8
+ *
9
+ * Usage:
10
+ * npx ts-node scripts/clean-corrupted-s3-repos.ts [--delete]
11
+ */
12
+
13
+async function main() {
14
+ const shouldDelete = process.argv.includes('--delete');
15
+
16
+ console.log('Scanning S3 for corrupted repository structures...\n');
17
+
18
+ const storage = await getStorage();
19
+
20
+ // List all repos
21
+ const result = await storage.list({ prefix: 'repos/' });
22
+
23
+ const corruptedRepos: string[] = [];
24
+ const seenRepos = new Set<string>();
25
+
26
+ // Check each prefix/object
27
+ if (result.prefixes) {
28
+ for (const prefix of result.prefixes) {
29
+ // Expected pattern: repos/owner/repo.git/
30
+ const parts = prefix.split('/');
31
+
32
+ if (parts.length === 4 && parts[2].endsWith('.git')) {
33
+ const repoPath = parts.slice(0, 3).join('/'); // repos/owner/repo.git
34
+
35
+ if (seenRepos.has(repoPath)) continue;
36
+ seenRepos.add(repoPath);
37
+
38
+ // Check what's inside this repo
39
+ const repoContents = await storage.list({ prefix: repoPath + '/' });
40
+
41
+ // Check if it has the corrupted nested structure
42
+ if (repoContents.prefixes) {
43
+ for (const subPrefix of repoContents.prefixes) {
44
+ const subParts = subPrefix.split('/');
45
+ const lastPart = subParts[subParts.length - 2]; // Get directory name
46
+
47
+ // If it starts with "repos" it's corrupted
48
+ if (lastPart && lastPart.startsWith('repos')) {
49
+ console.log(`❌ CORRUPTED: ${repoPath}/`);
50
+ console.log(` Found invalid directory: ${lastPart}`);
51
+ corruptedRepos.push(repoPath);
52
+ break;
53
+ }
54
+ }
55
+ }
56
+
57
+ // Also check if it has proper Git structure
58
+ const hasValidStructure = repoContents.objects?.some(obj =>
59
+ obj.key.endsWith('/HEAD') ||
60
+ obj.key.includes('/refs/') ||
61
+ obj.key.includes('/objects/')
62
+ );
63
+
64
+ if (!hasValidStructure && !corruptedRepos.includes(repoPath)) {
65
+ console.log(`⚠️ EMPTY or INCOMPLETE: ${repoPath}/`);
66
+ } else if (!corruptedRepos.includes(repoPath)) {
67
+ console.log(`✅ VALID: ${repoPath}/`);
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ console.log(`\n\nSummary:`);
74
+ console.log(`Found ${corruptedRepos.length} corrupted repositories`);
75
+
76
+ if (corruptedRepos.length > 0) {
77
+ console.log('\nCorrupted repositories:');
78
+ corruptedRepos.forEach(repo => console.log(` - ${repo}`));
79
+
80
+ if (shouldDelete) {
81
+ console.log('\n🗑️ Deleting corrupted repositories...');
82
+
83
+ for (const repoPath of corruptedRepos) {
84
+ console.log(`Deleting ${repoPath}...`);
85
+
86
+ // List ALL objects under this path
87
+ const toDelete = await storage.list({ prefix: repoPath + '/' });
88
+
89
+ if (toDelete.objects) {
90
+ for (const obj of toDelete.objects) {
91
+ await storage.delete(obj.key);
92
+ console.log(` Deleted: ${obj.key}`);
93
+ }
94
+ }
95
+ }
96
+
97
+ console.log('\n✅ Cleanup complete!');
98
+ } else {
99
+ console.log('\nTo delete these repositories, run:');
100
+ console.log(' npx ts-node scripts/clean-corrupted-s3-repos.ts --delete');
101
+ }
102
+ }
103
+}
104
+
105
+main().catch(console.error);
0
106
▼scripts/create-admin.ts
+78-0
1
+/**
2
+ * Create Admin Account Script
3
+ * Run with: bun run scripts/create-admin.ts
4
+ */
5
+
6
+import { createClient } from "@libsql/client";
7
+import * as bcrypt from "bcryptjs";
8
+
9
+// Load from .env
10
+const DATABASE_URL = process.env.DATABASE_URL || "libsql://opencodehub-swadhinbiswas.aws-ap-south-1.turso.io";
11
+const DATABASE_AUTH_TOKEN = process.env.DATABASE_AUTH_TOKEN;
12
+
13
+if (!DATABASE_AUTH_TOKEN) {
14
+ console.error("❌ DATABASE_AUTH_TOKEN is required. Run with: bun run scripts/create-admin.ts");
15
+ console.error(" Make sure your .env file has DATABASE_AUTH_TOKEN set.");
16
+ process.exit(1);
17
+}
18
+
19
+// Admin account details
20
+const ADMIN_USERNAME = "admin";
21
+const ADMIN_EMAIL = "[email protected]";
22
+const ADMIN_PASSWORD = "Admin@123"; // Change this!
23
+
24
+async function main() {
25
+ console.log("🔌 Connecting to database...");
26
+
27
+ const client = createClient({
28
+ url: DATABASE_URL,
29
+ authToken: DATABASE_AUTH_TOKEN,
30
+ });
31
+
32
+ // Check if admin already exists
33
+ const existing = await client.execute({
34
+ sql: "SELECT id, username, is_admin FROM users WHERE username = ?",
35
+ args: [ADMIN_USERNAME]
36
+ });
37
+
38
+ if (existing.rows.length > 0) {
39
+ console.log("⚠️ Admin user already exists. Updating to admin role...");
40
+ await client.execute({
41
+ sql: "UPDATE users SET is_admin = 1 WHERE username = ?",
42
+ args: [ADMIN_USERNAME]
43
+ });
44
+ console.log("✅ Admin role granted to existing user:", existing.rows[0].username);
45
+ return;
46
+ }
47
+
48
+ // Hash password
49
+ console.log("🔐 Hashing password...");
50
+ const passwordHash = await bcrypt.hash(ADMIN_PASSWORD, 10);
51
+
52
+ // Generate unique ID
53
+ const id = crypto.randomUUID();
54
+ const now = new Date().toISOString();
55
+
56
+ // Create admin user
57
+ console.log("👤 Creating admin user...");
58
+
59
+ await client.execute({
60
+ sql: `INSERT INTO users (id, username, email, password_hash, display_name, is_admin, is_active, email_verified, created_at, updated_at)
61
+ VALUES (?, ?, ?, ?, ?, 1, 1, 1, ?, ?)`,
62
+ args: [id, ADMIN_USERNAME, ADMIN_EMAIL, passwordHash, "Administrator", now, now]
63
+ });
64
+
65
+ console.log("✅ Admin account created successfully!");
66
+ console.log("");
67
+ console.log("📋 Admin Credentials:");
68
+ console.log(` Username: ${ADMIN_USERNAME}`);
69
+ console.log(` Email: ${ADMIN_EMAIL}`);
70
+ console.log(` Password: ${ADMIN_PASSWORD}`);
71
+ console.log("");
72
+ console.log("⚠️ Please change the password after first login!");
73
+}
74
+
75
+main().catch(err => {
76
+ console.error("❌ Error:", err.message);
77
+ process.exit(1);
78
+});
0
79
▼scripts/create_github_issues_from_checklist.sh
+106-0
1
+#!/usr/bin/env bash
2
+set -euo pipefail
3
+
4
+CHECKLIST_FILE="${1:-github-issues-checklist.md}"
5
+MODE="${2:-dry-run}" # dry-run | execute
6
+
7
+if [[ ! -f "$CHECKLIST_FILE" ]]; then
8
+ echo "Checklist file not found: $CHECKLIST_FILE" >&2
9
+ exit 1
10
+fi
11
+
12
+if ! command -v gh >/dev/null 2>&1; then
13
+ echo "GitHub CLI (gh) is not installed. Install it first, then rerun." >&2
14
+ exit 1
15
+fi
16
+
17
+if ! gh auth status >/dev/null 2>&1; then
18
+ echo "gh is not authenticated. Run: gh auth login" >&2
19
+ exit 1
20
+fi
21
+
22
+# Extract repo from git remote (owner/repo)
23
+REMOTE_URL="$(git remote get-url origin)"
24
+if [[ "$REMOTE_URL" =~ github.com[:/]([^/]+)/([^/.]+)(\.git)?$ ]]; then
25
+ OWNER="${BASH_REMATCH[1]}"
26
+ REPO="${BASH_REMATCH[2]}"
27
+ REPO_SLUG="$OWNER/$REPO"
28
+else
29
+ echo "Could not parse GitHub repo from origin remote: $REMOTE_URL" >&2
30
+ exit 1
31
+fi
32
+
33
+echo "Repo: $REPO_SLUG"
34
+echo "Mode: $MODE"
35
+
36
+mapfile -t ISSUE_LINES < <(grep -E '^- \[ \] G[0-9]{3} \|' "$CHECKLIST_FILE")
37
+if [[ ${#ISSUE_LINES[@]} -eq 0 ]]; then
38
+ echo "No issue lines found in $CHECKLIST_FILE" >&2
39
+ exit 1
40
+fi
41
+
42
+created=0
43
+skipped=0
44
+
45
+for line in "${ISSUE_LINES[@]}"; do
46
+ IFS='|' read -r col1 col2 col3 col4 col5 col6 <<< "$line"
47
+
48
+ gap_id="$(echo "$col1" | sed -E 's/^-[[:space:]]\[[[:space:]]\][[:space:]]*(G[0-9]{3}).*/\1/' | xargs)"
49
+ title_core="$(echo "$col2" | xargs)"
50
+ ws="$(echo "$col3" | sed -E 's/^[[:space:]]*WS:[[:space:]]*`([^`]+)`.*/\1/' | xargs)"
51
+ phase="$(echo "$col4" | sed -E 's/^[[:space:]]*Phase:[[:space:]]*`([^`]+)`.*/\1/' | xargs)"
52
+ priority="$(echo "$col5" | sed -E 's/^[[:space:]]*Priority:[[:space:]]*`([^`]+)`.*/\1/' | xargs)"
53
+ labels="$(echo "$col6" | sed -E 's/^[[:space:]]*Labels:[[:space:]]*`([^`]+)`.*/\1/' | xargs)"
54
+
55
+ issue_title="$gap_id $title_core"
56
+
57
+ # Skip if already exists by exact title
58
+ if gh issue list --repo "$REPO_SLUG" --search "\"$issue_title\" in:title" --limit 1 --json title | grep -q "$issue_title"; then
59
+ echo "[skip] $issue_title"
60
+ skipped=$((skipped+1))
61
+ continue
62
+ fi
63
+
64
+ issue_body=$(cat <<BODY
65
+Roadmap gap from \\`github-issues-checklist.md\\`.
66
+
67
+- Gap ID: \\`$gap_id\\`
68
+- Workstream: \\`$ws\\`
69
+- Phase: \\`$phase\\`
70
+- Priority: \\`$priority\\`
71
+- Suggested labels: \\`$labels\\`
72
+
73
+## Scope
74
+- Backend/API changes
75
+- UI/UX changes
76
+- Data model/migrations (if needed)
77
+- Docs updates in both trees:
78
+ - \\`docs/\\`
79
+ - \\`docs-site/src/content/docs/\\`
80
+
81
+## Acceptance Criteria
82
+- [ ] Feature behavior complete
83
+- [ ] Authorization/security checks in place
84
+- [ ] Unit/integration/e2e tests added
85
+- [ ] \\`npm run lint\\`, \\`npm run typecheck\\`, and tests pass
86
+- [ ] Documentation updated in both doc trees
87
+- [ ] Linked in roadmap tracker
88
+BODY
89
+)
90
+
91
+ if [[ "$MODE" == "execute" ]]; then
92
+ gh issue create --repo "$REPO_SLUG" --title "$issue_title" --body "$issue_body" >/dev/null
93
+ echo "[create] $issue_title"
94
+ created=$((created+1))
95
+ else
96
+ echo "[dry-run] $issue_title"
97
+ fi
98
+
99
+done
100
+
101
+echo
102
+echo "Done. Created: $created | Skipped existing: $skipped | Total lines: ${#ISSUE_LINES[@]}"
103
+if [[ "$MODE" != "execute" ]]; then
104
+ echo "Run with execute mode:"
105
+ echo " scripts/create_github_issues_from_checklist.sh github-issues-checklist.md execute"
106
+fi
0
107
▼scripts/debug-env.ts
+4-0
1
+
2
+
3
+console.log("DATABASE_DRIVER:", process.env.DATABASE_DRIVER);
4
+console.log("DATABASE_URL:", process.env.DATABASE_URL);
0
5
▼scripts/empty-r3-bucket.cjs
+97-0
1
+/**
2
+ * Empty R3 Bucket Script
3
+ *
4
+ * This script deletes ALL objects from the R3 bucket to start fresh.
5
+ * Use with caution!
6
+ *
7
+ * Usage: node scripts/empty-r3-bucket.cjs
8
+ */
9
+
10
+const { S3Client, ListObjectsV2Command, DeleteObjectsCommand } = require('@aws-sdk/client-s3');
11
+const fs = require('fs');
12
+const path = require('path');
13
+
14
+// Load .env file manually
15
+const envPath = path.join(__dirname, '..', '.env');
16
+const envFile = fs.readFileSync(envPath, 'utf8');
17
+envFile.split('\n').forEach(line => {
18
+ const match = line.match(/^([^#=]+)=(.*)$/);
19
+ if (match) {
20
+ const key = match[1].trim();
21
+ const value = match[2].trim().replace(/^["']|["']$/g, '');
22
+ process.env[key] = value;
23
+ }
24
+});
25
+
26
+const client = new S3Client({
27
+ region: process.env.STORAGE_REGION || 'auto',
28
+ endpoint: process.env.STORAGE_ENDPOINT,
29
+ credentials: {
30
+ accessKeyId: process.env.STORAGE_ACCESS_KEY_ID,
31
+ secretAccessKey: process.env.STORAGE_SECRET_ACCESS_KEY,
32
+ },
33
+ forcePathStyle: true,
34
+});
35
+
36
+const BUCKET = process.env.STORAGE_BUCKET;
37
+
38
+async function emptyBucket() {
39
+ console.log(`🗑️ Emptying bucket: ${BUCKET}\n`);
40
+
41
+ let totalDeleted = 0;
42
+ let continuationToken;
43
+
44
+ do {
45
+ // List objects
46
+ const listCommand = new ListObjectsV2Command({
47
+ Bucket: BUCKET,
48
+ ContinuationToken: continuationToken,
49
+ MaxKeys: 1000,
50
+ });
51
+
52
+ const listResponse = await client.send(listCommand);
53
+
54
+ if (!listResponse.Contents || listResponse.Contents.length === 0) {
55
+ console.log('No more objects to delete.');
56
+ break;
57
+ }
58
+
59
+ console.log(`Found ${listResponse.Contents.length} objects to delete...`);
60
+
61
+ // Delete objects in batches
62
+ const objectsToDelete = listResponse.Contents.map(obj => ({ Key: obj.Key }));
63
+
64
+ const deleteCommand = new DeleteObjectsCommand({
65
+ Bucket: BUCKET,
66
+ Delete: {
67
+ Objects: objectsToDelete,
68
+ Quiet: false,
69
+ },
70
+ });
71
+
72
+ const deleteResponse = await client.send(deleteCommand);
73
+
74
+ const deleted = deleteResponse.Deleted?.length || 0;
75
+ totalDeleted += deleted;
76
+
77
+ console.log(`✅ Deleted ${deleted} objects`);
78
+
79
+ if (deleteResponse.Errors && deleteResponse.Errors.length > 0) {
80
+ console.error('❌ Errors during deletion:');
81
+ deleteResponse.Errors.forEach(err => {
82
+ console.error(` - ${err.Key}: ${err.Message}`);
83
+ });
84
+ }
85
+
86
+ continuationToken = listResponse.NextContinuationToken;
87
+
88
+ } while (continuationToken);
89
+
90
+ console.log(`\n✅ Total objects deleted: ${totalDeleted}`);
91
+ console.log('🎉 Bucket is now empty!');
92
+}
93
+
94
+emptyBucket().catch(err => {
95
+ console.error('❌ Error emptying bucket:', err);
96
+ process.exit(1);
97
+});
0
98
▼scripts/fix_astro_class.py
+41-0
1
+
2
+import os
3
+import re
4
+
5
+def fix_astro_files(directory):
6
+ # Regex to find capitalized tags with class attribute
7
+ # Group 1: Opening tag and attributes before class
8
+ # Group 2: The class value
9
+ # Group 3: Attributes after class
10
+
11
+ # Pattern explanation:
12
+ # <([A-Z][\w\.]+) : Match <TagName where TagName starts with UpperCase (Component)
13
+ # [^>]*? : non-greedy match of anything before class
14
+ # \bclass= : match class= word boundary
15
+ pattern = re.compile(r'(<[A-Z][\w\.]+(?:\s+[^>]*?)?)\bclass=(["\'][^"\']*["\'])')
16
+
17
+ for root, dirs, files in os.walk(directory):
18
+ for file in files:
19
+ if file.endswith(".astro"):
20
+ filepath = os.path.join(root, file)
21
+ with open(filepath, 'r') as f:
22
+ content = f.read()
23
+
24
+ # Function to pass to re.sub to handle the replacement safely
25
+ def replace_match(match):
26
+ # Reconstruct correctly using className
27
+ return f'{match.group(1)}className={match.group(2)}'
28
+
29
+ new_content = pattern.sub(replace_match, content)
30
+
31
+ # Run it again to catch multiple class attributes or complex cases?
32
+ # The regex matches one instance. We might need to iterate if there are multiple uppercase tags on one line or multiple class attributes (unlikely).
33
+ # Actually re.sub replaces ALL non-overlapping occurrences.
34
+
35
+ if new_content != content:
36
+ print(f"Fixing {filepath}")
37
+ with open(filepath, 'w') as f:
38
+ f.write(new_content)
39
+
40
+fix_astro_files("/home/swadhin/owngit/OpenCodeHub/src/pages")
41
+fix_astro_files("/home/swadhin/owngit/OpenCodeHub/src/components")
0
42
▼scripts/generate-roadmap-issues.mjs
+80-0
1
+#!/usr/bin/env node
2
+import fs from 'node:fs';
3
+import path from 'node:path';
4
+
5
+const repoRoot = process.cwd();
6
+const checklistPath = path.join(repoRoot, 'github-issues-checklist.md');
7
+const outputPath = path.join(repoRoot, 'data', 'github-roadmap-issues.json');
8
+
9
+const source = fs.readFileSync(checklistPath, 'utf8');
10
+const lines = source.split(/\r?\n/);
11
+
12
+const issueLine = /^- \[ \] (G\d{3}) \| \[(Partial|Missing)\] (.+?) \| WS: `(WS\d+)` \| Phase: `(\d+)` \| Priority: `(low|medium|high)` \| Labels: `([^`]+)`$/;
13
+
14
+const issues = [];
15
+
16
+for (const line of lines) {
17
+ const match = line.match(issueLine);
18
+ if (!match) continue;
19
+
20
+ const [, id, status, feature, ws, phase, priority, labelCsv] = match;
21
+ const labels = labelCsv.split(',').map((label) => label.trim()).filter(Boolean);
22
+
23
+ const title = `[${id}] ${feature}`;
24
+ const body = [
25
+ `<!-- roadmap-id:${id} -->`,
26
+ '',
27
+ `## Metadata`,
28
+ `- Gap ID: ${id}`,
29
+ `- Status: ${status}`,
30
+ `- Workstream: ${ws}`,
31
+ `- Phase: ${phase}`,
32
+ `- Priority: ${priority}`,
33
+ '',
34
+ '## Problem',
35
+ 'Current state and gap from feature audit.',
36
+ '',
37
+ '## Scope',
38
+ '- Backend/API changes',
39
+ '- UI/UX changes',
40
+ '- Data model/migrations',
41
+ '- Docs updates (`docs/` + `docs-site/src/content/docs/`)',
42
+ '',
43
+ '## Acceptance Criteria',
44
+ '- [ ] Feature behavior complete',
45
+ '- [ ] Authorization/security checks in place',
46
+ '- [ ] Unit/integration/e2e tests added',
47
+ '- [ ] `lint` + `typecheck` + tests pass',
48
+ '- [ ] Docs updated in both doc trees',
49
+ '',
50
+ '## Technical Notes',
51
+ 'Relevant files/modules, rollout/rollback plan, migration details.',
52
+ '',
53
+ '## Source',
54
+ '- Checklist: `github-issues-checklist.md`',
55
+ '- Roadmap: `notimplemented.md`',
56
+ ].join('\n');
57
+
58
+ issues.push({
59
+ id,
60
+ status: status.toLowerCase(),
61
+ feature,
62
+ ws,
63
+ phase: Number(phase),
64
+ priority,
65
+ labels,
66
+ title,
67
+ body,
68
+ });
69
+}
70
+
71
+if (issues.length === 0) {
72
+ throw new Error('No issue rows parsed from github-issues-checklist.md');
73
+}
74
+
75
+issues.sort((a, b) => a.id.localeCompare(b.id));
76
+
77
+fs.mkdirSync(path.dirname(outputPath), { recursive: true });
78
+fs.writeFileSync(outputPath, `${JSON.stringify(issues, null, 2)}\n`, 'utf8');
79
+
80
+console.log(`Generated ${issues.length} issues -> ${path.relative(repoRoot, outputPath)}`);
0
81
▼scripts/list-s3-bucket.cjs
+81-0
1
+/**
2
+ * List S3 Bucket Contents
3
+ *
4
+ * Shows what's currently in the R3 bucket to verify it's clean
5
+ */
6
+
7
+const { S3Client, ListObjectsV2Command } = require('@aws-sdk/client-s3');
8
+const fs = require('fs');
9
+const path = require('path');
10
+
11
+// Load .env file manually
12
+const envPath = path.join(__dirname, '..', '.env');
13
+const envFile = fs.readFileSync(envPath, 'utf8');
14
+envFile.split('\n').forEach(line => {
15
+ const match = line.match(/^([^#=]+)=(.*)$/);
16
+ if (match) {
17
+ const key = match[1].trim();
18
+ const value = match[2].trim().replace(/^["']|["']$/g, '');
19
+ process.env[key] = value;
20
+ }
21
+});
22
+
23
+const client = new S3Client({
24
+ region: process.env.STORAGE_REGION || 'auto',
25
+ endpoint: process.env.STORAGE_ENDPOINT,
26
+ credentials: {
27
+ accessKeyId: process.env.STORAGE_ACCESS_KEY_ID,
28
+ secretAccessKey: process.env.STORAGE_SECRET_ACCESS_KEY,
29
+ },
30
+ forcePathStyle: true,
31
+});
32
+
33
+const BUCKET = process.env.STORAGE_BUCKET;
34
+
35
+async function listBucket() {
36
+ console.log(`📦 Listing contents of bucket: ${BUCKET}\n`);
37
+
38
+ let totalObjects = 0;
39
+ let continuationToken;
40
+
41
+ do {
42
+ const listCommand = new ListObjectsV2Command({
43
+ Bucket: BUCKET,
44
+ ContinuationToken: continuationToken,
45
+ MaxKeys: 100, // Show first 100
46
+ });
47
+
48
+ const response = await client.send(listCommand);
49
+
50
+ if (!response.Contents || response.Contents.length === 0) {
51
+ console.log('✅ Bucket is empty!');
52
+ break;
53
+ }
54
+
55
+ console.log(`Found ${response.Contents.length} objects:\n`);
56
+
57
+ response.Contents.forEach(obj => {
58
+ console.log(` ${obj.Key} (${obj.Size} bytes)`);
59
+ totalObjects++;
60
+ });
61
+
62
+ continuationToken = response.NextContinuationToken;
63
+
64
+ if (totalObjects >= 100) {
65
+ console.log('\n... (showing first 100 objects)');
66
+ break;
67
+ }
68
+
69
+ } while (continuationToken);
70
+
71
+ console.log(`\n📊 Total objects shown: ${totalObjects}`);
72
+
73
+ // Check for corrupted paths
74
+ console.log('\n🔍 Checking for corrupted paths (reposswadhinbiswas)...');
75
+ // We'd need to scan all to find these
76
+}
77
+
78
+listBucket().catch(err => {
79
+ console.error('❌ Error listing bucket:', err);
80
+ process.exit(1);
81
+});
0
82
▼scripts/manual-sync.ts
+31-0
1
+import { join } from "path";
2
+import { readdirSync, statSync } from "fs";
3
+import { uploadRepoToStorage, getStorageRepoPath, isCloudStorage } from "@/lib/git-storage";
4
+
5
+const REPOS_DIR = join(process.cwd(), "data", "repos");
6
+
7
+async function main() {
8
+ if (!await isCloudStorage()) {
9
+ console.log("Not using cloud storage. No sync needed.");
10
+ return;
11
+ }
12
+
13
+ const owners = readdirSync(REPOS_DIR);
14
+ for (const owner of owners) {
15
+ const ownerPath = join(REPOS_DIR, owner);
16
+ if (!statSync(ownerPath).isDirectory()) continue;
17
+
18
+ const repos = readdirSync(ownerPath);
19
+ for (const repo of repos) {
20
+ if (!repo.endsWith(".git")) continue;
21
+ const repoName = repo.replace(".git", "");
22
+ console.log(`Syncing ${owner}/${repoName}...`);
23
+ try {
24
+ await uploadRepoToStorage(join(ownerPath, repo), getStorageRepoPath(owner, repoName));
25
+ } catch (e) {
26
+ console.error(`Failed to sync ${owner}/${repoName}:`, e);
27
+ }
28
+ }
29
+ }
30
+}
31
+main().catch(console.error);
0
32
▼scripts/publish-cli.sh
+36-0
1
+#!/bin/bash
2
+
3
+# Publish CLI to npm and other registries
4
+set -e
5
+
6
+echo "🚀 Preparing to publish OpenCodeHub CLI..."
7
+
8
+# Move to CLI directory
9
+cd cli
10
+
11
+# 1. Clean and Build
12
+echo "📦 Building..."
13
+bun install
14
+bun run build
15
+
16
+# 2. Verify
17
+echo "✅ Verifying..."
18
+bun run test
19
+# bun doesn't have 'pack' yet, using npm for verification only if strict, otherwise skip or keep npm just for this.
20
+# Keeping npm pack for dry-run verification as it's standard.
21
+npm pack --dry-run
22
+
23
+# 3. Check Authentication
24
+echo "🔑 Checking npm authentication..."
25
+if ! npm whoami &> /dev/null; then
26
+ echo "❌ You are not logged in to npm."
27
+ echo "👉 Run 'npm login' first."
28
+ exit 1
29
+fi
30
+
31
+# 4. Publish
32
+echo "🚀 Publishing to npm..."
33
+# Note: Remove --dry-run to actually publish
34
+bun publish --access public
35
+
36
+echo "🎉 Published successfully!"
0
37
▼scripts/reset-database.cjs
+63-0
1
+/**
2
+ * Reset Database Script
3
+ *
4
+ * Drops all tables and recreates the schema from scratch
5
+ *
6
+ * Usage: node scripts/reset-database.cjs
7
+ */
8
+
9
+const { drizzle } = require('drizzle-orm/postgres-js');
10
+const postgres = require('postgres');
11
+const fs = require('fs');
12
+const path = require('path');
13
+
14
+// Load .env file manually
15
+const envPath = path.join(__dirname, '..', '.env');
16
+const envFile = fs.readFileSync(envPath, 'utf8');
17
+envFile.split('\n').forEach(line => {
18
+ const match = line.match(/^([^#=]+)=(.*)$/);
19
+ if (match) {
20
+ const key = match[1].trim();
21
+ const value = match[2].trim().replace(/^["']|["']$/g, '');
22
+ process.env[key] = value;
23
+ }
24
+});
25
+
26
+async function resetDatabase() {
27
+ const connectionString = process.env.DATABASE_URL;
28
+
29
+ console.log('🗑️ Resetting database...\n');
30
+
31
+ const sql = postgres(connectionString, { max: 1 });
32
+ const db = drizzle(sql);
33
+
34
+ try {
35
+ // Get all tables
36
+ const tables = await sql`
37
+ SELECT tablename
38
+ FROM pg_tables
39
+ WHERE schemaname = 'public'
40
+ `;
41
+
42
+ console.log(`Found ${tables.length} tables to drop:\n`);
43
+ tables.forEach(t => console.log(` - ${t.tablename}`));
44
+
45
+ // Drop all tables
46
+ for (const table of tables) {
47
+ console.log(`\nDropping table: ${table.tablename}`);
48
+ await sql.unsafe(`DROP TABLE IF EXISTS "${table.tablename}" CASCADE`);
49
+ }
50
+
51
+ console.log('\n✅ All tables dropped successfully!');
52
+ console.log('\nNow run: npm run db:push');
53
+ console.log('to recreate the schema\n');
54
+
55
+ } catch (error) {
56
+ console.error('❌ Error resetting database:', error);
57
+ process.exit(1);
58
+ } finally {
59
+ await sql.end();
60
+ }
61
+}
62
+
63
+resetDatabase();
0
64
▼scripts/test-git-server.ts
+115-0
1
+
2
+import { spawn } from "child_process";
3
+import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
4
+import { join } from "path";
5
+import { initRepository } from "../src/lib/git";
6
+import { initRepoInStorage, finalizeRepoInit } from "../src/lib/git-storage";
7
+
8
+// Configuration
9
+const TEST_OWNER = "test-user";
10
+const TEST_REPO = "test-repo";
11
+const SERVER_URL = "http://localhost:3001";
12
+const GIT_URL = `${SERVER_URL}/git/${TEST_OWNER}/${TEST_REPO}.git`;
13
+const TEMP_DIR = join(process.cwd(), ".tmp", "test-client");
14
+
15
+async function setupServerRepo() {
16
+ console.log("--- Setting up Server Repo ---");
17
+ console.log(`Creating ${TEST_OWNER}/${TEST_REPO} in storage...`);
18
+
19
+ // 1. Init in storage (prepares temp path)
20
+ const localPath = await initRepoInStorage(TEST_OWNER, TEST_REPO);
21
+
22
+ // 2. Init bare repo
23
+ await initRepository(localPath, {
24
+ repoName: TEST_REPO,
25
+ ownerName: TEST_OWNER,
26
+ readme: false // We will push our own
27
+ });
28
+
29
+ // 3. Finalize (upload to storage if cloud)
30
+ await finalizeRepoInit(TEST_OWNER, TEST_REPO);
31
+
32
+ console.log("Server repo ready.");
33
+}
34
+
35
+async function runClientTest() {
36
+ console.log("--- Running Client Test ---");
37
+
38
+ // Cleanup client temp
39
+ if (existsSync(TEMP_DIR)) {
40
+ rmSync(TEMP_DIR, { recursive: true, force: true });
41
+ }
42
+ mkdirSync(TEMP_DIR, { recursive: true });
43
+
44
+ const sourceDir = join(TEMP_DIR, "source");
45
+ const cloneDir = join(TEMP_DIR, "clone");
46
+
47
+ // 1. Create source repo
48
+ mkdirSync(sourceDir);
49
+ await runCmd("git", ["init"], sourceDir);
50
+ await runCmd("git", ["config", "user.email", "[email protected]"], sourceDir);
51
+ await runCmd("git", ["config", "user.name", "Test User"], sourceDir);
52
+
53
+ writeFileSync(join(sourceDir, "test.txt"), "Hello Git Server " + Date.now());
54
+ await runCmd("git", ["add", "."], sourceDir);
55
+ await runCmd("git", ["commit", "-m", "Initial commit"], sourceDir);
56
+
57
+ // 2. Push to server
58
+ console.log(`Pushing to ${GIT_URL}...`);
59
+ try {
60
+ await runCmd("git", ["push", GIT_URL, "master:main"], sourceDir);
61
+ console.log("Push successful.");
62
+ } catch (err) {
63
+ console.error("Push failed. Is the server running?");
64
+ throw err;
65
+ }
66
+
67
+ // 3. Clone from server
68
+ console.log(`Cloning from ${GIT_URL}...`);
69
+ await runCmd("git", ["clone", GIT_URL, cloneDir], TEMP_DIR);
70
+
71
+ // 4. Verify content
72
+ if (existsSync(join(cloneDir, "test.txt"))) {
73
+ console.log("SUCCESS: File verified in clone.");
74
+ } else {
75
+ throw new Error("FAILURE: File missing in clone.");
76
+ }
77
+}
78
+
79
+function runCmd(cmd: string, args: string[], cwd: string): Promise<void> {
80
+ return new Promise((resolve, reject) => {
81
+ const child = spawn(cmd, args, { cwd, stdio: 'inherit' });
82
+ child.on('close', (code) => {
83
+ if (code === 0) resolve();
84
+ else reject(new Error(`${cmd} exited with code ${code}`));
85
+ });
86
+ child.on('error', reject);
87
+ });
88
+}
89
+
90
+// Main
91
+async function main() {
92
+ const args = process.argv.slice(2);
93
+ const command = args[0];
94
+
95
+ if (command === "setup") {
96
+ await setupServerRepo();
97
+ } else if (command === "client") {
98
+ await runClientTest();
99
+ } else {
100
+ // Run both if no args, assuming server is handled externally between steps?
101
+ // Actually, we can run setup, BUT the server process needs to be ready.
102
+ // If we run setup here, it sets up the DATA.
103
+ // The server reads the DATA.
104
+ // So we can run setup, then user starts server, then we run client.
105
+ console.log("Usage: bun scripts/test-git-server.ts [setup|client]");
106
+ console.log("1. Run 'setup' to create the repo data.");
107
+ console.log("2. Start the server (npm run dev).");
108
+ console.log("3. Run 'client' to test push/clone.");
109
+ }
110
+}
111
+
112
+main().catch(err => {
113
+ console.error(err);
114
+ process.exit(1);
115
+});
0
116
▼scripts/test-storage-config.ts
+36-0
1
+
2
+import { getStorage, getStorageConfig, resetStorage } from "../src/lib/storage";
3
+
4
+async function main() {
5
+ console.log("--- Testing Storage Configuration ---");
6
+
7
+ // 1. Check what getStorageConfig sees from env
8
+ console.log("1. Environment Config:");
9
+ const envConfig = getStorageConfig();
10
+ console.log(`TYPE: ${envConfig.type}`);
11
+ console.log(`PATH: ${envConfig.basePath}`);
12
+
13
+ if (envConfig.type !== "local") {
14
+ console.error("FAIL: Expected 'local' from env, got", envConfig.type);
15
+ } else {
16
+ console.log("PASS: Environment config is 'local'");
17
+ }
18
+
19
+ // 2. Check what getStorage returns (might verify DB logic too if DB connection works)
20
+ console.log("\n2. Initializing Storage Adapter:");
21
+ try {
22
+ const storage = await getStorage();
23
+ console.log("Storage initialized successfully");
24
+
25
+ // Check if it's the right class
26
+ if (storage.constructor.name === "LocalStorageAdapter") {
27
+ console.log("PASS: Adapter is LocalStorageAdapter");
28
+ } else {
29
+ console.error(`FAIL: Adapter is ${storage.constructor.name}`);
30
+ }
31
+ } catch (err) {
32
+ console.error("Error initializing storage:", err);
33
+ }
34
+}
35
+
36
+main();
0
37
▼scripts/verify-env-config.ts
+67-0
1
+
2
+import { validateEnvironment } from "../src/lib/env-validation";
3
+import { getStorage } from "../src/lib/storage";
4
+
5
+// Mock process.env
6
+process.env.STORAGE_TYPE = "gdrive";
7
+process.env.GOOGLE_CLIENT_ID = "test-client-id";
8
+process.env.GOOGLE_CLIENT_SECRET = "test-client-secret";
9
+process.env.GOOGLE_REFRESH_TOKEN = "test-refresh-token";
10
+process.env.GOOGLE_FOLDER_ID = "test-folder-id";
11
+// Mock required vars to pass validation
12
+process.env.JWT_SECRET = "a".repeat(32);
13
+process.env.SESSION_SECRET = "a".repeat(32);
14
+process.env.INTERNAL_HOOK_SECRET = "a".repeat(32);
15
+process.env.SITE_URL = "http://localhost:3000";
16
+
17
+async function verify() {
18
+ console.log("Running verification...");
19
+
20
+ // 1. Test Environment Validation
21
+ console.log("\n1. Testing Environment Validation:");
22
+ const isValid = validateEnvironment(false); // Don't exit on error
23
+ if (isValid) {
24
+ console.log("✅ Validation passed with valid Google Drive vars");
25
+ } else {
26
+ console.error("❌ Validation failed unexpectedly");
27
+ process.exit(1);
28
+ }
29
+
30
+ // 2. Test Missing Vars
31
+ console.log("\n2. Testing Missing Google Drive Vars:");
32
+ const originalClientId = process.env.GOOGLE_CLIENT_ID;
33
+ delete process.env.GOOGLE_CLIENT_ID;
34
+ const isInvalid = !validateEnvironment(false);
35
+ if (isInvalid) {
36
+ console.log("✅ Validation correctly failed when GOOGLE_CLIENT_ID is missing");
37
+ } else {
38
+ console.error("❌ Validation passed despite missing GOOGLE_CLIENT_ID");
39
+ process.exit(1);
40
+ }
41
+ process.env.GOOGLE_CLIENT_ID = originalClientId; // Restore
42
+
43
+ // 3. Test Storage Config Loading
44
+ console.log("\n3. Testing Storage Configuration Loading:");
45
+ try {
46
+ const storage = await getStorage();
47
+ // @ts-ignore - Accessing protected/private property for verification or just checking type
48
+ const config = (storage as any).config;
49
+
50
+ if (config.type === 'gdrive' &&
51
+ config.googleClientId === 'test-client-id' &&
52
+ config.googleClientSecret === 'test-client-secret' &&
53
+ config.googleFolderId === 'test-folder-id') {
54
+ console.log("✅ Storage adapter loaded with correct configuration from env");
55
+ } else {
56
+ console.error("❌ Storage adapter loaded with incorrect configuration:", config);
57
+ process.exit(1);
58
+ }
59
+ } catch (error) {
60
+ console.error("❌ Failed to load storage adapter:", error);
61
+ process.exit(1);
62
+ }
63
+
64
+ console.log("\n🎉 ALL VERIFICATION CHECKS PASSED!");
65
+}
66
+
67
+verify();
0
68
▼scripts/verify-ratelimit.ts
+31-0
1
+
2
+import { rateLimiter } from "@/lib/rate-limit";
3
+
4
+async function runTest() {
5
+ console.log("Testing Rate Limiter...");
6
+ const key = "test:user1";
7
+ const limit = 5;
8
+ const window = 60 * 1000;
9
+
10
+ // Reset first
11
+ // Note: In realRedis mode we'd need to manually del key, here we just assume clean slate or expiry
12
+
13
+ console.log("Sending 5 requests (should allow)...");
14
+ for (let i = 0; i < 5; i++) {
15
+ const res = await rateLimiter.check(key, limit, window);
16
+ console.log(`Req ${i + 1}: Allowed=${res.allowed}, Remaining=${res.remaining}`);
17
+ if (!res.allowed) throw new Error("Should have allowed request within limit");
18
+ }
19
+
20
+ console.log("Sending 6th request (should block)...");
21
+ const res = await rateLimiter.check(key, limit, window);
22
+ console.log(`Req 6: Allowed=${res.allowed}, Remaining=${res.remaining}`);
23
+
24
+ if (res.allowed) {
25
+ console.error("FAILURE: Rate limiter did not block 6th request");
26
+ } else {
27
+ console.log("SUCCESS: Rate limiter blocked excess request");
28
+ }
29
+}
30
+
31
+runTest().catch(console.error);
0
32
▼scripts/worker.ts
+77-0
1
+
2
+import { getDatabase, schema } from "@/db";
3
+import { queueWorker } from "@/lib/queue-worker";
4
+import { syncAllMirrors } from "@/lib/mirror-sync";
5
+import { cleanupAllRepos } from "@/lib/cron/cleanup-branches";
6
+import { logger } from "@/lib/logger";
7
+import { eq, and, sql } from "drizzle-orm";
8
+
9
+const WORKER_INTERVAL = 5000; // 5 seconds for queue polling
10
+const MIRROR_SYNC_INTERVAL = 60 * 1000; // 1 minute
11
+const CLEANUP_INTERVAL = 60 * 60 * 1000; // 1 hour
12
+
13
+async function runQueueProcessor() {
14
+ const db = getDatabase();
15
+
16
+ try {
17
+ // Find repositories with queued items
18
+ // We group by repositoryId to process per-repo queues
19
+ // Actually QueueWorker handles locking per repo?
20
+ // Let's just find distinct repositoryIds that have queued items.
21
+ // Drizzle distinct on unsupported? Let's Fetch all pending items and get unique repoIds.
22
+
23
+ const pendingItems = await db.query.mergeQueueItems.findMany({
24
+ where: eq(schema.mergeQueueItems.status, "queued"),
25
+ columns: { repositoryId: true },
26
+ // limit: 100 ??
27
+ });
28
+
29
+ const repoIds = [...new Set(pendingItems.map(item => item.repositoryId))];
30
+
31
+ if (repoIds.length > 0) {
32
+ logger.info({ repoCount: repoIds.length }, "Processing merge queues");
33
+
34
+ // Process concurrently? Or sequentially to avoid load?
35
+ // Concurrently is fine as they are independent locks.
36
+ await Promise.all(repoIds.map(repoId => queueWorker.processQueue(repoId)));
37
+ }
38
+
39
+ } catch (error) {
40
+ logger.error({ err: error }, "Error in queue processor loop");
41
+ }
42
+}
43
+
44
+async function startWorker() {
45
+ logger.info("Starting OpenCodeHub Background Worker...");
46
+
47
+ // 1. Merge Queue Loop
48
+ setInterval(runQueueProcessor, WORKER_INTERVAL);
49
+ // Initial run
50
+ runQueueProcessor();
51
+
52
+ // 2. Mirror Sync Loop
53
+ setInterval(async () => {
54
+ try {
55
+ await syncAllMirrors();
56
+ } catch (e) {
57
+ logger.error({ err: e }, "Mirror sync failed");
58
+ }
59
+ }, MIRROR_SYNC_INTERVAL);
60
+
61
+ // 3. Cleanup Loop
62
+ setInterval(async () => {
63
+ try {
64
+ await cleanupAllRepos();
65
+ } catch (e) {
66
+ logger.error({ err: e }, "Cleanup failed");
67
+ }
68
+ }, CLEANUP_INTERVAL);
69
+
70
+ // Keep process alive
71
+ process.on('SIGINT', () => {
72
+ logger.info("Worker shutting down...");
73
+ process.exit(0);
74
+ });
75
+}
76
+
77
+startWorker().catch(console.error);
0
78
▼src/components/admin/AdminDashboard.tsx
+2-2
7
7
import QuickStatsRow from "./QuickStatsRow";
8
8
import AlertBanner from "./AlertBanner";
9
9
import RecentActivity from "./RecentActivity";
10
-import { Github } from "lucide-react";
10
+import { GitGraph } from "lucide-react";
11
11
12
12
// Error boundary for WebGL/Globe issues
13
13
class GlobeErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean }> {
139
139
{/* Right Column: Log Stream */}
140
140
<div className="col-span-3 flex flex-col gap-4 pointer-events-auto overflow-hidden">
141
141
<div className="flex justify-end shrink-0">
142
- <Github className="h-8 w-8 text-white opacity-30 hover:opacity-70 transition-opacity cursor-pointer" />
142
+ <GitGraph className="h-8 w-8 text-white opacity-30 hover:opacity-70 transition-opacity cursor-pointer" />
143
143
</div>
144
144
145
145
<div className="glass-panel rounded-xl flex-1 overflow-hidden min-h-0 relative">
146
146
▼src/components/admin/AlertBanner.tsx
+1-1
1
1
import { motion } from "framer-motion";
2
-import { AlertTriangle, CheckCircle, Info, Bell, X } from "lucide-react";
2
+import { AlertTriangle, CheckCircle, Info, X } from "lucide-react";
3
3
import { useState } from "react";
4
4
5
5
interface Alert {
6
6
▼src/components/admin/DashboardStats.tsx
+1-1
1
-import { motion, useMotionValue, useSpring, useTransform } from "framer-motion";
1
+import { motion } from "framer-motion";
2
2
import { useEffect, useState } from "react";
3
3
import { Database, GitPullRequest, Sparkles } from "lucide-react";
4
4
5
5
▼src/components/admin/QuickStatsRow.tsx
+1-1
1
1
import { motion } from "framer-motion";
2
-import { GitCommit, GitMerge, CircleDot, Users, Clock, TrendingUp, Activity, Zap } from "lucide-react";
2
+import { GitCommit, GitMerge, CircleDot, Users, TrendingUp } from "lucide-react";
3
3
import { useEffect, useState } from "react";
4
4
5
5
interface Props {
6
6
▼src/components/admin/SystemStatus.tsx
+1-5
1
-import { HardDrive, Cpu, Zap, Activity, Wifi, Server } from "lucide-react";
1
+import { HardDrive, Cpu, Zap, Activity } from "lucide-react";
2
2
import { motion } from "framer-motion";
3
3
4
4
interface SystemStatusProps {
15
15
// Glowing ring gauge component
16
16
function GlowingGauge({
17
17
value,
18
- color,
19
18
icon: Icon,
20
19
label,
21
20
gradientColors
22
21
}: {
23
22
value: number;
24
- color: string;
25
23
icon: typeof Cpu;
26
24
label: string;
27
25
gradientColors: [string, string];
97
95
<div className="flex items-center gap-10">
98
96
<GlowingGauge
99
97
value={status.cpuLoad}
100
- color="#3b82f6"
101
98
icon={Cpu}
102
99
label="CPU"
103
100
gradientColors={status.cpuLoad > 80 ? ["#ef4444", "#f97316"] : ["#3b82f6", "#06b6d4"]}
104
101
/>
105
102
<GlowingGauge
106
103
value={status.memoryUsage}
107
- color="#8b5cf6"
108
104
icon={Zap}
109
105
label="RAM"
110
106
gradientColors={status.memoryUsage > 80 ? ["#eab308", "#f97316"] : ["#8b5cf6", "#ec4899"]}
111
107
▼src/components/dashboard/StackCard.astro
+6-5
24
24
25
25
// Determine stack status color
26
26
const isFullyMerged = stack.prs.every(pr => pr.isMerged);
27
-const hasFailures = false; // TODO: Check CI status if available
27
+const hasFailures = stack.prs.some(pr => pr.status === 'failure' || pr.status === 'error');
28
+const isDraft = stack.prs.some(pr => pr.status === 'draft');
28
29
29
-const statusColor = isFullyMerged ? "text-green-400" : hasFailures ? "text-red-400" : "text-blue-400";
30
-const statusBorder = isFullyMerged ? "border-green-500/30" : hasFailures ? "border-red-500/30" : "border-blue-500/30";
31
-const statusBg = isFullyMerged ? "bg-green-500/10" : hasFailures ? "bg-red-500/10" : "bg-blue-500/10";
30
+const statusColor = isFullyMerged ? "text-green-400" : hasFailures ? "text-red-400" : isDraft ? "text-gray-400" : "text-blue-400";
31
+const statusBorder = isFullyMerged ? "border-green-500/30" : hasFailures ? "border-red-500/30" : isDraft ? "border-gray-500/30" : "border-blue-500/30";
32
+const statusBg = isFullyMerged ? "bg-green-500/10" : hasFailures ? "bg-red-500/10" : isDraft ? "bg-gray-500/10" : "bg-blue-500/10";
32
33
---
33
34
34
35
<div class="relative group">
47
48
</div>
48
49
</div>
49
50
<div class={`text-xs px-2 py-0.5 rounded-full border ${statusBorder} ${statusColor} ${statusBg}`}>
50
- {isFullyMerged ? "Merged" : "Active"}
51
+ {isFullyMerged ? "Merged" : hasFailures ? "Failed" : isDraft ? "Draft" : "Active"}
51
52
</div>
52
53
</div>
53
54
54
55
▼src/components/git/InteractiveRebase.tsx
+262-0
1
+
2
+import React, { useState } from 'react';
3
+import {
4
+ DndContext,
5
+ closestCenter,
6
+ KeyboardSensor,
7
+ PointerSensor,
8
+ useSensor,
9
+ useSensors,
10
+ DragOverlay,
11
+ type DragEndEvent
12
+} from '@dnd-kit/core';
13
+import {
14
+ arrayMove,
15
+ SortableContext,
16
+ sortableKeyboardCoordinates,
17
+ verticalListSortingStrategy,
18
+ useSortable
19
+} from '@dnd-kit/sortable';
20
+import { CSS } from '@dnd-kit/utilities';
21
+import { Button } from "@/components/ui/button";
22
+import {
23
+ Dialog,
24
+ DialogContent,
25
+ DialogDescription,
26
+ DialogHeader,
27
+ DialogTitle,
28
+ DialogTrigger,
29
+ DialogFooter,
30
+} from "@/components/ui/dialog";
31
+import { GripVertical, GitCommit, Trash2, Edit2, ArrowUpFromLine, Save, Loader2 } from 'lucide-react';
32
+import { toast } from 'sonner';
33
+
34
+interface Commit {
35
+ hash: string;
36
+ message: string;
37
+ author: string;
38
+}
39
+
40
+export interface InteractiveRebaseProps {
41
+ owner: string;
42
+ repo: string;
43
+ prNumber: number;
44
+ headBranch: string;
45
+ baseBranch: string;
46
+ commits: Commit[]; // Initial commit list
47
+}
48
+
49
+type ActionType = "pick" | "reword" | "squash" | "drop";
50
+
51
+interface RebaseItem extends Commit {
52
+ id: string; // Use hash as ID
53
+ action: ActionType;
54
+ newMessage?: string;
55
+}
56
+
57
+function SortableItem({ item, index, onActionChange, onMessageChange }: {
58
+ item: RebaseItem,
59
+ index: number,
60
+ onActionChange: (id: string, action: ActionType) => void,
61
+ onMessageChange: (id: string, msg: string) => void
62
+}) {
63
+ const {
64
+ attributes,
65
+ listeners,
66
+ setNodeRef,
67
+ transform,
68
+ transition,
69
+ isDragging
70
+ } = useSortable({ id: item.id });
71
+
72
+ const style = {
73
+ transform: CSS.Transform.toString(transform),
74
+ transition,
75
+ zIndex: isDragging ? 10 : 1,
76
+ opacity: isDragging ? 0.5 : 1
77
+ };
78
+
79
+ return (
80
+ <div ref={setNodeRef} style={style} className="flex items-center gap-3 p-3 bg-card border rounded mb-2 shadow-sm">
81
+ <div {...attributes} {...listeners} className="cursor-grab text-muted-foreground hover:text-foreground">
82
+ <GripVertical className="h-5 w-5" />
83
+ </div>
84
+
85
+ <select
86
+ className="h-8 rounded border bg-background text-sm px-2"
87
+ value={item.action}
88
+ onChange={(e) => onActionChange(item.id, e.target.value as ActionType)}
89
+ >
90
+ <option value="pick">Pick</option>
91
+ <option value="reword">Reword</option>
92
+ <option value="squash" disabled={index === 0}>Squash</option>
93
+ <option value="drop">Drop</option>
94
+ </select>
95
+
96
+ <div className="flex-1 min-w-0">
97
+ {item.action === "reword" ? (
98
+ <input
99
+ className="w-full bg-background border rounded px-2 py-1 text-sm font-mono"
100
+ value={item.newMessage || item.message}
101
+ onChange={(e) => onMessageChange(item.id, e.target.value)}
102
+ />
103
+ ) : (
104
+ <div className={`font-mono text-sm truncate ${item.action === 'drop' ? 'line-through text-muted-foreground' : ''}`}>
105
+ {item.message}
106
+ </div>
107
+ )}
108
+ </div>
109
+
110
+ <div className="text-xs text-muted-foreground font-mono">
111
+ {item.id.substring(0, 7)}
112
+ </div>
113
+ </div>
114
+ );
115
+}
116
+
117
+export function InteractiveRebase({ owner, repo, prNumber, commits: initialCommits }: InteractiveRebaseProps) {
118
+ const [isOpen, setIsOpen] = useState(false);
119
+ const [items, setItems] = useState<RebaseItem[]>(
120
+ initialCommits.map(c => ({ ...c, id: c.hash, action: "pick" }))
121
+ );
122
+ const [loading, setLoading] = useState(false);
123
+
124
+ const sensors = useSensors(
125
+ useSensor(PointerSensor),
126
+ useSensor(KeyboardSensor, {
127
+ coordinateGetter: sortableKeyboardCoordinates,
128
+ })
129
+ );
130
+
131
+ const handleDragEnd = (event: DragEndEvent) => {
132
+ const { active, over } = event;
133
+ if (active.id !== over?.id) {
134
+ setItems((items) => {
135
+ const oldIndex = items.findIndex(i => i.id === active.id);
136
+ const newIndex = items.findIndex(i => i.id === over?.id);
137
+ return arrayMove(items, oldIndex, newIndex);
138
+ });
139
+ }
140
+ };
141
+
142
+ const handleActionChange = (id: string, action: ActionType) => {
143
+ setItems(items => items.map(item =>
144
+ item.id === id ? { ...item, action } : item
145
+ ));
146
+ };
147
+
148
+ const handleMessageChange = (id: string, msg: string) => {
149
+ setItems(items => items.map(item =>
150
+ item.id === id ? { ...item, newMessage: msg } : item
151
+ ));
152
+ };
153
+
154
+ const handleSave = async () => {
155
+ // Validation: Ensure we don't drop all commits
156
+ if (items.every(i => i.action === 'drop')) {
157
+ toast.error("Cannot drop all commits. Use 'Delete Branch' instead.");
158
+ return;
159
+ }
160
+
161
+ // Validation: First commit cannot be squashed
162
+ if (items[0].action === 'squash') {
163
+ toast.error("Cannot squash the first commit. Pick or Reword it.");
164
+ return;
165
+ }
166
+
167
+ setLoading(true);
168
+ // Transform items to operations
169
+ const operations = items.map(item => ({
170
+ type: item.action,
171
+ hash: item.id,
172
+ newMessage: item.newMessage || item.message
173
+ }));
174
+
175
+ try {
176
+ const res = await fetch(`/api/repos/${owner}/${repo}/pulls/${prNumber}/rewrite`, {
177
+ method: "POST",
178
+ headers: { "Content-Type": "application/json" },
179
+ body: JSON.stringify({ operations })
180
+ });
181
+
182
+ const data = await res.json().catch(() => ({}));
183
+
184
+ if (!res.ok) {
185
+ // Handle specific error codes if available
186
+ const errorMessage = data.message || data.error || "Failed to rewrite history. Please try again.";
187
+ throw new Error(errorMessage);
188
+ }
189
+
190
+ toast.success("History rewritten successfully!", {
191
+ description: "The branch has been force-pushed with your new history."
192
+ });
193
+ setIsOpen(false);
194
+
195
+ // Reload after a short delay to let toast show
196
+ setTimeout(() => window.location.reload(), 1500);
197
+ } catch (e: any) {
198
+ console.error("Rebase error:", e);
199
+ toast.error("Rebase Failed", {
200
+ description: e.message || "An unexpected network error occurred.",
201
+ duration: 5000,
202
+ });
203
+ } finally {
204
+ setLoading(false);
205
+ }
206
+ };
207
+
208
+ return (
209
+ <Dialog open={isOpen} onOpenChange={setIsOpen}>
210
+ <DialogTrigger asChild>
211
+ <Button variant="outline" size="sm">
212
+ <Edit2 className="mr-2 h-4 w-4" />
213
+ Edit History
214
+ </Button>
215
+ </DialogTrigger>
216
+ <DialogContent className="sm:max-w-[700px] max-h-[85vh] flex flex-col">
217
+ <DialogHeader>
218
+ <DialogTitle>Interactive Rebase</DialogTitle>
219
+ <DialogDescription>
220
+ Drag to reorder. Squash commits into the one above. Drop unwanted commits.
221
+ </DialogDescription>
222
+ </DialogHeader>
223
+
224
+ <div className="flex-1 overflow-y-auto py-4">
225
+ <DndContext
226
+ sensors={sensors}
227
+ collisionDetection={closestCenter}
228
+ onDragEnd={handleDragEnd}
229
+ >
230
+ <SortableContext
231
+ items={items.map(i => i.id)}
232
+ strategy={verticalListSortingStrategy}
233
+ >
234
+ {items.map((item, index) => (
235
+ <SortableItem
236
+ key={item.id}
237
+ item={item}
238
+ index={index}
239
+ onActionChange={handleActionChange}
240
+ onMessageChange={handleMessageChange}
241
+ />
242
+ ))}
243
+ </SortableContext>
244
+ </DndContext>
245
+ </div>
246
+
247
+ <DialogFooter className="gap-2">
248
+ <div className="text-xs text-muted-foreground mr-auto content-center">
249
+ {items.filter(i => i.action === 'drop').length} dropped, {items.filter(i => i.action === 'squash').length} squashed
250
+ </div>
251
+ <Button variant="secondary" onClick={() => setIsOpen(false)} disabled={loading}>
252
+ Cancel
253
+ </Button>
254
+ <Button onClick={handleSave} disabled={loading} variant="destructive">
255
+ {loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
256
+ Confirm Rewrite
257
+ </Button>
258
+ </DialogFooter>
259
+ </DialogContent>
260
+ </Dialog>
261
+ );
262
+}
0
263
▼src/components/home/HeroSection.tsx
+2-2
3
3
import { Spotlight } from "@/components/ui/spotlight";
4
4
import { ColourfulText } from "@/components/ui/colourful-text";
5
5
import { motion } from "framer-motion";
6
-import { ArrowRight, Github, Star, GitBranch, Zap } from "lucide-react";
6
+import { ArrowRight, GitGraph, Star, GitBranch, Zap } from "lucide-react";
7
7
8
8
export default function HeroSection() {
9
9
const words = ["Teams", "Developers", "Enterprises", "Startups"];
84
84
href="/explore"
85
85
className="inline-flex items-center justify-center gap-2 rounded-lg border border-white/10 bg-white/5 px-8 py-3.5 text-sm font-medium text-foreground backdrop-blur-sm hover:bg-white/10 hover:border-white/20 transition-all"
86
86
>
87
- <Github className="h-4 w-4" />
87
+ <GitGraph className="h-4 w-4" />
88
88
Explore Repositories
89
89
</a>
90
90
</motion.div>
91
91
▼src/components/insights/InsightsCharts.tsx
+124-0
1
+
2
+import React from 'react';
3
+import {
4
+ LineChart,
5
+ Line,
6
+ XAxis,
7
+ YAxis,
8
+ CartesianGrid,
9
+ Tooltip,
10
+ Legend,
11
+ ResponsiveContainer,
12
+ BarChart,
13
+ Bar,
14
+} from 'recharts';
15
+
16
+type DailyStats = {
17
+ date: string;
18
+ cycleTime: number;
19
+ mergeCount: number;
20
+ reviewTime: number;
21
+ commitCount?: number;
22
+};
23
+
24
+interface InsightsChartsProps {
25
+ data: DailyStats[];
26
+}
27
+
28
+export const InsightsCharts: React.FC<InsightsChartsProps> = ({ data }) => {
29
+ return (
30
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
31
+ {/* Cycle Time Chart */}
32
+ <div className="bg-card border rounded-lg p-6 shadow-sm overflow-hidden">
33
+ <h3 className="text-lg font-semibold mb-4">Cycle Time (Hours)</h3>
34
+ <p className="text-sm text-muted-foreground mb-6">Average time from Creation to Merge</p>
35
+ <div className="w-full h-[300px]">
36
+ <ResponsiveContainer width="100%" height="100%" minWidth={0}>
37
+ <LineChart data={data} margin={{ top: 5, right: 20, bottom: 5, left: 0 }}>
38
+ <CartesianGrid strokeDasharray="3 3" opacity={0.1} />
39
+ <XAxis
40
+ dataKey="date"
41
+ tick={{ fontSize: 12 }}
42
+ tickFormatter={(value) => new Date(value).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })}
43
+ />
44
+ <YAxis />
45
+ <Tooltip
46
+ contentStyle={{ backgroundColor: 'hsl(var(--popover))', borderColor: 'hsl(var(--border))', color: 'hsl(var(--popover-foreground))' }}
47
+ itemStyle={{ color: 'hsl(var(--primary))' }}
48
+ />
49
+ <Legend />
50
+ <Line
51
+ type="monotone"
52
+ dataKey="cycleTime"
53
+ name="Avg Cycle Hours"
54
+ stroke="hsl(var(--primary))"
55
+ strokeWidth={2}
56
+ dot={false}
57
+ />
58
+ </LineChart>
59
+ </ResponsiveContainer>
60
+ </div>
61
+ </div>
62
+
63
+ {/* Deployment Frequency Chart */}
64
+ <div className="bg-card border rounded-lg p-6 shadow-sm overflow-hidden">
65
+ <h3 className="text-lg font-semibold mb-4">Deployment Frequency</h3>
66
+ <p className="text-sm text-muted-foreground mb-6">Number of PRs merged per day</p>
67
+ <div className="w-full h-[300px]">
68
+ <ResponsiveContainer width="100%" height="100%" minWidth={0}>
69
+ <BarChart data={data} margin={{ top: 5, right: 20, bottom: 5, left: 0 }}>
70
+ <CartesianGrid strokeDasharray="3 3" opacity={0.1} />
71
+ <XAxis
72
+ dataKey="date"
73
+ tick={{ fontSize: 12 }}
74
+ tickFormatter={(value) => new Date(value).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })}
75
+ />
76
+ <YAxis allowDecimals={false} />
77
+ <Tooltip
78
+ cursor={{ fill: 'hsl(var(--muted))', opacity: 0.2 }}
79
+ contentStyle={{ backgroundColor: 'hsl(var(--popover))', borderColor: 'hsl(var(--border))', color: 'hsl(var(--popover-foreground))' }}
80
+ />
81
+ <Legend />
82
+ <Bar
83
+ dataKey="mergeCount"
84
+ name="Merged PRs"
85
+ fill="hsl(var(--chart-2))"
86
+ radius={[4, 4, 0, 0]}
87
+ />
88
+ </BarChart>
89
+ </ResponsiveContainer>
90
+ </div>
91
+ </div>
92
+
93
+ {/* Commit Activity Chart */}
94
+ <div className="bg-card border rounded-lg p-6 shadow-sm overflow-hidden md:col-span-2 lg:col-span-1">
95
+ <h3 className="text-lg font-semibold mb-4">Commit Activity</h3>
96
+ <p className="text-sm text-muted-foreground mb-6">Number of commits per day</p>
97
+ <div className="w-full h-[300px]">
98
+ <ResponsiveContainer width="100%" height="100%" minWidth={0}>
99
+ <BarChart data={data} margin={{ top: 5, right: 20, bottom: 5, left: 0 }}>
100
+ <CartesianGrid strokeDasharray="3 3" opacity={0.1} />
101
+ <XAxis
102
+ dataKey="date"
103
+ tick={{ fontSize: 12 }}
104
+ tickFormatter={(value) => new Date(value).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })}
105
+ />
106
+ <YAxis allowDecimals={false} />
107
+ <Tooltip
108
+ cursor={{ fill: 'hsl(var(--muted))', opacity: 0.2 }}
109
+ contentStyle={{ backgroundColor: 'hsl(var(--popover))', borderColor: 'hsl(var(--border))', color: 'hsl(var(--popover-foreground))' }}
110
+ />
111
+ <Legend />
112
+ <Bar
113
+ dataKey="commitCount"
114
+ name="Commits"
115
+ fill="hsl(var(--chart-3))"
116
+ radius={[4, 4, 0, 0]}
117
+ />
118
+ </BarChart>
119
+ </ResponsiveContainer>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ );
124
+};
0
125
▼src/components/issues/IssueDetail.tsx
+353-215
14
14
Edit3,
15
15
Pin,
16
16
Lock,
17
- Trash2
17
+ Trash2,
18
+ Layers,
19
+ CheckSquare,
20
+ ListTodo
18
21
} from "lucide-react";
19
-import { useState } from "react";
22
+import { useEffect, useState } from "react";
23
+import { marked } from "marked";
24
+import DOMPurify from "isomorphic-dompurify";
25
+import { formatDistanceToNow } from "date-fns";
20
26
21
27
interface Issue {
22
28
id: string;
24
30
title: string;
25
31
body: string;
26
32
state: "open" | "closed";
33
+ status?: {
34
+ id: string;
35
+ name: string;
36
+ color: string;
37
+ type: string;
38
+ };
27
39
createdAt: string;
28
40
author: {
29
41
username: string;
37
49
name: string;
38
50
color: string;
39
51
}>;
52
+ type: string;
53
+ parent?: {
54
+ number: number;
55
+ title: string;
56
+ state: "open" | "closed";
57
+ };
58
+ children?: Array<{
59
+ number: number;
60
+ title: string;
61
+ state: "open" | "closed";
62
+ }>;
40
63
}
41
64
42
65
interface Props {
44
67
bodyHtml: string;
45
68
repoOwner: string;
46
69
repoName: string;
70
+ canLink?: boolean;
47
71
}
48
72
49
73
function timeAgo(date: string): string {
65
89
return `${diffMonths} months ago`;
66
90
}
67
91
68
-export default function IssueDetail({ issue, bodyHtml, repoOwner, repoName }: Props) {
92
+export default function IssueDetail({ issue, bodyHtml, repoOwner, repoName, canLink }: Props) {
69
93
const [showMenu, setShowMenu] = useState(false);
94
+ const [linkedPRs, setLinkedPRs] = useState<Array<{
95
+ id: string;
96
+ linkType: string;
97
+ pullRequest: { number: number; title: string; state: string };
98
+ }>>([]);
99
+ const [loadingLinks, setLoadingLinks] = useState(true);
100
+ const [crossRepoLinks, setCrossRepoLinks] = useState<Array<{
101
+ id: string;
102
+ linkType: string;
103
+ issue: { number: number; title: string; state: string };
104
+ repository: { name: string; owner: string };
105
+ }>>([]);
106
+ const [loadingCrossRepo, setLoadingCrossRepo] = useState(true);
107
+ const [crossRepoTarget, setCrossRepoTarget] = useState("");
108
+ const [crossRepoType, setCrossRepoType] = useState("relates");
109
+ const [crossRepoError, setCrossRepoError] = useState<string | null>(null);
110
+
111
+ async function convertToEpic() {
112
+ if (!confirm("Are you sure you want to convert this issue to an Epic?")) return;
113
+
114
+ try {
115
+ const res = await fetch(`/api/repos/${repoOwner}/${repoName}/issues/${issue.number}`, {
116
+ method: "PATCH",
117
+ body: JSON.stringify({ type: "epic" }),
118
+ headers: { "Content-Type": "application/json" }
119
+ });
120
+
121
+ if (res.ok) {
122
+ window.location.reload();
123
+ } else {
124
+ alert("Failed to convert to epic");
125
+ }
126
+ } catch (e) {
127
+ console.error(e);
128
+ alert("Failed to convert to epic");
129
+ }
130
+ }
131
+
132
+ useEffect(() => {
133
+ let isMounted = true;
134
+
135
+ async function loadLinks() {
136
+ try {
137
+ const res = await fetch(`/api/repos/${repoOwner}/${repoName}/issues/${issue.number}/linked-prs`);
138
+ if (!res.ok) throw new Error("Failed to load linked PRs");
139
+ const data = await res.json();
140
+ if (isMounted) {
141
+ setLinkedPRs(data.links || []);
142
+ }
143
+ } catch (e) {
144
+ if (isMounted) {
145
+ setLinkedPRs([]);
146
+ }
147
+ } finally {
148
+ if (isMounted) {
149
+ setLoadingLinks(false);
150
+ }
151
+ }
152
+ }
153
+
154
+ loadLinks();
155
+
156
+ return () => {
157
+ isMounted = false;
158
+ };
159
+ }, [repoOwner, repoName, issue.number]);
160
+
161
+ useEffect(() => {
162
+ let isMounted = true;
163
+
164
+ async function loadCrossRepoLinks() {
165
+ try {
166
+ const res = await fetch(`/api/repos/${repoOwner}/${repoName}/issues/${issue.number}/cross-repo-links`);
167
+ if (!res.ok) throw new Error("Failed to load linked issues");
168
+ const data = await res.json();
169
+ if (isMounted) {
170
+ setCrossRepoLinks(data.links || []);
171
+ }
172
+ } catch (e) {
173
+ if (isMounted) {
174
+ setCrossRepoLinks([]);
175
+ }
176
+ } finally {
177
+ if (isMounted) {
178
+ setLoadingCrossRepo(false);
179
+ }
180
+ }
181
+ }
182
+
183
+ loadCrossRepoLinks();
184
+
185
+ return () => {
186
+ isMounted = false;
187
+ };
188
+ }, [repoOwner, repoName, issue.number]);
189
+
190
+ async function handleCrossRepoLink() {
191
+ if (!crossRepoTarget.trim()) return;
192
+ setCrossRepoError(null);
193
+ try {
194
+ const res = await fetch(`/api/repos/${repoOwner}/${repoName}/issues/${issue.number}/cross-repo-links`, {
195
+ method: "POST",
196
+ headers: { "Content-Type": "application/json" },
197
+ body: JSON.stringify({ target: crossRepoTarget.trim(), linkType: crossRepoType })
198
+ });
199
+
200
+ if (!res.ok) {
201
+ const data = await res.json();
202
+ throw new Error(data.error?.message || "Failed to link issue");
203
+ }
204
+
205
+ const data = await res.json();
206
+ setCrossRepoLinks((prev) => [data.link, ...prev]);
207
+ setCrossRepoTarget("");
208
+ } catch (e: any) {
209
+ setCrossRepoError(e.message || "Failed to link issue");
210
+ }
211
+ }
70
212
71
213
return (
72
214
<div className="max-w-6xl mx-auto">
86
228
</span>
87
229
</div>
88
230
89
- {/* Edit Button */}
90
- <motion.button
91
- whileHover={{ scale: 1.05 }}
92
- whileTap={{ scale: 0.95 }}
93
- className="flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/5 text-sm text-gray-300 hover:bg-white/10 transition-all"
94
- >
95
- <Edit3 className="h-4 w-4" />
96
- Edit
97
- </motion.button>
231
+ <div className="flex items-center gap-2">
232
+ {/* Convert to Epic Button */}
233
+ {issue.type !== 'epic' && (
234
+ <button
235
+ onClick={convertToEpic}
236
+ className="flex items-center gap-2 px-4 py-2 rounded-lg border border-purple-500/30 bg-purple-500/10 text-sm text-purple-300 hover:bg-purple-500/20 transition-all"
237
+ >
238
+ <Layers className="h-4 w-4" />
239
+ convert to Epic
240
+ </button>
241
+ )}
242
+
243
+ {/* Edit Button */}
244
+ <motion.button
245
+ whileHover={{ scale: 1.05 }}
246
+ whileTap={{ scale: 0.95 }}
247
+ className="flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/5 text-sm text-gray-300 hover:bg-white/10 transition-all"
248
+ >
249
+ <Edit3 className="h-4 w-4" />
250
+ Edit
251
+ </motion.button>
252
+ </div>
98
253
</div>
99
254
100
- <div className="flex items-center gap-3 text-sm">
101
- {/* Status Badge */}
102
- <motion.div
103
- initial={{ scale: 0.9 }}
104
- animate={{ scale: 1 }}
105
- className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full font-medium ${issue.state === "open"
255
+ {/* Status & Type Badges */}
256
+ <div className="flex items-center gap-2">
257
+ {issue.status ? (
258
+ <motion.div
259
+ initial={{ scale: 0.9 }}
260
+ animate={{ scale: 1 }}
261
+ className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full font-medium text-white border"
262
+ style={{
263
+ backgroundColor: `${issue.status.color}20`,
264
+ borderColor: `${issue.status.color}40`,
265
+ color: issue.status.color
266
+ }}
267
+ >
268
+ <div className="h-2 w-2 rounded-full" style={{ backgroundColor: issue.status.color }} />
269
+ {issue.status.name}
270
+ </motion.div>
271
+ ) : (
272
+ <motion.div
273
+ initial={{ scale: 0.9 }}
274
+ animate={{ scale: 1 }}
275
+ className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full font-medium ${issue.state === "open"
106
276
? "bg-green-500/20 text-green-400 border border-green-500/30"
107
277
: "bg-purple-500/20 text-purple-400 border border-purple-500/30"
108
- }`}
109
- >
110
- {issue.state === "open" ? (
111
- <AlertCircle className="h-4 w-4" />
112
- ) : (
113
- <CheckCircle2 className="h-4 w-4" />
114
- )}
115
- {issue.state === "open" ? "Open" : "Closed"}
116
- </motion.div>
278
+ }`}
279
+ >
280
+ {issue.state === "open" ? (
281
+ <AlertCircle className="h-4 w-4" />
282
+ ) : (
283
+ <CheckCircle2 className="h-4 w-4" />
284
+ )}
285
+ {issue.state === "open" ? "Open" : "Closed"}
286
+ </motion.div>
287
+ )}
117
288
118
- <span className="text-gray-400">
119
- <span className="font-medium text-white hover:text-cyan-400 cursor-pointer transition-colors">
120
- {issue.author.username}
121
- </span>
122
- {" "}opened this issue {timeAgo(issue.createdAt)}
123
- </span>
289
+ <div className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full font-medium border ${issue.type === 'epic'
290
+ ? "bg-purple-500/10 text-purple-400 border-purple-500/20"
291
+ : issue.type === 'task'
292
+ ? "bg-blue-500/10 text-blue-400 border-blue-500/20"
293
+ : "bg-gray-500/10 text-gray-400 border-gray-500/20"
294
+ }`}>
295
+ {issue.type === 'epic' && <Layers className="h-3.5 w-3.5" />}
296
+ {issue.type === 'task' && <CheckSquare className="h-3.5 w-3.5" />}
297
+ {issue.type === 'issue' && <AlertCircle className="h-3.5 w-3.5" />}
298
+ <span className="capitalize">{issue.type}</span>
299
+ </div>
124
300
</div>
301
+
302
+ <span className="text-gray-400">
303
+ <span className="font-medium text-white hover:text-cyan-400 cursor-pointer transition-colors">
304
+ {issue.author.username}
305
+ </span>
306
+ {" "}opened this issue {timeAgo(issue.createdAt)}
307
+ </span>
125
308
</motion.div>
126
309
127
310
{/* Main Content Grid */}
128
311
<div className="grid grid-cols-1 lg:grid-cols-[1fr_280px] gap-8">
129
312
{/* Left Column - Issue Body & Comments */}
130
313
<div className="space-y-6">
131
- {/* Issue Body Card */}
132
- <motion.div
133
- initial={{ opacity: 0, y: 20 }}
134
- animate={{ opacity: 1, y: 0 }}
135
- transition={{ delay: 0.1 }}
136
- className="relative"
137
- >
138
- {/* Gradient border */}
139
- <div className="absolute -inset-[1px] bg-gradient-to-r from-cyan-500/20 via-transparent to-purple-500/20 rounded-xl opacity-50" />
140
-
141
- <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/80 backdrop-blur-sm overflow-hidden">
142
- {/* Author Header */}
143
- <div className="flex items-center justify-between px-4 py-3 bg-white/[0.02] border-b border-white/5">
144
- <div className="flex items-center gap-3">
145
- {issue.author.avatarUrl ? (
146
- <img
147
- src={issue.author.avatarUrl}
148
- alt=""
149
- className="h-8 w-8 rounded-full ring-2 ring-white/10"
150
- />
151
- ) : (
152
- <div className="h-8 w-8 rounded-full bg-gradient-to-br from-cyan-400 to-blue-600 flex items-center justify-center text-sm font-bold text-white ring-2 ring-white/10">
153
- {issue.author.username[0].toUpperCase()}
154
- </div>
155
- )}
156
- <div>
157
- <span className="font-medium text-white text-sm">
158
- {issue.author.username}
159
- </span>
160
- <span className="text-xs text-gray-500 ml-2">
161
- commented {timeAgo(issue.createdAt)}
162
- </span>
163
- </div>
164
- </div>
165
-
166
- {/* Actions Menu */}
167
- <div className="relative">
168
- <button
169
- onClick={() => setShowMenu(!showMenu)}
170
- className="p-1.5 rounded-lg hover:bg-white/10 transition-colors"
171
- >
172
- <MoreHorizontal className="h-4 w-4 text-gray-500" />
173
- </button>
174
-
175
- {showMenu && (
176
- <motion.div
177
- initial={{ opacity: 0, scale: 0.95 }}
178
- animate={{ opacity: 1, scale: 1 }}
179
- className="absolute right-0 top-full mt-1 w-48 rounded-lg border border-white/10 bg-[#161b22] shadow-xl z-10"
180
- >
181
- <div className="p-1">
182
- <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-300 hover:bg-white/5 rounded-md transition-colors">
183
- <Link2 className="h-4 w-4" />
184
- Copy link
185
- </button>
186
- <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-300 hover:bg-white/5 rounded-md transition-colors">
187
- <Pin className="h-4 w-4" />
188
- Pin issue
189
- </button>
190
- <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-300 hover:bg-white/5 rounded-md transition-colors">
191
- <Lock className="h-4 w-4" />
192
- Lock conversation
193
- </button>
194
- <div className="border-t border-white/5 my-1" />
195
- <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-red-400 hover:bg-red-500/10 rounded-md transition-colors">
196
- <Trash2 className="h-4 w-4" />
197
- Delete issue
198
- </button>
199
- </div>
200
- </motion.div>
201
- )}
202
- </div>
203
- </div>
204
-
205
- {/* Body Content */}
206
- <div
207
- className="p-5 prose prose-invert prose-sm max-w-none prose-p:text-gray-300 prose-headings:text-white prose-a:text-cyan-400 prose-code:text-pink-400 prose-pre:bg-black/30"
208
- dangerouslySetInnerHTML={{ __html: bodyHtml }}
209
- />
210
- </div>
211
- </motion.div>
212
-
213
- {/* Add Comment Section */}
214
- <motion.div
215
- initial={{ opacity: 0, y: 20 }}
216
- animate={{ opacity: 1, y: 0 }}
217
- transition={{ delay: 0.2 }}
218
- className="relative"
219
- >
220
- <div className="rounded-xl border border-white/10 bg-[#0d1117]/80 p-4">
221
- <textarea
222
- placeholder="Leave a comment..."
223
- className="w-full h-24 bg-transparent border border-white/10 rounded-lg px-4 py-3 text-sm text-white placeholder:text-gray-600 focus:outline-none focus:border-cyan-500/50 resize-none"
224
- />
225
- <div className="flex justify-end mt-3">
226
- <motion.button
227
- whileHover={{ scale: 1.02 }}
228
- whileTap={{ scale: 0.98 }}
229
- className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-gradient-to-r from-green-500 to-emerald-600 text-sm font-medium text-white shadow-lg shadow-green-500/20"
230
- >
231
- <MessageSquare className="h-4 w-4" />
232
- Comment
233
- </motion.button>
234
- </div>
235
- </div>
236
- </motion.div>
314
+ <div className="prose prose-invert max-w-none" dangerouslySetInnerHTML={{ __html: bodyHtml }} />
237
315
</div>
238
316
239
317
{/* Right Sidebar */}
243
321
transition={{ delay: 0.15 }}
244
322
className="space-y-6"
245
323
>
246
- {/* Assignees */}
247
324
<div className="relative">
248
- <div className="absolute -inset-[1px] bg-gradient-to-b from-cyan-500/10 to-transparent rounded-xl opacity-50" />
325
+ <div className="absolute -inset-[1px] bg-gradient-to-b from-emerald-500/10 to-transparent rounded-xl opacity-50" />
249
326
<div className="relative rounded-xl border border-white/10 bg-[#0d1117]/60 p-4">
250
- <div className="flex items-center justify-between mb-3">
251
- <h3 className="text-sm font-semibold text-white flex items-center gap-2">
252
- <User className="h-4 w-4 text-cyan-400" />
253
- Assignees
254
- </h3>
255
- <button className="text-xs text-gray-500 hover:text-cyan-400 transition-colors">
256
- Edit
257
- </button>
258
- </div>
259
- {issue.assignees && issue.assignees.length > 0 ? (
327
+ <h3 className="text-sm font-semibold text-white flex items-center gap-2 mb-3">
328
+ <Link2 className="h-4 w-4 text-emerald-400" />
329
+ Linked Issues
330
+ </h3>
331
+ {loadingCrossRepo ? (
332
+ <div className="text-xs text-gray-500">Loading...</div>
333
+ ) : crossRepoLinks.length === 0 ? (
334
+ <div className="text-xs text-gray-500">No linked issues</div>
335
+ ) : (
260
336
<div className="space-y-2">
261
- {issue.assignees.map(assignee => (
262
- <div key={assignee.username} className="flex items-center gap-2">
263
- <div className="h-6 w-6 rounded-full bg-gradient-to-br from-purple-400 to-pink-500 flex items-center justify-center text-xs font-bold text-white">
264
- {assignee.username[0].toUpperCase()}
265
- </div>
266
- <span className="text-sm text-gray-300">{assignee.username}</span>
267
- </div>
337
+ {crossRepoLinks.map((link) => (
338
+ <a
339
+ key={link.id}
340
+ href={`/${link.repository.owner}/${link.repository.name}/issues/${link.issue.number}`}
341
+ className="flex items-start justify-between gap-3 rounded-lg border border-white/5 bg-white/5 px-3 py-2 text-xs text-gray-300 hover:bg-white/10"
342
+ >
343
+ <span>
344
+ {link.repository.owner}/{link.repository.name}#{link.issue.number} {link.issue.title}
345
+ </span>
346
+ <span className="text-[10px] uppercase text-gray-500">
347
+ {link.linkType}
348
+ </span>
349
+ </a>
268
350
))}
269
351
</div>
270
- ) : (
271
- <p className="text-sm text-gray-500">No one assigned</p>
352
+ )}
353
+ {canLink && (
354
+ <div className="mt-3 space-y-2">
355
+ <div className="flex items-center gap-2">
356
+ <input
357
+ value={crossRepoTarget}
358
+ onChange={(e) => setCrossRepoTarget(e.target.value)}
359
+ placeholder="owner/repo#123"
360
+ className="flex-1 rounded-md border border-white/10 bg-white/5 px-2 py-1 text-xs text-white"
361
+ />
362
+ <select
363
+ value={crossRepoType}
364
+ onChange={(e) => setCrossRepoType(e.target.value)}
365
+ className="rounded-md border border-white/10 bg-white/5 px-2 py-1 text-xs text-white"
366
+ >
367
+ <option value="relates">Relates</option>
368
+ <option value="blocks">Blocks</option>
369
+ <option value="blocked_by">Blocked by</option>
370
+ <option value="duplicates">Duplicates</option>
371
+ </select>
372
+ </div>
373
+ <button
374
+ onClick={handleCrossRepoLink}
375
+ className="w-full rounded-md border border-emerald-500/30 bg-emerald-500/10 px-2 py-1 text-xs text-emerald-300 hover:bg-emerald-500/20"
376
+ >
377
+ Link issue
378
+ </button>
379
+ {crossRepoError && (
380
+ <div className="text-xs text-red-400">{crossRepoError}</div>
381
+ )}
382
+ </div>
272
383
)}
273
384
</div>
274
385
</div>
275
-
276
- {/* Labels */}
277
386
<div className="relative">
278
- <div className="absolute -inset-[1px] bg-gradient-to-b from-purple-500/10 to-transparent rounded-xl opacity-50" />
387
+ <div className="absolute -inset-[1px] bg-gradient-to-b from-cyan-500/10 to-transparent rounded-xl opacity-50" />
279
388
<div className="relative rounded-xl border border-white/10 bg-[#0d1117]/60 p-4">
280
- <div className="flex items-center justify-between mb-3">
281
- <h3 className="text-sm font-semibold text-white flex items-center gap-2">
282
- <Tag className="h-4 w-4 text-purple-400" />
283
- Labels
284
- </h3>
285
- <button className="text-xs text-gray-500 hover:text-purple-400 transition-colors">
286
- Edit
287
- </button>
288
- </div>
289
- {issue.labels && issue.labels.length > 0 ? (
290
- <div className="flex flex-wrap gap-2">
291
- {issue.labels.map(label => (
292
- <span
293
- key={label.name}
294
- className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium"
295
- style={{
296
- backgroundColor: `${label.color}20`,
297
- color: label.color,
298
- border: `1px solid ${label.color}40`
299
- }}
389
+ <h3 className="text-sm font-semibold text-white flex items-center gap-2 mb-3">
390
+ <GitBranch className="h-4 w-4 text-cyan-400" />
391
+ Linked Pull Requests
392
+ </h3>
393
+ {loadingLinks ? (
394
+ <div className="text-xs text-gray-500">Loading...</div>
395
+ ) : linkedPRs.length === 0 ? (
396
+ <div className="text-xs text-gray-500">No linked pull requests</div>
397
+ ) : (
398
+ <div className="space-y-2">
399
+ {linkedPRs.map((link) => (
400
+ <a
401
+ key={link.id}
402
+ href={`/${repoOwner}/${repoName}/pulls/${link.pullRequest.number}`}
403
+ className="flex items-start justify-between gap-3 rounded-lg border border-white/5 bg-white/5 px-3 py-2 text-xs text-gray-300 hover:bg-white/10"
300
404
>
301
- {label.name}
302
- </span>
405
+ <span>
406
+ #{link.pullRequest.number} {link.pullRequest.title}
407
+ </span>
408
+ <span className="text-[10px] uppercase text-gray-500">
409
+ {link.linkType}
410
+ </span>
411
+ </a>
303
412
))}
304
413
</div>
305
- ) : (
306
- <p className="text-sm text-gray-500">None yet</p>
307
414
)}
308
415
</div>
309
416
</div>
417
+ {/* Sub-tasks / Child Issues */}
418
+ {(issue.type === 'epic' || (issue.children && issue.children.length > 0)) && (
419
+ <div className="relative">
420
+ <div className="absolute -inset-[1px] bg-gradient-to-b from-purple-500/10 to-transparent rounded-xl opacity-50" />
421
+ <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/60 p-4">
422
+ <div className="flex items-center justify-between mb-3">
423
+ <h3 className="text-sm font-semibold text-white flex items-center gap-2">
424
+ <ListTodo className="h-4 w-4 text-purple-400" />
425
+ {issue.type === 'epic' ? 'Child Issues' : 'Sub-tasks'}
426
+ </h3>
427
+ {issue.children && issue.children.length > 0 && (
428
+ <span className="text-xs bg-white/10 px-2 py-0.5 rounded-full text-gray-300">
429
+ {issue.children.filter(c => c.state === 'closed').length} / {issue.children.length}
430
+ </span>
431
+ )}
432
+ </div>
310
433
311
- {/* Development */}
312
- <div className="relative">
313
- <div className="absolute -inset-[1px] bg-gradient-to-b from-orange-500/10 to-transparent rounded-xl opacity-50" />
314
- <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/60 p-4">
315
- <div className="flex items-center justify-between mb-3">
316
- <h3 className="text-sm font-semibold text-white flex items-center gap-2">
317
- <GitBranch className="h-4 w-4 text-orange-400" />
318
- Development
319
- </h3>
320
- </div>
321
- <p className="text-sm text-gray-500">
322
- No branches or pull requests linked
323
- </p>
324
- </div>
325
- </div>
434
+ {/* Progress Bar */}
435
+ {issue.children && issue.children.length > 0 && (
436
+ <div className="h-1.5 w-full bg-white/10 rounded-full mb-3 overflow-hidden">
437
+ <div
438
+ className="h-full bg-purple-500 transition-all duration-500"
439
+ style={{
440
+ width: `${(issue.children.filter(c => c.state === 'closed').length / issue.children.length) * 100}%`
441
+ }}
442
+ />
443
+ </div>
444
+ )}
326
445
327
- {/* Timeline Info */}
328
- <div className="relative">
329
- <div className="absolute -inset-[1px] bg-gradient-to-b from-green-500/10 to-transparent rounded-xl opacity-50" />
330
- <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/60 p-4">
331
- <h3 className="text-sm font-semibold text-white flex items-center gap-2 mb-3">
332
- <Clock className="h-4 w-4 text-green-400" />
333
- Timeline
334
- </h3>
335
- <div className="space-y-2 text-sm text-gray-500">
336
- <div className="flex items-center gap-2">
337
- <Calendar className="h-3.5 w-3.5" />
338
- <span>Created {timeAgo(issue.createdAt)}</span>
446
+ <div className="space-y-1">
447
+ {issue.children && issue.children.map(child => (
448
+ <a
449
+ key={child.number}
450
+ href={`/${repoOwner}/${repoName}/issues/${child.number}`}
451
+ className="flex items-start gap-2 p-2 rounded-lg hover:bg-white/5 transition-colors group"
452
+ >
453
+ <div className="mt-0.5">
454
+ {child.state === "open" ? (
455
+ <AlertCircle className="h-3.5 w-3.5 text-green-500" />
456
+ ) : (
457
+ <CheckCircle2 className="h-3.5 w-3.5 text-purple-500" />
458
+ )}
459
+ </div>
460
+ <div className="text-sm min-w-0">
461
+ <div className="text-gray-300 group-hover:text-cyan-400 truncate transition-colors">
462
+ {child.title}
463
+ </div>
464
+ </div>
465
+ </a>
466
+ ))}
467
+
468
+ {issue.type === 'epic' && (
469
+ <a
470
+ href={`/${repoOwner}/${repoName}/issues/new?parent=${issue.number}`}
471
+ className="flex items-center gap-2 p-2 rounded-lg hover:bg-white/5 text-sm text-gray-400 hover:text-cyan-400 transition-colors mt-2 border border-dashed border-white/10 hover:border-cyan-500/30 justify-center"
472
+ >
473
+ <ListTodo className="h-3.5 w-3.5" />
474
+ Add sub-task
475
+ </a>
476
+ )}
339
477
</div>
340
478
</div>
341
479
</div>
342
- </div>
480
+ )}
343
481
</motion.div>
344
482
</div>
345
483
</div>
346
484
▼src/components/issues/IssuesList.tsx
+181-86
11
11
ArrowUpDown,
12
12
Plus,
13
13
Sparkles,
14
- Filter
14
+ Filter,
15
+ Layers,
16
+ CheckSquare,
17
+ FileText,
18
+ ChevronDown,
19
+ ChevronRight,
20
+ LayoutList
15
21
} from "lucide-react";
16
22
import { useState } from "react";
17
23
19
25
id: string;
20
26
number: number;
21
27
title: string;
28
+ type: string;
22
29
state: "open" | "closed";
23
30
createdAt: string;
24
31
commentCount: number;
30
37
name: string;
31
38
color: string;
32
39
}>;
40
+ parent?: {
41
+ number: number;
42
+ title: string;
43
+ state: "open" | "closed";
44
+ };
45
+ children?: Array<{
46
+ number: number;
47
+ title: string;
48
+ state: "open" | "closed";
49
+ }>;
33
50
}
34
51
35
52
interface Props {
62
79
export default function IssuesList({ issues, openCount, closedCount, repoOwner, repoName }: Props) {
63
80
const [filter, setFilter] = useState<"open" | "closed" | "all">("open");
64
81
const [searchQuery, setSearchQuery] = useState("");
82
+ const [groupByEpic, setGroupByEpic] = useState(false);
83
+ const [expandedEpics, setExpandedEpics] = useState<Record<string, boolean>>({});
84
+
85
+ const toggleEpic = (id: string, e: React.MouseEvent) => {
86
+ e.preventDefault();
87
+ e.stopPropagation();
88
+ setExpandedEpics(prev => ({ ...prev, [id]: !prev[id] }));
89
+ };
65
90
66
91
const filteredIssues = issues.filter(issue => {
67
92
const matchesFilter = filter === "all" || issue.state === filter;
68
93
const matchesSearch = issue.title.toLowerCase().includes(searchQuery.toLowerCase());
69
- return matchesFilter && matchesSearch;
94
+
95
+ // If grouping by epic, hide sub-tasks from top level (they appear inside epics)
96
+ // Unless they are orphans (parent not found in current list? actually logic is simpler: hide if has parent)
97
+ // But wait, if I hide if has parent, and the parent is NOT in the list (e.g. parent is closed but filter is open), then the sub-task disappears?
98
+ // Correct. Standard Epic view usually implies viewing the Epic to see children.
99
+ // But if filtering by "Open", we maybe want to see open tasks of a closed Epic?
100
+ // GitHub doesn't really have "Group by Epic" in issue list. Jira does.
101
+ // Let's stick to: If groupByEpic is on, show top-level items only (no parent).
102
+ // If an issue has a parent, it is hidden from top level.
103
+ const isTopLevel = !groupByEpic || !issue.parent;
104
+
105
+ return matchesFilter && matchesSearch && isTopLevel;
70
106
});
71
107
72
108
return (
94
130
95
131
{/* Action Buttons */}
96
132
<div className="flex gap-3">
133
+ <button
134
+ onClick={() => setGroupByEpic(!groupByEpic)}
135
+ className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg border transition-all ${groupByEpic
136
+ ? "bg-purple-500/20 border-purple-500/50 text-purple-300"
137
+ : "border-white/10 bg-white/5 text-gray-300 hover:bg-white/10"
138
+ }`}
139
+ >
140
+ <Layers className="h-4 w-4" />
141
+ <span className="hidden sm:inline">Group by Epic</span>
142
+ </button>
143
+
97
144
<motion.a
98
145
href={`/${repoOwner}/${repoName}/labels`}
99
146
whileHover={{ scale: 1.02 }}
101
148
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/5 text-sm text-gray-300 hover:bg-white/10 hover:border-white/20 transition-all"
102
149
>
103
150
<Tag className="h-4 w-4" />
104
- Labels
151
+ <span className="hidden sm:inline">Labels</span>
105
152
</motion.a>
153
+
106
154
<motion.a
107
155
href={`/${repoOwner}/${repoName}/milestones`}
108
156
whileHover={{ scale: 1.02 }}
110
158
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/5 text-sm text-gray-300 hover:bg-white/10 hover:border-white/20 transition-all"
111
159
>
112
160
<Calendar className="h-4 w-4" />
113
- Milestones
161
+ <span className="hidden sm:inline">Milestones</span>
114
162
</motion.a>
163
+
115
164
<motion.a
116
165
href={`/${repoOwner}/${repoName}/issues/new`}
117
166
whileHover={{ scale: 1.05 }}
119
168
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-gradient-to-r from-green-500 to-emerald-600 text-sm font-medium text-white shadow-lg shadow-green-500/25 hover:shadow-green-500/40 transition-all"
120
169
>
121
170
<Plus className="h-4 w-4" />
122
- New Issue
171
+ <span className="hidden sm:inline">New Issue</span>
123
172
</motion.a>
124
173
</div>
125
174
</motion.div>
141
190
<button
142
191
onClick={() => setFilter("open")}
143
192
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-all ${filter === "open"
144
- ? "bg-green-500/10 text-green-400 border border-green-500/30"
145
- : "text-gray-400 hover:text-white hover:bg-white/5"
193
+ ? "bg-green-500/10 text-green-400 border border-green-500/30"
194
+ : "text-gray-400 hover:text-white hover:bg-white/5"
146
195
}`}
147
196
>
148
197
<AlertCircle className="h-4 w-4" />
151
200
<button
152
201
onClick={() => setFilter("closed")}
153
202
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-all ${filter === "closed"
154
- ? "bg-purple-500/10 text-purple-400 border border-purple-500/30"
155
- : "text-gray-400 hover:text-white hover:bg-white/5"
203
+ ? "bg-purple-500/10 text-purple-400 border border-purple-500/30"
204
+ : "text-gray-400 hover:text-white hover:bg-white/5"
156
205
}`}
157
206
>
158
207
<CheckCircle2 className="h-4 w-4" />
161
210
</div>
162
211
163
212
<div className="flex items-center gap-3 text-sm text-gray-500">
164
- <button className="flex items-center gap-1 hover:text-white transition-colors">
165
- <User className="h-3.5 w-3.5" />
166
- Author
167
- </button>
168
- <button className="flex items-center gap-1 hover:text-white transition-colors">
169
- <Tag className="h-3.5 w-3.5" />
170
- Label
171
- </button>
172
213
<button className="flex items-center gap-1 hover:text-white transition-colors">
173
214
<ArrowUpDown className="h-3.5 w-3.5" />
174
215
Sort
181
222
<AnimatePresence mode="popLayout">
182
223
{filteredIssues.length > 0 ? (
183
224
filteredIssues.map((issue, index) => (
184
- <motion.a
185
- key={issue.id}
186
- href={`/${repoOwner}/${repoName}/issues/${issue.number}`}
187
- initial={{ opacity: 0, x: -20 }}
188
- animate={{ opacity: 1, x: 0 }}
189
- exit={{ opacity: 0, x: 20 }}
190
- transition={{ delay: index * 0.03 }}
191
- className="flex items-start gap-3 p-4 hover:bg-white/[0.02] transition-colors group"
192
- >
193
- {/* Status Icon */}
194
- <div className="mt-0.5">
195
- {issue.state === "open" ? (
196
- <div className="relative">
197
- <AlertCircle className="h-5 w-5 text-green-500" />
198
- <div className="absolute inset-0 bg-green-500 blur-md opacity-0 group-hover:opacity-30 transition-opacity" />
199
- </div>
200
- ) : (
201
- <div className="relative">
202
- <CheckCircle2 className="h-5 w-5 text-purple-500" />
203
- <div className="absolute inset-0 bg-purple-500 blur-md opacity-0 group-hover:opacity-30 transition-opacity" />
204
- </div>
225
+ <div key={issue.id}>
226
+ <motion.a
227
+ href={`/${repoOwner}/${repoName}/issues/${issue.number}`}
228
+ initial={{ opacity: 0, x: -20 }}
229
+ animate={{ opacity: 1, x: 0 }}
230
+ exit={{ opacity: 0, x: 20 }}
231
+ transition={{ delay: index * 0.03 }}
232
+ className="flex items-start gap-3 p-4 hover:bg-white/[0.02] transition-colors group relative"
233
+ >
234
+ {/* Expand/Collapse for Epics */}
235
+ {groupByEpic && issue.type === 'epic' && issue.children && issue.children.length > 0 && (
236
+ <button
237
+ onClick={(e) => toggleEpic(issue.id, e)}
238
+ className="absolute left-1 top-4 p-1 text-gray-500 hover:text-white rounded hover:bg-white/10"
239
+ >
240
+ {expandedEpics[issue.id] ? (
241
+ <ChevronDown className="h-4 w-4" />
242
+ ) : (
243
+ <ChevronRight className="h-4 w-4" />
244
+ )}
245
+ </button>
205
246
)}
206
- </div>
207
247
208
- {/* Content */}
209
- <div className="flex-1 min-w-0">
210
- <div className="flex items-center gap-2 mb-1">
211
- <span className="font-semibold text-white group-hover:text-cyan-400 transition-colors truncate">
212
- {issue.title}
213
- </span>
214
- {/* Labels */}
215
- {issue.labels?.map(label => (
216
- <span
217
- key={label.name}
218
- className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium"
219
- style={{
220
- backgroundColor: `${label.color}20`,
221
- color: label.color,
222
- border: `1px solid ${label.color}40`
223
- }}
224
- >
225
- {label.name}
226
- </span>
227
- ))}
248
+ {/* Status Icon */}
249
+ <div className={`mt-0.5 ${groupByEpic && issue.type === 'epic' ? 'ml-6' : ''}`}>
250
+ {issue.state === "open" ? (
251
+ <div className="relative">
252
+ <AlertCircle className="h-5 w-5 text-green-500" />
253
+ <div className="absolute inset-0 bg-green-500 blur-md opacity-0 group-hover:opacity-30 transition-opacity" />
254
+ </div>
255
+ ) : (
256
+ <div className="relative">
257
+ <CheckCircle2 className="h-5 w-5 text-purple-500" />
258
+ <div className="absolute inset-0 bg-purple-500 blur-md opacity-0 group-hover:opacity-30 transition-opacity" />
259
+ </div>
260
+ )}
228
261
</div>
229
- <div className="flex items-center gap-2 text-xs text-gray-500">
230
- <span>#{issue.number}</span>
231
- <span>•</span>
232
- <span>opened {timeAgo(issue.createdAt)}</span>
233
- <span>•</span>
234
- <span className="flex items-center gap-1">
235
- {issue.author.avatarUrl ? (
236
- <img
237
- src={issue.author.avatarUrl}
238
- alt=""
239
- className="h-4 w-4 rounded-full"
240
- />
241
- ) : (
242
- <div className="h-4 w-4 rounded-full bg-gradient-to-br from-cyan-400 to-blue-500 flex items-center justify-center text-[8px] text-white font-bold">
243
- {issue.author.username[0].toUpperCase()}
244
- </div>
245
- )}
246
- <span className="hover:text-cyan-400 transition-colors">
247
- {issue.author.username}
262
+
263
+ {/* Type Icon */}
264
+ <div className="mt-0.5 text-gray-500">
265
+ {issue.type === "epic" ? (
266
+ <Layers className="h-5 w-5 text-purple-400" />
267
+ ) : issue.type === "task" ? (
268
+ <CheckSquare className="h-5 w-5 text-blue-400" />
269
+ ) : (
270
+ <div />
271
+ )}
272
+ </div>
273
+
274
+ {/* Content */}
275
+ <div className="flex-1 min-w-0">
276
+ <div className="flex items-center gap-2 mb-1">
277
+ <span className="font-semibold text-white group-hover:text-cyan-400 transition-colors truncate">
278
+ {issue.title}
279
+ </span>
280
+ {/* Labels */}
281
+ {issue.labels?.map(label => (
282
+ <span
283
+ key={label.name}
284
+ className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium"
285
+ style={{
286
+ backgroundColor: `${label.color}20`,
287
+ color: label.color,
288
+ border: `1px solid ${label.color}40`
289
+ }}
290
+ >
291
+ {label.name}
292
+ </span>
293
+ ))}
294
+ </div>
295
+ <div className="flex items-center gap-2 text-xs text-gray-500">
296
+ <span>#{issue.number}</span>
297
+ <span>•</span>
298
+ <span>opened {timeAgo(issue.createdAt)}</span>
299
+ <span>•</span>
300
+ <span className="flex items-center gap-1">
301
+ {issue.author.avatarUrl ? (
302
+ <img
303
+ src={issue.author.avatarUrl}
304
+ alt=""
305
+ className="h-4 w-4 rounded-full"
306
+ />
307
+ ) : (
308
+ <div className="h-4 w-4 rounded-full bg-gradient-to-br from-cyan-400 to-blue-500 flex items-center justify-center text-[8px] text-white font-bold">
309
+ {issue.author.username[0].toUpperCase()}
310
+ </div>
311
+ )}
312
+ <span className="hover:text-cyan-400 transition-colors">
313
+ {issue.author.username}
314
+ </span>
248
315
</span>
249
- </span>
316
+ </div>
250
317
</div>
251
- </div>
252
318
253
- {/* Comment Count */}
254
- {issue.commentCount > 0 && (
255
- <div className="flex items-center gap-1 text-xs text-gray-500 group-hover:text-gray-400 transition-colors">
256
- <MessageSquare className="h-4 w-4" />
257
- {issue.commentCount}
319
+ {/* Comment Count */}
320
+ {issue.commentCount > 0 && (
321
+ <div className="flex items-center gap-1 text-xs text-gray-500 group-hover:text-gray-400 transition-colors">
322
+ <MessageSquare className="h-4 w-4" />
323
+ {issue.commentCount}
324
+ </div>
325
+ )}
326
+ </motion.a>
327
+
328
+ {/* Render Children (Sub-tasks) */}
329
+ {groupByEpic && issue.type === 'epic' && expandedEpics[issue.id] && issue.children && (
330
+ <div className="bg-white/[0.01] border-t border-white/5">
331
+ {issue.children.map(child => (
332
+ <a
333
+ key={child.number}
334
+ href={`/${repoOwner}/${repoName}/issues/${child.number}`}
335
+ className="flex items-center gap-3 py-3 pr-4 pl-16 hover:bg-white/[0.02] transition-colors border-b last:border-0 border-white/5 group/child"
336
+ >
337
+ <div className="mt-0.5">
338
+ {child.state === "open" ? (
339
+ <AlertCircle className="h-4 w-4 text-green-500" />
340
+ ) : (
341
+ <CheckCircle2 className="h-4 w-4 text-purple-500" />
342
+ )}
343
+ </div>
344
+ <div className="mt-0.5 text-blue-400">
345
+ <CheckSquare className="h-4 w-4" />
346
+ </div>
347
+ <span className="text-sm text-gray-300 group-hover/child:text-cyan-400 transition-colors truncate">
348
+ {child.title}
349
+ </span>
350
+ <span className="text-xs text-gray-600 ml-auto">#{child.number}</span>
351
+ </a>
352
+ ))}
258
353
</div>
259
354
)}
260
- </motion.a>
355
+ </div>
261
356
))
262
357
) : (
263
358
<motion.div
264
359
▼src/components/kanban/Card.tsx
+82-0
1
+
2
+import React, { useState } from "react";
3
+import { useSortable } from "@dnd-kit/sortable";
4
+import { CSS } from "@dnd-kit/utilities";
5
+import { Trash2 } from "lucide-react";
6
+
7
+export type Card = {
8
+ id: string;
9
+ columnId: string;
10
+ contentId?: string | null;
11
+ contentType?: string | null;
12
+ note?: string | null;
13
+ order: number;
14
+};
15
+
16
+interface Props {
17
+ card: Card;
18
+ deleteCard?: (id: string) => void;
19
+}
20
+
21
+export const KanbanCard: React.FC<Props> = ({ card, deleteCard }) => {
22
+ const {
23
+ setNodeRef,
24
+ attributes,
25
+ listeners,
26
+ transform,
27
+ transition,
28
+ isDragging,
29
+ } = useSortable({
30
+ id: card.id,
31
+ data: {
32
+ type: "Card",
33
+ card,
34
+ },
35
+ });
36
+
37
+ const style = {
38
+ transition,
39
+ transform: CSS.Transform.toString(transform),
40
+ };
41
+
42
+ if (isDragging) {
43
+ return (
44
+ <div
45
+ ref={setNodeRef}
46
+ style={style}
47
+ className="opacity-30 bg-mainBackgroundColor p-2.5 h-[100px] min-h-[100px] items-center flex text-left rounded-xl border-2 border-rose-500 cursor-grab relative"
48
+ />
49
+ );
50
+ }
51
+
52
+ return (
53
+ <div
54
+ ref={setNodeRef}
55
+ style={style}
56
+ {...attributes}
57
+ {...listeners}
58
+ className="bg-[#21262d] p-2.5 h-[100px] min-h-[100px] items-start flex flex-col justify-center text-left rounded-xl hover:ring-2 hover:ring-inset hover:ring-cyan-500 cursor-grab relative border border-gray-700 group"
59
+ >
60
+ <div className="flex justify-between items-start w-full gap-2">
61
+ <p className="whitespace-pre-wrap text-gray-200 text-sm flex-1">{card.note || "Content"}</p>
62
+ {deleteCard && (
63
+ <button
64
+ onMouseDown={(e) => {
65
+ e.stopPropagation(); // Prevent drag start
66
+ }}
67
+ onClick={(e) => {
68
+ e.stopPropagation();
69
+ deleteCard(card.id);
70
+ }}
71
+ className="opacity-0 group-hover:opacity-100 p-1 text-gray-500 hover:text-red-400 transition-opacity rounded"
72
+ >
73
+ <Trash2 className="w-3 h-3" />
74
+ </button>
75
+ )}
76
+ </div>
77
+ {card.contentId && (
78
+ <span className="text-xs text-blue-400 mt-2">Linked to {card.contentType}</span>
79
+ )}
80
+ </div>
81
+ );
82
+};
0
83
▼src/components/kanban/Column.tsx
+123-0
1
+
2
+import React, { useMemo, useState } from "react";
3
+import { SortableContext, useSortable } from "@dnd-kit/sortable";
4
+import { CSS } from "@dnd-kit/utilities";
5
+import { type Card, KanbanCard } from "./Card";
6
+import { Plus } from "lucide-react";
7
+
8
+export type Column = {
9
+ id: string;
10
+ name: string;
11
+ order: number;
12
+}; // & cards: Card[] in parent
13
+
14
+interface Props {
15
+ column: Column & { cards: Card[] };
16
+ createCard: (columnId: string, note: string) => void;
17
+ deleteCard: (cardId: string) => void;
18
+}
19
+
20
+export const KanbanColumn: React.FC<Props> = ({ column, createCard, deleteCard }) => {
21
+ const {
22
+ setNodeRef,
23
+ attributes,
24
+ listeners,
25
+ transform,
26
+ transition,
27
+ isDragging,
28
+ } = useSortable({
29
+ id: column.id,
30
+ data: {
31
+ type: "Column",
32
+ column,
33
+ },
34
+ });
35
+
36
+ const [isCreating, setIsCreating] = useState(false);
37
+ const [newCardNote, setNewCardNote] = useState("");
38
+
39
+ const handleCreateCard = () => {
40
+ if (newCardNote.trim()) {
41
+ createCard(column.id, newCardNote);
42
+ setNewCardNote("");
43
+ setIsCreating(false);
44
+ }
45
+ };
46
+
47
+ const style = {
48
+ transition,
49
+ transform: CSS.Transform.toString(transform),
50
+ };
51
+
52
+ const cardIds = useMemo(() => {
53
+ return column.cards.map((card) => card.id);
54
+ }, [column.cards]);
55
+
56
+ if (isDragging) {
57
+ return (
58
+ <div
59
+ ref={setNodeRef}
60
+ style={style}
61
+ className="bg-columnBackgroundColor opacity-40 border-2 border-pink-500 w-[350px] h-[500px] max-h-[500px] rounded-md flex flex-col"
62
+ ></div>
63
+ );
64
+ }
65
+
66
+ return (
67
+ <div
68
+ ref={setNodeRef}
69
+ style={style}
70
+ className="bg-[#161b22] w-[350px] h-[500px] max-h-[500px] rounded-md flex flex-col border border-gray-800"
71
+ >
72
+ <div
73
+ {...attributes}
74
+ {...listeners}
75
+ onClick={() => { }}
76
+ className="bg-[#0d1117] text-md h-[60px] cursor-grab rounded-md rounded-b-none p-3 font-bold border-b border-gray-800 flex items-center justify-between"
77
+ >
78
+ <div className="flex gap-2 text-gray-200">
79
+ {column.name}
80
+ <span className="flex justify-center items-center bg-gray-800 px-2 py-1 text-sm rounded-full text-gray-400">
81
+ {column.cards.length}
82
+ </span>
83
+ </div>
84
+ </div>
85
+
86
+ <div className="flex flex-grow flex-col gap-4 p-2 overflow-x-hidden overflow-y-auto">
87
+ <SortableContext items={cardIds}>
88
+ {column.cards.map((card) => (
89
+ <KanbanCard key={card.id} card={card} deleteCard={deleteCard} />
90
+ ))}
91
+ </SortableContext>
92
+ </div>
93
+
94
+ <div className="p-2 border-t border-gray-800">
95
+ {!isCreating ? (
96
+ <button
97
+ onClick={() => setIsCreating(true)}
98
+ className="flex gap-2 items-center border-columnBackgroundColor border-2 rounded-md p-4 border-x-columnBackgroundColor hover:bg-mainBackgroundColor hover:text-rose-500 active:bg-black w-full"
99
+ >
100
+ <Plus />
101
+ Add task
102
+ </button>
103
+ ) : (
104
+ <div className="flex gap-2 items-center p-2">
105
+ <input
106
+ autoFocus
107
+ className="bg-[#0d1117] border border-gray-700 rounded p-2 w-full text-white"
108
+ placeholder="Card content..."
109
+ value={newCardNote}
110
+ onChange={(e) => setNewCardNote(e.target.value)}
111
+ onKeyDown={(e) => {
112
+ if (e.key === "Enter") handleCreateCard();
113
+ if (e.key === "Escape") setIsCreating(false);
114
+ }}
115
+ />
116
+ <button onClick={handleCreateCard} className="text-green-400 font-bold p-2">Add</button>
117
+ <button onClick={() => setIsCreating(false)} className="text-gray-500 p-2">X</button>
118
+ </div>
119
+ )}
120
+ </div>
121
+ </div>
122
+ );
123
+};
0
124
▼src/components/kanban/KanbanBoard.tsx
+345-0
1
+
2
+import React, { useMemo, useState } from "react";
3
+import {
4
+ DndContext,
5
+ closestCenter,
6
+ KeyboardSensor,
7
+ PointerSensor,
8
+ useSensor,
9
+ useSensors,
10
+ DragOverlay,
11
+ defaultDropAnimationSideEffects,
12
+ type DragStartEvent,
13
+ type DragOverEvent,
14
+ type DragEndEvent,
15
+} from "@dnd-kit/core";
16
+import {
17
+ SortableContext,
18
+ arrayMove,
19
+ sortableKeyboardCoordinates,
20
+ horizontalListSortingStrategy,
21
+} from "@dnd-kit/sortable";
22
+import { KanbanColumn, type Column as ColumnType } from "./Column";
23
+import { KanbanCard, type Card as CardType } from "./Card";
24
+import { createPortal } from "react-dom";
25
+import { Plus } from "lucide-react";
26
+import { toast } from "sonner";
27
+
28
+interface Props {
29
+ project: any;
30
+ initialColumns: (ColumnType & { cards: CardType[] })[];
31
+}
32
+
33
+export const KanbanBoard: React.FC<Props> = ({ project, initialColumns }) => {
34
+ const [columns, setColumns] = useState(initialColumns);
35
+ const [activeColumn, setActiveColumn] = useState<(ColumnType & { cards: CardType[] }) | null>(null);
36
+ const [activeCard, setActiveCard] = useState<CardType | null>(null);
37
+
38
+ const sensors = useSensors(
39
+ useSensor(PointerSensor, {
40
+ activationConstraint: {
41
+ distance: 3, // 3px movement required to start drag
42
+ },
43
+ }),
44
+ useSensor(KeyboardSensor, {
45
+ coordinateGetter: sortableKeyboardCoordinates,
46
+ })
47
+ );
48
+
49
+ const columnIds = useMemo(() => columns.map((col) => col.id), [columns]);
50
+
51
+ function onDragStart(event: DragStartEvent) {
52
+ if (event.active.data.current?.type === "Column") {
53
+ setActiveColumn(event.active.data.current.column as (ColumnType & { cards: CardType[] }));
54
+ return;
55
+ }
56
+
57
+ if (event.active.data.current?.type === "Card") {
58
+ setActiveCard(event.active.data.current.card);
59
+ return;
60
+ }
61
+ }
62
+
63
+
64
+ function onDragEnd(event: DragEndEvent) {
65
+ setActiveColumn(null);
66
+ setActiveCard(null);
67
+
68
+ const { active, over } = event;
69
+ if (!over) return;
70
+
71
+ const activeId = active.id;
72
+ const overId = over.id;
73
+
74
+ if (activeId === overId) return;
75
+
76
+ const isActiveColumn = active.data.current?.type === "Column";
77
+ if (isActiveColumn) {
78
+ setColumns((columns) => {
79
+ const activeIndex = columns.findIndex((col) => col.id === activeId);
80
+ const overIndex = columns.findIndex((col) => col.id === overId);
81
+
82
+ const newCols = arrayMove(columns, activeIndex, overIndex);
83
+
84
+ // Save new order to backend
85
+ updateOrder("column", newCols.map((col, idx) => ({ id: col.id, order: idx })));
86
+
87
+ return newCols;
88
+ });
89
+ }
90
+
91
+ // Handle Card Drop Persistence
92
+ const isActiveCard = active.data.current?.type === "Card";
93
+ if (isActiveCard) {
94
+ // We need to find the column of the active card in the *current* state (which was updated by onDragOver)
95
+ // But onDragEnd acts on the state.
96
+ // Better strategy: We can assume setColumns in onDragOver updated the local state correctly.
97
+ // We just need to trigger the save.
98
+
99
+ // Find the column containing the moved card
100
+ const activeColumn = columns.find(col => col.cards.some(c => c.id === activeId));
101
+ if (activeColumn) {
102
+ // Save new order for this column
103
+ // Note: If we moved between columns, we theoretically should save BOTH columns,
104
+ // but our API 'move' endpoint might be simpler if we send the full list for the TARGET column.
105
+ // Or if we send just one card move.
106
+
107
+ // Our move API "card" type expects: { items: [...], columnId: ... }
108
+ // So we send the full list of the target column.
109
+
110
+ updateOrder("card", activeColumn.cards.map((c, i) => ({ id: c.id, order: i, columnId: activeColumn.id })), activeColumn.id);
111
+ }
112
+ }
113
+ }
114
+
115
+ function onDragOver(event: DragOverEvent) {
116
+ const { active, over } = event;
117
+ if (!over) return;
118
+
119
+ const activeId = active.id;
120
+ const overId = over.id;
121
+
122
+ if (activeId === overId) return;
123
+
124
+ const isActiveCard = active.data.current?.type === "Card";
125
+ const isOverCard = over.data.current?.type === "Card";
126
+ const isOverColumn = over.data.current?.type === "Column";
127
+
128
+ if (!isActiveCard) return;
129
+
130
+ // Moving a card over another card
131
+ if (isActiveCard && isOverCard) {
132
+ setColumns((columns) => {
133
+ const activeColumnIndex = columns.findIndex((col) =>
134
+ col.cards.some((card) => card.id === activeId)
135
+ );
136
+ const overColumnIndex = columns.findIndex((col) =>
137
+ col.cards.some((card) => card.id === overId)
138
+ );
139
+
140
+ if (activeColumnIndex === -1 || overColumnIndex === -1) return columns;
141
+
142
+ const activeColumn = columns[activeColumnIndex];
143
+ const overColumn = columns[overColumnIndex];
144
+
145
+ const activeCardIndex = activeColumn.cards.findIndex(
146
+ (card) => card.id === activeId
147
+ );
148
+ const overCardIndex = overColumn.cards.findIndex(
149
+ (card) => card.id === overId
150
+ );
151
+
152
+ if (activeColumnIndex === overColumnIndex) {
153
+ // Same column reorder
154
+ const newCards = arrayMove(activeColumn.cards, activeCardIndex, overCardIndex);
155
+ const newColumns = [...columns];
156
+ newColumns[activeColumnIndex] = { ...activeColumn, cards: newCards };
157
+
158
+ // We usually wait for DragEnd to sync, but for visual we simplify updates here.
159
+ return newColumns;
160
+ }
161
+
162
+ // Different column
163
+ const newActiveCards = [...activeColumn.cards];
164
+ const [movedCard] = newActiveCards.splice(activeCardIndex, 1);
165
+
166
+ const newOverCards = [...overColumn.cards];
167
+ // Insert before or after based on direction could be tricky, for simplicity insert at over index
168
+ newOverCards.splice(overCardIndex, 0, movedCard);
169
+
170
+ const newColumns = [...columns];
171
+ newColumns[activeColumnIndex] = { ...activeColumn, cards: newActiveCards };
172
+ newColumns[overColumnIndex] = { ...overColumn, cards: newOverCards };
173
+
174
+ return newColumns;
175
+ });
176
+ }
177
+
178
+ // Moving a card over a column (empty or not)
179
+ if (isActiveCard && isOverColumn) {
180
+ setColumns((columns) => {
181
+ const activeColumnIndex = columns.findIndex((col) =>
182
+ col.cards.some((card) => card.id === activeId)
183
+ );
184
+ const overColumnIndex = columns.findIndex((col) => col.id === overId);
185
+
186
+ if (activeColumnIndex === overColumnIndex) return columns;
187
+ if (activeColumnIndex === -1 || overColumnIndex === -1) return columns;
188
+
189
+ const activeColumn = columns[activeColumnIndex];
190
+ const overColumn = columns[overColumnIndex];
191
+
192
+ const activeCardIndex = activeColumn.cards.findIndex((card) => card.id === activeId);
193
+ const newActiveCards = [...activeColumn.cards];
194
+ const [movedCard] = newActiveCards.splice(activeCardIndex, 1);
195
+
196
+ const newOverCards = [...overColumn.cards];
197
+ newOverCards.push(movedCard); // Add to end
198
+
199
+ const newColumns = [...columns];
200
+ newColumns[activeColumnIndex] = { ...activeColumn, cards: newActiveCards };
201
+ newColumns[overColumnIndex] = { ...overColumn, cards: newOverCards };
202
+
203
+ return newColumns;
204
+ });
205
+ }
206
+ }
207
+
208
+ // Finalize card drop (save to backend)
209
+ // This is simplified. Proper DnD needs careful handling of DragOver vs DragEnd source of truth.
210
+ // For this demo, we'll implement a debounced save in DragOver or use DragEnd for final sync?
211
+ // Actually, easiest is to capture the final state in DragEnd for cross-column too?
212
+ // DragOver mutates state for visual feedback. DragEnd confirms it.
213
+ // But DragEnd only fires if we dropped.
214
+ // The logic in onDragEnd for cards:
215
+
216
+ /*
217
+ function onDragEnd(event: DragEndEvent) {
218
+ ...
219
+ if (isActiveCard) {
220
+ // Find where it ended up
221
+ const activeColumn = columns.find(col => col.cards.some(c => c.id === activeId));
222
+ if (activeColumn) {
223
+ const cardIndex = activeColumn.cards.findIndex(c => c.id === activeId);
224
+ // Sync with backend
225
+ }
226
+ }
227
+ }
228
+ */
229
+
230
+ async function updateOrder(type: "column" | "card", items: any[], columnId?: string) {
231
+ try {
232
+ await fetch(`/api/projects/${project.id}/move`, {
233
+ method: "PUT",
234
+ headers: { "Content-Type": "application/json" },
235
+ body: JSON.stringify({ type, items, columnId })
236
+ });
237
+ } catch (e) {
238
+ console.error("Failed to sync order", e);
239
+ }
240
+ }
241
+
242
+ // Create columns
243
+ async function createColumn() {
244
+ const name = prompt("Column name:");
245
+ if (!name) return;
246
+
247
+ try {
248
+ const res = await fetch(`/api/projects/${project.id}/columns`, {
249
+ method: "POST",
250
+ body: JSON.stringify({ name }),
251
+ headers: { "Content-Type": "application/json" }
252
+ });
253
+ const newCol = await res.json();
254
+ setColumns([...columns, { ...newCol.data, cards: [] }]);
255
+ toast.success("Column created");
256
+ } catch (e) {
257
+ toast.error("Failed to create column");
258
+ }
259
+ }
260
+
261
+ async function createCard(columnId: string, note: string) {
262
+ if (!note) return;
263
+ try {
264
+ const res = await fetch(`/api/projects/${project.id}/cards`, {
265
+ method: "POST",
266
+ body: JSON.stringify({ columnId, note }),
267
+ headers: { "Content-Type": "application/json" }
268
+ });
269
+ const newCard = await res.json();
270
+
271
+ setColumns(cols => cols.map(col => {
272
+ if (col.id === columnId) {
273
+ return { ...col, cards: [newCard.data, ...col.cards] };
274
+ }
275
+ return col;
276
+ }));
277
+ toast.success("Card created");
278
+ } catch (e) {
279
+ toast.error("Failed to create card");
280
+ }
281
+ }
282
+
283
+ async function deleteCard(cardId: string) {
284
+ try {
285
+ await fetch(`/api/projects/${project.id}/cards/${cardId}`, {
286
+ method: "DELETE"
287
+ });
288
+ setColumns(cols => cols.map(col => ({
289
+ ...col,
290
+ cards: col.cards.filter(c => c.id !== cardId)
291
+ })));
292
+ toast.success("Card deleted");
293
+ } catch (e) {
294
+ toast.error("Failed to delete card");
295
+ }
296
+ }
297
+
298
+ return (
299
+ <div
300
+ className="flex h-full w-full overflow-x-auto overflow-y-hidden px-4 md:px-8 pb-4"
301
+ >
302
+ <DndContext
303
+ sensors={sensors}
304
+ collisionDetection={closestCenter}
305
+ onDragStart={onDragStart}
306
+ onDragOver={onDragOver}
307
+ onDragEnd={onDragEnd}
308
+ >
309
+ <div className="flex gap-4">
310
+ <SortableContext items={columnIds} strategy={horizontalListSortingStrategy}>
311
+ {columns.map((col) => (
312
+ <KanbanColumn
313
+ key={col.id}
314
+ column={col}
315
+ createCard={createCard}
316
+ deleteCard={deleteCard}
317
+ />
318
+ ))}
319
+ </SortableContext>
320
+
321
+ <button
322
+ onClick={createColumn}
323
+ className="h-[50px] w-[350px] min-w-[350px] cursor-pointer rounded-lg bg-mainBackgroundColor border-2 border-columnBackgroundColor p-4 ring-rose-500 hover:ring-2 flex gap-2"
324
+ >
325
+ <Plus /> Add Column
326
+ </button>
327
+ </div>
328
+
329
+ {createPortal(
330
+ <DragOverlay>
331
+ {activeColumn && (
332
+ <KanbanColumn
333
+ column={activeColumn}
334
+ createCard={createCard}
335
+ deleteCard={deleteCard}
336
+ />
337
+ )}
338
+ {activeCard && <KanbanCard card={activeCard} />}
339
+ </DragOverlay>,
340
+ document.body
341
+ )}
342
+ </DndContext>
343
+ </div>
344
+ );
345
+};
0
346