From d74f77fd33362c4216851439d35a1a42ca718fd9 Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 01:14:36 +0200 Subject: [PATCH 01/11] =?UTF-8?q?feat(FN-002):=20complete=20Step=201=20?= =?UTF-8?q?=E2=80=94=20Root=20Monorepo=20Tooling=20and=20Repository=20Hygi?= =?UTF-8?q?ene?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 15 ++++ .fusion/config.json | 132 +++++++++++++++++++++++++++++++++++ .fusion/fusion.db | Bin 0 -> 983040 bytes .fusion/memory/2026-05-13.md | 3 + .fusion/memory/DREAMS.md | 3 + .fusion/memory/MEMORY.md | 19 +++++ .gitignore | 50 +++++++++++++ Makefile | 29 ++++++++ package.json | 20 ++++++ pnpm-workspace.yaml | 3 + 10 files changed, 274 insertions(+) create mode 100644 .editorconfig create mode 100644 .fusion/config.json create mode 100644 .fusion/fusion.db create mode 100644 .fusion/memory/2026-05-13.md create mode 100644 .fusion/memory/DREAMS.md create mode 100644 .fusion/memory/MEMORY.md create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 package.json create mode 100644 pnpm-workspace.yaml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7875149 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.py] +indent_size = 4 + +[Makefile] +indent_style = tab diff --git a/.fusion/config.json b/.fusion/config.json new file mode 100644 index 0000000..bc1393a --- /dev/null +++ b/.fusion/config.json @@ -0,0 +1,132 @@ +{ + "settings": { + "globalPause": false, + "enginePaused": false, + "maxConcurrent": 2, + "maxTriageConcurrent": 2, + "globalMaxConcurrent": 4, + "maxWorktrees": 4, + "pollIntervalMs": 15000, + "heartbeatMultiplier": 1, + "groupOverlappingFiles": true, + "overlapIgnorePaths": [], + "autoMerge": true, + "mergeStrategy": "direct", + "directMergeCommitStrategy": "auto", + "requirePrApproval": false, + "pushAfterMerge": false, + "pushRemote": "origin", + "unavailableNodePolicy": "block", + "recycleWorktrees": false, + "executorAllowSiblingBranchRename": false, + "worktreeNaming": "random", + "taskPrefix": "FN", + "includeTaskIdInCommit": true, + "commitAuthorEnabled": true, + "commitAuthorName": "Fusion", + "commitAuthorEmail": "noreply@runfusion.ai", + "modelPresets": [], + "autoSelectModelPreset": false, + "completionDocumentationMode": "off", + "defaultPresetBySize": {}, + "autoResolveConflicts": true, + "smartConflictResolution": true, + "mergerAutostashMaxAgeHours": 24, + "worktreeRebaseBeforeMerge": true, + "worktreeRebaseRemote": "", + "worktreeRebaseLocalBase": true, + "mergeConflictStrategy": "smart-prefer-main", + "mergeStrategyOverlapBehavior": "flip-to-prefer-branch", + "workflowStepTimeoutMs": 360000, + "workflowRevisionForkOnScopeMismatch": true, + "strictScopeEnforcement": false, + "buildRetryCount": 0, + "verificationFixRetries": 3, + "buildTimeoutMs": 300000, + "requirePlanApproval": false, + "agentProvisioning": {}, + "specStalenessEnabled": false, + "specStalenessMaxAgeMs": 21600000, + "taskStuckTimeoutMs": 600000, + "staleHighFanoutBlockerAgeThresholdMs": 7200000, + "aiSessionTtlMs": 604800000, + "aiSessionCleanupIntervalMs": 3600000, + "autoUnpauseEnabled": true, + "autoUnpauseBaseDelayMs": 300000, + "autoUnpauseMaxDelayMs": 3600000, + "maxStuckKills": 6, + "preserveProgressOnStuckRequeue": true, + "maxPostReviewFixes": 1, + "maxSpawnedAgentsPerParent": 5, + "maxSpawnedAgentsGlobal": 20, + "maintenanceIntervalMs": 300000, + "autoArchiveDoneTasksEnabled": true, + "autoArchiveDoneAfterMs": 172800000, + "archiveAgentLogMode": "compact", + "autoUpdatePrStatus": false, + "githubCommentOnDone": false, + "githubTrackingEnabledByDefault": false, + "githubAuthMode": "gh-cli", + "autoBackupEnabled": false, + "autoBackupSchedule": "0 2 * * *", + "autoBackupRetention": 7, + "autoBackupDir": ".fusion/backups", + "memoryBackupEnabled": false, + "memoryBackupSchedule": "0 3 * * *", + "memoryBackupRetention": 14, + "memoryBackupDir": ".fusion/backups/memory", + "memoryBackupScope": "all", + "autoSummarizeTitles": false, + "useAiMergeCommitSummary": true, + "insightExtractionEnabled": false, + "insightExtractionSchedule": "0 2 * * *", + "insightExtractionMinIntervalMs": 86400000, + "taskEvaluationEnabled": false, + "taskEvaluationSchedule": "0 5 * * *", + "taskEvaluationFollowUpPolicy": "off", + "memoryEnabled": true, + "memoryBackendType": "qmd", + "memoryAutoSummarizeEnabled": false, + "memoryAutoSummarizeThresholdChars": 50000, + "memoryAutoSummarizeSchedule": "0 3 * * *", + "memoryDreamsEnabled": false, + "memoryDreamsSchedule": "0 4 * * *", + "runStepsInNewSessions": false, + "maxParallelSteps": 2, + "missionStaleThresholdMs": 600000, + "missionMaxTaskRetries": 3, + "missionHealthCheckIntervalMs": 300000, + "reflectionEnabled": false, + "reflectionIntervalMs": 3600000, + "reflectionAfterTask": true, + "reviewHandoffPolicy": "disabled", + "showQuickChatFAB": false, + "researchSettings": { + "enabled": true, + "enabledSources": { + "webSearch": true, + "pageFetch": true, + "github": false, + "localDocs": true, + "llmSynthesis": true + }, + "limits": { + "maxConcurrentRuns": 3, + "maxSourcesPerRun": 20, + "maxDurationMs": 300000, + "requestTimeoutMs": 30000 + } + }, + "evalSettings": { + "enabled": false, + "intervalMs": 86400000, + "followUpPolicy": "suggest-only", + "retentionDays": 30 + }, + "researchEnabled": true, + "researchMaxConcurrentRuns": 3, + "researchDefaultTimeout": 300000, + "researchMaxSourcesPerRun": 20, + "researchMaxSynthesisRounds": 2 + } +} \ No newline at end of file diff --git a/.fusion/fusion.db b/.fusion/fusion.db new file mode 100644 index 0000000000000000000000000000000000000000..8f15bff3167bcc6209a295b10b76a44f85800676 GIT binary patch literal 983040 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

}2AZ>T=`1^tdUD?ND=J=uHY<-v zEjrs9p8BK=#m1#jc<$oG$MP##nP-5phSE}DEnmp279$qH9`vMs1{KJ)Su~0Jt!BczsQz&lp#>NFO8WsQpmCbOsbR=P5u=;xQEK!}a2lz!97VUk(#8f0H)&{h z+uD;PXJJFw_IJDA;eaYG25rO$y+{84|D>(X`_X;m-7nMHOi)(*ed4r!xb8D)747Yh z{{XFC^6rPXklCV*P1@ct9s8rdu(|H6*SQ-+{=I{FzxLn-lZAGtp_FMj@CWHYtD`$3Mg!0 zJkcJ|^&2(PD`FS4u5>uEf)?sn4p_hTRF_X<(K-ojel=+m(p6CRth{JGQJQ3fjo)@% zDz4@71AXhxRlV(u2QaFHHFOrmL={@h{Z~MzE49l59A?W@_=#=}^C#!5rI%yek2L@43~{_nnjx57)1lJ98N zv0QJG@8l#FEB+KCK~X!J;;4e29mcu#_Bhz5WA#Bh8?-~~s*8rq{-mXMw7T%HSTDGh z;`MH)ovc7e!Ys4)STkY0@bxPqXY?LedN7vP9M?4%^C9CPMd>Gis)b;#Hl^6$gp#FsGSJqbf{=eY7wo{xLbQ- zk%>X{0z-r|<^)bTex7g&85G5Xv1aILoNNHWI&Y%Dij^T84R)+x7hDYXAlNu=$ zsmbfxLOHf|(rUCc#l1%T>UG;ZMGVlcEq9%z7)P`i&Tiaof-H11#~3x8X5~qtI{n34 zw`y;7ZLC6EkPy+#&Mdbi<->_M&Wuu^Q-XjVNR@E1}3& zvHMG*tG8(MawZ&PBzCjL3GE{91m~85LQ5Me>6S_zOp~V&pi?=yrJRx=6;f-o%TDvM z*)@)wfIAw89fkg)mo(j{-%HK|Po*fw(k9sj2Rvny#N49j0v5-Nyj$9+)LxX!JKMXp zYKi{7B{9%hP)eI?)Bt&#{_+&61c{#rQxZ!g{P!l2Fi}jew?-hG1X`MW3)mZ^+uW$oaeYI(^c1O@ z3u=FgQq$ckJCN60MY1N-Ru=6H~v7y3i)}%X0B_t{K zm0UO4V-aa1v-!y7!0z!!E`OwbNXz47neIB}kiKkrYeCN1HgE5XJu}xyq*{=6`AD0l zS_-P5?r>s*c_ofF4V*crt*7;l(}o~J0dQOAkZe)yiec!+cz%loiUuRLR0Z!I`Q!O1@4{Xr}VTHVA`-EEGdb z>T=RaCFd6Mset7!CN6K*x+=07H*Jafu7pgol4)v398K2SRwmP%LsAr)OOGfbg~B&! z#j|S)OPnnMFX5H`X0k$i%_><1-YelZ(L5cf@m(C4y0@UK!g8UwR4k;IbFGzo4!R=e zSa4ch3a%@>BQbZq!j^;MnK3(&c5-^%v$fc@^9SLP|0JT~WalqfB8fQ+@D5m3YjTt| z)-NN%5L4L{TI0A076-=?_G&d3$b62YBgFZGo&b>HHFs|vKKGIKnFM*Y7#zbJ6m-tR zW-wZjjY1~~S-!0)!*Ed|=K!C%5NO=lDihKI4pdH()RBhsl5KC#)P zzl4nBh4yDii;O>x_y7kNtvuy|3tVF&8tJ$jZK&VVal{8$ZQ9Tb^>TDXyr4O~8) zNZ!%cf%gi>qZdQ*Uia+PuZZ(gg`nr?E`gEb>PELKhwPV=>*g(Wv1dk`aF-P%4OMF1k7x+zK*{tFW?T9nmc0Pzo(xNkI zHCkPw{8+RXXq{4P)2I9wI$x%vKa2VK%Oa+X|6V#u)C(VXP8rj2!+d6m1L65R*+Av; z6sWSav_zd69XdW(#}O$bI}lm;$<#^ft=I0T z3f0OP=9dFD3U5HLf_E(xp99bPB(&R6pjQ?j<_j1j8Ce(|+?~9l8fC?JR)K5EC`Z%> zv~bHS8dt>PofAYt)0(>$k|t#oSRSV37Fs_bYl!0Y+52~v{CFjTy?BMr)seIAo`|Lg z!+O-4W?i$d_ZJd|IzEt0(ERAjmwQ`Il>e}0QbCqhL5FWTUH<6x^z|Ey*Gr3g*C-O7 z4talQ>&Az6x5UHsQfihPT@4xo=mf&Gj-o$G=LSep-^I>Ce;7pT|CP$Nqm$y!C;{-uf;{daR%J zG5g7-q9mmx>6?2G>3jMkNs05$PRv>E_t#n(e90lXm)rG;p5Z>6c=$lqX-lOAjRh4(Ey*wy1 zhF7C$JCFX|Q0Omby|LMQ$&&%S%ZOd^er%q9Ux!x54c z8`Y5;t<>rGByT?wSZ7A;5IWykak?5rb*}T+-F=o$bf4131bbqlcL}CAt4}_;hk3{u zO3Kw_ho2TgINII2*2EDHvS1E1zH);$Yn)L1ADZmchozkIWFwWcMjN# z_7Wn*h$%0Vb_j2&a=UXPV#Az>4N-%xAr>OMu(1Vqx6#{rp{j*q#%O0aB4SL)u0Lu_ zeB}!rb7X4QqCGE?d)oS-0ijaBh$n&lU^2IRygqh5r1qs7h6p~T4QQEdTXVsQJ|i!a z2BEo7r#o*vtIIGK+N#BU<|Gx>BN5<8L#<9_AE-?VauZ$P_O_70UI1FTsdpRX(~)4; zx^&uD!~qML^D0hVC9VTn&}g5@Q3|wY+a;@taw2T3Xht1JxZ!S?1-F$+s93GAFnXI| z)IX=RRZJHg@Rq*0;2%{Ylunp*jFYy8$nGbR_9Buq9N)~_jMw7?OlOXQpefFW z79Qy+hE7h67`XI3Tgj#{lc|AXNzOSTUJPUzQ1T)tF)-Nm*%i0G^tQ{rop`rGy@eWY zXI{inh%NYjG=;plLVR|C}sr*Mlqu zp66*%n_`wSOZjZzxs$#~{_Dz_o>O=~MY*nI3UrWduv{k};Uu}fChciBck-}tPvIY3 zlU#z{Ub&mdH{qT|UPfj0fG(CeOA&)}dLZo9r+;bev-pN%V)@dip-k>MKzdZB(Scc` zzE}^04E4jV2ESp?c=)wqed(hw$<(2dNP-|%NnD?Drnw`d9h&ASBoW-?VF0;HET$f{c zC0!J}lxSQvMNY*~PSGK3kvDgZttcPiSq?6V`YJj+W-XvBS*N-&pfs;iFMF|HchqK+ zg2Je3&goD(^c!Vo(`4=sS7QY)g4ud&%}#vMSr6!v!FH_Gj~y-T3`jE3DPpT_Hp#L0 z@529|`Voo#!Jj|X1b%Qg_R9PA>Tj8kKPAmhojZ;2EqnE6-%@(=FW&m$%43g_Q|b3B z#m~`hKibm8gxw>8(&V?ls95HPSBlH70|RJ| z&)@Jo#|Gazqjz{Wcfsd{oy(MmRLAQf|AbBN|E3Spx2mj3iK!8r369grOM{N>BeqQ+&OQAe3ke6bmM zgkrXE(^Yj^XZGYxW?2;OEwQQgFFt$r^oXuf#DO*A>YNPL33l*i)$9A~g6~zQQ;xwivlpWt_B`q}cjQt|xy4|p^Mf&$XTBn-0W^IfX7>dTbVe5NR>8$(+OTv|S=YWa9^BtQ^ zVr5h;qA%POr&ioaC@ht%h7G)qxAH;dIA>jG1kyt~El;7_JrcAc>|7(F(M~s+JiHq1-=XLm zcv??hscfz7Eyax$LR$A($rMB9GDy*Nsm2^O(51T~dHlPLq-|&~E`6jLBvXb?Ah4qt zj1uzY?7QKd$6b@|aS-dwu6;8~$x`mLzC|&{p@Uk!-0!{Kv_eMF`Lq(|p9+0B-(S5& z?yFI;(cQK#_YV>0b~%u2Y2FnGGF15J!T%^(QY< za_t`OZ+8q>$FTL&yU&OzpRG-~j*FPeyBD|@ii?G8E}K5;9&zl2SnCmYy@ywQ+)x7! z-=O~m-+KH1uFLok{r^|#-#?^(|A7Aeefk%FKmY_l00ck)1V8`;KmY_l00ck)1m2wl zE?k(FzRj)r^whNU$OZo8J@3xOpw=J&0w4eaAOHd&00JNY0w4eaAOHeqBf# diff --git a/.fusion/memory/DREAMS.md b/.fusion/memory/DREAMS.md new file mode 100644 index 0000000..1e1f4f5 --- /dev/null +++ b/.fusion/memory/DREAMS.md @@ -0,0 +1,3 @@ +# Memory Dreams + + diff --git a/.fusion/memory/MEMORY.md b/.fusion/memory/MEMORY.md new file mode 100644 index 0000000..3407ce8 --- /dev/null +++ b/.fusion/memory/MEMORY.md @@ -0,0 +1,19 @@ +# Project Memory + + + +## Architecture + + + +## Conventions + + + +## Pitfalls + + + +## Context + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6aea1c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# Dependencies +node_modules/ +.pnpm-store/ + +# Build outputs +dist/ +build/ +*.tsbuildinfo + +# Environment +.env +.env.local +.env.*.local + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +logs/ + +# Testing +coverage/ + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.venv/ +venv/ +ENV/ +env/ +.egg-info/ +dist/ + +# Docker volumes +docker-volumes/ + +# Misc +.cache/ +.temp/ +tmp/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..145d157 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +.PHONY: install lint test typecheck build dev compose-up compose-down clean + +install: + pnpm install + cd apps/api && python3 -m venv .venv && .venv/bin/pip install -e ".[dev]" + +lint: + pnpm lint + +test: + pnpm test + +typecheck: + pnpm typecheck + +build: + pnpm build + +dev: + pnpm dev + +compose-up: + docker compose up --build -d + +compose-down: + docker compose down + +clean: + rm -rf node_modules apps/web/node_modules apps/api/.venv diff --git a/package.json b/package.json new file mode 100644 index 0000000..1393fea --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "headquarter", + "version": "0.0.1", + "private": true, + "description": "Hosted workspace and tool-orchestration platform", + "scripts": { + "lint": "pnpm --filter @headquarter/web lint && pnpm --filter @headquarter/api lint", + "test": "pnpm --filter @headquarter/web test && pnpm --filter @headquarter/api test", + "typecheck": "pnpm --filter @headquarter/web typecheck && pnpm --filter @headquarter/api typecheck", + "build": "pnpm --filter @headquarter/web build && pnpm --filter @headquarter/api build", + "dev": "pnpm --parallel --filter @headquarter/web --filter @headquarter/api dev", + "compose:up": "docker compose up --build -d", + "compose:down": "docker compose down" + }, + "packageManager": "pnpm@11.1.1", + "engines": { + "node": ">=20", + "pnpm": ">=9" + } +} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..e9b0dad --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - 'apps/*' + - 'packages/*' From 79203d2f0f80a346a41d70e5bf294783fcf7846b Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 01:23:01 +0200 Subject: [PATCH 02/11] =?UTF-8?q?feat(FN-002):=20complete=20Step=202=20?= =?UTF-8?q?=E2=80=94=20React=20Frontend=20App=20Skeleton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/.env.example | 3 + apps/web/eslint.config.js | 28 + apps/web/index.html | 13 + apps/web/package.json | 35 + apps/web/src/App.css | 70 + apps/web/src/App.tsx | 30 + apps/web/src/__tests__/App.test.tsx | 26 + apps/web/src/__tests__/setup.ts | 1 + apps/web/src/index.css | 20 + apps/web/src/main.tsx | 10 + apps/web/tsconfig.app.json | 25 + apps/web/tsconfig.json | 7 + apps/web/tsconfig.node.json | 19 + apps/web/vite.config.ts | 22 + pnpm-lock.yaml | 2862 +++++++++++++++++++++++++++ pnpm-workspace.yaml | 4 + 16 files changed, 3175 insertions(+) create mode 100644 apps/web/.env.example create mode 100644 apps/web/eslint.config.js create mode 100644 apps/web/index.html create mode 100644 apps/web/package.json create mode 100644 apps/web/src/App.css create mode 100644 apps/web/src/App.tsx create mode 100644 apps/web/src/__tests__/App.test.tsx create mode 100644 apps/web/src/__tests__/setup.ts create mode 100644 apps/web/src/index.css create mode 100644 apps/web/src/main.tsx create mode 100644 apps/web/tsconfig.app.json create mode 100644 apps/web/tsconfig.json create mode 100644 apps/web/tsconfig.node.json create mode 100644 apps/web/vite.config.ts create mode 100644 pnpm-lock.yaml diff --git a/apps/web/.env.example b/apps/web/.env.example new file mode 100644 index 0000000..d5655aa --- /dev/null +++ b/apps/web/.env.example @@ -0,0 +1,3 @@ +# Frontend runtime configuration +VITE_API_URL=http://localhost:8000 +VITE_APP_NAME=Headquarter diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/apps/web/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/apps/web/index.html b/apps/web/index.html new file mode 100644 index 0000000..c5880da --- /dev/null +++ b/apps/web/index.html @@ -0,0 +1,13 @@ + + + + + + + Headquarter + + +

+ + + diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..15b17f6 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,35 @@ +{ + "name": "@headquarter/web", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "test": "vitest run", + "typecheck": "tsc -b --noEmit" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.4.0", + "@eslint/js": "^9.24.0", + "eslint": "^9.24.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "jsdom": "^26.0.0", + "@types/node": "^22.0.0", + "typescript": "~5.7.0", + "typescript-eslint": "^8.29.0", + "vite": "^6.3.0", + "vitest": "^3.1.0" + } +} diff --git a/apps/web/src/App.css b/apps/web/src/App.css new file mode 100644 index 0000000..ba880d4 --- /dev/null +++ b/apps/web/src/App.css @@ -0,0 +1,70 @@ +.app { + display: flex; + flex-direction: column; + min-height: 100dvh; +} + +.app-header { + padding: 2rem; + text-align: center; + border-bottom: 1px solid rgba(255,255,255,0.08); +} + +.app-header h1 { + margin: 0; + font-size: 2.5rem; + color: var(--accent); +} + +.tagline { + margin: 0.5rem 0 0; + opacity: 0.8; +} + +.app-main { + flex: 1; + padding: 2rem; + display: flex; + justify-content: center; + align-items: flex-start; +} + +.status-card { + background: rgba(255,255,255,0.04); + border: 1px solid rgba(255,255,255,0.08); + border-radius: 0.75rem; + padding: 1.5rem 2rem; + min-width: 280px; +} + +.status-card h2 { + margin: 0 0 1rem; + font-size: 1.25rem; +} + +.status-row { + display: flex; + justify-content: space-between; + padding: 0.5rem 0; + border-bottom: 1px solid rgba(255,255,255,0.06); +} + +.status-row:last-child { + border-bottom: none; +} + +.status-label { + opacity: 0.7; +} + +.status-value { + font-weight: 500; +} + +.app-footer { + padding: 1rem 2rem; + text-align: center; + opacity: 0.5; + font-size: 0.875rem; + border-top: 1px solid rgba(255,255,255,0.08); +} diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx new file mode 100644 index 0000000..93675ef --- /dev/null +++ b/apps/web/src/App.tsx @@ -0,0 +1,30 @@ +import './App.css' + +function App() { + return ( +
+
+

Headquarter

+

Hosted workspace and tool-orchestration platform

+
+
+
+

Platform Status

+
+ API + Checking… +
+
+ Version + 0.0.1 +
+
+
+
+

Scaffolded by FN-002

+
+
+ ) +} + +export default App diff --git a/apps/web/src/__tests__/App.test.tsx b/apps/web/src/__tests__/App.test.tsx new file mode 100644 index 0000000..273c047 --- /dev/null +++ b/apps/web/src/__tests__/App.test.tsx @@ -0,0 +1,26 @@ +import { describe, it, expect } from 'vitest' +import { render, screen } from '@testing-library/react' +import App from '../App' + +describe('App', () => { + it('renders the platform name', () => { + render() + expect(screen.getByText('Headquarter')).toBeInTheDocument() + }) + + it('renders the tagline', () => { + render() + expect(screen.getByText('Hosted workspace and tool-orchestration platform')).toBeInTheDocument() + }) + + it('renders the platform status section', () => { + render() + expect(screen.getByText('Platform Status')).toBeInTheDocument() + expect(screen.getByTestId('api-status')).toHaveTextContent('Checking…') + }) + + it('renders the scaffold footer', () => { + render() + expect(screen.getByText('Scaffolded by FN-002')).toBeInTheDocument() + }) +}) diff --git a/apps/web/src/__tests__/setup.ts b/apps/web/src/__tests__/setup.ts new file mode 100644 index 0000000..a9d0dd3 --- /dev/null +++ b/apps/web/src/__tests__/setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest' diff --git a/apps/web/src/index.css b/apps/web/src/index.css new file mode 100644 index 0000000..809a2fc --- /dev/null +++ b/apps/web/src/index.css @@ -0,0 +1,20 @@ +:root { + --bg: #0f172a; + --fg: #e2e8f0; + --accent: #38bdf8; + font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif; + line-height: 1.5; + color-scheme: dark; +} + +body { + margin: 0; + background: var(--bg); + color: var(--fg); +} + +#root { + min-height: 100dvh; + display: flex; + flex-direction: column; +} diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx new file mode 100644 index 0000000..db032b7 --- /dev/null +++ b/apps/web/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/apps/web/tsconfig.app.json b/apps/web/tsconfig.app.json new file mode 100644 index 0000000..fe426d4 --- /dev/null +++ b/apps/web/tsconfig.app.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/apps/web/tsconfig.node.json b/apps/web/tsconfig.node.json new file mode 100644 index 0000000..872ffbd --- /dev/null +++ b/apps/web/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts new file mode 100644 index 0000000..f3c9219 --- /dev/null +++ b/apps/web/vite.config.ts @@ -0,0 +1,22 @@ +/// +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + server: { + port: 5173, + proxy: { + '/api': { + target: process.env.VITE_API_URL || 'http://localhost:8000', + changeOrigin: true, + }, + }, + }, + test: { + globals: true, + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{ts,tsx}'], + setupFiles: ['./src/__tests__/setup.ts'], + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..32437a6 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2862 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + apps/web: + dependencies: + react: + specifier: ^19.0.0 + version: 19.2.6 + react-dom: + specifier: ^19.0.0 + version: 19.2.6(react@19.2.6) + devDependencies: + '@eslint/js': + specifier: ^9.24.0 + version: 9.39.4 + '@testing-library/jest-dom': + specifier: ^6.6.3 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.0 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@types/node': + specifier: ^22.0.0 + version: 22.19.19 + '@types/react': + specifier: ^19.0.0 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.0.0 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^4.4.0 + version: 4.7.0(vite@6.4.2(@types/node@22.19.19)) + eslint: + specifier: ^9.24.0 + version: 9.39.4 + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.39.4) + eslint-plugin-react-refresh: + specifier: ^0.4.19 + version: 0.4.26(eslint@9.39.4) + globals: + specifier: ^16.0.0 + version: 16.5.0 + jsdom: + specifier: ^26.0.0 + version: 26.1.0 + typescript: + specifier: ~5.7.0 + version: 5.7.3 + typescript-eslint: + specifier: ^8.29.0 + version: 8.59.3(eslint@9.39.4)(typescript@5.7.3) + vite: + specifier: ^6.3.0 + version: 6.4.2(@types/node@22.19.19) + vitest: + specifier: ^3.1.0 + version: 3.2.4(@types/node@22.19.19)(jsdom@26.1.0) + +packages: + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.60.3': + resolution: {integrity: sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.3': + resolution: {integrity: sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.3': + resolution: {integrity: sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.3': + resolution: {integrity: sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.3': + resolution: {integrity: sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.3': + resolution: {integrity: sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.60.3': + resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.60.3': + resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.60.3': + resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.60.3': + resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.60.3': + resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.60.3': + resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.60.3': + resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.60.3': + resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.60.3': + resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.60.3': + resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.60.3': + resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.60.3': + resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.60.3': + resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.3': + resolution: {integrity: sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.3': + resolution: {integrity: sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.3': + resolution: {integrity: sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.3': + resolution: {integrity: sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.3': + resolution: {integrity: sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==} + cpu: [x64] + os: [win32] + + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@22.19.19': + resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@typescript-eslint/eslint-plugin@8.59.3': + resolution: {integrity: sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.59.3 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.59.3': + resolution: {integrity: sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.59.3': + resolution: {integrity: sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.59.3': + resolution: {integrity: sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.59.3': + resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.59.3': + resolution: {integrity: sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.59.3': + resolution: {integrity: sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.59.3': + resolution: {integrity: sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.59.3': + resolution: {integrity: sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.59.3': + resolution: {integrity: sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.10.29: + resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001792: + resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + electron-to-chromium@1.5.354: + resolution: {integrity: sha512-JaBHwWcfIdmSAfWM5l3uwjGd431j8YEMikZ+K/2nXVuBqJKyZ0f+2h4n4JY5AyNiZmnY9qQr2RU3v9DxDmHMNg==} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react-refresh@0.4.26: + resolution: {integrity: sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==} + peerDependencies: + eslint: '>=8.40' + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.44: + resolution: {integrity: sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==} + + nwsapi@2.2.23: + resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + react-dom@19.2.6: + resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==} + peerDependencies: + react: ^19.2.6 + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@19.2.6: + resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} + engines: {node: '>=0.10.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + rollup@4.60.3: + resolution: {integrity: sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.59.3: + resolution: {integrity: sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@6.4.2: + resolution: {integrity: sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + ws@8.20.1: + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@adobe/css-tools@4.4.4': {} + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': + dependencies: + eslint: 9.39.4 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.60.3': + optional: true + + '@rollup/rollup-android-arm64@4.60.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.3': + optional: true + + '@rollup/rollup-darwin-x64@4.60.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.3': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.3': + optional: true + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.29.2 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@babel/runtime': 7.29.2 + '@testing-library/dom': 10.4.1 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@22.19.19': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@5.7.3))(eslint@9.39.4)(typescript@5.7.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.59.3 + eslint: 9.39.4 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@5.7.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.59.3 + debug: 4.4.3 + eslint: 9.39.4 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.59.3(typescript@5.7.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + debug: 4.4.3 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.59.3': + dependencies: + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 + + '@typescript-eslint/tsconfig-utils@8.59.3(typescript@5.7.3)': + dependencies: + typescript: 5.7.3 + + '@typescript-eslint/type-utils@8.59.3(eslint@9.39.4)(typescript@5.7.3)': + dependencies: + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.7.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + debug: 4.4.3 + eslint: 9.39.4 + ts-api-utils: 2.5.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.59.3': {} + + '@typescript-eslint/typescript-estree@8.59.3(typescript@5.7.3)': + dependencies: + '@typescript-eslint/project-service': 8.59.3(typescript@5.7.3) + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.0 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.59.3(eslint@9.39.4)(typescript@5.7.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.7.3) + eslint: 9.39.4 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.59.3': + dependencies: + '@typescript-eslint/types': 8.59.3 + eslint-visitor-keys: 5.0.1 + + '@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@22.19.19))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.2(@types/node@22.19.19) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@6.4.2(@types/node@22.19.19))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.4.2(@types/node@22.19.19) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + agent-base@7.1.4: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + argparse@2.0.1: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + + assertion-error@2.0.1: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.29: {} + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.29 + caniuse-lite: 1.0.30001792 + electron-to-chromium: 1.5.354 + node-releases: 2.0.44 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + cac@6.7.14: {} + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001792: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + check-error@2.1.3: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css.escape@1.5.1: {} + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + + csstype@3.2.3: {} + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + dequal@2.0.3: {} + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + + electron-to-chromium@1.5.354: {} + + entities@6.0.1: {} + + es-module-lexer@1.7.0: {} + + esbuild@0.25.12: + 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 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@5.2.0(eslint@9.39.4): + dependencies: + eslint: 9.39.4 + + eslint-plugin-react-refresh@0.4.26(eslint@9.39.4): + dependencies: + eslint: 9.39.4 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + expect-type@1.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.5.0: {} + + has-flag@4.0.0: {} + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-potential-custom-element-name@1.0.1: {} + + isexe@2.0.0: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsdom@26.1.0: + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.23 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.20.1 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loupe@3.2.1: {} + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + min-indent@1.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + ms@2.1.3: {} + + nanoid@3.3.12: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.44: {} + + nwsapi@2.2.23: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + postcss@8.5.14: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + punycode@2.3.1: {} + + react-dom@19.2.6(react@19.2.6): + dependencies: + react: 19.2.6 + scheduler: 0.27.0 + + react-is@17.0.2: {} + + react-refresh@0.17.0: {} + + react@19.2.6: {} + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + resolve-from@4.0.0: {} + + rollup@4.60.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.3 + '@rollup/rollup-android-arm64': 4.60.3 + '@rollup/rollup-darwin-arm64': 4.60.3 + '@rollup/rollup-darwin-x64': 4.60.3 + '@rollup/rollup-freebsd-arm64': 4.60.3 + '@rollup/rollup-freebsd-x64': 4.60.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.3 + '@rollup/rollup-linux-arm-musleabihf': 4.60.3 + '@rollup/rollup-linux-arm64-gnu': 4.60.3 + '@rollup/rollup-linux-arm64-musl': 4.60.3 + '@rollup/rollup-linux-loong64-gnu': 4.60.3 + '@rollup/rollup-linux-loong64-musl': 4.60.3 + '@rollup/rollup-linux-ppc64-gnu': 4.60.3 + '@rollup/rollup-linux-ppc64-musl': 4.60.3 + '@rollup/rollup-linux-riscv64-gnu': 4.60.3 + '@rollup/rollup-linux-riscv64-musl': 4.60.3 + '@rollup/rollup-linux-s390x-gnu': 4.60.3 + '@rollup/rollup-linux-x64-gnu': 4.60.3 + '@rollup/rollup-linux-x64-musl': 4.60.3 + '@rollup/rollup-openbsd-x64': 4.60.3 + '@rollup/rollup-openharmony-arm64': 4.60.3 + '@rollup/rollup-win32-arm64-msvc': 4.60.3 + '@rollup/rollup-win32-ia32-msvc': 4.60.3 + '@rollup/rollup-win32-x64-gnu': 4.60.3 + '@rollup/rollup-win32-x64-msvc': 4.60.3 + fsevents: 2.3.3 + + rrweb-cssom@0.8.0: {} + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@3.1.1: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + symbol-tree@3.2.4: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.5.0(typescript@5.7.3): + dependencies: + typescript: 5.7.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.59.3(eslint@9.39.4)(typescript@5.7.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@5.7.3))(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.7.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + eslint: 9.39.4 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + typescript@5.7.3: {} + + undici-types@6.21.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite-node@3.2.4(@types/node@22.19.19): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.4.2(@types/node@22.19.19) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@6.4.2(@types/node@22.19.19): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.14 + rollup: 4.60.3 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 22.19.19 + fsevents: 2.3.3 + + vitest@3.2.4(@types/node@22.19.19)(jsdom@26.1.0): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.4.2(@types/node@22.19.19)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.16 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.4.2(@types/node@22.19.19) + vite-node: 3.2.4(@types/node@22.19.19) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.19 + jsdom: 26.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + ws@8.20.1: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e9b0dad..d75bbb0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,7 @@ packages: - 'apps/*' - 'packages/*' +allowBuilds: + esbuild: set this to true or false +onlyBuiltDependencies: + - esbuild From 80f04a5c451f1d58fb683191a3685058d0af6dab Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 01:31:58 +0200 Subject: [PATCH 03/11] =?UTF-8?q?feat(FN-002):=20complete=20Step=203=20?= =?UTF-8?q?=E2=80=94=20FastAPI=20Backend=20App=20Skeleton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/app/__init__.py | 0 apps/api/app/config.py | 28 ++++++++++++++ apps/api/app/main.py | 22 +++++++++++ apps/api/headquarter_api.egg-info/PKG-INFO | 13 +++++++ apps/api/headquarter_api.egg-info/SOURCES.txt | 10 +++++ .../dependency_links.txt | 1 + .../api/headquarter_api.egg-info/requires.txt | 9 +++++ .../headquarter_api.egg-info/top_level.txt | 1 + apps/api/package.json | 12 ++++++ apps/api/pyproject.toml | 38 +++++++++++++++++++ apps/api/tests/__init__.py | 0 apps/api/tests/test_health.py | 14 +++++++ 12 files changed, 148 insertions(+) create mode 100644 apps/api/app/__init__.py create mode 100644 apps/api/app/config.py create mode 100644 apps/api/app/main.py create mode 100644 apps/api/headquarter_api.egg-info/PKG-INFO create mode 100644 apps/api/headquarter_api.egg-info/SOURCES.txt create mode 100644 apps/api/headquarter_api.egg-info/dependency_links.txt create mode 100644 apps/api/headquarter_api.egg-info/requires.txt create mode 100644 apps/api/headquarter_api.egg-info/top_level.txt create mode 100644 apps/api/package.json create mode 100644 apps/api/pyproject.toml create mode 100644 apps/api/tests/__init__.py create mode 100644 apps/api/tests/test_health.py diff --git a/apps/api/app/__init__.py b/apps/api/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/app/config.py b/apps/api/app/config.py new file mode 100644 index 0000000..02afc3c --- /dev/null +++ b/apps/api/app/config.py @@ -0,0 +1,28 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + extra="ignore", + ) + + app_name: str = "Headquarter API" + debug: bool = False + api_v1_prefix: str = "/api/v1" + + # Authentik OIDC placeholders (to be wired in FN-004) + authentik_issuer_url: str = "" + authentik_client_id: str = "" + authentik_client_secret: str = "" + + # Database (to be wired in FN-004) + database_url: str = "postgresql://postgres:postgres@localhost:5432/headquarter" + + # Deployment + root_domain: str = "localhost" + tool_subdomain_pattern: str = "{tool}-{project}-{user}.tools.{root_domain}" + + +settings = Settings() diff --git a/apps/api/app/main.py b/apps/api/app/main.py new file mode 100644 index 0000000..ae550c4 --- /dev/null +++ b/apps/api/app/main.py @@ -0,0 +1,22 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from app.config import settings + +app = FastAPI( + title=settings.app_name, + debug=settings.debug, +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:5173"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +@app.get("/health") +def health() -> dict[str, str]: + return {"status": "ok", "service": settings.app_name} diff --git a/apps/api/headquarter_api.egg-info/PKG-INFO b/apps/api/headquarter_api.egg-info/PKG-INFO new file mode 100644 index 0000000..ff539f4 --- /dev/null +++ b/apps/api/headquarter_api.egg-info/PKG-INFO @@ -0,0 +1,13 @@ +Metadata-Version: 2.4 +Name: headquarter-api +Version: 0.0.1 +Summary: Headquarter FastAPI backend +Requires-Python: >=3.11 +Requires-Dist: fastapi>=0.115.0 +Requires-Dist: uvicorn[standard]>=0.34.0 +Requires-Dist: pydantic-settings>=2.8.0 +Provides-Extra: dev +Requires-Dist: pytest>=8.3.0; extra == "dev" +Requires-Dist: httpx>=0.28.0; extra == "dev" +Requires-Dist: ruff>=0.11.0; extra == "dev" +Requires-Dist: mypy>=1.15.0; extra == "dev" diff --git a/apps/api/headquarter_api.egg-info/SOURCES.txt b/apps/api/headquarter_api.egg-info/SOURCES.txt new file mode 100644 index 0000000..7591d30 --- /dev/null +++ b/apps/api/headquarter_api.egg-info/SOURCES.txt @@ -0,0 +1,10 @@ +pyproject.toml +app/__init__.py +app/config.py +app/main.py +headquarter_api.egg-info/PKG-INFO +headquarter_api.egg-info/SOURCES.txt +headquarter_api.egg-info/dependency_links.txt +headquarter_api.egg-info/requires.txt +headquarter_api.egg-info/top_level.txt +tests/test_health.py \ No newline at end of file diff --git a/apps/api/headquarter_api.egg-info/dependency_links.txt b/apps/api/headquarter_api.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/api/headquarter_api.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/apps/api/headquarter_api.egg-info/requires.txt b/apps/api/headquarter_api.egg-info/requires.txt new file mode 100644 index 0000000..723d1de --- /dev/null +++ b/apps/api/headquarter_api.egg-info/requires.txt @@ -0,0 +1,9 @@ +fastapi>=0.115.0 +uvicorn[standard]>=0.34.0 +pydantic-settings>=2.8.0 + +[dev] +pytest>=8.3.0 +httpx>=0.28.0 +ruff>=0.11.0 +mypy>=1.15.0 diff --git a/apps/api/headquarter_api.egg-info/top_level.txt b/apps/api/headquarter_api.egg-info/top_level.txt new file mode 100644 index 0000000..b80f0bd --- /dev/null +++ b/apps/api/headquarter_api.egg-info/top_level.txt @@ -0,0 +1 @@ +app diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..d9d80da --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,12 @@ +{ + "name": "@headquarter/api", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": ".venv/bin/uvicorn app.main:app --reload --port 8000", + "build": "echo 'Python build step skipped (no compilation required)'", + "lint": ".venv/bin/ruff check app tests", + "test": ".venv/bin/pytest", + "typecheck": ".venv/bin/mypy app tests" + } +} diff --git a/apps/api/pyproject.toml b/apps/api/pyproject.toml new file mode 100644 index 0000000..a19d610 --- /dev/null +++ b/apps/api/pyproject.toml @@ -0,0 +1,38 @@ +[project] +name = "headquarter-api" +version = "0.0.1" +description = "Headquarter FastAPI backend" +requires-python = ">=3.11" +dependencies = [ + "fastapi>=0.115.0", + "uvicorn[standard]>=0.34.0", + "pydantic-settings>=2.8.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.3.0", + "httpx>=0.28.0", + "ruff>=0.11.0", + "mypy>=1.15.0", +] + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +packages = ["app"] + +[tool.ruff] +line-length = 100 +target-version = "py311" + +[tool.ruff.lint] +select = ["E", "F", "I", "N", "W", "UP"] + +[tool.mypy] +python_version = "3.11" +strict = true +warn_return_any = true +warn_unused_configs = true diff --git a/apps/api/tests/__init__.py b/apps/api/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/api/tests/test_health.py b/apps/api/tests/test_health.py new file mode 100644 index 0000000..966e62c --- /dev/null +++ b/apps/api/tests/test_health.py @@ -0,0 +1,14 @@ +from fastapi.testclient import TestClient + +from app.config import settings +from app.main import app + +client = TestClient(app) + + +def test_health_returns_ok() -> None: + response = client.get("/health") + assert response.status_code == 200 + data = response.json() + assert data["status"] == "ok" + assert data["service"] == settings.app_name From 9eaf4cfa36e561e40730ec56ea31fa6461e8efc5 Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 01:32:16 +0200 Subject: [PATCH 04/11] fix(FN-002): ignore Python egg-info directories --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6aea1c8..f7f899a 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ venv/ ENV/ env/ .egg-info/ +*.egg-info/ dist/ # Docker volumes From 1f1c95cdde313bb2c9124535bc91c0c0a2774d09 Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 01:40:58 +0200 Subject: [PATCH 05/11] =?UTF-8?q?feat(FN-002):=20complete=20Step=204=20?= =?UTF-8?q?=E2=80=94=20Documentation=20Structure=20and=20Environment=20Exa?= =?UTF-8?q?mples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 28 +++ apps/api/headquarter_api.egg-info/PKG-INFO | 13 - apps/api/headquarter_api.egg-info/SOURCES.txt | 10 - .../dependency_links.txt | 1 - .../api/headquarter_api.egg-info/requires.txt | 9 - .../headquarter_api.egg-info/top_level.txt | 1 - docs/README.md | 15 ++ docs/architecture.md | 236 ++++++++++++++++++ docs/deployment.md | 60 +++++ docs/development.md | 139 +++++++++++ 10 files changed, 478 insertions(+), 34 deletions(-) create mode 100644 .env.example delete mode 100644 apps/api/headquarter_api.egg-info/PKG-INFO delete mode 100644 apps/api/headquarter_api.egg-info/SOURCES.txt delete mode 100644 apps/api/headquarter_api.egg-info/dependency_links.txt delete mode 100644 apps/api/headquarter_api.egg-info/requires.txt delete mode 100644 apps/api/headquarter_api.egg-info/top_level.txt create mode 100644 docs/README.md create mode 100644 docs/architecture.md create mode 100644 docs/deployment.md create mode 100644 docs/development.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..eaee669 --- /dev/null +++ b/.env.example @@ -0,0 +1,28 @@ +# App identity +APP_NAME=Headquarter +ROOT_DOMAIN=localhost +TOOL_DOMAIN=tools.localhost + +# API / Web URLs +API_URL=http://localhost:8000 +WEB_URL=http://localhost:5173 + +# Database (local development) +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=headquarter +DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB} + +# Authentik OIDC placeholders (wire in FN-004) +AUTHENTIK_ISSUER_URL=https://auth.example.com/application/o/headquarter/ +AUTHENTIK_CLIENT_ID=your-client-id +AUTHENTIK_CLIENT_SECRET=your-client-secret + +# Traefik / deployment placeholders (wire in FN-006) +TRAEFIK_NETWORK=traefik +TRAEFIK_ENTRYPOINT=websecure +TRAEFIK_CERT_RESOLVER=letsencrypt +TOOL_SUBDOMAIN_PATTERN={tool}-{project}-{user}.tools.${ROOT_DOMAIN} + +# Secrets (generate strong random values for production) +SECRET_ENCRYPTION_KEY=change-me-in-production diff --git a/apps/api/headquarter_api.egg-info/PKG-INFO b/apps/api/headquarter_api.egg-info/PKG-INFO deleted file mode 100644 index ff539f4..0000000 --- a/apps/api/headquarter_api.egg-info/PKG-INFO +++ /dev/null @@ -1,13 +0,0 @@ -Metadata-Version: 2.4 -Name: headquarter-api -Version: 0.0.1 -Summary: Headquarter FastAPI backend -Requires-Python: >=3.11 -Requires-Dist: fastapi>=0.115.0 -Requires-Dist: uvicorn[standard]>=0.34.0 -Requires-Dist: pydantic-settings>=2.8.0 -Provides-Extra: dev -Requires-Dist: pytest>=8.3.0; extra == "dev" -Requires-Dist: httpx>=0.28.0; extra == "dev" -Requires-Dist: ruff>=0.11.0; extra == "dev" -Requires-Dist: mypy>=1.15.0; extra == "dev" diff --git a/apps/api/headquarter_api.egg-info/SOURCES.txt b/apps/api/headquarter_api.egg-info/SOURCES.txt deleted file mode 100644 index 7591d30..0000000 --- a/apps/api/headquarter_api.egg-info/SOURCES.txt +++ /dev/null @@ -1,10 +0,0 @@ -pyproject.toml -app/__init__.py -app/config.py -app/main.py -headquarter_api.egg-info/PKG-INFO -headquarter_api.egg-info/SOURCES.txt -headquarter_api.egg-info/dependency_links.txt -headquarter_api.egg-info/requires.txt -headquarter_api.egg-info/top_level.txt -tests/test_health.py \ No newline at end of file diff --git a/apps/api/headquarter_api.egg-info/dependency_links.txt b/apps/api/headquarter_api.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/apps/api/headquarter_api.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/api/headquarter_api.egg-info/requires.txt b/apps/api/headquarter_api.egg-info/requires.txt deleted file mode 100644 index 723d1de..0000000 --- a/apps/api/headquarter_api.egg-info/requires.txt +++ /dev/null @@ -1,9 +0,0 @@ -fastapi>=0.115.0 -uvicorn[standard]>=0.34.0 -pydantic-settings>=2.8.0 - -[dev] -pytest>=8.3.0 -httpx>=0.28.0 -ruff>=0.11.0 -mypy>=1.15.0 diff --git a/apps/api/headquarter_api.egg-info/top_level.txt b/apps/api/headquarter_api.egg-info/top_level.txt deleted file mode 100644 index b80f0bd..0000000 --- a/apps/api/headquarter_api.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -app diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..2e905ed --- /dev/null +++ b/docs/README.md @@ -0,0 +1,15 @@ +# Headquarter Documentation + +This directory contains architecture, development, and deployment documentation for the Headquarter platform. + +## Index + +- [Architecture](architecture.md) — System architecture, stack decisions, and MVP phases *(FN-001)* +- [Development](development.md) — Local setup, prerequisites, and day-to-day commands *(FN-002)* +- [Deployment](deployment.md) — Deployment assumptions, Portainer/Traefik skeleton, and follow-up scope *(FN-002)* + +## Quick Links + +- [Root README](../README.md) +- [Deploy Skeleton](../deploy/README.md) +- [Project Brief](project-brief.md) — Original product brief and confirmed stack diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..8282bc2 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,236 @@ +# Headquarter Architecture + +> Canonical architecture specification for the hosted workspace and tool-orchestration platform. +> Decisions in this document override ad-hoc choices in implementation tasks. + +## 1. Overview & Goals + +Headquarter is a hosted control plane where authenticated users create projects, connect Git repositories, and spawn containerized tools (RunFusion, code-server, and future tools). The platform is manifest-driven and provider-abstracted so new tools, Git providers, runtimes, and access providers can be added without rewriting core orchestration logic. + +**MVP scope:** Single-user projects, Authentik OIDC auth, Docker runtime, Traefik subdomain routing, Portainer-managed Docker Compose deployment. + +## 2. Domain Model + +```text +User ──< Project ──< Repository + └──< ToolInstance + +Tool (manifest-driven, global registry) +``` + +- **User:** Authentik-managed identity. MVP assumes individual users; schema leaves room for teams/shared projects later. +- **Project:** Owned by a user. Contains repositories and spawned tool instances. +- **Repository:** Git-backed workspace. Clone, fetch, and push via provider-independent adapters. +- **Tool:** Manifest-driven definition (image, ports, mounts, env, health checks, routing rules). Defined in FN-003. +- **ToolInstance:** A running container spawned from a Tool manifest for a specific project. Receives workspace mounts, config mounts, secrets, and Traefik routing labels. + +## 3. Provider Interfaces + +The backend must define provider contracts before implementing any concrete adapter. + +### 3.1 GitProvider + +```python +class GitProvider(Protocol): + def clone(self, repo_url: str, dest: Path, credentials: GitCredentials) -> None: ... + def fetch(self, repo_path: Path, credentials: GitCredentials) -> None: ... + def push(self, repo_path: Path, credentials: GitCredentials) -> None: ... +``` + +- Adapters: GitHub, GitLab, Gitea, Forgejo, etc. +- Credentials: generated SSH keys (per-repository) or access tokens. +- SSH keys must be scoped per repository connection for clean revocation. + +### 3.2 RuntimeProvider + +```python +class RuntimeProvider(Protocol): + def spawn(self, manifest: ToolManifest, project: Project, config: SpawnConfig) -> ToolInstance: ... + def stop(self, instance: ToolInstance) -> None: ... + def health(self, instance: ToolInstance) -> HealthStatus: ... +``` + +- MVP adapter: Docker Compose service generation + Docker API. +- Future adapters: Kubernetes, Nomad, etc. + +### 3.3 AccessProvider + +```python +class AccessProvider(Protocol): + def route(self, instance: ToolInstance, domain: str) -> RoutingConfig: ... +``` + +- MVP adapter: Traefik labels on Docker containers. +- Future adapter: Cloudflare Tunnel, etc. + +## 4. Deployment Architecture + +### 4.1 MVP Target + +- **Orchestration:** Portainer-managed Docker Compose stack. +- **Reverse Proxy:** Existing Traefik instance (external to the app stack). +- **Network:** Shared Traefik Docker network; app stack attaches to it. +- **Certificate Resolution:** Let's Encrypt or internal CA via Traefik cert resolver. + +### 4.2 Subdomain Routing + +Path-based routing is avoided because many tools expect to run at `/`. + +Pattern: +``` +https://{tool}-{project}-{user}.{tool_domain} +``` + +Examples: +``` +https://runfusion-myapp-alice.tools.example.com +https://code-myapp-alice.tools.example.com +``` + +### 4.3 Compose Skeleton + +- `docker-compose.yml`: local development (backend, frontend, PostgreSQL). +- `docker-compose.traefik.yml`: deployment overlay with Traefik labels and external network. +- Environment-driven; no secrets committed to repository. + +## 5. Security Boundaries + +### 5.1 Authentication + +- Authentik OIDC for user login. +- FastAPI backend validates JWT/id tokens at API boundaries. +- Frontend stores tokens securely (httpOnly cookie or secure storage pattern). + +### 5.2 Secrets + +- Never treat secrets as plaintext config. +- Support encrypted storage at rest and runtime injection as: + - Environment variables + - Mounted secret files +- Encryption key is an environment secret (`SECRET_ENCRYPTION_KEY`). + +### 5.3 SSH Keys + +- Generated per repository connection. +- Stored encrypted. +- Injected into tool containers at runtime for Git operations. + +### 5.4 Container Isolation + +- Each tool instance runs in its own container. +- Resource limits declared in tool manifest. +- Workspace and config mounts are scoped to user/project. + +## 6. Data & Storage + +### 6.1 Database + +- PostgreSQL for relational data (users, projects, repositories, tool instances, manifests). +- Schema migrations managed by backend (Alembic or equivalent). + +### 6.2 Filesystem Layout (Conceptual) + +```text +/data/ + users/{userId}/tool-configs/{toolId}/ + projects/{projectId}/repo/ + projects/{projectId}/tool-configs/{toolId}/ +``` + +- Repository workspace storage: Docker volumes or local bind mounts. +- Tool config storage: persistent host mounts, separate from repository workspaces. +- Config scopes: global default → user-level → project-level → tool-instance override. + +## 7. Tool Manifest & Orchestration + +Tools are defined by manifests (FN-003) that declare: + +- Runtime image / image tag +- Node/npm version expectations (for executable environments) +- Bootstrap / install commands +- Command execution needs +- Workspace mounts +- Config mounts +- Environment variables +- Secrets +- Ports +- Health checks +- Resource limits +- Traefik routing needs (subdomain pattern, middleware) + +The platform reads manifests and generates: +- Docker Compose service definitions +- Traefik labels for routing +- Volume mounts for workspace and config +- Secret injection at runtime + +## 8. MVP Phases + +| Phase | Task | Deliverable | +|-------|------|-------------| +| Foundation | FN-002 | Monorepo scaffold, build/test/lint pipelines | +| Registry | FN-003 | Manifest schema, RunFusion and code-server manifests | +| Backend | FN-004 | FastAPI app, domain models, API endpoints, DB migrations | +| Frontend | FN-005 | Auth-ready shell, navigation, placeholder screens | +| Deployment | FN-006 | Docker Compose overlays, Traefik labels, Portainer config | +| Git Model | FN-007 | Provider interface, SSH key generation, credential storage | +| RunFusion POC | FN-008 | Executable environment proof of concept | +| Secrets & Config | FN-009 | Encrypted secrets, persistent config mounts | +| code-server Spawn | FN-010 | code-server manifest, spawn script, runtime integration | + +## 9. Extension Points + +- **New Git providers:** Implement `GitProvider` protocol. +- **New tools:** Add a manifest to the registry (no code changes required for standard containers). +- **New runtimes:** Implement `RuntimeProvider` protocol. +- **New access providers:** Implement `AccessProvider` protocol. +- **Teams/organizations:** Add `Organization` and `ProjectMember` entities later. + +## 10. Environment Assumptions + +Required environment variables (no defaults in production): + +```env +APP_NAME= +ROOT_DOMAIN= +TOOL_DOMAIN= +API_URL= +TRAEFIK_NETWORK= +TRAEFIK_ENTRYPOINT= +TRAEFIK_CERT_RESOLVER= +AUTHENTIK_ISSUER_URL= +AUTHENTIK_CLIENT_ID= +AUTHENTIK_CLIENT_SECRET= +DATABASE_URL= +SECRET_ENCRYPTION_KEY= +``` + +Local development uses `.env.example` and safe defaults. + +## 11. Technology Boundaries + +| Layer | Choice | Migration Path | +|-------|--------|----------------| +| Frontend | React + Vite | Next.js, Vue, etc. if needed | +| Backend | FastAPI | Any ASGI framework | +| Database | PostgreSQL | Managed Postgres, CockroachDB | +| Runtime | Docker Compose | Kubernetes, Nomad | +| Access | Traefik | Cloudflare Tunnel, custom proxy | +| Auth | Authentik OIDC | Any OIDC provider | + +## 12. Acceptance Criteria for Architecture Compliance + +Any implementation task must: + +1. Respect provider interfaces (no hardcoded GitHub/Traefik logic in core orchestration). +2. Keep secrets out of committed files and plaintext logs. +3. Use environment variables for deployment-specific values. +4. Leave schema room for multi-user teams without rewriting ownership models. +5. Support adding a new tool via manifest + registry entry alone (no new backend code for standard containers). + +## 13. Deferred Decisions + +- **Multi-tenancy:** MVP is single-tenant deployment. Multi-tenant routing and isolation are future concerns. +- **High availability:** No replicas or load balancing in MVP. +- **Backup strategy:** Out of MVP scope; rely on host-level volume backups. +- **Rate limiting:** Not in MVP; add at Traefik or API gateway layer later. diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..eb59e09 --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,60 @@ +# Deployment Guide + +## Overview + +The MVP deployment target is a **Portainer-managed Docker Compose stack** with an existing **Traefik** reverse proxy. + +This document covers the scaffold-level deployment assumptions created in FN-002. Detailed deployment automation (dynamic labels for spawned tool containers, secret rotation, CI/CD pipelines) is follow-up scope for **FN-006**. + +## Stack Assumptions + +- **Reverse proxy**: Traefik (already running on the target host) +- **Orchestration**: Portainer managing Docker Compose stacks +- **Network**: External Traefik network named `traefik` (or as configured) +- **Routing**: Subdomain-based (`{tool}-{project}-{user}.tools.{ROOT_DOMAIN}`) +- **TLS**: Traefik cert resolver (e.g., `letsencrypt` or Cloudflare) + +## Deployment Files + +| File | Purpose | +|------|---------| +| `docker-compose.yml` | Local development (API, web, Postgres) | +| `docker-compose.traefik.yml` | Deployment overlay with Traefik labels | +| `deploy/portainer.env.example` | Deployment environment variables | +| `deploy/traefik-labels.example.yml` | Example Traefik labels for services | +| `deploy/README.md` | Deploy skeleton usage notes | + +## Environment Variables + +See `.env.example` for the full variable list. Key deployment variables: + +```env +APP_NAME=Headquarter +ROOT_DOMAIN=example.com +TOOL_DOMAIN=tools.example.com +TRAEFIK_NETWORK=traefik +TRAEFIK_ENTRYPOINT=websecure +TRAEFIK_CERT_RESOLVER=letsencrypt +AUTHENTIK_ISSUER_URL=https://auth.example.com/application/o/headquarter/ +AUTHENTIK_CLIENT_ID= +AUTHENTIK_CLIENT_SECRET= +POSTGRES_PASSWORD= +SECRET_ENCRYPTION_KEY= +``` + +## Local vs Production + +- **Local**: `docker compose up --build -d` uses `docker-compose.yml` only. +- **Production**: Portainer deploys the stack using the main compose file plus the Traefik overlay. + +## Scoped Secrets + +Do not commit real secrets. Use: + +- Portainer environment variables (stored in Portainer, not in Git) +- `.env` files (ignored by Git, documented in `.env.example`) +- Docker secrets (to be evaluated in FN-006) + +## Follow-up Work + +- **FN-006**: Full deployment automation, dynamic Traefik labels for spawned tool containers, Portainer stack definitions, and CI/CD integration. diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..010964d --- /dev/null +++ b/docs/development.md @@ -0,0 +1,139 @@ +# Development Guide + +## Prerequisites + +- **Node.js** ≥ 20 and **pnpm** ≥ 9 +- **Python** ≥ 3.11 with `venv` support +- **Docker** and **Docker Compose** (for local services) + +## Installation + +```bash +# Install Node dependencies and Python virtualenv + packages +make install + +# Or manually: +pnpm install +cd apps/api && python3 -m venv .venv && .venv/bin/pip install -e ".[dev]" +``` + +## Environment Setup + +Copy the root environment example and fill in local values: + +```bash +cp .env.example .env +``` + +Copy the frontend environment example: + +```bash +cp apps/web/.env.example apps/web/.env +``` + +## Running Locally + +### Frontend only + +```bash +cd apps/web +pnpm dev # Vite dev server on http://localhost:5173 +``` + +### Backend only + +```bash +cd apps/api +.venv/bin/uvicorn app.main:app --reload --port 8000 +``` + +### Both (via root script) + +```bash +pnpm dev # Runs frontend and backend in parallel +``` + +### With Docker Compose + +```bash +docker compose up --build -d +``` + +## Testing + +### Frontend + +```bash +pnpm --filter @headquarter/web test +``` + +Uses **Vitest** + **Testing Library** + **jsdom**. + +### Backend + +```bash +pnpm --filter @headquarter/api test +``` + +Or directly with pytest: + +```bash +cd apps/api && .venv/bin/pytest +``` + +### All tests + +```bash +make test +# or +pnpm test +``` + +## Linting and Type Checking + +### Frontend + +```bash +pnpm --filter @headquarter/web lint +pnpm --filter @headquarter/web typecheck +``` + +### Backend + +```bash +pnpm --filter @headquarter/api lint +pnpm --filter @headquarter/api typecheck +``` + +### All + +```bash +make lint +make typecheck +``` + +## Building + +```bash +make build +# or +pnpm build +``` + +## Project Layout + +```text +├── apps/ +│ ├── web/ # Vite React TypeScript frontend +│ └── api/ # FastAPI Python backend +├── docs/ # Documentation +├── deploy/ # Deployment skeleton files +├── docker-compose.yml +└── package.json # Root monorepo scripts +``` + +## Conventions + +- **Frontend**: React functional components, TypeScript strict mode, ESLint + Ruff-like rules. +- **Backend**: FastAPI, Pydantic settings, pytest, ruff, mypy. +- **Commits**: Conventional commits with task ID prefix, e.g. `feat(FN-002): description`. From 2e8a558b71926d1be7e133d5819d6c514ae6e102 Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 01:47:03 +0200 Subject: [PATCH 06/11] fix(FN-002): remove forward references from docs to files not yet created --- docs/README.md | 6 +++--- docs/deployment.md | 2 ++ docs/development.md | 12 +++++++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/README.md b/docs/README.md index 2e905ed..872e746 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,6 +10,6 @@ This directory contains architecture, development, and deployment documentation ## Quick Links -- [Root README](../README.md) -- [Deploy Skeleton](../deploy/README.md) -- [Project Brief](project-brief.md) — Original product brief and confirmed stack +- Root README — to be added in Step 6 +- Deploy Skeleton — to be added in Step 5 +- [Project Brief](project-brief.md) — Original product brief and confirmed stack *(FN-001)* diff --git a/docs/deployment.md b/docs/deployment.md index eb59e09..e2206f1 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -16,6 +16,8 @@ This document covers the scaffold-level deployment assumptions created in FN-002 ## Deployment Files +The following files will be added as part of the Docker Compose skeleton (Step 5): + | File | Purpose | |------|---------| | `docker-compose.yml` | Local development (API, web, Postgres) | diff --git a/docs/development.md b/docs/development.md index 010964d..99b132e 100644 --- a/docs/development.md +++ b/docs/development.md @@ -55,9 +55,11 @@ pnpm dev # Runs frontend and backend in parallel ### With Docker Compose -```bash -docker compose up --build -d -``` +> Docker Compose files will be added in Step 5. Once created, run: +> +> ```bash +> docker compose up --build -d +> ``` ## Testing @@ -127,8 +129,8 @@ pnpm build │ ├── web/ # Vite React TypeScript frontend │ └── api/ # FastAPI Python backend ├── docs/ # Documentation -├── deploy/ # Deployment skeleton files -├── docker-compose.yml +├── deploy/ # Deployment skeleton files (added in Step 5) +├── docker-compose.yml (added in Step 5) └── package.json # Root monorepo scripts ``` From 7b45344237052a84b4c7177cddf9dd48a6bcf4e3 Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 01:58:18 +0200 Subject: [PATCH 07/11] =?UTF-8?q?feat(FN-002):=20complete=20Step=205=20?= =?UTF-8?q?=E2=80=94=20Docker=20Compose=20and=20Portainer/Traefik=20Skelet?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/Dockerfile | 15 ++++++++++ apps/web/Dockerfile | 15 ++++++++++ apps/web/nginx.conf | 19 ++++++++++++ deploy/README.md | 36 ++++++++++++++++++++++ deploy/portainer.env.example | 27 +++++++++++++++++ deploy/traefik-labels.example.yml | 27 +++++++++++++++++ docker-compose.traefik.yml | 26 ++++++++++++++++ docker-compose.yml | 50 +++++++++++++++++++++++++++++++ 8 files changed, 215 insertions(+) create mode 100644 apps/api/Dockerfile create mode 100644 apps/web/Dockerfile create mode 100644 apps/web/nginx.conf create mode 100644 deploy/README.md create mode 100644 deploy/portainer.env.example create mode 100644 deploy/traefik-labels.example.yml create mode 100644 docker-compose.traefik.yml create mode 100644 docker-compose.yml diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile new file mode 100644 index 0000000..0793f1d --- /dev/null +++ b/apps/api/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.14-slim + +WORKDIR /app + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +COPY pyproject.toml ./ +RUN pip install --no-cache-dir -e ".[dev]" + +COPY app/ ./app/ + +EXPOSE 8000 + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile new file mode 100644 index 0000000..9719b64 --- /dev/null +++ b/apps/web/Dockerfile @@ -0,0 +1,15 @@ +FROM node:25-alpine AS builder + +WORKDIR /app +COPY package.json ./ +RUN npm install -g pnpm && pnpm install + +COPY . . +RUN pnpm build + +FROM nginx:alpine + +COPY --from=builder /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 diff --git a/apps/web/nginx.conf b/apps/web/nginx.conf new file mode 100644 index 0000000..7e7c1b8 --- /dev/null +++ b/apps/web/nginx.conf @@ -0,0 +1,19 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api { + proxy_pass http://api:8000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } +} diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..eb5449b --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,36 @@ +# Deploy Skeleton + +This directory contains deployment-focused examples and environment templates for the Headquarter platform. + +## Files + +| File | Purpose | +|------|---------| +| `portainer.env.example` | Environment variables for a Portainer-managed stack | +| `traefik-labels.example.yml` | Reference Traefik labels for services and tool instances | + +## Assumptions + +- An external **Traefik** reverse proxy is already running on the target Docker host. +- Traefik is attached to a Docker network (default name: `traefik`). +- The Traefik network is declared as `external: true` in compose overlays. +- TLS termination and certificate resolution are handled by Traefik. + +## Usage + +1. Copy `portainer.env.example` to your secrets manager or Portainer environment configuration. +2. Fill in all empty values (client IDs, secrets, database password, encryption key). +3. Deploy the stack via Portainer using `docker-compose.yml` + `docker-compose.traefik.yml`. +4. Tool subdomain labels will be generated dynamically in FN-006. + +## Traefik Network + +Create the Traefik network if it does not exist: + +```bash +docker network create traefik +``` + +## Follow-up + +Detailed deployment automation, dynamic labels for spawned tool containers, and CI/CD integration are planned in **FN-006**. diff --git a/deploy/portainer.env.example b/deploy/portainer.env.example new file mode 100644 index 0000000..02072fd --- /dev/null +++ b/deploy/portainer.env.example @@ -0,0 +1,27 @@ +# Portainer stack environment variables (no secrets committed) +# Copy and configure in Portainer UI or your secrets manager. + +APP_NAME=Headquarter +ROOT_DOMAIN=example.com +TOOL_DOMAIN=tools.example.com +API_URL=https://api.example.com +WEB_URL=https://example.com + +# Database +POSTGRES_USER=headquarter +POSTGRES_DB=headquarter +POSTGRES_PASSWORD= + +# Authentik OIDC +AUTHENTIK_ISSUER_URL=https://auth.example.com/application/o/headquarter/ +AUTHENTIK_CLIENT_ID= +AUTHENTIK_CLIENT_SECRET= + +# Traefik +TRAEFIK_NETWORK=traefik +TRAEFIK_ENTRYPOINT=websecure +TRAEFIK_CERT_RESOLVER=letsencrypt +TOOL_SUBDOMAIN_PATTERN={tool}-{project}-{user}.tools.{ROOT_DOMAIN} + +# Secrets +SECRET_ENCRYPTION_KEY= diff --git a/deploy/traefik-labels.example.yml b/deploy/traefik-labels.example.yml new file mode 100644 index 0000000..c311ceb --- /dev/null +++ b/deploy/traefik-labels.example.yml @@ -0,0 +1,27 @@ +# Example Traefik labels for Headquarter services +# These labels are applied automatically by docker-compose.traefik.yml. +# Use this file as a reference when adding custom tool container labels later. + +# API service labels +api_labels: &api + - "traefik.enable=true" + - "traefik.http.routers.headquarter-api.rule=Host(`api.example.com`)" + - "traefik.http.routers.headquarter-api.entrypoints=websecure" + - "traefik.http.routers.headquarter-api.tls.certresolver=letsencrypt" + - "traefik.http.services.headquarter-api.loadbalancer.server.port=8000" + +# Web service labels +web_labels: &web + - "traefik.enable=true" + - "traefik.http.routers.headquarter-web.rule=Host(`example.com`)" + - "traefik.http.routers.headquarter-web.entrypoints=websecure" + - "traefik.http.routers.headquarter-web.tls.certresolver=letsencrypt" + - "traefik.http.services.headquarter-web.loadbalancer.server.port=80" + +# Tool instance labels (template for dynamically spawned containers) +tool_labels: &tool + - "traefik.enable=true" + - "traefik.http.routers.{tool_name}.rule=Host(`{subdomain}`)" + - "traefik.http.routers.{tool_name}.entrypoints=websecure" + - "traefik.http.routers.{tool_name}.tls.certresolver=letsencrypt" + - "traefik.http.services.{tool_name}.loadbalancer.server.port={port}" diff --git a/docker-compose.traefik.yml b/docker-compose.traefik.yml new file mode 100644 index 0000000..ce22cf0 --- /dev/null +++ b/docker-compose.traefik.yml @@ -0,0 +1,26 @@ +services: + api: + networks: + - default + - traefik + labels: + - "traefik.enable=true" + - "traefik.http.routers.headquarter-api.rule=Host(`api.${ROOT_DOMAIN}`)" + - "traefik.http.routers.headquarter-api.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}" + - "traefik.http.routers.headquarter-api.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-letsencrypt}" + - "traefik.http.services.headquarter-api.loadbalancer.server.port=8000" + + web: + networks: + - default + - traefik + labels: + - "traefik.enable=true" + - "traefik.http.routers.headquarter-web.rule=Host(`${ROOT_DOMAIN}`)" + - "traefik.http.routers.headquarter-web.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}" + - "traefik.http.routers.headquarter-web.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-letsencrypt}" + - "traefik.http.services.headquarter-web.loadbalancer.server.port=80" + +networks: + traefik: + external: true diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7101348 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,50 @@ +services: + api: + build: + context: ./apps/api + dockerfile: Dockerfile + ports: + - "8000:8000" + env_file: + - .env + environment: + - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-headquarter} + depends_on: + db: + condition: service_healthy + volumes: + - api-data:/data + restart: unless-stopped + + web: + build: + context: ./apps/web + dockerfile: Dockerfile + ports: + - "5173:80" + env_file: + - .env + depends_on: + - api + restart: unless-stopped + + db: + image: postgres:17-alpine + environment: + - POSTGRES_USER=${POSTGRES_USER:-postgres} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres} + - POSTGRES_DB=${POSTGRES_DB:-headquarter} + volumes: + - postgres-data:/var/lib/postgresql/data + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-headquarter}"] + interval: 5s + timeout: 5s + retries: 5 + restart: unless-stopped + +volumes: + postgres-data: + api-data: From a091895998a181aeb7aa4898a718e4032dc73747 Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 02:08:53 +0200 Subject: [PATCH 08/11] fix(FN-002): address Dockerfile build order, base images, .dockerignore, docs --- apps/api/.dockerignore | 15 +++++++++++++++ apps/api/Dockerfile | 5 ++--- apps/web/.dockerignore | 6 ++++++ apps/web/Dockerfile | 2 +- docker-compose.yml | 8 ++++++-- docs/README.md | 2 +- docs/development.md | 12 +++++------- 7 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 apps/api/.dockerignore create mode 100644 apps/web/.dockerignore diff --git a/apps/api/.dockerignore b/apps/api/.dockerignore new file mode 100644 index 0000000..4546fea --- /dev/null +++ b/apps/api/.dockerignore @@ -0,0 +1,15 @@ +__pycache__/ +*.py[cod] +*$py.class +*.so +.venv/ +venv/ +ENV/ +env/ +*.egg-info/ +dist/ +build/ +.git/ +.env +.env.local +*.log diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index 0793f1d..717540f 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -1,15 +1,14 @@ -FROM python:3.14-slim +FROM python:3.12-slim WORKDIR /app ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 +COPY app/ ./app/ COPY pyproject.toml ./ RUN pip install --no-cache-dir -e ".[dev]" -COPY app/ ./app/ - EXPOSE 8000 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/apps/web/.dockerignore b/apps/web/.dockerignore new file mode 100644 index 0000000..390f589 --- /dev/null +++ b/apps/web/.dockerignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +.git/ +.env +.env.local +*.log diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index 9719b64..f624c23 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -1,4 +1,4 @@ -FROM node:25-alpine AS builder +FROM node:22-alpine AS builder WORKDIR /app COPY package.json ./ diff --git a/docker-compose.yml b/docker-compose.yml index 7101348..b987b78 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,6 +12,12 @@ services: depends_on: db: condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8000/health')\""] + interval: 10s + timeout: 5s + retries: 3 + start_period: 5s volumes: - api-data:/data restart: unless-stopped @@ -22,8 +28,6 @@ services: dockerfile: Dockerfile ports: - "5173:80" - env_file: - - .env depends_on: - api restart: unless-stopped diff --git a/docs/README.md b/docs/README.md index 872e746..29dedb5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,5 +11,5 @@ This directory contains architecture, development, and deployment documentation ## Quick Links - Root README — to be added in Step 6 -- Deploy Skeleton — to be added in Step 5 +- [Deploy Skeleton](../deploy/README.md) - [Project Brief](project-brief.md) — Original product brief and confirmed stack *(FN-001)* diff --git a/docs/development.md b/docs/development.md index 99b132e..010964d 100644 --- a/docs/development.md +++ b/docs/development.md @@ -55,11 +55,9 @@ pnpm dev # Runs frontend and backend in parallel ### With Docker Compose -> Docker Compose files will be added in Step 5. Once created, run: -> -> ```bash -> docker compose up --build -d -> ``` +```bash +docker compose up --build -d +``` ## Testing @@ -129,8 +127,8 @@ pnpm build │ ├── web/ # Vite React TypeScript frontend │ └── api/ # FastAPI Python backend ├── docs/ # Documentation -├── deploy/ # Deployment skeleton files (added in Step 5) -├── docker-compose.yml (added in Step 5) +├── deploy/ # Deployment skeleton files +├── docker-compose.yml └── package.json # Root monorepo scripts ``` From 28728fdcea3a69c64a6f04933956a35ccd91307d Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 02:18:25 +0200 Subject: [PATCH 09/11] =?UTF-8?q?feat(FN-002):=20complete=20Step=206=20?= =?UTF-8?q?=E2=80=94=20Root=20README=20and=20Scope=20Boundaries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..ca622ad --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# Headquarter + +Hosted workspace and tool-orchestration platform where authenticated users create projects, connect Git repositories, and spawn self-hosted tools such as RunFusion and code-server. + +## Current Status + +This repository is an initial scaffold (FN-002). It provides: + +- React + Vite + TypeScript frontend (`apps/web`) +- FastAPI + Python backend (`apps/api`) +- Root monorepo tooling (pnpm workspace, Makefile) +- Docker Compose local development stack +- Deployment skeleton for Portainer + Traefik +- Automated tests (Vitest + pytest) + +## Repository Layout + +```text +├── apps/ +│ ├── web/ # React frontend +│ └── api/ # FastAPI backend +├── docs/ # Architecture, development, and deployment docs +├── deploy/ # Portainer/Traefik deployment examples +├── docker-compose.yml +├── docker-compose.traefik.yml +├── package.json # Root monorepo scripts +├── Makefile # Common local workflows +└── .env.example # Shared environment variables +``` + +## Prerequisites + +- Node.js ≥ 20 and pnpm ≥ 9 +- Python ≥ 3.11 +- Docker and Docker Compose (optional, for local Postgres) + +## Quickstart + +```bash +# Install dependencies +make install + +# Copy environment examples +cp .env.example .env +cp apps/web/.env.example apps/web/.env + +# Run tests +make test + +# Start frontend and backend in development mode +make dev +``` + +### Docker Compose + +```bash +docker compose up --build -d +``` + +This starts the API, web frontend, and PostgreSQL. + +## Commands + +| Command | Description | +|---------|-------------| +| `make install` | Install Node and Python dependencies | +| `make dev` | Start frontend and backend in parallel | +| `make test` | Run frontend and backend tests | +| `make lint` | Run linters | +| `make typecheck` | Run type checkers | +| `make build` | Build frontend and backend | +| `make compose-up` | Start Docker Compose stack | +| `make compose-down` | Stop Docker Compose stack | + +## Documentation + +- [Architecture](docs/architecture.md) — System design and MVP phases +- [Development](docs/development.md) — Local setup and day-to-day commands +- [Deployment](docs/deployment.md) — Portainer/Traefik assumptions +- [Deploy Skeleton](deploy/README.md) — Deployment file reference + +## Scope Boundaries + +This scaffold intentionally defers detailed implementation to follow-up tasks: + +- **FN-004** — Backend domain models, database migrations, API endpoints, auth integration +- **FN-005** — Frontend dashboard navigation, project creation, authenticated flows +- **FN-006** — Full deployment automation, dynamic Traefik labels for spawned tool containers +- **FN-003** — Manifest-driven tool registry +- **FN-007** — Provider-independent Git connection model +- **FN-008** — RunFusion executable environment proof of concept +- **FN-009** — Persistent config and secrets handling +- **FN-010** — code-server manifest and spawn flow + +## License + +TBD From 9f8bd0a6b63b2597ba8532c8384f0eebc88a779a Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 02:26:06 +0200 Subject: [PATCH 10/11] =?UTF-8?q?feat(FN-002):=20complete=20Step=208=20?= =?UTF-8?q?=E2=80=94=20Documentation=20&=20Delivery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .fusion/fusion.db-shm | Bin 0 -> 32768 bytes .fusion/fusion.db-wal | Bin 0 -> 65952 bytes .fusion/memory/2026-05-14.md | 3 +++ docs/README.md | 2 +- docs/deployment.md | 2 +- pnpm-lock.yaml | 2 ++ 6 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .fusion/fusion.db-shm create mode 100644 .fusion/fusion.db-wal create mode 100644 .fusion/memory/2026-05-14.md diff --git a/.fusion/fusion.db-shm b/.fusion/fusion.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..e579f2922b3294ea8f8ace1c18a0a1cc0302487e GIT binary patch literal 32768 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 E2cP|aSO5S3 literal 0 HcmV?d00001 diff --git a/.fusion/memory/2026-05-14.md b/.fusion/memory/2026-05-14.md new file mode 100644 index 0000000..5114766 --- /dev/null +++ b/.fusion/memory/2026-05-14.md @@ -0,0 +1,3 @@ +# Daily Memory 2026-05-14 + + diff --git a/docs/README.md b/docs/README.md index 29dedb5..9363b93 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,6 +10,6 @@ This directory contains architecture, development, and deployment documentation ## Quick Links -- Root README — to be added in Step 6 +- [Root README](../README.md) - [Deploy Skeleton](../deploy/README.md) - [Project Brief](project-brief.md) — Original product brief and confirmed stack *(FN-001)* diff --git a/docs/deployment.md b/docs/deployment.md index e2206f1..d301f98 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -16,7 +16,7 @@ This document covers the scaffold-level deployment assumptions created in FN-002 ## Deployment Files -The following files will be added as part of the Docker Compose skeleton (Step 5): +The following deployment files are part of the scaffold: | File | Purpose | |------|---------| diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32437a6..707e929 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,8 @@ importers: .: {} + apps/api: {} + apps/web: dependencies: react: From e9ec1f46d9952f66dc0d50869dd73b1f46a1c211 Mon Sep 17 00:00:00 2001 From: Fusion Date: Thu, 14 May 2026 02:26:47 +0200 Subject: [PATCH 11/11] fix(FN-002): ignore .fusion internals in git --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index f7f899a..0dd3816 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,9 @@ dist/ # Docker volumes docker-volumes/ +# Fusion internals +.fusion/ + # Misc .cache/ .temp/