docs: add rendered developer and player handbook

This commit is contained in:
2026-07-28 23:59:52 +00:00
parent 1181f6f4e0
commit bf3f73708e
18 changed files with 2216 additions and 3 deletions
+4
View File
@@ -119,6 +119,10 @@ packages/infra/prisma/generated/
# Docusaurus cache and generated files
.docusaurus
# VitePress documentation build output
docs/.vitepress/cache/
docs/.vitepress/dist/
# Serverless directories
.serverless/
+30 -1
View File
@@ -41,7 +41,8 @@ TypeScript로 호환 이관하는 pnpm 모노레포입니다. 기준 구현은
| `tools/integration-tests` | PostgreSQL/Redis 및 ref↔core 통합·차등 테스트 |
| `tools/frontend-legacy-parity` | 실제 Chromium 기반 화면·상호작용 비교 |
| `tools/legacy-db-migration` | 레거시 장기보존 데이터 CLI 이관 |
| `docs` | 런타임, 테스트, 배포 prefix와 운영 문서 |
| `tools/docs` | 플레이어 커맨드 문서 자동 생성 |
| `docs` | VitePress 핸드북과 런타임·테스트·운영 문서 |
런타임의 영속 입력은 PostgreSQL `input_event`가 담당합니다. game API가
요청을 기록하고 turn daemon이 claim한 뒤, 게임 상태·로그·예약 턴·결과와
@@ -60,6 +61,7 @@ battle simulation 등 해당 기능의 계약에만 사용하며 게임 mutation
- Vue 3 + Pinia + Vue Router + Vite
- PostgreSQL + Prisma, Redis
- Vitest와 Playwright/Chromium
- VitePress 정적 HTML 문서 사이트
package manager 버전은 루트 `package.json``packageManager`를 따릅니다.
현재 값은 `pnpm@11.17.0`입니다. 저장소는 Node `engines`를 고정하지 않으므로
@@ -144,6 +146,29 @@ pnpm --filter @sammo-ts/game-api dev
pnpm --filter @sammo-ts/game-engine dev
```
## 문서 사이트
개발자 아키텍처·파일 흐름·핵심 클래스와 플레이어 커맨드·시기별 가이드는
[`docs/index.md`](docs/index.md)에서 시작합니다. VitePress 개발 서버와 정적 HTML
빌드는 다음 명령으로 실행합니다.
```sh
pnpm docs:dev
pnpm docs:build
pnpm docs:preview
```
`docs:dev``docs:build`는 먼저 `pnpm docs:generate`를 실행합니다. 이 단계는
현재 장수·국가 명령 등록부와 각 `commandSpec`에서
`docs/user/command-catalog.generated.md`를 다시 만듭니다. 생성 파일을 직접
수정하지 말고 명령 정의나 생성기를 고쳐 주세요. 정적 HTML은
`docs/.vitepress/dist`에 생성되며 Git에서 제외됩니다.
핸드북이 조사한 코드 기준은
[`docs/reference-baseline.md`](docs/reference-baseline.md)에 고정해 두었습니다.
기능 리팩터링 뒤에는 기준 commit부터 현재 commit까지의 diff를 대조하고 관련
페이지, 생성 목록과 기준선을 같은 변경에서 갱신해 주세요.
## DB schema와 migration
Gateway는 기본적으로 PostgreSQL `public` schema를 사용하고, 게임은
@@ -204,6 +229,10 @@ worktree 흐름을 사용합니다.
## 핵심 문서
- [core2026 핸드북](docs/index.md)
- [문서 기준 커밋](docs/reference-baseline.md)
- [개발자 핸드북](docs/developer/index.md)
- [플레이어 가이드](docs/user/index.md)
- [테스트 정책](docs/testing-policy.md)
- [테스트 suite 감사](docs/test-suite-audit.md)
- [통합 테스트](docs/integration-tests.md)
+61
View File
@@ -0,0 +1,61 @@
import { defineConfig } from 'vitepress';
export default defineConfig({
lang: 'ko-KR',
title: 'core2026 핸드북',
description: 'SAM core2026 개발자 내부 문서와 플레이어 이용 가이드',
cleanUrls: true,
lastUpdated: true,
head: [['meta', { name: 'theme-color', content: '#6b3f22' }]],
themeConfig: {
siteTitle: 'core2026 핸드북',
nav: [
{ text: '개발자', link: '/developer/' },
{ text: '플레이어', link: '/user/' },
{ text: '기준 커밋', link: '/reference-baseline' },
],
sidebar: {
'/developer/': [
{
text: '개발자 핸드북',
items: [
{ text: '시작하기', link: '/developer/' },
{ text: '시스템 아키텍처', link: '/developer/system-architecture' },
{ text: '요청·턴·저장 흐름', link: '/developer/request-turn-persistence' },
{ text: '도메인 로직과 핵심 클래스', link: '/developer/domain-and-classes' },
{ text: '파일 지도와 변경 절차', link: '/developer/code-map' },
],
},
],
'/user/': [
{
text: '플레이어 가이드',
items: [
{ text: '시작하기', link: '/user/' },
{ text: '시간과 턴', link: '/user/time-and-turns' },
{ text: '커맨드와 실행 시기', link: '/user/commands-and-timing' },
{ text: '커맨드 전체 목록', link: '/user/command-catalog.generated' },
{ text: '국가 운영과 주요 기능', link: '/user/nation-and-features' },
],
},
],
},
search: {
provider: 'local',
},
outline: {
level: [2, 3],
label: '이 페이지에서',
},
docFooter: {
prev: '이전',
next: '다음',
},
lastUpdated: {
text: '마지막 변경',
},
footer: {
message: '현재 구현을 설명하는 문서입니다. 기준 커밋과 검증 범위를 함께 확인해 주세요.',
},
},
});
+83
View File
@@ -0,0 +1,83 @@
# 파일 지도와 변경 절차
## 최상위 지도
```text
core2026/
├─ app/
│ ├─ gateway-frontend/ 계정·로비·관리 UI
│ ├─ gateway-api/ 인증·profile·operation·orchestrator
│ ├─ game-frontend/ profile 게임 SPA
│ ├─ game-api/ tRPC/SSE와 mutation 수신
│ └─ game-engine/ turn daemon과 persistence orchestration
├─ packages/
│ ├─ common/ 공통 타입·직렬화·RNG
│ ├─ logic/ 명령·constraint·전투·trigger·scenario
│ ├─ infra/ Prisma·PostgreSQL·Redis
│ └─ tools-scripts/ resource schema 도구
├─ resources/ scenario·map·unitset·명령 profile
├─ tools/
│ ├─ integration-tests/ DB/Redis 및 ref 차등
│ ├─ frontend-legacy-parity/ 실제 Chromium 비교
│ ├─ legacy-db-migration/ 장기보존 데이터 CLI
│ ├─ build-scripts/ profile resource 복사 기반 build 도구
│ └─ docs/ 문서 생성 도구
└─ docs/ VitePress 소스와 상세 설계 문서
```
## 기능에서 파일로
| 기능 | 시작점 | 핵심 하위 경계 |
| -------------- | --------------------------------------------- | ------------------------------------- |
| 로그인·session | `gateway-api/src/router.ts` | `auth/*`, `account/router.ts`, Redis |
| profile 운영 | `gateway-api/src/adminRouter.ts` | `orchestrator/*`, gateway Prisma |
| 게임 인증 | `game-api/src/context.ts`, `router/auth` | session actor, profile·sanction |
| 메인 턴 입력 | `game-frontend/src/stores/mainDashboard.ts` | `router/turns`, `turns/*` |
| command 실행 | `game-engine/src/turn/reservedTurnHandler.ts` | `packages/logic/src/actions/turn` |
| 월간 lifecycle | `game-engine/src/turn/turnDaemon.ts` | `monthly*Handler.ts`, scenario events |
| 전투 | `actions/turn/general/che_출병.ts` | `packages/logic/src/war` |
| DB load/flush | `game-engine/src/turn/worldLoader.ts` | `databaseHooks.ts`, `packages/infra` |
| 공개/국가 정보 | `game-api/src/router/public`, `router/nation` | DTO와 redaction |
| 화면 parity | `game-frontend/src/views`, `styles` | `tools/frontend-legacy-parity` |
## 변경 절차
### API나 화면
router의 input, auth procedure, transaction과 response를 먼저 정한 뒤 frontend 호출부와 오류·loading 상태를
연결합니다. public prefix에서 direct navigation, asset, tRPC와 SSE URL을 확인합니다. UI를 바꾸면 실제
Chromium에서 ref와 geometry·computed style·interaction을 비교합니다.
### 도메인 로직
ref entry point부터 SQL·로그까지 호출 순서를 찾고, `packages/logic` 계산과 engine context/persistence를 함께
수정합니다. unit test만으로 끝내지 않고 fixed seed, 전체 state, RNG trace, 실패 side effect와 가능한
ref 차등을 확인합니다.
### DB
기존 migration을 고치지 않고 새 migration을 만듭니다. 빈 DB 전체 적용, 기존 DB 증분 적용, 재실행 no-op,
constraint/index와 runtime query, backup/restore 또는 rollback 경로를 확인합니다.
## 문서 사이트
```sh
pnpm install
pnpm docs:generate
pnpm docs:dev
pnpm docs:build
pnpm docs:preview
```
`docs:dev``docs:build`는 먼저 커맨드 목록을 생성합니다. 정적 결과는 `docs/.vitepress/dist`에 생기며
Git에 포함하지 않습니다. 문서만 바꿔도 Prettier, 생성 결과의 clean diff, VitePress build와 내부 링크를
검증해 주세요.
## 리팩터링 체크포인트
- [문서 기준선](../reference-baseline.md)과 현재 commit의 diff를 먼저 봅니다.
- 파일 이동만 했는지 소유권·transaction·호출 순서까지 바뀌었는지 구분합니다.
- public API, DB schema, action key와 resource format은 내부 파일명보다 강한 계약입니다.
- `rg`로 이 페이지의 이전 경로가 남았는지 확인합니다.
- command key를 바꿨다면 저장된 예약 턴과 profile resource의 migration/호환 경로가 필요합니다.
- 문서의 광범위한 “완료” 표현은 관련 integration·ref 차등·Chromium 증거가 있을 때만 사용합니다.
+106
View File
@@ -0,0 +1,106 @@
# 도메인 로직과 핵심 클래스
## 핵심 entity
`packages/logic/src/domain/entities.ts`가 HTTP나 Prisma row에 종속되지 않은 `General`, `City`, `Nation`,
`Troop`, diplomacy와 trigger 상태를 정의합니다. engine 전용 `TurnGeneral`, `TurnWorldState`,
`TurnEvent``app/game-engine/src/turn/types.ts`에서 실행 시간·예약 턴·월간 상태를 더합니다.
| entity | 핵심 책임 |
| ------- | -------------------------------------------------------------------------- |
| General | 능력치, 경험·공헌, 소속·도시·부대, 병력·훈련·사기, 자원, 특기·아이템, meta |
| City | 소유 국가, 규모, 인구·농업·상업·치안, 수비·성벽, 보급·전선 상태 |
| Nation | 수도, 국고·군량, 등급·국가 타입, 기술과 국가 meta |
| Troop | 부대장·구성원과 부대 상태 |
| World | 현재 연·월, 최근 턴 시각, scenario config/meta와 전체 entity collection |
Prisma row를 곧바로 게임 규칙에 넘기지 않습니다. `worldLoader.ts`와 API의 row mapper가 DB 표현을 domain
표현으로 바꾸고, flush 계층이 반대 변환을 담당합니다.
## 명령 정의
`GeneralActionDefinition`은 장수·국가 예약 명령이 공유하는 계약입니다.
- `key`, `name`: 저장 key와 화면 표시명
- `parseArgs`: 외부 입력을 실행 인자로 변환
- `buildPermissionConstraints`: 예약 입력 자체를 허용할지 판단
- `buildMinConstraints`: command table에서 현재 가능한지 사전 판단
- `buildConstraints`: 실행 시점의 전체 조건
- `getPreReqTurn`, `getPostReqTurn`: 연속 실행과 재사용 대기
- `resolve`: domain state와 effect를 계산
각 파일의 `commandSpec`은 category, 인자 필요 여부, schema와 definition factory를 등록합니다.
`GENERAL_TURN_COMMAND_KEYS`, `NATION_TURN_COMMAND_KEYS`가 전체 key 집합이며, `TurnCommandProfile`
profile별 subset을 선택합니다.
## Constraint 시스템
`packages/logic/src/constraints`는 “무엇이 필요한가”와 “현재 view가 무엇을 알고 있는가”를 분리합니다.
`ConstraintContext`에는 actor, city, nation, args, env와 평가 mode가 있고 `StateView`가 entity와 대상
정보를 제공합니다.
평가 결과는 다음 셋입니다.
- `allow`: 현재 정보로 조건을 만족합니다.
- `deny`: 이유가 확정된 실패입니다.
- `unknown`: 대상 입력이나 추가 state가 없어 아직 판정할 수 없습니다.
API command table은 `unknown`의 missing requirement가 대상 입력뿐이면 `needsInput`, 그 밖이면
`unknown`으로 보여 줍니다. 예약 뒤 실제 실행에서는 전체 context로 다시 판단합니다.
## 핵심 클래스와 조립 지점
### TurnDaemonLifecycle
`app/game-engine/src/lifecycle/turnDaemonLifecycle.ts`에 있습니다. clock, control queue, hook과 run budget을
조정하며 pause/resume/manual/scheduled run의 상태 전이를 소유합니다.
### DatabaseTurnDaemonLease
`app/game-engine/src/lifecycle/databaseTurnDaemonLease.ts`에 있습니다. profile별 단일 active owner와 fencing을
관리합니다. daemon 계산이 맞아도 lease를 잃었다면 결과를 저장하면 안 됩니다.
### InMemoryTurnWorld
`app/game-engine/src/turn/inMemoryWorld.ts`에 있습니다. entity map, dirty/create/delete set, log, message,
event, checkpoint와 월 변경을 소유합니다. `peekDirtyState()`는 저장할 변경을 보여 주고 성공한 flush 뒤
정리됩니다.
### EngineStateManager
`app/game-engine/src/turn/engineStateManager.ts`에 있습니다. world와 예약 턴 store 같은 mutable participant를
등록하고 계산 단위의 capture/restore/transaction을 제공합니다. PostgreSQL transaction을 대신하지 않고
실패한 계산의 메모리 rollback을 담당합니다.
### InMemoryReservedTurnStore와 ReservedTurnHandler
`reservedTurnStore.ts`는 장수 30칸·국가 12칸 예약 queue를 메모리에 유지합니다.
`reservedTurnHandler.ts`는 명령 loading, constraint, action context, AI fallback, progress/cooldown,
효과·로그와 queue rotation을 연결합니다.
### GeneralActionPipeline과 trigger module
`packages/logic/src/actions/engine.ts`, `triggers/*`는 명령 본체 전후의 특기·아이템·국가 특성 효과를
일정한 우선순위로 적용합니다. 같은 module 목록이라도 실행 순서가 결과와 RNG 소비를 바꿀 수 있습니다.
### WarEngine
`packages/logic/src/war/engine.ts`가 전투 resolution을, `war/actions.ts`와 trigger module이 확장 효과를,
`war/aftermath.ts`가 피해·점령·외교·후속 state를 계산합니다. `che_출병.ts`가 map, unit set, diplomacy,
time, seed와 aftermath를 조립하는 실제 장수 명령 entry입니다.
### GatewayOrchestrator
`app/gateway-api/src/orchestrator/gatewayOrchestrator.ts`가 DB의 profile desired state를 process state에
맞춥니다. `workspaceManager.ts`, `buildRunner.ts`, `seedProfileDatabase.ts`, `pm2ProcessManager.ts`
commit worktree 준비부터 build, seed, start/stop을 나눕니다.
## 새 명령을 추가할 때
1. 가장 가까운 ref command의 constraint, 실행 순서, RNG, 로그와 DB side effect를 조사합니다.
2. `packages/logic/src/actions/turn/{general,nation}`에 definition과 `commandSpec`을 작성합니다.
3. 해당 `*_TURN_COMMAND_KEYS`와 필요한 `resources/turn-commands` profile에 key를 등록합니다.
4. 인자가 있으면 Zod schema와 `app/game-api/src/turns/commandInput.ts`의 화면 입력 field를 연결합니다.
5. engine action context가 대상 entity·map·unit set·시간·seed를 완전하게 공급하는지 확인합니다.
6. permission/min/full 실패, 성공, 연속 턴, cooldown과 persistence를 테스트합니다.
7. `pnpm docs:generate`로 플레이어 커맨드 목록을 갱신하고 ref 매핑 문서를 함께 수정합니다.
+29
View File
@@ -0,0 +1,29 @@
# 개발자 핸드북
이 핸드북은 새 기능을 어디에 넣을지뿐 아니라 요청이 어떤 경계를 지나 상태로 남는지 설명합니다. 먼저
[문서 기준선](../reference-baseline.md)을 확인하고, 변경 성격에 따라 다음 순서로 읽어 주세요.
| 변경하려는 것 | 먼저 읽을 문서 | 주로 확인할 코드 |
| -------------------------- | ---------------------------------------------------- | -------------------------------------------- |
| 화면·라우팅·조회 API | [시스템 아키텍처](./system-architecture.md) | `app/*-frontend`, `app/*-api` |
| 턴 입력·게임 상태 mutation | [요청·턴·저장 흐름](./request-turn-persistence.md) | `app/game-api`, `app/game-engine` |
| 명령·전투·월간 로직 | [도메인 로직과 핵심 클래스](./domain-and-classes.md) | `packages/logic`, `app/game-engine/src/turn` |
| 새 파일 위치·검증 범위 | [파일 지도와 변경 절차](./code-map.md) | package manifest, test, docs |
## 읽을 때 지켜야 할 경계
- `packages/logic`의 순수 계산과 `app/game-engine`의 scheduling·persistence orchestration을 구분합니다.
- game API가 mutation을 받는 것과 engine이 world mutation을 확정하는 것은 다른 단계입니다.
- PostgreSQL `input_event`가 내구성 있는 작업 경계이며 Redis는 realtime fan-out과 일부 보조 worker
통신에 사용됩니다.
- 로그인한 사용자, 게임 장수, 국가 직책은 같은 개념이 아닙니다. actor와 소유권은 session에서
서버가 결정합니다.
- `resources/`의 scenario·map·unit set·turn-command profile이 런타임 구성을 바꿉니다. 기본 TypeScript
목록만 보고 실제 profile을 단정하지 않습니다.
- ref 호환 변경은 결과뿐 아니라 판정·정렬·반올림·RNG 소비·로그·저장 순서를 비교합니다.
## 기존 상세 문서와의 관계
이 핸드북은 탐색용 상위 지도입니다. 상세한 호환 근거와 테스트 절차는 `docs/architecture/*`,
`docs/integration-tests.md`, `docs/frontend-legacy-parity.md`에 유지합니다. 상위 작업공간의
`../docs/ref-core2026-mapping.md`는 ref entry point와 core 구현의 end-to-end 대응 인덱스입니다.
@@ -0,0 +1,99 @@
# 요청·턴·저장 흐름
## 조회 요청
일반 query는 다음 경로를 따릅니다.
```text
Vue view/store
-> tRPC client
-> game-api router
-> session actor + 입력 validation
-> Prisma query
-> 권한에 맞춘 DTO/redaction
-> 화면 상태
```
조회는 engine의 in-memory object를 직접 공유하지 않습니다. 따라서 daemon이 transaction을 commit하기 전의
중간 계산은 API query에 노출되지 않습니다.
## API가 직접 끝내는 mutation
예약 턴, 메시지처럼 API가 DB에서 완결할 수 있는 변경도 `input_event`를 사용합니다.
1. `Idempotency-Key`와 tRPC path로 요청 identity를 만듭니다.
2. `app/game-api/src/inputEventBoundary.ts`가 중복·처리 상태를 확인합니다.
3. 같은 PostgreSQL transaction에서 대상 row와 input event 결과를 저장합니다.
4. commit 뒤 응답하고 필요한 realtime 갱신을 알립니다.
동일 revision을 전제로 한 예약 턴 수정은 다른 탭이나 요청이 먼저 갱신했으면 충돌합니다. frontend는 최신
목록을 다시 불러와 사용자의 변경을 덮어쓰지 않게 해야 합니다.
## engine이 처리하는 mutation
```text
game-api mutation
-> input_event PENDING
-> daemon claim (FOR UPDATE SKIP LOCKED)
-> lease/fencing 확인
-> EngineStateManager savepoint
-> command/turn/monthly handler가 InMemoryTurnWorld 변경
-> world + 예약 턴 + log + message + event 결과 flush
-> input_event COMPLETED를 같은 DB transaction으로 commit
-> commit 이후 realtime 신호
```
계산이나 DB 쓰기가 실패하면 `EngineStateManager`가 등록된 in-memory participant를 savepoint로 되돌립니다.
DB transaction도 commit되지 않아 메모리와 DB의 부분 진행을 피합니다. lease를 잃은 worker는 stale 결과를
commit할 수 없어야 합니다.
## 한 tick의 처리
`TurnDaemonLifecycle`은 clock과 schedule에서 다음 실행 시점을 구합니다. 턴을 시작하면
`InMemoryTurnProcessor``InMemoryTurnWorld``turnTime`, 그다음 `general.id` 순서로 실행 대상을
결정합니다. checkpoint는 재시작 시 이미 처리한 동일 시점의 장수를 건너뛰는 기준입니다.
장수 한 명의 예약 명령은 대략 다음 순서입니다.
1. `InMemoryReservedTurnStore`에서 첫 예약 명령을 읽습니다.
2. 명령 key를 `GeneralTurnCommandLoader` 또는 `NationTurnCommandLoader`로 불러옵니다.
3. `actionContextBuilder`가 대상 도시·국가·장수, map, unit set, 외교, 시간과 RNG를 구성합니다.
4. permission/min/full constraint를 목적에 맞게 평가합니다.
5. 선행 턴이 있으면 진행 상태를 쌓고, 완성된 시점에 `resolve()`를 실행합니다.
6. effect와 직접 변경을 world에 반영하고 로그·메시지·후속 턴 시간을 기록합니다.
7. 실행된 queue를 당기고 끝에 기본 `휴식`을 채웁니다.
예약 시 통과와 실행 시 성공은 같지 않습니다. 그 사이 자원, 도시 소유, 외교, 직책이 바뀔 수 있으므로 full
constraint는 실행 순간 다시 평가됩니다.
## 월 변경 경계
`InMemoryTurnWorld.advanceMonth()`는 다음 순서를 보존합니다.
1. 다음 연·월을 계산합니다.
2. `beforeMonthChanged` handler를 등록 순서대로 실행합니다.
3. world의 현재 연·월을 바꿉니다.
4. `onMonthChanged` handler를 등록 순서대로 실행합니다.
5. 연도가 바뀌었으면 `onYearChanged`를 실행합니다.
`turnDaemon.ts``composeCalendarHandlers()` 순서에는 월간 event, 수입, 연감, PRE_MONTH 상태 정리,
국가 명령, 국가 통계, 외교, 전쟁 설정, 방랑, 국가 수, 통일, 토너먼트, 경매, 전선 상태가 포함됩니다.
이 순서는 ref의 관찰 가능한 결과와 RNG·persistence에 영향을 주므로 리팩터링 시 단순 정렬하지 않습니다.
## RNG 경계
게임 결과에 영향을 주는 난수는 `LiteHashDRBG``RandUtil`을 사용합니다. seed에는 hidden seed와
action/month/general 같은 context가 직렬화됩니다. main RNG의 소비 순서를 유지해야 하는 로직과 독립된
재현 가능 substream을 써야 하는 fallback을 구분합니다. authoritative path에 `Math.random()`을 넣지
않습니다.
## 장애를 추적할 위치
| 증상 | 우선 확인 |
| ----------------------------- | --------------------------------------------------------------------- |
| 같은 mutation이 두 번 보임 | idempotency key, `input_event` 상태·attempt |
| 요청은 성공했지만 화면이 늦음 | DB commit 결과, Redis/SSE fan-out |
| daemon이 처리하지 않음 | profile gate, pause 상태, lease owner, PENDING claim |
| 재시작 뒤 일부 턴 반복 | checkpoint와 general turn ordering |
| DB와 메모리가 다름 | `EngineStateManager`, `databaseHooks`, flush 대상 누락 |
| ref와 결과가 다름 | constraint 순서, action context, RNG trace, rounding, log/effect 순서 |
+99
View File
@@ -0,0 +1,99 @@
# 시스템 아키텍처
## 런타임 구성
```text
브라우저
├─ /gateway/ ─ gateway-frontend ─ tRPC ─ gateway-api
│ ├─ PostgreSQL public schema
│ ├─ Redis session
│ └─ orchestrator ─ Git worktree / build / PM2
└─ /{profile}/ ─ game-frontend ─ tRPC/SSE ─ game-api
├─ profile별 PostgreSQL schema
├─ Redis realtime/battle worker
└─ input_event ─ game-engine
├─ in-memory world
└─ transactional flush
```
Gateway는 계정·session·profile lifecycle을, game 계층은 한 profile의 플레이와 턴 진행을 소유합니다.
`/gateway`, `/che`, `/hwe` 같은 외부 prefix와 `/image/*`의 Caddy 소유권은 애플리케이션 밖의 배포
계약입니다.
## 애플리케이션
### gateway-frontend
`app/gateway-frontend/src/main.ts`가 Vue 앱과 router를 시작합니다. 가입·로그인·계정·로비·관리자 화면은
`src/views`에 있고, profile 선택 뒤 game frontend로 이동합니다. 브라우저에 보이는 `VITE_*` 값은
공개 설정이며 secret이 아닙니다.
### gateway-api
`app/gateway-api/src/server.ts``router.ts`가 HTTP/tRPC 경계입니다.
- `auth/*`, `account/router.ts`: Kakao·로컬 계정, session 발급·폐기, 사용자 저장소
- `lobby/profileStatusService.ts`: 사용자에게 보여 줄 profile 상태
- `adminRouter.ts`, `adminAuth.ts`: 관리자 권한과 operation 입력
- `orchestrator/*`: 원하는 profile 상태를 Git worktree, build, seed, PM2 프로세스에 반영
Gateway DB는 기본 `public` schema를 사용합니다. game profile DB를 직접 게임 로직의 source of truth로
대체하지 않습니다.
### game-frontend
`app/game-frontend/src/main.ts`가 profile base path 아래 Vue SPA를 시작합니다. `src/views`가 공개 정보,
메인 턴 입력, 국가 운영, 경매·토너먼트·기록 화면을 나누고 `src/stores/mainDashboard.ts`가 메인 화면의
query, 예약 턴 revision, mutation과 realtime refresh를 조정합니다.
UI는 서버가 반환한 command table의 `available`, `blocked`, `needsInput`, `unknown` 상태를 사용합니다.
클라이언트가 장수 ID나 직책을 보냈다는 이유만으로 권한이 생기지 않습니다.
### game-api
`app/game-api/src/server.ts``router.ts`가 query/mutation을 공개합니다. router는 기능 단위로
`src/router/*`에 나뉩니다.
- 읽기: world, public, directory, ranking, yearbook, dynasty 등은 권한·redaction을 거쳐 DB에서 조회합니다.
- 플레이: turns, join, nation, troop, diplomacy, messages, auction, betting, tournament 등이 있습니다.
- mutation: `inputEventBoundary.ts`가 idempotency와 PostgreSQL 작업 경계를 만듭니다.
- realtime: SSE와 Redis 알림은 commit 이후 화면 갱신 신호입니다.
### game-engine
`app/game-engine/src/index.ts`가 daemon entry point이고 `src/turn/turnDaemon.ts`가 profile resource,
world snapshot, command registry, calendar handler, persistence hook과 lease를 조립합니다.
한 daemon owner가 `TurnDaemonLifecycle`을 통해 정해진 tick과 durable inbox를 처리합니다. 실제 장수·도시·국가
상태는 `InMemoryTurnWorld`에서 계산하고, 성공한 작업만 `databaseHooks.ts`를 통해 PostgreSQL에 flush합니다.
lease/fencing은 오래된 daemon owner의 commit을 막습니다.
## 공유 package
| package | 책임 | 넣지 말아야 할 것 |
| ------------------------ | ------------------------------------------------------------------------------- | ---------------------------------- |
| `packages/common` | 공통 type, 직렬화, `LiteHashDRBG`, `RandUtil`, session/sanction 유틸리티 | profile DB orchestration |
| `packages/logic` | entity, constraint, command, battle, trigger, scenario parsing 같은 도메인 계산 | HTTP·Vue·Prisma transaction 소유권 |
| `packages/infra` | Prisma client, PostgreSQL·Redis connector, log와 turn-engine DB adapter | 게임 규칙 결정 |
| `packages/tools-scripts` | resource schema 생성·검증 | 런타임 요청 처리 |
## 데이터와 구성
- `packages/infra/prisma/schema.gateway.prisma`: 계정·profile·operation 같은 gateway 모델
- `packages/infra/prisma/schema.game.prisma`: profile별 world·general·city·nation·turn·event·log 모델
- `resources/scenario`: 시작 연도, 상수, 월간 event 등 scenario 정의
- `resources/map`, `resources/unitset`: 지형과 병종 정의
- `resources/turn-commands`: profile별 허용 명령 목록
새 persistence field는 schema와 새 migration만으로 끝나지 않습니다. domain type, loader, in-memory dirty
tracking, transaction flush, reload 검증까지 연결해야 합니다.
## 인증·권한 모델
Gateway session은 사용자 identity를, game token은 profile과 게임 역할을 전달합니다. game API는 session으로
내 장수를 조회한 뒤 그 장수의 국가·직책·sanction과 대상 resource의 관계를 판단합니다. 공개 endpoint도
비공개 국가 정보, 타 사용자 archive, 비밀 명령을 DTO에서 제거해야 합니다.
권한 변경을 검증할 때는 무인증, 일반 사용자, 본인, 같은 국가, 다른 국가, NPC, 직책 보유자, sanction
적용자를 필요한 범위에서 나눕니다. 거부된 mutation은 world와 queue에 side effect를 남기지 않아야 합니다.
+32
View File
@@ -0,0 +1,32 @@
---
layout: home
hero:
name: core2026 핸드북
text: 구현과 플레이를 한곳에서 설명합니다
tagline: 현재 코드의 아키텍처·실행 흐름·핵심 클래스와 커맨드·시기별 이용 방법을 연결한 문서입니다.
actions:
- theme: brand
text: 개발자 핸드북
link: /developer/
- theme: alt
text: 플레이어 가이드
link: /user/
features:
- title: 코드에서 실행까지
details: frontend, tRPC, input_event, daemon, in-memory world와 PostgreSQL flush의 실제 경계를 따라갑니다.
- title: 커맨드와 시기
details: 장수·국가 커맨드 전체 목록을 소스에서 생성하고, 언제 왜 실행 가능하거나 막히는지 설명합니다.
- title: 고정된 기준선
details: 문서가 조사한 Git 기준 커밋과 재검증 지점을 밝혀 이후 리팩터링의 출발점을 남깁니다.
---
## 문서 성격
이 사이트는 `report/`의 작업 일지가 아니라 제품 저장소 안에서 계속 갱신하는 핸드북입니다.
개발자는 [시스템 아키텍처](./developer/system-architecture.md)부터, 플레이어는
[시간과 턴](./user/time-and-turns.md)부터 읽으면 됩니다.
문서의 사실관계는 [기준 커밋](./reference-baseline.md)의 코드에서 확인했습니다. 기능을 변경했다면 같은
작업에서 관련 페이지와 자동 생성 커맨드 목록을 함께 갱신해 주세요.
+39
View File
@@ -0,0 +1,39 @@
# 문서 기준선
## 코드 기준 커밋
이 핸드북의 최초 전면 갱신은 다음 상태를 기준으로 조사했습니다.
| 항목 | 값 |
| ----------- | ------------------------------------------ |
| 저장소 | `devsam/core2026.git` |
| 브랜치 | `main` |
| 기준 커밋 | `1181f6f4e03cbed77b1c40b6b572585f6e395a2c` |
| 기준 일자 | 2026-07-28 |
| 레거시 위치 | 형제 저장소 `../ref/sam``devel` |
기준 커밋은 “이 버전이 완성됐다”는 선언이 아니라 문장과 소스 경로를 다시 대조할 출발점입니다. 이후
리팩터링에서 설명과 코드가 어긋나면 다음 순서로 갱신해 주세요.
1. 이 페이지의 기준 커밋과 현재 `main` 사이의 변경 파일을 확인합니다.
2. 해당 문서가 가리키는 entry point, 호출 순서, transaction과 오류 경로를 다시 추적합니다.
3. 커맨드 등록부를 바꿨다면 `pnpm docs:generate`로 생성 페이지의 차이를 확인합니다.
4. `pnpm docs:build`로 링크와 정적 HTML 생성을 검증합니다.
5. 기준 커밋과 기준 일자를 현재 조사한 commit으로 바꾸고, 검증하지 못한 범위를 명시합니다.
## 사실 수준
- 이 핸드북의 아키텍처·파일·클래스 설명은 기준 커밋의 정적 코드와 기존 검증 문서를 교차 확인한
결과입니다.
- `input_event` transaction, daemon lease, ref 차등, 실제 Chromium 같은 동작 증명은 각 테스트와
기존 상세 문서가 담당합니다. 이 핸드북 자체의 HTML 빌드가 제품 동작을 다시 증명하지는 않습니다.
- profile과 scenario가 명령 목록·상수·맵·병종을 바꿀 수 있으므로 플레이어 화면의 현재 가능 여부가
정적 표보다 우선입니다.
## 상세 근거 문서
- [레거시 이관 제약](./architecture/rewrite-constraints.md)
- [턴 daemon lifecycle](./architecture/turn-daemon-lifecycle.md)
- [PostgreSQL schema](./architecture/postgres-schema.md)
- [프론트엔드 CSS 구조](./frontend-css-architecture.md)
- [레거시 화면 비교](./frontend-legacy-parity.md)
+188
View File
@@ -0,0 +1,188 @@
---
title: 커맨드 전체 목록
outline: deep
---
<!-- 이 파일은 tools/docs/generate-command-reference.mjs가 생성합니다. 직접 수정하지 말아 주세요. -->
# 커맨드 전체 목록
이 페이지는 현재 소스의 명령 등록부와 각 `commandSpec`에서 자동 생성됩니다.
총 90개이며, profile의 `resources/turn-commands/*.json` 설정에 따라 실제 서버에서 일부가 제외될 수
있습니다. “기본 실행 단위”는 정의에 고정된 선행 턴만 표시합니다. 자원, 신분, 도시, 외교, 시나리오 시점처럼
실행 순간에 달라지는 조건은 [커맨드와 실행 시기](./commands-and-timing.md)를 확인해 주세요.
## 장수 커맨드
등록된 명령은 55개입니다.
### 전략
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| -------------- | ------------------ | -------------- | -------------- |
| `che_거병` | 거병 | 없음 | 1턴 |
| `che_임관` | 임관 | 필요 | 1턴 |
| `che_랜덤임관` | 무작위 국가로 임관 | 없음 | 1턴 |
| `che_건국` | 건국 | 필요 | 1턴 |
### 군사
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| -------------- | --------- | -------------- | -------------- |
| `che_귀환` | 귀환 | 없음 | 1턴 |
| `che_훈련` | 훈련 | 없음 | 1턴 |
| `cr_맹훈련` | 맹훈련 | 없음 | 1턴 |
| `che_전투태세` | 전투태세 | 없음 | 1턴 |
| `che_단련` | 단련 | 없음 | 1턴 |
| `che_숙련전환` | 숙련전환 | 필요 | 1턴 |
| `che_사기진작` | 사기진작 | 없음 | 1턴 |
| `che_출병` | 출병 | 필요 | 1턴 |
| `che_집합` | 집합 | 없음 | 1턴 |
| `che_모병` | 모병 | 필요 | 1턴 |
| `che_소집해제` | 소집해제 | 없음 | 1턴 |
| `che_이동` | 이동 | 필요 | 1턴 |
| `che_방랑` | 방랑 | 없음 | 1턴 |
| `che_첩보` | 첩보 | 필요 | 1턴 |
| `che_파괴` | 파괴 | 필요 | 1턴 |
| `che_선동` | 선동 | 필요 | 1턴 |
| `che_탈취` | 탈취 | 필요 | 1턴 |
| `che_강행` | 강행 | 필요 | 1턴 |
### 인사
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| ------------------ | ---------------- | -------------- | -------------- |
| `che_등용수락` | 등용수락 | 필요 | 1턴 |
| `che_장수대상임관` | 장수를 따라 임관 | 필요 | 1턴 |
| `che_인재탐색` | 인재탐색 | 없음 | 1턴 |
| `che_접경귀환` | 접경귀환 | 없음 | 1턴 |
| `che_하야` | 하야 | 없음 | 1턴 |
| `che_등용` | 등용 | 필요 | 1턴 |
### 국가
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| ---------------- | ---------------- | -------------- | -------------- |
| `cr_건국` | 건국 | 필요 | 1턴 |
| `che_무작위건국` | 무작위 도시 건국 | 필요 | 1턴 |
| `che_선양` | 선양 | 필요 | 1턴 |
| `che_모반시도` | 모반시도 | 없음 | 1턴 |
| `che_증여` | 증여 | 필요 | 1턴 |
| `che_해산` | 해산 | 없음 | 1턴 |
### 개인
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| -------------------- | ---------------- | -------------- | -------------- |
| `che_요양` | 요양 | 없음 | 1턴 |
| `che_견문` | 견문 | 없음 | 1턴 |
| `che_장비매매` | 장비매매 | 필요 | 1턴 |
| `che_내정특기초기화` | 내정 특기 초기화 | 없음 | 1턴 |
| `che_전투특기초기화` | 전투 특기 초기화 | 없음 | 1턴 |
| `che_군량매매` | 군량매매 | 필요 | 1턴 |
| `che_은퇴` | 은퇴 | 없음 | 1턴 |
| `휴식` | 휴식 | 없음 | 1턴 |
### 내정
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| -------------- | --------- | -------------- | -------------- |
| `che_주민선정` | 주민 선정 | 없음 | 1턴 |
| `che_정착장려` | 정착 장려 | 없음 | 1턴 |
| `che_농지개간` | 농지 개간 | 없음 | 1턴 |
| `che_상업투자` | 상업 투자 | 없음 | 1턴 |
| `che_기술연구` | 기술 연구 | 없음 | 1턴 |
| `che_치안강화` | 치안 강화 | 없음 | 1턴 |
| `che_수비강화` | 수비 강화 | 없음 | 1턴 |
| `che_성벽보수` | 성벽 보수 | 없음 | 1턴 |
| `che_징병` | 징병 | 필요 | 1턴 |
| `che_물자조달` | 물자조달 | 없음 | 1턴 |
| `che_헌납` | 헌납 | 필요 | 1턴 |
### 계략
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| ---------- | --------- | -------------- | -------------- |
| `che_화계` | 화계 | 필요 | 1턴 |
### 특수
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| ------------- | --------- | -------------- | -------------- |
| `che_NPC능동` | NPC능동 | 필요 | 1턴 |
## 국가 커맨드
등록된 명령은 35개입니다.
### 휴식
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| ------- | --------- | -------------- | -------------- |
| `휴식` | 휴식 | 없음 | 1턴 |
### 인사
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| ------------------ | -------------- | -------------- | -------------- |
| `che_포상` | 포상 | 필요 | 1턴 |
| `che_부대탈퇴지시` | 부대 탈퇴 지시 | 필요 | 1턴 |
| `che_발령` | 발령 | 필요 | 1턴 |
| `che_몰수` | 몰수 | 필요 | 1턴 |
### 외교
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| -------------------- | ---------------- | -------------- | -------------- |
| `che_선전포고` | 선전포고 | 필요 | 1턴 |
| `che_종전제의` | 종전 제의 | 필요 | 1턴 |
| `che_불가침제의` | 불가침 제의 | 필요 | 1턴 |
| `che_불가침파기제의` | 불가침 파기 제의 | 필요 | 1턴 |
| `che_이호경식` | 이호경식 | 필요 | 1턴 |
| `che_급습` | 급습 | 필요 | 1턴 |
| `che_물자원조` | 원조 | 필요 | 1턴 |
### 전략
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| -------------- | --------- | -------------- | -------------- |
| `che_의병모집` | 의병모집 | 없음 | 1턴 |
| `che_허보` | 허보 | 필요 | 1턴 |
| `che_필사즉생` | 필사즉생 | 없음 | 1턴 |
| `che_백성동원` | 백성동원 | 필요 | 1턴 |
| `che_수몰` | 수몰 | 필요 | 1턴 |
| `che_피장파장` | 피장파장 | 필요 | 1턴 |
### 특수
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| -------------------- | ------------- | -------------- | -------------- |
| `che_초토화` | 초토화 | 필요 | 1턴 |
| `cr_인구이동` | 인구이동 | 필요 | 1턴 |
| `event_원융노병연구` | 원융노병 연구 | 없음 | 24턴 연속 실행 |
| `event_화시병연구` | 화시병 연구 | 없음 | 12턴 연속 실행 |
| `event_음귀병연구` | 음귀병 연구 | 없음 | 12턴 연속 실행 |
| `event_대검병연구` | 대검병 연구 | 없음 | 12턴 연속 실행 |
| `event_화륜차연구` | 화륜차 연구 | 없음 | 24턴 연속 실행 |
| `event_산저병연구` | 산저병 연구 | 없음 | 12턴 연속 실행 |
| `event_극병연구` | 극병 연구 | 없음 | 24턴 연속 실행 |
| `event_상병연구` | 상병 연구 | 없음 | 24턴 연속 실행 |
| `event_무희연구` | 무희 연구 | 없음 | 24턴 연속 실행 |
### 국가
| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |
| -------------------- | ---------------- | -------------- | -------------- |
| `che_천도` | 천도 | 필요 | 1턴 |
| `che_국호변경` | 국호변경 | 필요 | 1턴 |
| `che_무작위수도이전` | 무작위 수도 이전 | 없음 | 1턴 |
| `che_국기변경` | 국기변경 | 필요 | 1턴 |
| `che_증축` | 증축 | 없음 | 1턴 |
| `che_감축` | 감축 | 없음 | 1턴 |
## 생성 근거
- 등록 순서: `packages/logic/src/actions/turn/general/index.ts`,
`packages/logic/src/actions/turn/nation/index.ts`
- 표시명·분류·입력 여부·다중 턴: 각 명령 모듈의 `commandSpec``ActionDefinition`
- 생성 명령: `pnpm docs:generate`
+98
View File
@@ -0,0 +1,98 @@
# 커맨드와 실행 시기
## 화면의 상태 표시
서버는 내 장수, 도시, 국가와 현재 시점으로 각 커맨드를 사전 평가합니다.
| 표시 | 의미 |
| -------------- | ----------------------------------------------------------------------------- |
| 사용 가능 | 현재 알려진 조건을 만족합니다. 실행 시 다시 검사합니다. |
| 대상 선택 필요 | 도시·장수·국가·수량 같은 입력을 고르면 판정할 수 있습니다. |
| 사용 불가 | 현재 확정된 조건을 만족하지 않으며 이유가 함께 표시됩니다. |
| 정보 부족 | 사전 화면에 없는 실행 context가 필요합니다. 실제 실행 성공을 뜻하지 않습니다. |
전체 key와 화면 이름은 [자동 생성 커맨드 목록](./command-catalog.generated.md)에 있습니다.
## 시기별로 보는 장수 커맨드
### 재야·방랑 상태
임관, 무작위 임관, 장수를 따라 임관, 거병, 건국 계열이 중심입니다. 서버의 가입 방식이 “무작위 임관만”
이면 직접 국가를 고르는 임관·등용장 수락은 막힙니다. 초반에는 국가별 인원 제한과 건국 가능 도시·시점도
적용될 수 있습니다.
### 국가 소속·도시 주둔
농지 개간, 상업 투자, 치안 강화, 수비 강화, 성벽 보수, 정착 장려, 기술 연구, 헌납처럼 도시·국가를
전제로 한 내정 커맨드를 사용할 수 있습니다. 대부분은 다음을 함께 검사합니다.
- 재야나 방랑 국가가 아닌지
- 내 장수가 실제 소속 도시를 점유하고 있는지
- 해당 도시가 보급 상태인지
- 목표 내정치가 이미 최대인지
- 필요한 금·쌀과 능력·병력이 있는지
### 병력이 있을 때
훈련, 사기 진작, 단련, 전투 태세, 숙련 전환, 이동·집합·귀환, 출병을 검토할 수 있습니다. 출병은 병력과
군량뿐 아니라 인접·경로, 대상 도시, 외교 관계, 보호 기간과 병종 조건을 실행 시점에 다시 확인합니다.
### 대상이 필요한 행동
등용, 증여, 첩보, 화계, 파괴, 선동, 탈취, 이동·출병 등은 대상이나 수량을 선택해야 합니다. 목록에 대상을
고를 수 없으면 이미 사라졌거나 현재 권한·공개 범위에서 선택할 수 없는 경우일 수 있습니다.
### 국가 이탈·신분 변경
하야, 은퇴, 방랑, 선양, 모반 시도, 해산은 장수 또는 국가의 소속과 생애 상태를 크게 바꿉니다. 군주인지,
국가가 방랑 상태인지, 건국 직후인지, 후계 대상이 유효한지 같은 추가 조건이 있습니다. 로그와 확인 문구를
읽고 가까운 후속 예턴도 함께 점검해 주세요.
## 시기별로 보는 국가 커맨드
국가 예턴은 국가 운영 권한이 있을 때 보입니다. 많은 국가 명령은 군주만 실행할 수 있고, 일부 화면 운영
기능은 직책별 권한을 따릅니다.
### 상시 국가 운영
포상, 발령, 부대 탈퇴 지시, 몰수, 물자 원조, 국호·국기 변경, 천도, 증축·감축, 인구 이동 등이 있습니다.
국고·군량, 수도와 대상 도시, 장수·직책, 보급과 도시 규모 조건을 확인합니다.
### 외교 상태에 따른 명령
선전포고는 기본 구현에서 scenario 시작 연도보다 적어도 1년 지난 뒤에 허용되며, 대상 국가와의 상태도
맞아야 합니다. 종전·불가침·불가침 파기 제안은 현재 외교 관계와 최소 기간을 검사합니다. 상대가 제안을
수락하는 즉시 행동은 일반 예약 국가 턴과 별도의 승인 흐름으로 처리됩니다.
### 전략 커맨드
의병 모집, 허보, 필사즉생, 백성 동원, 이호경식, 수몰, 급습, 피장파장, 초토화는 군주 여부와 전략 커맨드
재사용 가능 상태를 검사합니다. 명령마다 대상 도시·국가, 교전, 수도, 자원과 추가 상태가 다릅니다.
초반 보호 기간에는 의병 모집·몰수 등 일부 행동이 제한됩니다.
### 특수 병종 연구
`event_*연구` 명령은 scenario가 해당 명령을 profile에 넣고 필요한 연구 flag가 아직 없을 때 사용할 수
있습니다. 군주와 국고 조건을 검사하며 11턴 또는 23턴을 연속으로 쌓은 뒤 비용을 지불하고 병종 사용
권한을 얻습니다. 소스의 선행 턴 값 11·23은 완료 턴을 포함하지 않으므로 화면에서 필요한 전체 실행은
각각 12턴·24턴입니다.
## 보호 기간과 연도
scenario의 `startYear``openingPartYear`가 초반 제한의 기준입니다. 다음은 대표적인 동작이며 서버별
상수에 따라 실제 연도가 달라집니다.
- 출병은 예약 사전 판단과 실제 실행 모두 보호 기간을 고려합니다.
- 방랑, 의병 모집, 몰수 같은 일부 국가 변화도 초반에는 제한됩니다.
- 기술 연구의 허용 기술 단계와 임관 인원 제한은 경과 연도에 따라 넓어질 수 있습니다.
- 선전포고는 시작 직후가 아니라 상대 연도 1 이상에서 가능합니다.
“몇 년 몇 월부터”를 고정 문구로 외우기보다 현재 서버의 시작 연도와 화면의 불가 이유를 확인해 주세요.
## 실패했을 때
1. 메인 로그에서 실패 이유를 확인합니다.
2. 자원·병력, 소속 도시와 보급, 직책, 대상과 외교 상태를 다시 확인합니다.
3. 여러 턴 뒤 실행될 명령이면 앞선 예턴이 상태를 바꾸는지 살펴봅니다.
4. command table을 새로 불러와 대상 목록과 가능 상태를 갱신합니다.
5. 동일 요청을 반복 전송하지 말고 화면이 최신 revision을 받은 뒤 다시 저장해 주세요.
+37
View File
@@ -0,0 +1,37 @@
# 플레이어 가이드
core2026에서는 지금 누르는 버튼이 즉시 모든 결과를 만드는 것이 아니라, 많은 행동을 **예턴**에 넣고 내
장수의 다음 턴에 실행합니다. 국가 커맨드도 같은 방식이지만 군주·직책과 국가 상태에 따라 이용 범위가
달라집니다.
## 처음 접속했을 때
1. Gateway에서 로그인하고 열려 있는 서버 profile을 선택합니다.
2. 게임에 장수가 없다면 장수 생성·참가 화면에서 이름, 능력치 등 필요한 정보를 정합니다.
3. 메인 화면에서 현재 도시·국가·자원·다음 턴 시각을 확인합니다.
4. 예턴 목록의 가까운 순서부터 실행할 커맨드를 지정합니다.
5. 국가에 소속됐다면 국가 정보·도시·장수·외교 화면에서 상황을 확인합니다.
서버마다 scenario, 시작 연도, 턴 간격, 가입 방식, map, 병종과 허용 커맨드가 다를 수 있습니다. 이 문서는
기본 구현을 설명하며 현재 화면의 가능/불가 표시와 운영 공지를 우선해 주세요.
## 어디서 무엇을 하나요?
| 메뉴 | 용도 |
| -------------------------- | ---------------------------------------------- |
| 메인 | 장수·국가 예턴, 현재 상태, 최근 기록 |
| 현재 도시·국가 정보 | 도시 내정치·보급·전선, 국가 자원·기술 |
| 국가 장수·인사 | 소속 장수와 직책, 권한이 있으면 인사·방침 설정 |
| 외교 | 국가 관계와 제안·교전 상태 확인 |
| 부대 | 부대 생성·가입·관리 |
| 경매·토너먼트·베팅 | 해당 기간에 열린 참가·입찰·예측 기능 |
| 게시판·메시지 | 공개/국가 소통과 개인 메시지 |
| 연감·명장·왕조·과거 플레이 | 현재·과거 기록 열람 |
| 내 정보·설정 | 표시·알림 등 개인 설정 |
## 다음에 읽을 문서
- 예턴이 언제 실행되는지: [시간과 턴](./time-and-turns.md)
- 어떤 조건에서 커맨드를 쓸 수 있는지: [커맨드와 실행 시기](./commands-and-timing.md)
- 현재 소스에 등록된 전체 목록: [커맨드 전체 목록](./command-catalog.generated.md)
- 국가 직책과 부가 기능: [국가 운영과 주요 기능](./nation-and-features.md)
+64
View File
@@ -0,0 +1,64 @@
# 국가 운영과 주요 기능
## 국가 직책과 정보 공개
국가에 소속됐다고 모든 국가 기능을 바꿀 수 있는 것은 아닙니다. 군주와 직책별 권한이 국가 예턴, 인사,
전략·재정 설정, 비밀 장수 정보와 정찰 차단 같은 기능에 적용됩니다. 같은 국가 장수에게만 보이는 정보도
있고, 공개 목록에는 일부 값이 가려질 수 있습니다.
내가 직접 URL이나 장수 번호를 입력해도 권한은 늘어나지 않습니다. 서버가 로그인 session에서 내 장수를
찾아 국가·직책·대상 관계를 판단합니다.
## 국가 운영 화면
| 화면 | 할 수 있는 일 |
| ----------- | --------------------------------------------- |
| 국가 정보 | 국호·국기, 수도, 국고·군량, 기술과 공지 확인 |
| 국가 도시 | 소유 도시의 내정·인구·방어·보급 상태 비교 |
| 국가 장수 | 소속 장수의 공개 정보와 직책 확인 |
| 인사 | 권한이 있으면 임명·해임, 추방, 봉록·권한 설정 |
| 전략·재정 | 세율·전쟁 차단·비밀 제한 등 허용된 국가 설정 |
| 비밀 장수 | 같은 국가 및 직책 권한에 맞춘 상세 정보 |
| 전투 본부 | 전선·출병 판단에 필요한 국가 전투 정보 |
| 정찰 메시지 | 정찰 관련 기록과 차단 상태 |
변경 버튼이 보이더라도 실행 전에 직책과 대상이 다시 검사됩니다. 권한이 바뀐 직후에는 화면을 새로 불러와
주세요.
## 외교와 즉시 승인
선전포고 같은 일부 외교 행동은 국가 예턴으로 실행합니다. 종전·불가침·파기 제안은 제안 생성과 상대의
수락이 나뉘며, 수락은 현재 관계와 권한을 다시 확인하는 즉시 action입니다. 제안 뒤 국가가 멸망하거나
관계·직책이 바뀌면 수락할 수 없을 수 있습니다.
## 부대
부대 화면에서는 생성, 가입, 탈퇴와 권한이 있는 관리 기능을 사용합니다. 부대 상태는 장수 예턴과 국가
발령·탈퇴 지시에도 영향을 받을 수 있습니다. 출병 전에 부대장·소속과 예약된 명령을 함께 확인해 주세요.
## 경매
경매는 열림·입찰·마감 단계가 있습니다. 입찰은 로그인한 내 장수와 자원을 기준으로 저장되고, 마감은 daemon
worker가 낙찰과 정산을 확정합니다. 이미 마감됐거나 자원이 바뀌면 이전에 보던 화면의 입찰 가능 상태가
유효하지 않을 수 있습니다.
## 토너먼트와 베팅
토너먼트는 등록·진행·종료와 보상 정산 lifecycle을 가집니다. 국가 베팅도 열림과 마감·정산 event가
scenario 달력에 의해 발생합니다. 각 화면의 현재 상태와 마감 시각을 확인해 주세요. profile에서 event가
열리지 않았다면 메뉴가 있어도 참여할 수 없습니다.
## 게시판·메시지
공개 글, 국가 범위 글, 개인 메시지는 서로 공개 범위가 다릅니다. 작성자·수신자·국가 관계와 운영 권한에
따라 조회·수정 범위가 결정됩니다. 민감한 외교나 국가 정보를 공개 게시판에 적지 말아 주세요.
## 기록
- 연감은 월 경계의 world 상태를 보존합니다.
- 명장·순위·왕조 화면은 현재 또는 종료된 시즌의 집계 데이터를 보여 줍니다.
- 과거 플레이는 로그인한 사용자 소유 archive만 볼 수 있습니다.
- 다른 사용자의 archive나 국가 비밀 정보는 URL을 바꿔도 공개되지 않습니다.
기록 화면의 값은 실시간 메인 상태와 갱신 시점이 다를 수 있습니다. 월 정산이나 시즌 종료 직후에는 해당
기능의 최신 상태 표시를 함께 확인해 주세요.
+52
View File
@@ -0,0 +1,52 @@
# 시간과 턴
## 현실 시간과 게임 달력
서버는 profile의 턴 schedule에 따라 tick을 진행합니다. 한 tick마다 실행 시각이 된 장수의 명령을 처리하고,
게임 달력이 다음 달로 넘어갈 경계에서는 월간 정산과 event도 처리합니다. 실제 턴 간격은 서버 설정에 따라
달라지므로 메인 화면의 다음 턴 시각을 확인해 주세요.
게임 달력은 1월부터 12월까지 진행하고 12월 다음은 다음 해 1월입니다. 월이 바뀔 때 수입, 외교·전쟁 상태,
도시·국가 상태, 연감, scenario event 같은 여러 처리가 정해진 순서로 일어납니다. 그러므로 같은 커맨드도
월 변경 직전과 직후에 자원이나 조건이 달라질 수 있습니다.
## 예턴
- 장수 예턴은 최대 30칸입니다.
- 국가 예턴은 최대 12칸입니다.
- 위쪽에 가까운 명령부터 차례로 소비됩니다.
- 대상 도시·장수·국가, 병종, 수량 등이 필요한 커맨드는 입력값까지 저장됩니다.
- 비어 있는 칸과 실행 뒤 밀려난 끝 칸은 기본적으로 `휴식`으로 채워집니다.
여러 칸에 한꺼번에 같은 명령을 넣거나 예턴을 앞뒤로 이동할 수 있습니다. 다른 탭에서 먼저 수정하면 revision
충돌이 날 수 있으므로 새 목록을 불러온 뒤 다시 적용해 주세요.
## 예약할 수 있어도 실행에 실패할 수 있습니다
예턴을 넣는 시점과 실제 실행 시점 사이에 다음 상태가 바뀔 수 있습니다.
- 가진 금·쌀과 병력
- 도시의 소유 국가, 보급과 전선 상태
- 내 소속 국가와 직책
- 대상 장수·도시·국가의 존재와 외교 관계
- 전쟁·불가침 기간과 전략 커맨드 재사용 상태
- scenario 연도, 가입 방식, 기술 수준과 병종 개방
입력 화면은 현재 정보로 사전 판단하지만, 실제 턴에서는 전체 조건을 다시 확인합니다. 실패 이유는 명령
로그에서 확인하고 다음 예턴을 조정해 주세요.
## 여러 턴이 필요한 행동
일부 커맨드는 한 칸에서 즉시 끝나지 않고 선행 턴을 쌓습니다. 진행 중 조건이 깨지거나 명령이 바뀌면 결과가
달라질 수 있습니다. 현재 기본 목록에서는 국가의 특수 병종 연구가 12턴 또는 24턴의 연속 실행을 요구합니다.
정확한 값은 [커맨드 전체 목록](./command-catalog.generated.md)의 “기본 실행 단위”에 표시됩니다.
## 월 변경 전 확인할 것
- 금·쌀이 필요한 예턴을 계속 감당할 수 있는지 확인해 주세요.
- 공격·외교·전략 명령은 보호 기간, 교전 상태와 대상 도시가 그대로인지 확인해 주세요.
- 국가 운영자는 국고·군량, 직책과 외교 제안의 만료·수락 상태를 확인해 주세요.
- 경매·토너먼트·국가 베팅은 각 화면에 열린 기간과 마감 상태가 표시될 때만 입력해 주세요.
월간 event는 scenario resource가 결정하므로 모든 서버가 같은 월에 같은 event를 실행한다고 가정하지
말아 주세요.
+7 -2
View File
@@ -21,7 +21,11 @@
"check:legacy:nation": "node tools/compare-command-constraints.mjs --include '^Nation/' --check && node tools/compare-command-logs.mjs --include '^Nation/' --mode action --check",
"check:legacy:general": "node tools/compare-command-constraints.mjs --include '^General/' --check && node tools/compare-command-logs.mjs --include '^General/' --mode action --check && node tools/compare-general-turn-contracts.mjs --check",
"test:e2e:frontend-legacy": "playwright test --config tools/frontend-legacy-parity/playwright.config.mjs --tsconfig tools/frontend-legacy-parity/tsconfig.json",
"migrate:legacy": "pnpm --filter @sammo-ts/legacy-db-migration migrate"
"migrate:legacy": "pnpm --filter @sammo-ts/legacy-db-migration migrate",
"docs:generate": "node tools/docs/generate-command-reference.mjs",
"docs:dev": "pnpm docs:generate && vitepress dev docs",
"docs:build": "pnpm docs:generate && vitepress build docs",
"docs:preview": "vitepress preview docs"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
@@ -39,6 +43,7 @@
"turbo": "^2.10.6",
"typescript": "6.0.2",
"typescript-eslint": "^8.65.0",
"vue-eslint-parser": "^10.4.1"
"vue-eslint-parser": "^10.4.1",
"vitepress": "1.6.4"
}
}
+1059
View File
@@ -56,6 +56,9 @@ importers:
typescript-eslint:
specifier: ^8.65.0
version: 8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.2)
vitepress:
specifier: 1.6.4
version: 1.6.4(@algolia/client-search@5.56.0)(@types/node@26.1.1)(lightningcss@1.30.2)(postcss@8.5.6)(search-insights@2.17.3)(typescript@6.0.2)
vue-eslint-parser:
specifier: ^10.4.1
version: 10.4.1(eslint@10.8.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)
@@ -510,6 +513,82 @@ importers:
packages:
'@algolia/abtesting@1.22.0':
resolution: {integrity: sha512-BFR6zNowNKcY7Ou7TaJc9QWexES4YKPbmf/OTFofpdsdhz4x6q0lbxp3duO0EHnyrN7rE4ba/TSXuY+BDGu4+g==}
engines: {node: '>= 14.0.0'}
'@algolia/autocomplete-core@1.17.7':
resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==}
'@algolia/autocomplete-plugin-algolia-insights@1.17.7':
resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==}
peerDependencies:
search-insights: '>= 1 < 3'
'@algolia/autocomplete-preset-algolia@1.17.7':
resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==}
peerDependencies:
'@algolia/client-search': '>= 4.9.1 < 6'
algoliasearch: '>= 4.9.1 < 6'
'@algolia/autocomplete-shared@1.17.7':
resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==}
peerDependencies:
'@algolia/client-search': '>= 4.9.1 < 6'
algoliasearch: '>= 4.9.1 < 6'
'@algolia/client-abtesting@5.56.0':
resolution: {integrity: sha512-7r4Z3NC7yU1oAQVWJNA2HX7tX481F3pJvCGyLIXiTdBcthz4Q/o21jwcMYDFkuI92UWTNBQQmHYgwHo1zS5dzg==}
engines: {node: '>= 14.0.0'}
'@algolia/client-analytics@5.56.0':
resolution: {integrity: sha512-avmjXQSq+jadFO8Xl2em05/uQdQnEmHsJyOAdVbZkmVgpMfxL12aJwVVfGNwYr9nulcpuJN1X0lTaQ5wxuNGcA==}
engines: {node: '>= 14.0.0'}
'@algolia/client-common@5.56.0':
resolution: {integrity: sha512-v2TPStUhY//ripPjIVclZ8AWc7DEGooXULZGFlFu37zNatgHjw34oZZ+OSbbc/YHO+xZwPl62I1k8xH1m4S2eg==}
engines: {node: '>= 14.0.0'}
'@algolia/client-insights@5.56.0':
resolution: {integrity: sha512-P0ehROpM4Sem3Sqo5x2cKPgj67D3G3jy0rh1Amwkcvsfr6tkvIcdCmerieanqTF7NxUMPNFLkpIFeMO8Rpa50w==}
engines: {node: '>= 14.0.0'}
'@algolia/client-personalization@5.56.0':
resolution: {integrity: sha512-SXK3Vn3WVxyzbm31oePZBJkp1wpOyuWdd4B/Pv7n0aXDxmeSWhC1R1FC1517mMrFAIaPH4Rt0x6RUe7ZNjz8FA==}
engines: {node: '>= 14.0.0'}
'@algolia/client-query-suggestions@5.56.0':
resolution: {integrity: sha512-5+ZdX8garFnmycnZgKhtXHePEaLj5zqDxI/0lkhhluzCcvTn0/PvvTirTg8hHYetQHvn7GDyeAiqTAieMvMW4A==}
engines: {node: '>= 14.0.0'}
'@algolia/client-search@5.56.0':
resolution: {integrity: sha512-+mKUdYvqOi0BcvpAEyCEw49vSBptufIcfibtHz2bdr1pI789M46Yt0uQEk/sxtK3teh71OQvVFHaTDzShUWewQ==}
engines: {node: '>= 14.0.0'}
'@algolia/ingestion@1.56.0':
resolution: {integrity: sha512-9g/zj+AZx5moFcdFIrYQoVrueXivjUcc3MQHtCYT8WhIuk1lUh1AyEhvJCS0XBZld09cLvd1AZ3BvDBpVpX2UA==}
engines: {node: '>= 14.0.0'}
'@algolia/monitoring@1.56.0':
resolution: {integrity: sha512-Qf3Sr6f9A9uxCZUf3MXS0d2b877uYzEB5yxqpVGXAhcJnBCQjrRRon0KvefpGkxy+BshrIJs96OUoMtGqXTFDA==}
engines: {node: '>= 14.0.0'}
'@algolia/recommend@5.56.0':
resolution: {integrity: sha512-GXWG1rWc5wu8hY4N33Y3b6ernY6sAdAvmKWN/zHAiACOx40WnpG0TVX5YazCAr/9gOYGInSiM2A0y2jy2xbiDA==}
engines: {node: '>= 14.0.0'}
'@algolia/requester-browser-xhr@5.56.0':
resolution: {integrity: sha512-7t24cBxaInS3mZb7ddEaZT/tp6q+/aR4YttsQVyP1/i+LmwPR34atO35KjaLFCcRVrlP7sYOAqkCfg6lIRB+ew==}
engines: {node: '>= 14.0.0'}
'@algolia/requester-fetch@5.56.0':
resolution: {integrity: sha512-R7ePHgVYmDFjZpvrsVAfbDz/d4RxKAYZ5/vgLfIsCVRZRryjWl/3INOxpOICzitehQ5FjNtNjcLQTrmHPTcHBQ==}
engines: {node: '>= 14.0.0'}
'@algolia/requester-node-http@5.56.0':
resolution: {integrity: sha512-PIOUXlSnrqM0S+WOgDRb4RzotydJH7ZoT6tOyL7tAO7qJOfvX5wsEW8Pe+PMKMwvuI4/gIyK9cg2H7lJXqnc4Q==}
engines: {node: '>= 14.0.0'}
'@babel/generator@7.28.5':
resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==}
engines: {node: '>=6.9.0'}
@@ -552,6 +631,29 @@ packages:
'@chevrotain/utils@10.5.0':
resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==}
'@docsearch/css@3.8.2':
resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==}
'@docsearch/js@3.8.2':
resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==}
'@docsearch/react@3.8.2':
resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==}
peerDependencies:
'@types/react': '>= 16.8.0 < 19.0.0'
react: '>= 16.8.0 < 19.0.0'
react-dom: '>= 16.8.0 < 19.0.0'
search-insights: '>= 1 < 3'
peerDependenciesMeta:
'@types/react':
optional: true
react:
optional: true
react-dom:
optional: true
search-insights:
optional: true
'@electric-sql/pglite-socket@0.0.6':
resolution: {integrity: sha512-6RjmgzphIHIBA4NrMGJsjNWK4pu+bCWJlEWlwcxFTVY3WT86dFpKwbZaGWZV6C5Rd7sCk1Z0CI76QEfukLAUXw==}
hasBin: true
@@ -584,102 +686,204 @@ packages:
'@emnapi/wasi-threads@1.2.2':
resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
'@esbuild/aix-ppc64@0.21.5':
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
'@esbuild/aix-ppc64@0.27.2':
resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.21.5':
resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.27.2':
resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.21.5':
resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
'@esbuild/android-arm@0.27.2':
resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.21.5':
resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
'@esbuild/android-x64@0.27.2':
resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.21.5':
resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-arm64@0.27.2':
resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.21.5':
resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.27.2':
resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.21.5':
resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-arm64@0.27.2':
resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.21.5':
resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.27.2':
resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.21.5':
resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm64@0.27.2':
resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.21.5':
resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
'@esbuild/linux-arm@0.27.2':
resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.21.5':
resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.27.2':
resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.21.5':
resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-loong64@0.27.2':
resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.21.5':
resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-mips64el@0.27.2':
resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.21.5':
resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.27.2':
resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.21.5':
resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.27.2':
resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.21.5':
resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.27.2':
resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.21.5':
resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
'@esbuild/linux-x64@0.27.2':
resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==}
engines: {node: '>=18'}
@@ -692,6 +896,12 @@ packages:
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.21.5':
resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.27.2':
resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==}
engines: {node: '>=18'}
@@ -704,6 +914,12 @@ packages:
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.21.5':
resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
'@esbuild/openbsd-x64@0.27.2':
resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==}
engines: {node: '>=18'}
@@ -716,24 +932,48 @@ packages:
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.21.5':
resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
'@esbuild/sunos-x64@0.27.2':
resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.21.5':
resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.27.2':
resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.21.5':
resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.27.2':
resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.21.5':
resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
'@esbuild/win32-x64@0.27.2':
resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==}
engines: {node: '>=18'}
@@ -840,6 +1080,12 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
'@iconify-json/simple-icons@1.2.92':
resolution: {integrity: sha512-hR0ozxR97t1dzWw+esoxFijZ15gagt7EIgF3CNifu2yICXhS7gnun4Y+j+odJQtNSl7wvqMdoLbViIShwe/fdw==}
'@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
'@img/colour@1.0.0':
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
engines: {node: '>=18'}
@@ -1693,6 +1939,30 @@ packages:
cpu: [x64]
os: [win32]
'@shikijs/core@2.5.0':
resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==}
'@shikijs/engine-javascript@2.5.0':
resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==}
'@shikijs/engine-oniguruma@2.5.0':
resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==}
'@shikijs/langs@2.5.0':
resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==}
'@shikijs/themes@2.5.0':
resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==}
'@shikijs/transformers@2.5.0':
resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==}
'@shikijs/types@2.5.0':
resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==}
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
@@ -2012,6 +2282,9 @@ packages:
'@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
'@types/hast@3.0.5':
resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@@ -2021,6 +2294,9 @@ packages:
'@types/markdown-it@14.1.2':
resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
'@types/mdurl@2.0.0':
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
@@ -2036,6 +2312,9 @@ packages:
'@types/react@19.2.7':
resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==}
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
'@types/web-bluetooth@0.0.21':
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
@@ -2098,6 +2377,16 @@ packages:
resolution: {integrity: sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.3.3':
resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==}
'@vitejs/plugin-vue@5.2.4':
resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
engines: {node: ^18.0.0 || >=20.0.0}
peerDependencies:
vite: ^5.0.0 || ^6.0.0
vue: ^3.2.25
'@vitejs/plugin-vue@6.0.3':
resolution: {integrity: sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -2190,6 +2479,9 @@ packages:
'@vue/shared@3.5.26':
resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==}
'@vueuse/core@12.8.2':
resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==}
'@vueuse/core@13.9.0':
resolution: {integrity: sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==}
peerDependencies:
@@ -2200,12 +2492,59 @@ packages:
peerDependencies:
vue: ^3.5.0
'@vueuse/integrations@12.8.2':
resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==}
peerDependencies:
async-validator: ^4
axios: ^1
change-case: ^5
drauu: ^0.4
focus-trap: ^7
fuse.js: ^7
idb-keyval: ^6
jwt-decode: ^4
nprogress: ^0.2
qrcode: ^1.5
sortablejs: ^1
universal-cookie: ^7
peerDependenciesMeta:
async-validator:
optional: true
axios:
optional: true
change-case:
optional: true
drauu:
optional: true
focus-trap:
optional: true
fuse.js:
optional: true
idb-keyval:
optional: true
jwt-decode:
optional: true
nprogress:
optional: true
qrcode:
optional: true
sortablejs:
optional: true
universal-cookie:
optional: true
'@vueuse/metadata@12.8.2':
resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==}
'@vueuse/metadata@13.9.0':
resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==}
'@vueuse/metadata@14.1.0':
resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==}
'@vueuse/shared@12.8.2':
resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==}
'@vueuse/shared@13.9.0':
resolution: {integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==}
peerDependencies:
@@ -2377,6 +2716,10 @@ packages:
ajv@8.17.1:
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
algoliasearch@5.56.0:
resolution: {integrity: sha512-PrqppUmhT4ENdas2pH9caE7efUcxy6EcSFhWzosiVuQBzu2tQ5yLTI6jwomT/1cuBnivzGfxiJCqDNN9FRRh+Q==}
engines: {node: '>= 14.0.0'}
alien-signals@3.1.2:
resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==}
@@ -2514,6 +2857,9 @@ packages:
caniuse-lite@1.0.30001764:
resolution: {integrity: sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
chai@6.2.2:
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
engines: {node: '>=18'}
@@ -2522,6 +2868,12 @@ packages:
resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
engines: {node: '>=8'}
character-entities-html4@2.1.0:
resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
character-entities-legacy@3.0.0:
resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
charm@0.1.2:
resolution: {integrity: sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==}
@@ -2554,6 +2906,9 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
commander@2.15.1:
resolution: {integrity: sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==}
@@ -2672,6 +3027,9 @@ packages:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
dotenv@16.6.1:
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
engines: {node: '>=12'}
@@ -2704,6 +3062,9 @@ packages:
electron-to-chromium@1.5.267:
resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==}
emoji-regex-xs@1.0.0:
resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
empathic@2.0.0:
resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==}
engines: {node: '>=14'}
@@ -2734,6 +3095,11 @@ packages:
es-toolkit@1.43.0:
resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==}
esbuild@0.21.5:
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
engines: {node: '>=12'}
hasBin: true
esbuild@0.27.2:
resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==}
engines: {node: '>=18'}
@@ -2948,6 +3314,9 @@ packages:
flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
focus-trap@7.8.0:
resolution: {integrity: sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==}
follow-redirects@1.15.11:
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
engines: {node: '>=4.0'}
@@ -3042,6 +3411,12 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
hast-util-to-html@9.0.5:
resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
hono@4.10.6:
resolution: {integrity: sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==}
engines: {node: '>=16.9.0'}
@@ -3055,6 +3430,9 @@ packages:
hookable@6.1.1:
resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==}
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
@@ -3310,13 +3688,34 @@ packages:
resolution: {integrity: sha512-i053Kc0MgdUv/hu9mCyq67TYfPXFj3/MV8I7ZW5wvJNixIyXC0VztMPUjIVj/449nQo+BsxFD4Fdk/sA/uqKPQ==}
engines: {node: '>= 20.0.0'}
mark.js@8.11.1:
resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
markdown-it@14.1.0:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
mdast-util-to-hast@13.2.1:
resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
micromark-util-character@2.1.1:
resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
micromark-util-encode@2.0.1:
resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
micromark-util-sanitize-uri@2.0.1:
resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
micromark-util-symbol@2.0.1:
resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
micromark-util-types@2.0.2:
resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
mime@3.0.0:
resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
engines: {node: '>=10.0.0'}
@@ -3334,6 +3733,9 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
minisearch@7.2.0:
resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==}
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
@@ -3415,6 +3817,9 @@ packages:
resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
engines: {node: '>=14.0.0'}
oniguruma-to-es@3.1.1:
resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==}
optionator@0.9.4:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
@@ -3612,6 +4017,14 @@ packages:
resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==}
engines: {node: '>=12'}
preact@10.29.7:
resolution: {integrity: sha512-DCHYrK/B10yUD3ZjLfhZ3WIE/9Vf9VFUODcRE2dRomTYDpJk6z6L9wecSfhfE6M9ZTHUdyQkoC46arIDhEV84Q==}
peerDependencies:
preact-render-to-string: '>=5'
peerDependenciesMeta:
preact-render-to-string:
optional: true
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -3650,6 +4063,9 @@ packages:
proper-lockfile@4.1.2:
resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
property-information@7.2.0:
resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==}
prosemirror-changeset@2.3.1:
resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==}
@@ -3764,6 +4180,15 @@ packages:
resolution: {integrity: sha512-0/Y+7IEiTgVGPrLFKy8oAEArSyEJkU0zvgV5xyi9NzNQ+SLZmyFbUsWIbgPcd4UdUh00opXGKlXJwMmsis5Byw==}
engines: {node: '>= 18'}
regex-recursion@6.0.2:
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
regex-utilities@2.3.0:
resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
regex@6.1.0:
resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
regexp-to-ast@0.5.0:
resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==}
@@ -3889,6 +4314,9 @@ packages:
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
search-insights@2.17.3:
resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==}
secure-json-parse@4.1.0:
resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==}
@@ -3928,6 +4356,9 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
shiki@2.5.0:
resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==}
shimmer@1.2.1:
resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==}
@@ -3967,6 +4398,9 @@ packages:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
speakingurl@14.0.1:
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
engines: {node: '>=0.10.0'}
@@ -3995,6 +4429,9 @@ packages:
std-env@3.9.0:
resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==}
stringify-entities@4.0.4:
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
superjson@2.2.6:
resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==}
engines: {node: '>=16'}
@@ -4017,6 +4454,9 @@ packages:
os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
hasBin: true
tabbable@6.5.0:
resolution: {integrity: sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==}
tailwindcss@4.1.18:
resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
@@ -4066,6 +4506,9 @@ packages:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
ts-api-utils@2.5.0:
resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
engines: {node: '>=18.12'}
@@ -4199,6 +4642,21 @@ packages:
undici-types@8.3.0:
resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==}
unist-util-is@6.0.1:
resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
unist-util-position@5.0.0:
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
unist-util-stringify-position@4.0.0:
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
unist-util-visit-parents@6.0.2:
resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
unist-util-visit@5.1.0:
resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
unrun@0.2.22:
resolution: {integrity: sha512-vlQce4gTLNyCZxGylEQXGG+fSrrEFWiM/L8aghtp+t6j8xXh+lmsBtQJknG7ZSvv7P+/MRgbQtHWHBWk981uTg==}
engines: {node: '>=20.19.0'}
@@ -4233,6 +4691,12 @@ packages:
resolution: {integrity: sha512-Njrh4U8UODGajoZ44QS2C/BsoEM9DTI/aCqY5swsizb+/ap0FamvnCMcZAxrR5+aoC0ZqkawEfpC/N2SBc+xeA==}
engines: {node: '>=18.12.0'}
vfile-message@4.0.3:
resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
vite-tsconfig-paths@6.0.3:
resolution: {integrity: sha512-7bL7FPX/DSviaZGYUKowWF1AiDVWjMjxNbE8lyaVGDezkedWqfGhlnQ4BZXre0ZN5P4kAgIJfAlgFDVyjrCIyg==}
peerDependencies:
@@ -4241,6 +4705,37 @@ packages:
vite:
optional: true
vite@5.4.21:
resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
'@types/node': ^18.0.0 || >=20.0.0
less: '*'
lightningcss: ^1.21.0
sass: '*'
sass-embedded: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
vite@7.3.0:
resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -4321,6 +4816,18 @@ packages:
yaml:
optional: true
vitepress@1.6.4:
resolution: {integrity: sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==}
hasBin: true
peerDependencies:
markdown-it-mathjax3: ^4
postcss: ^8
peerDependenciesMeta:
markdown-it-mathjax3:
optional: true
postcss:
optional: true
vitest@4.0.16:
resolution: {integrity: sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==}
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
@@ -4452,8 +4959,123 @@ packages:
zod@4.3.5:
resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==}
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
snapshots:
'@algolia/abtesting@1.22.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)(search-insights@2.17.3)':
dependencies:
'@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)(search-insights@2.17.3)
'@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)
transitivePeerDependencies:
- '@algolia/client-search'
- algoliasearch
- search-insights
'@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)(search-insights@2.17.3)':
dependencies:
'@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)
search-insights: 2.17.3
transitivePeerDependencies:
- '@algolia/client-search'
- algoliasearch
'@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)':
dependencies:
'@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)
'@algolia/client-search': 5.56.0
algoliasearch: 5.56.0
'@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)':
dependencies:
'@algolia/client-search': 5.56.0
algoliasearch: 5.56.0
'@algolia/client-abtesting@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/client-analytics@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/client-common@5.56.0': {}
'@algolia/client-insights@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/client-personalization@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/client-query-suggestions@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/client-search@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/ingestion@1.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/monitoring@1.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/recommend@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
'@algolia/requester-browser-xhr@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-fetch@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@algolia/requester-node-http@5.56.0':
dependencies:
'@algolia/client-common': 5.56.0
'@babel/generator@7.28.5':
dependencies:
'@babel/parser': 7.28.5
@@ -4499,6 +5121,31 @@ snapshots:
'@chevrotain/utils@10.5.0': {}
'@docsearch/css@3.8.2': {}
'@docsearch/js@3.8.2(@algolia/client-search@5.56.0)(search-insights@2.17.3)':
dependencies:
'@docsearch/react': 3.8.2(@algolia/client-search@5.56.0)(search-insights@2.17.3)
preact: 10.29.7
transitivePeerDependencies:
- '@algolia/client-search'
- '@types/react'
- preact-render-to-string
- react
- react-dom
- search-insights
'@docsearch/react@3.8.2(@algolia/client-search@5.56.0)(search-insights@2.17.3)':
dependencies:
'@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)(search-insights@2.17.3)
'@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.56.0)(algoliasearch@5.56.0)
'@docsearch/css': 3.8.2
algoliasearch: 5.56.0
optionalDependencies:
search-insights: 2.17.3
transitivePeerDependencies:
- '@algolia/client-search'
'@electric-sql/pglite-socket@0.0.6(@electric-sql/pglite@0.3.2)':
dependencies:
'@electric-sql/pglite': 0.3.2
@@ -4541,81 +5188,150 @@ snapshots:
tslib: 2.8.1
optional: true
'@esbuild/aix-ppc64@0.21.5':
optional: true
'@esbuild/aix-ppc64@0.27.2':
optional: true
'@esbuild/android-arm64@0.21.5':
optional: true
'@esbuild/android-arm64@0.27.2':
optional: true
'@esbuild/android-arm@0.21.5':
optional: true
'@esbuild/android-arm@0.27.2':
optional: true
'@esbuild/android-x64@0.21.5':
optional: true
'@esbuild/android-x64@0.27.2':
optional: true
'@esbuild/darwin-arm64@0.21.5':
optional: true
'@esbuild/darwin-arm64@0.27.2':
optional: true
'@esbuild/darwin-x64@0.21.5':
optional: true
'@esbuild/darwin-x64@0.27.2':
optional: true
'@esbuild/freebsd-arm64@0.21.5':
optional: true
'@esbuild/freebsd-arm64@0.27.2':
optional: true
'@esbuild/freebsd-x64@0.21.5':
optional: true
'@esbuild/freebsd-x64@0.27.2':
optional: true
'@esbuild/linux-arm64@0.21.5':
optional: true
'@esbuild/linux-arm64@0.27.2':
optional: true
'@esbuild/linux-arm@0.21.5':
optional: true
'@esbuild/linux-arm@0.27.2':
optional: true
'@esbuild/linux-ia32@0.21.5':
optional: true
'@esbuild/linux-ia32@0.27.2':
optional: true
'@esbuild/linux-loong64@0.21.5':
optional: true
'@esbuild/linux-loong64@0.27.2':
optional: true
'@esbuild/linux-mips64el@0.21.5':
optional: true
'@esbuild/linux-mips64el@0.27.2':
optional: true
'@esbuild/linux-ppc64@0.21.5':
optional: true
'@esbuild/linux-ppc64@0.27.2':
optional: true
'@esbuild/linux-riscv64@0.21.5':
optional: true
'@esbuild/linux-riscv64@0.27.2':
optional: true
'@esbuild/linux-s390x@0.21.5':
optional: true
'@esbuild/linux-s390x@0.27.2':
optional: true
'@esbuild/linux-x64@0.21.5':
optional: true
'@esbuild/linux-x64@0.27.2':
optional: true
'@esbuild/netbsd-arm64@0.27.2':
optional: true
'@esbuild/netbsd-x64@0.21.5':
optional: true
'@esbuild/netbsd-x64@0.27.2':
optional: true
'@esbuild/openbsd-arm64@0.27.2':
optional: true
'@esbuild/openbsd-x64@0.21.5':
optional: true
'@esbuild/openbsd-x64@0.27.2':
optional: true
'@esbuild/openharmony-arm64@0.27.2':
optional: true
'@esbuild/sunos-x64@0.21.5':
optional: true
'@esbuild/sunos-x64@0.27.2':
optional: true
'@esbuild/win32-arm64@0.21.5':
optional: true
'@esbuild/win32-arm64@0.27.2':
optional: true
'@esbuild/win32-ia32@0.21.5':
optional: true
'@esbuild/win32-ia32@0.27.2':
optional: true
'@esbuild/win32-x64@0.21.5':
optional: true
'@esbuild/win32-x64@0.27.2':
optional: true
@@ -4726,6 +5442,12 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
'@iconify-json/simple-icons@1.2.92':
dependencies:
'@iconify/types': 2.0.0
'@iconify/types@2.0.0': {}
'@img/colour@1.0.0': {}
'@img/sharp-darwin-arm64@0.34.5':
@@ -5331,6 +6053,46 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.55.1':
optional: true
'@shikijs/core@2.5.0':
dependencies:
'@shikijs/engine-javascript': 2.5.0
'@shikijs/engine-oniguruma': 2.5.0
'@shikijs/types': 2.5.0
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.5
hast-util-to-html: 9.0.5
'@shikijs/engine-javascript@2.5.0':
dependencies:
'@shikijs/types': 2.5.0
'@shikijs/vscode-textmate': 10.0.2
oniguruma-to-es: 3.1.1
'@shikijs/engine-oniguruma@2.5.0':
dependencies:
'@shikijs/types': 2.5.0
'@shikijs/vscode-textmate': 10.0.2
'@shikijs/langs@2.5.0':
dependencies:
'@shikijs/types': 2.5.0
'@shikijs/themes@2.5.0':
dependencies:
'@shikijs/types': 2.5.0
'@shikijs/transformers@2.5.0':
dependencies:
'@shikijs/core': 2.5.0
'@shikijs/types': 2.5.0
'@shikijs/types@2.5.0':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.5
'@shikijs/vscode-textmate@10.0.2': {}
'@standard-schema/spec@1.1.0': {}
'@tailwindcss/node@4.1.18':
@@ -5638,6 +6400,10 @@ snapshots:
'@types/geojson@7946.0.16': {}
'@types/hast@3.0.5':
dependencies:
'@types/unist': 3.0.3
'@types/json-schema@7.0.15': {}
'@types/linkify-it@5.0.0': {}
@@ -5647,6 +6413,10 @@ snapshots:
'@types/linkify-it': 5.0.0
'@types/mdurl': 2.0.0
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.3
'@types/mdurl@2.0.0': {}
'@types/node@25.0.3':
@@ -5667,6 +6437,8 @@ snapshots:
dependencies:
csstype: 3.2.3
'@types/unist@3.0.3': {}
'@types/web-bluetooth@0.0.21': {}
'@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.2))(eslint@10.8.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.2)':
@@ -5760,6 +6532,13 @@ snapshots:
'@typescript-eslint/types': 8.65.0
eslint-visitor-keys: 5.0.1
'@ungap/structured-clone@1.3.3': {}
'@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@26.1.1)(lightningcss@1.30.2))(vue@3.5.26(typescript@6.0.2))':
dependencies:
vite: 5.4.21(@types/node@26.1.1)(lightningcss@1.30.2)
vue: 3.5.26(typescript@6.0.2)
'@vitejs/plugin-vue@6.0.3(vite@7.3.0(@types/node@26.1.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))(vue@3.5.26(typescript@6.0.2))':
dependencies:
'@rolldown/pluginutils': 1.0.0-beta.53
@@ -5917,6 +6696,15 @@ snapshots:
'@vue/shared@3.5.26': {}
'@vueuse/core@12.8.2(typescript@6.0.2)':
dependencies:
'@types/web-bluetooth': 0.0.21
'@vueuse/metadata': 12.8.2
'@vueuse/shared': 12.8.2(typescript@6.0.2)
vue: 3.5.26(typescript@6.0.2)
transitivePeerDependencies:
- typescript
'@vueuse/core@13.9.0(vue@3.5.26(typescript@6.0.2))':
dependencies:
'@types/web-bluetooth': 0.0.21
@@ -5931,10 +6719,28 @@ snapshots:
'@vueuse/shared': 14.1.0(vue@3.5.26(typescript@6.0.2))
vue: 3.5.26(typescript@6.0.2)
'@vueuse/integrations@12.8.2(focus-trap@7.8.0)(typescript@6.0.2)':
dependencies:
'@vueuse/core': 12.8.2(typescript@6.0.2)
'@vueuse/shared': 12.8.2(typescript@6.0.2)
vue: 3.5.26(typescript@6.0.2)
optionalDependencies:
focus-trap: 7.8.0
transitivePeerDependencies:
- typescript
'@vueuse/metadata@12.8.2': {}
'@vueuse/metadata@13.9.0': {}
'@vueuse/metadata@14.1.0': {}
'@vueuse/shared@12.8.2(typescript@6.0.2)':
dependencies:
vue: 3.5.26(typescript@6.0.2)
transitivePeerDependencies:
- typescript
'@vueuse/shared@13.9.0(vue@3.5.26(typescript@6.0.2))':
dependencies:
vue: 3.5.26(typescript@6.0.2)
@@ -6045,6 +6851,23 @@ snapshots:
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
algoliasearch@5.56.0:
dependencies:
'@algolia/abtesting': 1.22.0
'@algolia/client-abtesting': 5.56.0
'@algolia/client-analytics': 5.56.0
'@algolia/client-common': 5.56.0
'@algolia/client-insights': 5.56.0
'@algolia/client-personalization': 5.56.0
'@algolia/client-query-suggestions': 5.56.0
'@algolia/client-search': 5.56.0
'@algolia/ingestion': 1.56.0
'@algolia/monitoring': 1.56.0
'@algolia/recommend': 5.56.0
'@algolia/requester-browser-xhr': 5.56.0
'@algolia/requester-fetch': 5.56.0
'@algolia/requester-node-http': 5.56.0
alien-signals@3.1.2: {}
amp-message@0.1.2:
@@ -6162,6 +6985,8 @@ snapshots:
caniuse-lite@1.0.30001764: {}
ccount@2.0.1: {}
chai@6.2.2: {}
chalk@3.0.0:
@@ -6169,6 +6994,10 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
character-entities-html4@2.1.0: {}
character-entities-legacy@3.0.0: {}
charm@0.1.2: {}
chevrotain@10.5.0:
@@ -6212,6 +7041,8 @@ snapshots:
color-name@1.1.4: {}
comma-separated-tokens@2.0.3: {}
commander@2.15.1: {}
confbox@0.2.2: {}
@@ -6292,6 +7123,10 @@ snapshots:
detect-libc@2.1.2: {}
devlop@1.1.0:
dependencies:
dequal: 2.0.3
dotenv@16.6.1: {}
dotenv@17.2.3: {}
@@ -6307,6 +7142,8 @@ snapshots:
electron-to-chromium@1.5.267: {}
emoji-regex-xs@1.0.0: {}
empathic@2.0.0: {}
empathic@2.0.1: {}
@@ -6328,6 +7165,32 @@ snapshots:
es-toolkit@1.43.0: {}
esbuild@0.21.5:
optionalDependencies:
'@esbuild/aix-ppc64': 0.21.5
'@esbuild/android-arm': 0.21.5
'@esbuild/android-arm64': 0.21.5
'@esbuild/android-x64': 0.21.5
'@esbuild/darwin-arm64': 0.21.5
'@esbuild/darwin-x64': 0.21.5
'@esbuild/freebsd-arm64': 0.21.5
'@esbuild/freebsd-x64': 0.21.5
'@esbuild/linux-arm': 0.21.5
'@esbuild/linux-arm64': 0.21.5
'@esbuild/linux-ia32': 0.21.5
'@esbuild/linux-loong64': 0.21.5
'@esbuild/linux-mips64el': 0.21.5
'@esbuild/linux-ppc64': 0.21.5
'@esbuild/linux-riscv64': 0.21.5
'@esbuild/linux-s390x': 0.21.5
'@esbuild/linux-x64': 0.21.5
'@esbuild/netbsd-x64': 0.21.5
'@esbuild/openbsd-x64': 0.21.5
'@esbuild/sunos-x64': 0.21.5
'@esbuild/win32-arm64': 0.21.5
'@esbuild/win32-ia32': 0.21.5
'@esbuild/win32-x64': 0.21.5
esbuild@0.27.2:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.2
@@ -6591,6 +7454,10 @@ snapshots:
flatted@3.3.3: {}
focus-trap@7.8.0:
dependencies:
tabbable: 6.5.0
follow-redirects@1.15.11(debug@4.3.7(supports-color@7.2.0)):
optionalDependencies:
debug: 4.3.7(supports-color@7.2.0)
@@ -6675,6 +7542,24 @@ snapshots:
dependencies:
function-bind: 1.1.2
hast-util-to-html@9.0.5:
dependencies:
'@types/hast': 3.0.5
'@types/unist': 3.0.3
ccount: 2.0.1
comma-separated-tokens: 2.0.3
hast-util-whitespace: 3.0.0
html-void-elements: 3.0.0
mdast-util-to-hast: 13.2.1
property-information: 7.2.0
space-separated-tokens: 2.0.2
stringify-entities: 4.0.4
zwitch: 2.0.4
hast-util-whitespace@3.0.0:
dependencies:
'@types/hast': 3.0.5
hono@4.10.6: {}
hookable@5.5.3: {}
@@ -6683,6 +7568,8 @@ snapshots:
hookable@6.1.1: {}
html-void-elements@3.0.0: {}
http-errors@2.0.1:
dependencies:
depd: 2.0.0
@@ -6891,6 +7778,8 @@ snapshots:
iconv-lite: 0.7.3
lru-cache: 11.5.2
mark.js@8.11.1: {}
markdown-it@14.1.0:
dependencies:
argparse: 2.0.1
@@ -6900,8 +7789,37 @@ snapshots:
punycode.js: 2.3.1
uc.micro: 2.1.0
mdast-util-to-hast@13.2.1:
dependencies:
'@types/hast': 3.0.5
'@types/mdast': 4.0.4
'@ungap/structured-clone': 1.3.3
devlop: 1.1.0
micromark-util-sanitize-uri: 2.0.1
trim-lines: 3.0.1
unist-util-position: 5.0.0
unist-util-visit: 5.1.0
vfile: 6.0.3
mdurl@2.0.0: {}
micromark-util-character@2.1.1:
dependencies:
micromark-util-symbol: 2.0.1
micromark-util-types: 2.0.2
micromark-util-encode@2.0.1: {}
micromark-util-sanitize-uri@2.0.1:
dependencies:
micromark-util-character: 2.1.1
micromark-util-encode: 2.0.1
micromark-util-symbol: 2.0.1
micromark-util-symbol@2.0.1: {}
micromark-util-types@2.0.2: {}
mime@3.0.0: {}
minimatch@10.1.1:
@@ -6914,6 +7832,8 @@ snapshots:
minipass@7.1.2: {}
minisearch@7.2.0: {}
mitt@3.0.1: {}
mkdirp@1.0.4: {}
@@ -6987,6 +7907,12 @@ snapshots:
on-exit-leak-free@2.1.2: {}
oniguruma-to-es@3.1.1:
dependencies:
emoji-regex-xs: 1.0.0
regex: 6.1.0
regex-recursion: 6.0.2
optionator@0.9.4:
dependencies:
deep-is: 0.1.4
@@ -7236,6 +8162,8 @@ snapshots:
postgres@3.4.7: {}
preact@10.29.7: {}
prelude-ls@1.2.1: {}
prettier-linter-helpers@1.0.1:
@@ -7274,6 +8202,8 @@ snapshots:
retry: 0.12.0
signal-exit: 3.0.7
property-information@7.2.0: {}
prosemirror-changeset@2.3.1:
dependencies:
prosemirror-transform: 1.11.0
@@ -7434,6 +8364,16 @@ snapshots:
'@redis/search': 5.10.0(@redis/client@5.10.0)
'@redis/time-series': 5.10.0(@redis/client@5.10.0)
regex-recursion@6.0.2:
dependencies:
regex-utilities: 2.3.0
regex-utilities@2.3.0: {}
regex@6.1.0:
dependencies:
regex-utilities: 2.3.0
regexp-to-ast@0.5.0: {}
remeda@2.21.3:
@@ -7638,6 +8578,8 @@ snapshots:
scheduler@0.27.0: {}
search-insights@2.17.3: {}
secure-json-parse@4.1.0: {}
semver@7.5.4:
@@ -7691,6 +8633,17 @@ snapshots:
shebang-regex@3.0.0: {}
shiki@2.5.0:
dependencies:
'@shikijs/core': 2.5.0
'@shikijs/engine-javascript': 2.5.0
'@shikijs/engine-oniguruma': 2.5.0
'@shikijs/langs': 2.5.0
'@shikijs/themes': 2.5.0
'@shikijs/types': 2.5.0
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.5
shimmer@1.2.1: {}
siginfo@2.0.0: {}
@@ -7727,6 +8680,8 @@ snapshots:
source-map@0.6.1: {}
space-separated-tokens@2.0.2: {}
speakingurl@14.0.1: {}
split2@4.2.0: {}
@@ -7743,6 +8698,11 @@ snapshots:
std-env@3.9.0: {}
stringify-entities@4.0.4:
dependencies:
character-entities-html4: 2.1.0
character-entities-legacy: 3.0.0
superjson@2.2.6:
dependencies:
copy-anything: 4.0.5
@@ -7760,6 +8720,8 @@ snapshots:
systeminformation@5.28.10:
optional: true
tabbable@6.5.0: {}
tailwindcss@4.1.18: {}
tapable@2.3.0: {}
@@ -7796,6 +8758,8 @@ snapshots:
tree-kill@1.2.2: {}
trim-lines@3.0.1: {}
ts-api-utils@2.5.0(typescript@6.0.2):
dependencies:
typescript: 6.0.2
@@ -7922,6 +8886,29 @@ snapshots:
undici-types@8.3.0: {}
unist-util-is@6.0.1:
dependencies:
'@types/unist': 3.0.3
unist-util-position@5.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-stringify-position@4.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-visit-parents@6.0.2:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.1
unist-util-visit@5.1.0:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.1
unist-util-visit-parents: 6.0.2
unrun@0.2.22(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(synckit@0.11.13):
dependencies:
rolldown: 1.0.0-beta.58(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)
@@ -7949,6 +8936,16 @@ snapshots:
verkit@0.3.0: {}
vfile-message@4.0.3:
dependencies:
'@types/unist': 3.0.3
unist-util-stringify-position: 4.0.0
vfile@6.0.3:
dependencies:
'@types/unist': 3.0.3
vfile-message: 4.0.3
vite-tsconfig-paths@6.0.3(supports-color@7.2.0)(typescript@6.0.2)(vite@7.3.1(@types/node@26.1.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)):
dependencies:
debug: 4.4.3(supports-color@7.2.0)
@@ -7960,6 +8957,16 @@ snapshots:
- supports-color
- typescript
vite@5.4.21(@types/node@26.1.1)(lightningcss@1.30.2):
dependencies:
esbuild: 0.21.5
postcss: 8.5.6
rollup: 4.55.1
optionalDependencies:
'@types/node': 26.1.1
fsevents: 2.3.3
lightningcss: 1.30.2
vite@7.3.0(@types/node@26.1.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0):
dependencies:
esbuild: 0.27.2
@@ -7990,6 +8997,56 @@ snapshots:
lightningcss: 1.30.2
tsx: 4.21.0
vitepress@1.6.4(@algolia/client-search@5.56.0)(@types/node@26.1.1)(lightningcss@1.30.2)(postcss@8.5.6)(search-insights@2.17.3)(typescript@6.0.2):
dependencies:
'@docsearch/css': 3.8.2
'@docsearch/js': 3.8.2(@algolia/client-search@5.56.0)(search-insights@2.17.3)
'@iconify-json/simple-icons': 1.2.92
'@shikijs/core': 2.5.0
'@shikijs/transformers': 2.5.0
'@shikijs/types': 2.5.0
'@types/markdown-it': 14.1.2
'@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@26.1.1)(lightningcss@1.30.2))(vue@3.5.26(typescript@6.0.2))
'@vue/devtools-api': 7.7.9
'@vue/shared': 3.5.26
'@vueuse/core': 12.8.2(typescript@6.0.2)
'@vueuse/integrations': 12.8.2(focus-trap@7.8.0)(typescript@6.0.2)
focus-trap: 7.8.0
mark.js: 8.11.1
minisearch: 7.2.0
shiki: 2.5.0
vite: 5.4.21(@types/node@26.1.1)(lightningcss@1.30.2)
vue: 3.5.26(typescript@6.0.2)
optionalDependencies:
postcss: 8.5.6
transitivePeerDependencies:
- '@algolia/client-search'
- '@types/node'
- '@types/react'
- async-validator
- axios
- change-case
- drauu
- fuse.js
- idb-keyval
- jwt-decode
- less
- lightningcss
- nprogress
- preact-render-to-string
- qrcode
- react
- react-dom
- sass
- sass-embedded
- search-insights
- sortablejs
- stylus
- sugarss
- terser
- typescript
- universal-cookie
vitest@4.0.16(@types/node@26.1.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0):
dependencies:
'@vitest/expect': 4.0.16
@@ -8140,3 +9197,5 @@ snapshots:
grammex: 3.1.12
zod@4.3.5: {}
zwitch@2.0.4: {}
+129
View File
@@ -0,0 +1,129 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import prettier from 'prettier';
const workspaceRoot = process.cwd();
const outputPath = path.join(workspaceRoot, 'docs', 'user', 'command-catalog.generated.md');
const scopes = [
{
id: 'general',
title: '장수 커맨드',
directory: path.join(workspaceRoot, 'packages', 'logic', 'src', 'actions', 'turn', 'general'),
registry: path.join(workspaceRoot, 'packages', 'logic', 'src', 'actions', 'turn', 'general', 'index.ts'),
registryName: 'GENERAL_TURN_COMMAND_KEYS',
},
{
id: 'nation',
title: '국가 커맨드',
directory: path.join(workspaceRoot, 'packages', 'logic', 'src', 'actions', 'turn', 'nation'),
registry: path.join(workspaceRoot, 'packages', 'logic', 'src', 'actions', 'turn', 'nation', 'index.ts'),
registryName: 'NATION_TURN_COMMAND_KEYS',
},
];
const readQuoted = (source, pattern) => source.match(pattern)?.[1] ?? null;
const parseRegistry = (source, registryName) => {
const arrayBody = source.match(new RegExp(`export const ${registryName} = \\[([\\s\\S]*?)\\] as const;`))?.[1];
if (!arrayBody) {
throw new Error(`명령 등록부를 찾을 수 없습니다: ${registryName}`);
}
return Array.from(arrayBody.matchAll(/['"]([^'"]+)['"]/g), (match) => match[1]);
};
const parseCommand = async (scope, key) => {
const sourcePath = path.join(scope.directory, `${key}.ts`);
const source = await fs.readFile(sourcePath, 'utf8');
const actionName =
readQuoted(source, /const ACTION_NAME\s*=\s*['"]([^'"]+)['"]/) ??
readQuoted(source, /public (?:override )?readonly name(?:\s*:\s*string)?\s*=\s*['"]([^'"]+)['"]/) ??
readQuoted(source, /const DEFAULT_CONFIG[\s\S]*?\bname:\s*['"]([^'"]+)['"]/) ??
readQuoted(source, /(?:super|createEventResearchCommand)\([\s\S]*?\{[\s\S]*?\bname:\s*['"]([^'"]+)['"]/);
const category =
readQuoted(source, /export const commandSpec[\s\S]*?\bcategory:\s*['"]([^'"]+)['"]/) ??
(key.startsWith('event_') ? '특수' : null);
const reqArg =
source.match(/export const commandSpec[\s\S]*?\breqArg:\s*(true|false)/)?.[1] ??
(key.startsWith('event_') ? 'false' : null);
const preReqTurn = source.match(/\bpreReqTurn:\s*(\d+)/)?.[1] ?? null;
if (!actionName || !category || !reqArg) {
throw new Error(
`${path.relative(workspaceRoot, sourcePath)}에서 문서 메타데이터를 읽지 못했습니다: ` +
JSON.stringify({ actionName, category, reqArg })
);
}
return {
key,
name: actionName,
category,
needsInput: reqArg === 'true',
preReqTurn,
};
};
const escapeCell = (value) => String(value).replaceAll('|', '\\|').replaceAll('\n', ' ');
const renderScope = (scope, commands) => {
const categoryOrder = [...new Set(commands.map((command) => command.category))];
const sections = categoryOrder.map((category) => {
const rows = commands
.filter((command) => command.category === category)
.map((command) => {
const timing = command.preReqTurn ? `${Number(command.preReqTurn) + 1}턴 연속 실행` : '1턴';
return `| \`${escapeCell(command.key)}\` | ${escapeCell(command.name)} | ${
command.needsInput ? '필요' : '없음'
} | ${timing} |`;
})
.join('\n');
return `### ${category}\n\n| 내부 키 | 화면 이름 | 대상·수량 입력 | 기본 실행 단위 |\n| --- | --- | --- | --- |\n${rows}`;
});
return `## ${scope.title}\n\n등록된 명령은 ${commands.length}개입니다.\n\n${sections.join('\n\n')}`;
};
const generated = [];
for (const scope of scopes) {
const registrySource = await fs.readFile(scope.registry, 'utf8');
const keys = parseRegistry(registrySource, scope.registryName);
const commands = [];
for (const key of keys) {
commands.push(await parseCommand(scope, key));
}
generated.push({ scope, commands });
}
const total = generated.reduce((sum, entry) => sum + entry.commands.length, 0);
const markdown = `---
title: 커맨드 전체 목록
outline: deep
---
<!-- 이 파일은 tools/docs/generate-command-reference.mjs가 생성합니다. 직접 수정하지 말아 주세요. -->
# 커맨드 전체 목록
이 페이지는 현재 소스의 명령 등록부와 각 \`commandSpec\`에서 자동 생성됩니다.
${total}개이며, profile의 \`resources/turn-commands/*.json\` 설정에 따라 실제 서버에서 일부가 제외될 수
있습니다. “기본 실행 단위”는 정의에 고정된 선행 턴만 표시합니다. 자원, 신분, 도시, 외교, 시나리오 시점처럼
실행 순간에 달라지는 조건은 [커맨드와 실행 시기](./commands-and-timing.md)를 확인해 주세요.
${generated.map(({ scope, commands }) => renderScope(scope, commands)).join('\n\n')}
## 생성 근거
- 등록 순서: \`packages/logic/src/actions/turn/general/index.ts\`,
\`packages/logic/src/actions/turn/nation/index.ts\`
- 표시명·분류·입력 여부·다중 턴: 각 명령 모듈의 \`commandSpec\`\`ActionDefinition\`
- 생성 명령: \`pnpm docs:generate\`
`;
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, await prettier.format(markdown, { parser: 'markdown' }), 'utf8');
console.log(
`generated ${path.relative(workspaceRoot, outputPath)} (${generated[0].commands.length} general, ${
generated[1].commands.length
} nation)`
);