From 18e09342da6b56dbd439c9670eeefd267883549c Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Fri, 19 Jun 2026 04:47:18 +0800
Subject: [PATCH 01/17] feat: game options validation, Hide & Seek manager,
role system, API events, matchmaker improvements, and config updates
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
=== Game Options Validation (src/game/GameOptions.ts) [NEW] ===
+ GameOptionsValidator.validateGameSettings() — validates all game settings
+ Numeric range checks: playerSpeed, killCooldown, vision, tasks, timers
+ Enum checks: GameMap, GameKeyword, KillDistance, TaskBarMode, GameMode,
RulesPreset
+ Dependency check: numEmergencies <= maxPlayers
+ Role settings validation for all 9 roles
+ Used in WaterwayServer.handleHostGameMessage() on room creation
+ Used in Room constructor on player.syncsettings RPC
=== Hide & Seek Game Mode Manager (src/game/modes/HideAndSeekManager.ts) [NEW] ===
+ Full lifecycle: Hiding -> Seeking -> FinalHide -> Ended
+ Automatic seeker selection (configured playerId or random)
+ Hider tracking with task-completion win condition
+ Body reports and meetings disabled during gameplay
+ Final hide phase: seeker speed boost, position ping mechanic
+ Player disconnect handling:
- Seeker leaves -> hiders win
- All hiders leave -> seeker wins
+ Integrated into Room.handleStartGame() and Room.handleRpcMessage()
+ Clean teardown on destroy / game end
=== Role System (src/game/roles/) [NEW] ===
[NEW] BaseRole.ts — server-side role base class
+ Cooldown management (startCooldown, canUseAbility, isOnCooldown)
+ Hooks: onGameStart, onKill, onTaskComplete, onDeath, onMeetingStart,
onAbilityUse, onFixedUpdate, onGameEnd
+ roleType, teamType, isActive, currentCooldown, lastAbilityUse
[NEW] NoisemakerRole.ts — crewmate, emits alert on task completion
+ onTaskComplete(): broadcasts position reveal to nearby players
+ Configurable alert duration and impostor visibility
[NEW] PhantomRole.ts — impostor, vanishes on death
+ onDeath(): enters invisible ghost state instead of dying
+ Vanish duration + cooldown from role settings
+ Auto-dies after vanish expires
[NEW] TrackerRole.ts — crewmate, tracks a target
+ onAbilityUse(target): starts periodic position updates
+ Tracking duration, cooldown, and update delay from role settings
+ Auto-stops after duration
[NEW] DetectiveRole.ts — crewmate, inspects suspects
+ onAbilityUse(target): checks kill count, reports suspicion level
+ suspectLimit from role settings
+ Tracks murder records during game
[NEW] ViperRole.ts — impostor, delayed poison kills
+ onKill(target): applies poison instead of instant kill
+ Target dies after viperDissolveTime seconds
+ All poisons cleared on Viper death or game end
[NEW] RoleManager.ts — role assignment and lifecycle
+ assignRoles(): reads roleChances from settings, rolls dice per player
+ Respects maxPlayers limits and team constraints
+ Routes game events (kill, task, death, meeting) to correct role
+ Plugin-extensible via static registerRole()
=== New API Events (src/api/events/) [NEW] ===
[NEW] room/HideAndSeekStart.ts — room.hideandseekstart
Fields: room, seekerPlayer, hiderPlayers, hideDuration
[NEW] room/HiderCaught.ts — room.hidercaught
Fields: room, hiderPlayer, seekerPlayer, remainingHiders
[NEW] room/HideAndSeekEnd.ts — room.hideandseekend
Fields: room, winner ("seekers"|"hiders"|"none"), reason
[NEW] player/TaskStarted.ts — player.taskstarted
Fields: room, player, taskType, taskId
[NEW] player/TaskProgress.ts — player.taskprogress
Fields: room, player, taskId, progress
[NEW] player/TaskCompleted.ts — player.taskcompleted
Fields: room, player, taskType, taskId, isLongTask
[NEW] player/LevelChanged.ts — player.levelchanged
Fields: room, player, oldLevel, newLevel
Emitted in Room.on("player.setlevel") handler
[NEW] player/MurderFail.ts — player.murderfail
Fields: room, player (killer), target, reason, details?
Emitted from Room.on("player.checkmurder") when murder is invalid
[MODIFY] events/room/index.ts — export HideAndSeekStart, HideAndSeekEnd,
HiderCaught
[NEW] events/player/index.ts — export all new player events
[MODIFY] events/index.ts — export player module
=== HTTP Matchmaker (src/Matchmaker.ts) [MODIFY] ===
[NEW] GET / — server dashboard HTML page
Shows: room/player/connection/plugin counts, public room list table,
API endpoint reference. Dark theme, zero external CSS.
[ENHANCE] GET /api/games/:id — now returns full room info
+ Players array: clientId, username, color, hat, skin, visor,
isHost, isDead
+ GameState, GameMode, Privacy strings
[FIX] GET /api/filtertags — was stub returning [], now dynamic
+ Scans active rooms for maps, languages, game modes
+ Merges with configurable static filterTags from server config
+ Returns FilterTagJson[] with Name, DisplayName, Type, Count
[NEW] POST /api/games/:id/refresh — room metadata refresh
+ Host can update privacy and room name
+ Returns updated GameListingJson
=== Config Updates (src/WaterwayServer.ts, bin/createDefaultConfig.ts) [MODIFY] ===
[ADD] GameListingConfig.filterTags
Type: { name: string; displayName: string; icon?: string }[]
Served by GET /api/filtertags as static filter buttons
[ADD] RoomsConfig.allowedGameModes
Type: number[] — restrict which game modes rooms can use
Empty array = all modes allowed
[ADD] RoomsConfig.defaultGameMode
Type: number — game mode for new rooms (default 1 = Normal)
[ENHANCE] Room constructor applies enforceSettings from config on creation
Settings in enforceSettings cannot be overridden by host
[MODIFY] bin/createDefaultConfig.ts — all new fields with defaults
=== Room Integration (src/Room.ts) [MODIFY] ===
+ gameModeManager property — active game mode handler (HideAndSeekManager)
+ roleManager property — role assignment and lifecycle
+ Validates game options on player.syncsettings (rejects invalid)
+ Emits PlayerLevelChangedEvent on player.setlevel
+ Emits PlayerTaskCompletedEvent on player.completetask
+ Emits PlayerMurderFailEvent on player.checkmurder (when !isValid)
+ Routes kill/task/death through roleManager
+ Routes RPCs through gameModeManager
+ Cleanup in _reset() and destroy()
=== Verification ===
- TypeScript compilation passes cleanly (tsc --noEmit exits 0)
- All events follow existing Waterway event patterns
- Role classes extend Waterway BaseRole (server-side), not SkeldJS BaseRole
---
bin/createDefaultConfig.ts | 5 +-
changelog.txt | 149 +
package-lock.json | 2701 +++++++++++++
src/Matchmaker.ts | 408 +-
src/Room.ts | 152 +
src/WaterwayServer.ts | 42 +
src/api/events/index.ts | 1 +
src/api/events/player/LevelChanged.ts | 30 +
src/api/events/player/MurderFail.ts | 42 +
src/api/events/player/TaskCompleted.ts | 34 +
src/api/events/player/TaskProgress.ts | 30 +
src/api/events/player/TaskStarted.ts | 30 +
src/api/events/player/index.ts | 5 +
src/api/events/room/HideAndSeekEnd.ts | 28 +
src/api/events/room/HideAndSeekStart.ts | 31 +
src/api/events/room/HiderCaught.ts | 30 +
src/api/events/room/index.ts | 3 +
src/game/GameOptions.ts | 595 +++
src/game/index.ts | 1 +
src/game/modes/HideAndSeekManager.ts | 472 +++
src/game/modes/index.ts | 1 +
src/game/roles/BaseRole.ts | 120 +
src/game/roles/DetectiveRole.ts | 110 +
src/game/roles/NoisemakerRole.ts | 84 +
src/game/roles/PhantomRole.ts | 110 +
src/game/roles/RoleManager.ts | 242 ++
src/game/roles/TrackerRole.ts | 132 +
src/game/roles/ViperRole.ts | 134 +
src/game/roles/index.ts | 7 +
yarn.lock | 4603 ++++++++---------------
30 files changed, 7342 insertions(+), 2990 deletions(-)
create mode 100644 changelog.txt
create mode 100644 package-lock.json
create mode 100644 src/api/events/player/LevelChanged.ts
create mode 100644 src/api/events/player/MurderFail.ts
create mode 100644 src/api/events/player/TaskCompleted.ts
create mode 100644 src/api/events/player/TaskProgress.ts
create mode 100644 src/api/events/player/TaskStarted.ts
create mode 100644 src/api/events/player/index.ts
create mode 100644 src/api/events/room/HideAndSeekEnd.ts
create mode 100644 src/api/events/room/HideAndSeekStart.ts
create mode 100644 src/api/events/room/HiderCaught.ts
create mode 100644 src/game/GameOptions.ts
create mode 100644 src/game/index.ts
create mode 100644 src/game/modes/HideAndSeekManager.ts
create mode 100644 src/game/modes/index.ts
create mode 100644 src/game/roles/BaseRole.ts
create mode 100644 src/game/roles/DetectiveRole.ts
create mode 100644 src/game/roles/NoisemakerRole.ts
create mode 100644 src/game/roles/PhantomRole.ts
create mode 100644 src/game/roles/RoleManager.ts
create mode 100644 src/game/roles/TrackerRole.ts
create mode 100644 src/game/roles/ViperRole.ts
create mode 100644 src/game/roles/index.ts
diff --git a/bin/createDefaultConfig.ts b/bin/createDefaultConfig.ts
index 4db6a4845..374755883 100644
--- a/bin/createDefaultConfig.ts
+++ b/bin/createDefaultConfig.ts
@@ -26,7 +26,8 @@ export function createDefaultConfig(): WaterwayConfig {
ignoreSearchTerms: false,
maxResults: 10,
removeExtraFilters: false,
- requireExactMatches: true
+ requireExactMatches: true,
+ filterTags: []
},
plugins: {
loadDirectory: true
@@ -52,6 +53,8 @@ export function createDefaultConfig(): WaterwayConfig {
},
gameCodes: "v2",
enforceSettings: {},
+ allowedGameModes: [],
+ defaultGameMode: 1,
authoritativeServer: false,
advanced: {
unknownObjects: false
diff --git a/changelog.txt b/changelog.txt
new file mode 100644
index 000000000..8f1433171
--- /dev/null
+++ b/changelog.txt
@@ -0,0 +1,149 @@
+feat: game options validation, Hide & Seek manager, role system, API events, matchmaker improvements, and config updates
+
+=== Game Options Validation (src/game/GameOptions.ts) [NEW] ===
+ + GameOptionsValidator.validateGameSettings() — validates all game settings
+ + Numeric range checks: playerSpeed, killCooldown, vision, tasks, timers
+ + Enum checks: GameMap, GameKeyword, KillDistance, TaskBarMode, GameMode,
+ RulesPreset
+ + Dependency check: numEmergencies <= maxPlayers
+ + Role settings validation for all 9 roles
+ + Used in WaterwayServer.handleHostGameMessage() on room creation
+ + Used in Room constructor on player.syncsettings RPC
+
+=== Hide & Seek Game Mode Manager (src/game/modes/HideAndSeekManager.ts) [NEW] ===
+ + Full lifecycle: Hiding -> Seeking -> FinalHide -> Ended
+ + Automatic seeker selection (configured playerId or random)
+ + Hider tracking with task-completion win condition
+ + Body reports and meetings disabled during gameplay
+ + Final hide phase: seeker speed boost, position ping mechanic
+ + Player disconnect handling:
+ - Seeker leaves -> hiders win
+ - All hiders leave -> seeker wins
+ + Integrated into Room.handleStartGame() and Room.handleRpcMessage()
+ + Clean teardown on destroy / game end
+
+=== Role System (src/game/roles/) [NEW] ===
+
+ [NEW] BaseRole.ts — server-side role base class
+ + Cooldown management (startCooldown, canUseAbility, isOnCooldown)
+ + Hooks: onGameStart, onKill, onTaskComplete, onDeath, onMeetingStart,
+ onAbilityUse, onFixedUpdate, onGameEnd
+ + roleType, teamType, isActive, currentCooldown, lastAbilityUse
+
+ [NEW] NoisemakerRole.ts — crewmate, emits alert on task completion
+ + onTaskComplete(): broadcasts position reveal to nearby players
+ + Configurable alert duration and impostor visibility
+
+ [NEW] PhantomRole.ts — impostor, vanishes on death
+ + onDeath(): enters invisible ghost state instead of dying
+ + Vanish duration + cooldown from role settings
+ + Auto-dies after vanish expires
+
+ [NEW] TrackerRole.ts — crewmate, tracks a target
+ + onAbilityUse(target): starts periodic position updates
+ + Tracking duration, cooldown, and update delay from role settings
+ + Auto-stops after duration
+
+ [NEW] DetectiveRole.ts — crewmate, inspects suspects
+ + onAbilityUse(target): checks kill count, reports suspicion level
+ + suspectLimit from role settings
+ + Tracks murder records during game
+
+ [NEW] ViperRole.ts — impostor, delayed poison kills
+ + onKill(target): applies poison instead of instant kill
+ + Target dies after viperDissolveTime seconds
+ + All poisons cleared on Viper death or game end
+
+ [NEW] RoleManager.ts — role assignment and lifecycle
+ + assignRoles(): reads roleChances from settings, rolls dice per player
+ + Respects maxPlayers limits and team constraints
+ + Routes game events (kill, task, death, meeting) to correct role
+ + Plugin-extensible via static registerRole()
+
+=== New API Events (src/api/events/) [NEW] ===
+
+ [NEW] room/HideAndSeekStart.ts — room.hideandseekstart
+ Fields: room, seekerPlayer, hiderPlayers, hideDuration
+
+ [NEW] room/HiderCaught.ts — room.hidercaught
+ Fields: room, hiderPlayer, seekerPlayer, remainingHiders
+
+ [NEW] room/HideAndSeekEnd.ts — room.hideandseekend
+ Fields: room, winner ("seekers"|"hiders"|"none"), reason
+
+ [NEW] player/TaskStarted.ts — player.taskstarted
+ Fields: room, player, taskType, taskId
+
+ [NEW] player/TaskProgress.ts — player.taskprogress
+ Fields: room, player, taskId, progress
+
+ [NEW] player/TaskCompleted.ts — player.taskcompleted
+ Fields: room, player, taskType, taskId, isLongTask
+
+ [NEW] player/LevelChanged.ts — player.levelchanged
+ Fields: room, player, oldLevel, newLevel
+ Emitted in Room.on("player.setlevel") handler
+
+ [NEW] player/MurderFail.ts — player.murderfail
+ Fields: room, player (killer), target, reason, details?
+ Emitted from Room.on("player.checkmurder") when murder is invalid
+
+ [MODIFY] events/room/index.ts — export HideAndSeekStart, HideAndSeekEnd,
+ HiderCaught
+ [NEW] events/player/index.ts — export all new player events
+ [MODIFY] events/index.ts — export player module
+
+=== HTTP Matchmaker (src/Matchmaker.ts) [MODIFY] ===
+
+ [NEW] GET / — server dashboard HTML page
+ Shows: room/player/connection/plugin counts, public room list table,
+ API endpoint reference. Dark theme, zero external CSS.
+
+ [ENHANCE] GET /api/games/:id — now returns full room info
+ + Players array: clientId, username, color, hat, skin, visor,
+ isHost, isDead
+ + GameState, GameMode, Privacy strings
+
+ [FIX] GET /api/filtertags — was stub returning [], now dynamic
+ + Scans active rooms for maps, languages, game modes
+ + Merges with configurable static filterTags from server config
+ + Returns FilterTagJson[] with Name, DisplayName, Type, Count
+
+ [NEW] POST /api/games/:id/refresh — room metadata refresh
+ + Host can update privacy and room name
+ + Returns updated GameListingJson
+
+=== Config Updates (src/WaterwayServer.ts, bin/createDefaultConfig.ts) [MODIFY] ===
+
+ [ADD] GameListingConfig.filterTags
+ Type: { name: string; displayName: string; icon?: string }[]
+ Served by GET /api/filtertags as static filter buttons
+
+ [ADD] RoomsConfig.allowedGameModes
+ Type: number[] — restrict which game modes rooms can use
+ Empty array = all modes allowed
+
+ [ADD] RoomsConfig.defaultGameMode
+ Type: number — game mode for new rooms (default 1 = Normal)
+
+ [ENHANCE] Room constructor applies enforceSettings from config on creation
+ Settings in enforceSettings cannot be overridden by host
+
+ [MODIFY] bin/createDefaultConfig.ts — all new fields with defaults
+
+=== Room Integration (src/Room.ts) [MODIFY] ===
+
+ + gameModeManager property — active game mode handler (HideAndSeekManager)
+ + roleManager property — role assignment and lifecycle
+ + Validates game options on player.syncsettings (rejects invalid)
+ + Emits PlayerLevelChangedEvent on player.setlevel
+ + Emits PlayerTaskCompletedEvent on player.completetask
+ + Emits PlayerMurderFailEvent on player.checkmurder (when !isValid)
+ + Routes kill/task/death through roleManager
+ + Routes RPCs through gameModeManager
+ + Cleanup in _reset() and destroy()
+
+=== Verification ===
+ - TypeScript compilation passes cleanly (tsc --noEmit exits 0)
+ - All events follow existing Waterway event patterns
+ - Role classes extend Waterway BaseRole (server-side), not SkeldJS BaseRole
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..5e99ae13d
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2701 @@
+{
+ "name": "@skeldjs/waterway",
+ "version": "3.0.3",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@skeldjs/waterway",
+ "version": "3.0.3",
+ "license": "GPL-3.0-only",
+ "dependencies": {
+ "@koa/router": "^14.0.0",
+ "@schemastore/package": "^0.0.6",
+ "@skeldjs/au-client": "^3.0.3",
+ "@skeldjs/au-constants": "^3.0.3",
+ "@skeldjs/au-core": "^3.0.3",
+ "@skeldjs/au-protocol": "^3.0.3",
+ "@skeldjs/au-text": "^3.0.3",
+ "@skeldjs/events": "^3.0.3",
+ "@skeldjs/hazel": "^3.0.3",
+ "chalk": "^4.1.1",
+ "chokidar": "^3.5.2",
+ "compare-versions": "^3.6.0",
+ "dotenv": "^10.0.0",
+ "koa": "^3.0.3",
+ "koa-body": "^6.0.1",
+ "minimatch": "^3.0.4",
+ "prompts": "^2.4.2",
+ "query-registry": "^2.5.0",
+ "reflect-metadata": "^0.1.13",
+ "resolve-from": "^5.0.0",
+ "resolve-pkg": "^2.0.0",
+ "triple-beam": "^1.3.0",
+ "vorpal": "^1.12.0"
+ },
+ "bin": {
+ "waterway": "dist/bin/pkg.js"
+ },
+ "devDependencies": {
+ "@types/koa": "^2",
+ "@types/koa__router": "^12",
+ "@types/minimatch": "^3.0.5",
+ "@types/node": "^24.7.0",
+ "@types/prompts": "^2.0.14",
+ "@types/triple-beam": "^1.3.2",
+ "@types/vorpal": "^1.12.0",
+ "@types/yargs": "^17.0.0",
+ "pinst": "^2.1.6",
+ "ts-node": "^10.0.0",
+ "typedoc": "^0.28.13",
+ "typedoc-plugin-external-module-map": "^2.2.0",
+ "typescript": "^5.9.3"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@gerrit0/mini-shiki": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz",
+ "integrity": "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/engine-oniguruma": "^3.23.0",
+ "@shikijs/langs": "^3.23.0",
+ "@shikijs/themes": "^3.23.0",
+ "@shikijs/types": "^3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ }
+ },
+ "node_modules/@hapi/bourne": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz",
+ "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@koa/router": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/@koa/router/-/router-14.0.0.tgz",
+ "integrity": "sha512-LBSu5K0qAaaQcXX/0WIB9PGDevyCxxpnc1uq13vV/CgObaVxuis5hKl3Eboq/8gcb6ebnkAStW9NB/Em2eYyFA==",
+ "deprecated": "Please upgrade to v15 or higher. All reported bugs in this version are fixed in newer releases, dependencies have been updated, and security has been improved.",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.1",
+ "http-errors": "^2.0.0",
+ "koa-compose": "^4.1.0",
+ "path-to-regexp": "^8.2.0"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
+ "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@paralleldrive/cuid2": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz",
+ "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "^1.1.5"
+ }
+ },
+ "node_modules/@schemastore/package": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/@schemastore/package/-/package-0.0.6.tgz",
+ "integrity": "sha512-uNloNHoyHttSSdeuEkkSC+mdxJXMKlcUPOMb//qhQbIQijXg8x54VmAw3jm6GJZQ5DBtIqGBd66zEQCDCChQVA==",
+ "license": "MIT"
+ },
+ "node_modules/@shikijs/engine-oniguruma": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz",
+ "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0",
+ "@shikijs/vscode-textmate": "^10.0.2"
+ }
+ },
+ "node_modules/@shikijs/langs": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz",
+ "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0"
+ }
+ },
+ "node_modules/@shikijs/themes": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz",
+ "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/types": "3.23.0"
+ }
+ },
+ "node_modules/@shikijs/types": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz",
+ "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/vscode-textmate": "^10.0.2",
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@shikijs/vscode-textmate": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
+ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@skeldjs/au-client": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@skeldjs/au-client/-/au-client-3.0.3.tgz",
+ "integrity": "sha512-ZvjcLCjQcljl2EUtPIUL5uqG1pyjvU1rgL02CxkGc6IFNW3VF+rDHJZHjjsun7gMzyoTOTP+zqTUf/7/5KQQKw==",
+ "license": "GPL-3.0-only"
+ },
+ "node_modules/@skeldjs/au-constants": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@skeldjs/au-constants/-/au-constants-3.0.3.tgz",
+ "integrity": "sha512-A8xD3XuX5eyrw+HWuqNNrMhw1TQ78Vdq8Zypg5cgYxWe12hgpqf22X9XgOkru1M/943u1xmkQoNvASDD0/j38g==",
+ "license": "GPL-3.0-only"
+ },
+ "node_modules/@skeldjs/au-core": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@skeldjs/au-core/-/au-core-3.0.3.tgz",
+ "integrity": "sha512-Mobo6sn8CjLDmo9DT1YTQlQMtVzuTQy9+obK5e4vVhhXSDfebl4avk6A+6l9ZW/FqgQfZOARmFpV30vZGdJ7Cg==",
+ "license": "GPL-3.0-only",
+ "dependencies": {
+ "@skeldjs/au-constants": "3.0.3",
+ "@skeldjs/au-protocol": "3.0.3",
+ "@skeldjs/events": "3.0.3",
+ "@skeldjs/hazel": "3.0.3"
+ }
+ },
+ "node_modules/@skeldjs/au-protocol": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@skeldjs/au-protocol/-/au-protocol-3.0.3.tgz",
+ "integrity": "sha512-35KuC7ZOgfMlwn2NSfpabC0di76NAYsqjV2oWhdrmxvXcXPsOgoXx1R4K8V8dsh5Chj9odCysyjUOMbsLkI5mQ==",
+ "license": "GPL-3.0-only",
+ "dependencies": {
+ "@skeldjs/au-constants": "3.0.3",
+ "@skeldjs/hazel": "3.0.3"
+ }
+ },
+ "node_modules/@skeldjs/au-text": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@skeldjs/au-text/-/au-text-3.0.3.tgz",
+ "integrity": "sha512-00fKMnEViEPFNOF6q+0eM7IXvYgXcqIDhZGNTbKwTLqWhhwWp0QJgXBBRws4otoAd0/jiyS/zFYq+WzA3Vh57Q==",
+ "license": "GPL-3.0-only"
+ },
+ "node_modules/@skeldjs/events": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@skeldjs/events/-/events-3.0.3.tgz",
+ "integrity": "sha512-ZONbKbfq1aIY8JxSFWBmhxuWcn7mvvEvxM1OUO/e/+0zwQYqWCN5Jwv2u+rumXqPoerVBT1kd/I6JZ2kXaM5/A==",
+ "license": "GPL-3.0-only"
+ },
+ "node_modules/@skeldjs/hazel": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@skeldjs/hazel/-/hazel-3.0.3.tgz",
+ "integrity": "sha512-AQCsgcJVBGXSLd1mkgyvpttL3n5QUg7919pc18gmdIWGh8XJ9yl3XoGmg5uUCZXVcpQ21RfrrdL98zEfQQyumQ==",
+ "license": "GPL-3.0-only"
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
+ "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.6",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
+ "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/co-body": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/@types/co-body/-/co-body-6.1.3.tgz",
+ "integrity": "sha512-UhuhrQ5hclX6UJctv5m4Rfp52AfG9o9+d9/HwjxhVB5NjXxr5t9oKgJxN8xRHgr35oo8meUEHUPFWiKg6y71aA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/content-disposition": {
+ "version": "0.5.9",
+ "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.9.tgz",
+ "integrity": "sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/cookies": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.2.tgz",
+ "integrity": "sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/express": "*",
+ "@types/keygrip": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/express": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
+ "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^5.0.0",
+ "@types/serve-static": "^2"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
+ "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*",
+ "@types/send": "*"
+ }
+ },
+ "node_modules/@types/formidable": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.6.tgz",
+ "integrity": "sha512-L4HcrA05IgQyNYJj6kItuIkXrInJvsXTPC5B1i64FggWKKqSL+4hgt7asiSNva75AoLQjq29oPxFfU4GAQ6Z2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/http-assert": {
+ "version": "1.5.6",
+ "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.6.tgz",
+ "integrity": "sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/http-errors": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/keygrip": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz",
+ "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/koa": {
+ "version": "2.15.2",
+ "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.15.2.tgz",
+ "integrity": "sha512-CB+iyjjh1uS5N6/CKwXvw0qA7USMS2WVc4Tjf660yCjhdvqzNr8gdFcIawB41zGGptOQ+d1fnpaQWIIUXYxR3w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/accepts": "*",
+ "@types/content-disposition": "*",
+ "@types/cookies": "*",
+ "@types/http-assert": "*",
+ "@types/http-errors": "*",
+ "@types/keygrip": "*",
+ "@types/koa-compose": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/koa__router": {
+ "version": "12.0.5",
+ "resolved": "https://registry.npmjs.org/@types/koa__router/-/koa__router-12.0.5.tgz",
+ "integrity": "sha512-1HeLxuDn4n5it1yZYCSyOYXo++73zT0ffoviXnPxbwbxLbvDFEvWD9ZzpRiIpK4oKR0pi+K+Mk/ZjyROjW3HSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/koa": "*"
+ }
+ },
+ "node_modules/@types/koa-compose": {
+ "version": "3.2.9",
+ "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.9.tgz",
+ "integrity": "sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/koa": "*"
+ }
+ },
+ "node_modules/@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.13.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
+ "integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.18.0"
+ }
+ },
+ "node_modules/@types/prompts": {
+ "version": "2.4.9",
+ "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz",
+ "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "kleur": "^3.0.3"
+ }
+ },
+ "node_modules/@types/qs": {
+ "version": "6.15.1",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz",
+ "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/serve-static": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/triple-beam": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/vorpal": {
+ "version": "1.12.8",
+ "resolved": "https://registry.npmjs.org/@types/vorpal/-/vorpal-1.12.8.tgz",
+ "integrity": "sha512-Qt+Yxa1q6QCaYMxZFXlyPOF3ktIscTelNr1AFYuKM7/Dhlki4gvc476uFyA/hYvskSA6V8W+55x9FjlbAPcYdQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/yargs": {
+ "version": "17.0.35",
+ "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz",
+ "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.3",
+ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.5",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",
+ "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
+ "integrity": "sha512-wiXutNjDUlNEDWHcYH3jtZUhd3c4/VojassD8zHdHCY13xbZy2XbW+NKQwA0tWGBVzDA9qEzYwfoSsWmviidhw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "license": "MIT"
+ },
+ "node_modules/babel-polyfill": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
+ "integrity": "sha512-F2rZGQnAdaHWQ8YAoeRbukc7HS9QgdgeyJ0rQDd485v9opwuPvjpPFcOOT/WmkKTdgy9ESgSPXDcTNpzrGr6iQ==",
+ "license": "MIT",
+ "dependencies": {
+ "babel-runtime": "^6.26.0",
+ "core-js": "^2.5.0",
+ "regenerator-runtime": "^0.10.5"
+ }
+ },
+ "node_modules/babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+ "license": "MIT",
+ "dependencies": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ }
+ },
+ "node_modules/babel-runtime/node_modules/regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
+ "license": "MIT"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
+ "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/builtins": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz",
+ "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==",
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.0.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
+ "integrity": "sha512-25tABq090YNKkF6JH7lcwO0zFJTRke4Jcq9iX2nr/Sz0Cjjv4gckmwlW6Ty/aoyFd6z3ysR2hMGC2GFugmBo6A==",
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/cli-width": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-1.1.1.tgz",
+ "integrity": "sha512-eMU2akIeEIkCxGXUNmDnJq1KzOIiPnJ+rKqRe6hcxE3vIOPvpMrBYOn/Bl7zNlYJj/zQxXquAnozHUCf9Whnsg==",
+ "license": "ISC"
+ },
+ "node_modules/co-body": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/co-body/-/co-body-6.2.0.tgz",
+ "integrity": "sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@hapi/bourne": "^3.0.0",
+ "inflation": "^2.0.0",
+ "qs": "^6.5.2",
+ "raw-body": "^2.3.3",
+ "type-is": "^1.6.16"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/co-body/node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/co-body/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/co-body/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/co-body/node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/compare-versions": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
+ "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==",
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "license": "MIT"
+ },
+ "node_modules/content-disposition": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
+ "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookies": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz",
+ "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "keygrip": "~1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/core-js": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
+ "hasInstallScript": true,
+ "license": "MIT"
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
+ "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==",
+ "license": "MIT"
+ },
+ "node_modules/delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+ "license": "MIT"
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dezalgo": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+ "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
+ "license": "ISC",
+ "dependencies": {
+ "asap": "^2.0.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/diff": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
+ "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
+ "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/exit-hook": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
+ "integrity": "sha512-MsG3prOVw1WtLXAZbM3KiYtooKR1LvxHh3VHsVtIy0uiUu8usxgB/94DP2HxtD/661lLdB6yzQ09lGJSQr6nkg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/figures": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+ "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5",
+ "object-assign": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/formidable": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz",
+ "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@paralleldrive/cuid2": "^2.2.2",
+ "dezalgo": "^1.0.4",
+ "once": "^1.4.0",
+ "qs": "^6.11.0"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/tunnckoCore/commissions"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fromentries": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
+ "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-assert": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz",
+ "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==",
+ "license": "MIT",
+ "dependencies": {
+ "deep-equal": "~1.0.1",
+ "http-errors": "~1.8.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-assert/node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/http-assert/node_modules/http-errors": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+ "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/http-assert/node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/in-publish": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
+ "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==",
+ "license": "ISC",
+ "bin": {
+ "in-install": "in-install.js",
+ "in-publish": "in-publish.js",
+ "not-in-install": "not-in-install.js",
+ "not-in-publish": "not-in-publish.js"
+ }
+ },
+ "node_modules/inflation": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.1.0.tgz",
+ "integrity": "sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/inquirer": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.11.0.tgz",
+ "integrity": "sha512-LIwC+g/fJbmKhDm341+RqDIV4jPf/n3pMway9xg8Ovt6CCQo1ozXhmuKTcoNIWhWJJKsSGZP+Rnuq7JgM7mE2A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^1.1.0",
+ "ansi-regex": "^2.0.0",
+ "chalk": "^1.0.0",
+ "cli-cursor": "^1.0.1",
+ "cli-width": "^1.0.1",
+ "figures": "^1.3.5",
+ "lodash": "^3.3.1",
+ "readline2": "^1.0.1",
+ "run-async": "^0.1.0",
+ "rx-lite": "^3.1.2",
+ "strip-ansi": "^3.0.0",
+ "through": "^2.3.6"
+ }
+ },
+ "node_modules/inquirer/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/inquirer/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/inquirer/node_modules/lodash": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+ "integrity": "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ==",
+ "license": "MIT"
+ },
+ "node_modules/inquirer/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
+ "license": "MIT",
+ "dependencies": {
+ "number-is-nan": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/isomorphic-unfetch": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz",
+ "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "node-fetch": "^2.6.1",
+ "unfetch": "^4.2.0"
+ }
+ },
+ "node_modules/keygrip": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
+ "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tsscmp": "1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/koa": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/koa/-/koa-3.2.1.tgz",
+ "integrity": "sha512-e7IpWJrnanNUroVK2taAgMxoEZvHLXdQiNjeExSu/DEIWm83jaKGBgb7tLmu2rMYpA027qFB3iLR/k3AVpFRnA==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^1.3.8",
+ "content-disposition": "~1.0.1",
+ "content-type": "^1.0.5",
+ "cookies": "~0.9.1",
+ "delegates": "^1.0.0",
+ "destroy": "^1.2.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "fresh": "~0.5.2",
+ "http-assert": "^1.5.0",
+ "http-errors": "^2.0.0",
+ "koa-compose": "^4.1.0",
+ "mime-types": "^3.0.1",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/koa-body": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/koa-body/-/koa-body-6.0.1.tgz",
+ "integrity": "sha512-M8ZvMD8r+kPHy28aWP9VxL7kY8oPWA+C7ZgCljrCMeaU7uX6wsIQgDHskyrAr9sw+jqnIXyv4Mlxri5R4InIJg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/co-body": "^6.1.0",
+ "@types/formidable": "^2.0.5",
+ "@types/koa": "^2.13.5",
+ "co-body": "^6.1.0",
+ "formidable": "^2.0.1",
+ "zod": "^3.19.1"
+ }
+ },
+ "node_modules/koa-compose": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz",
+ "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==",
+ "license": "MIT"
+ },
+ "node_modules/linkify-it": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz",
+ "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/markdown-it"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "license": "MIT"
+ },
+ "node_modules/log-update": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz",
+ "integrity": "sha512-4vSow8gbiGnwdDNrpy1dyNaXWKSCIPop0EHdE8GrnngHoJujM3QhvHUN/igsYCgPoHo7pFOezlJ61Hlln0KHyA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^1.0.0",
+ "cli-cursor": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/lunr": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "license": "ISC"
+ },
+ "node_modules/markdown-it": {
+ "version": "14.2.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz",
+ "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/markdown-it"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.1",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/mute-stream": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",
+ "integrity": "sha512-EbrziT4s8cWPmzr47eYVW3wimS4HsvlnV5ri1xw1aR6JQo/OrJX5rkl32K/QQHdxeabJETtfeaROGhd8W7uBgg==",
+ "license": "ISC"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-localstorage": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-0.6.0.tgz",
+ "integrity": "sha512-t9dKMce8qUs2KK02ZiBgzZSykUxc+5UcML7/20a62ruHwfh7+bNQvrH/auxY5gFNexTwAFdr+DbptxlLq4+7qQ==",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+ "integrity": "sha512-GZ+g4jayMqzCRMgB2sol7GiCLjKfS1PINkjmx8spcKce1LiVqcbQreXwqs2YAFXC6R03VIG28ZS31t8M866v6A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
+ "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pinst": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/pinst/-/pinst-2.1.6.tgz",
+ "integrity": "sha512-B4dYmf6nEXg1NpDSB+orYWvKa5Kfmz5KzWC29U59dpVM4S/+xp0ak/JMEsw04UQTNNKps7klu0BUalr343Gt9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fromentries": "^1.3.2"
+ },
+ "bin": {
+ "pinst": "bin.js"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.15.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
+ "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/query-registry": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/query-registry/-/query-registry-2.6.0.tgz",
+ "integrity": "sha512-Z5oNq7qH0g96qBTx2jAvS0X71hKP4tETtSJKEl6BdihzYqh9QKiJQBMT7qIQuzxR9lxfiso+aXCFhZ+EcAoppQ==",
+ "license": "MIT",
+ "dependencies": {
+ "isomorphic-unfetch": "^3.1.0",
+ "make-error": "^1.3.6",
+ "tiny-lru": "^8.0.2",
+ "url-join": "4.0.1",
+ "validate-npm-package-name": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/readline2": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz",
+ "integrity": "sha512-8/td4MmwUB6PkZUbV25uKz7dfrmjYWxsW8DVfibWdlHRk/l/DfHKn4pU+dfcoGLFgWOdyGCzINRQD7jn+Bv+/g==",
+ "license": "MIT",
+ "dependencies": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "mute-stream": "0.0.5"
+ }
+ },
+ "node_modules/reflect-metadata": {
+ "version": "0.1.14",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz",
+ "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
+ "integrity": "sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==",
+ "license": "MIT"
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-2.0.0.tgz",
+ "integrity": "sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ==",
+ "license": "MIT",
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
+ "integrity": "sha512-reSjH4HuiFlxlaBaFCiS6O76ZGG2ygKoSlCsipKdaZuKSPx/+bt9mULkn4l0asVzbEfQQmXRg6Wp6gv6m0wElw==",
+ "license": "MIT",
+ "dependencies": {
+ "exit-hook": "^1.0.0",
+ "onetime": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-async": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
+ "integrity": "sha512-qOX+w+IxFgpUpJfkv2oGN0+ExPs68F4sZHfaRRx4dDexAQkG83atugKVEylyT5ARees3HBbfmuvnjbrd8j9Wjw==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.3.0"
+ }
+ },
+ "node_modules/rx-lite": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz",
+ "integrity": "sha512-1I1+G2gteLB8Tkt8YI1sJvSIfa0lWuRtC8GjvtyPBcLSF5jBCCJJqKrpER5JU5r6Bhe+i9/pK3VMuUcXu0kdwQ=="
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
+ "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "license": "MIT"
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
+ "license": "MIT",
+ "dependencies": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "license": "MIT"
+ },
+ "node_modules/tiny-lru": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz",
+ "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT"
+ },
+ "node_modules/triple-beam": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+ "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tsscmp": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
+ "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.x"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz",
+ "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==",
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^2.0.0",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/type-is/node_modules/content-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
+ "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/typedoc": {
+ "version": "0.28.19",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.19.tgz",
+ "integrity": "sha512-wKh+lhdmMFivMlc6vRRcMGXeGEHGU2g8a2CkPTJjJlwRf1iXbimWIPcFolCqe4E0d/FRtGszpIrsp3WLpDB8Pw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@gerrit0/mini-shiki": "^3.23.0",
+ "lunr": "^2.3.9",
+ "markdown-it": "^14.1.1",
+ "minimatch": "^10.2.5",
+ "yaml": "^2.8.3"
+ },
+ "bin": {
+ "typedoc": "bin/typedoc"
+ },
+ "engines": {
+ "node": ">= 18",
+ "pnpm": ">= 10"
+ },
+ "peerDependencies": {
+ "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x"
+ }
+ },
+ "node_modules/typedoc-plugin-external-module-map": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/typedoc-plugin-external-module-map/-/typedoc-plugin-external-module-map-2.2.0.tgz",
+ "integrity": "sha512-wWou92pJOMJk6cpUpbIEsBjtZKGLnu9UsVamXhndhkyBBvOBxaS+dx8USPDYByuy1Xx05V36IF+GOLqjPTpfyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^20.14.14"
+ },
+ "peerDependencies": {
+ "typedoc": ">=0.27 <2.0"
+ }
+ },
+ "node_modules/typedoc-plugin-external-module-map/node_modules/@types/node": {
+ "version": "20.19.43",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
+ "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/typedoc-plugin-external-module-map/node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/typedoc/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/typedoc/node_modules/brace-expansion": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/typedoc/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+ "license": "MIT"
+ },
+ "node_modules/unfetch": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz",
+ "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==",
+ "license": "MIT"
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/url-join": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
+ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
+ "license": "MIT"
+ },
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/validate-npm-package-name": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz",
+ "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==",
+ "license": "ISC",
+ "dependencies": {
+ "builtins": "^5.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vorpal": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/vorpal/-/vorpal-1.12.0.tgz",
+ "integrity": "sha512-lYEhd75l75P3D1LKpm4KqdOSpNyNdDJ9ixEZmC5ZAZUKGy6JNexfMdQ9SNaT5pCHuzuXXRJQedJ+CdqNg/D4Kw==",
+ "license": "MIT",
+ "dependencies": {
+ "babel-polyfill": "^6.3.14",
+ "chalk": "^1.1.0",
+ "in-publish": "^2.0.0",
+ "inquirer": "0.11.0",
+ "lodash": "^4.5.1",
+ "log-update": "^1.0.2",
+ "minimist": "^1.2.0",
+ "node-localstorage": "^0.6.0",
+ "strip-ansi": "^3.0.0",
+ "wrap-ansi": "^2.0.0"
+ },
+ "engines": {
+ "iojs": ">= 1.0.0",
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/vorpal/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/vorpal/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/vorpal/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==",
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/src/Matchmaker.ts b/src/Matchmaker.ts
index 91db5bdb9..3f550b039 100644
--- a/src/Matchmaker.ts
+++ b/src/Matchmaker.ts
@@ -7,10 +7,10 @@ import * as crypto from "crypto";
import * as KoaRouter from "@koa/router";
import { HazelWriter } from "@skeldjs/hazel";
import { RoomCode, Version } from "@skeldjs/au-client";
-import { DisconnectReason, Filters, GameKeyword, GameMap, GameMode, Platform, QuickChatMode, StringName } from "@skeldjs/au-core";
+import { DisconnectReason, Filters, GameKeyword, GameMap, GameMode, GameState, Platform, QuickChatMode, StringName } from "@skeldjs/au-core";
import { WaterwayServer } from "./WaterwayServer";
-import { Room, RoomPrivacy } from "./Room";
+import { Room, RoomPrivacy, logMaps } from "./Room";
import { Logger } from "./Logger";
export type GameListingJson = {
@@ -41,6 +41,31 @@ export type GameFoundByCodeJson = {
UntranslatedRegion: string;
}
+export type PlayerListingJson = {
+ ClientId: number;
+ Username: string;
+ Color: number;
+ HatId: string;
+ SkinId: string;
+ VisorId: string;
+ IsHost: boolean;
+ IsDead: boolean;
+}
+
+export type GameInfoJson = GameListingJson & {
+ Players: PlayerListingJson[];
+ GameState: string;
+ GameMode: string;
+ Privacy: string;
+}
+
+export type FilterTagJson = {
+ Name: string;
+ DisplayName: string;
+ Type: string;
+ Count: number;
+}
+
export type MatchmakerTokenPayload = {
Content: {
Puid: string;
@@ -368,6 +393,252 @@ export class Matchmaker {
}
}
+ // ── Root / Home Page ──
+ router.get("/", async (ctx) => {
+ const totalPlayers = [...this.server.connections.values()].filter(c => c.room).length;
+ const totalConnections = this.server.connections.size;
+ const totalRooms = this.server.rooms.size;
+
+ const publicRooms = [...this.server.rooms.values()]
+ .filter(r => r.privacy === RoomPrivacy.Public);
+
+ const roomRows = publicRooms.slice(0, 20).map(room => {
+ const mapName = logMaps[room.settings.map] || GameMap[room.settings.map] || "Unknown";
+ const modeName = GameMode[room.settings.gameMode] || "Normal";
+ const stateName = GameState[room.gameState] || "Lobby";
+ const roomAge = Math.floor((Date.now() - room.createdAt) / 1000);
+ const ageStr = roomAge < 60 ? `${roomAge}s` : `${Math.floor(roomAge / 60)}m`;
+
+ return `
+ ${room.code} |
+ ${room.roomName} |
+ ${room.players.size}/${room.settings.maxPlayers} |
+ ${mapName} |
+ ${modeName} |
+ ${stateName} |
+ ${ageStr} |
+
`;
+ }).join("");
+
+ const html = `
+
+
+
+
+ Waterway — Among Us Server
+
+
+
+
+
+
+ 📡 Active Rooms
+ ${roomRows
+ ? `
+
+ | Code | Host | Players | Map | Mode | State | Age |
+
+ ${roomRows}
+
`
+ : `No active rooms. Host a game to get started!
`}
+
+ 🔌 API Endpoints
+
+
+
GET/
+
This page — server overview
+
+
+
POST/api/user
+
Get a matchmaker authentication token
+
+
+
GET/api/games/:id
+
Get detailed info for a specific room
+
+
+
GET/api/games/filtered
+
Search and filter public rooms
+
+
+
POST/api/games
+
Find host IP/port for a game code
+
+
+
GET/api/filters
+
List available search filters
+
+
+
GET/api/filtertags
+
Dynamic filter tags from active rooms
+
+
+
+
+
+
+`;
+
+ ctx.type = "text/html";
+ ctx.status = 200;
+ ctx.body = html;
+ });
+
router.post("/api/user", async (ctx) => {
const body = (ctx.request as any).body;
if (ctx.req.headers["content-type"] !== "application/json") {
@@ -713,9 +984,68 @@ export class Matchmaker {
});
router.get("/api/filtertags", ctx => {
- // TODO: when does this get called?
+ // Build dynamic filter tags from active rooms
+ const tags: FilterTagJson[] = [];
+
+ // Map tags
+ const mapCounts: Record = {};
+ const languageCounts: Record = {};
+ const gameModeCounts: Record = {};
+
+ for (const [, room] of this.server.rooms) {
+ if (room.privacy === RoomPrivacy.Private &&
+ !this.server.config.gameListing.ignorePrivacy) {
+ continue;
+ }
+
+ const mapName = logMaps[room.settings.map] || GameMap[room.settings.map] || "Unknown";
+ mapCounts[mapName] = (mapCounts[mapName] || 0) + 1;
+
+ const langName = GameKeyword[room.settings.keywords] || "Unknown";
+ languageCounts[langName] = (languageCounts[langName] || 0) + 1;
+
+ const modeName = GameMode[room.settings.gameMode] || "Normal";
+ gameModeCounts[modeName] = (gameModeCounts[modeName] || 0) + 1;
+ }
+
+ // Build map filter tags
+ for (const [name, count] of Object.entries(mapCounts)) {
+ tags.push({ Name: name, DisplayName: name, Type: "map", Count: count });
+ }
+
+ // Build language filter tags
+ for (const [name, count] of Object.entries(languageCounts)) {
+ tags.push({ Name: name, DisplayName: name, Type: "language", Count: count });
+ }
+
+ // Build game mode filter tags
+ for (const [name, count] of Object.entries(gameModeCounts)) {
+ tags.push({ Name: name, DisplayName: name, Type: "gameMode", Count: count });
+ }
+
+ // Include configured static filter tags from server config
+ const configuredTags = this.server.config.gameListing.filterTags;
+ if (configuredTags && Array.isArray(configuredTags)) {
+ for (const tag of configuredTags) {
+ tags.push({
+ Name: tag.name,
+ DisplayName: tag.displayName,
+ Type: "tag",
+ Count: 0,
+ });
+ }
+ } else {
+ // Default tags if none configured
+ tags.push(
+ { Name: "Beginner", DisplayName: "Beginner Friendly", Type: "tag", Count: 0 },
+ { Name: "Expert", DisplayName: "Expert", Type: "tag", Count: 0 },
+ { Name: "Casual", DisplayName: "Casual", Type: "tag", Count: 0 },
+ { Name: "Serious", DisplayName: "Serious", Type: "tag", Count: 0 },
+ );
+ }
+
ctx.status = 200;
- ctx.body = [];
+ ctx.body = tags;
});
router.get("/api/games/:game_id", ctx => {
@@ -735,15 +1065,83 @@ export class Matchmaker {
this.logger.info("Client found room: %s", foundRoom);
+ // Build enhanced player listing
+ const players: PlayerListingJson[] = [];
+ for (const [, player] of foundRoom.players) {
+ const playerInfo = player.getPlayerInfo();
+ const connection = foundRoom.connections.get(player.clientId);
+ players.push({
+ ClientId: player.clientId,
+ Username: player.username || "Unknown",
+ Color: playerInfo?.currentOutfit?.color ?? -1,
+ HatId: playerInfo?.currentOutfit?.hatId ?? "",
+ SkinId: playerInfo?.currentOutfit?.skinId ?? "",
+ VisorId: playerInfo?.currentOutfit?.visorId ?? "",
+ IsHost: foundRoom.authorityId === player.clientId ||
+ (connection ? foundRoom.actingHosts.has(connection) : false),
+ IsDead: playerInfo?.isDead ?? false,
+ });
+ }
+
+ const gameListing = this.getGameListing(ctx.socket.remoteAddress || "", foundRoom);
+ const gameInfo: GameInfoJson = {
+ ...gameListing,
+ Players: players,
+ GameState: GameState[foundRoom.gameState] || "Unknown",
+ GameMode: GameMode[foundRoom.settings.gameMode] || "Normal",
+ Privacy: foundRoom.privacy === RoomPrivacy.Public ? "Public" : "Private",
+ };
+
ctx.status = 200;
ctx.body = {
Errors: null,
- Game: this.getGameListing(ctx.socket.remoteAddress || "", foundRoom),
+ Game: gameInfo,
Region: StringName.NoTranslation,
UntranslatedRegion: this.server.config.clusterName,
} as GameFoundByCodeJson;
});
+ router.post("/api/games/:game_id/refresh", ctx => {
+ if (!this.verifyRequest(ctx)) {
+ ctx.status = 401;
+ return;
+ }
+
+ const gameCode = parseInt(ctx.params.game_id);
+ const foundRoom = this.server.rooms.get(gameCode);
+
+ if (!foundRoom) {
+ ctx.status = 404;
+ ctx.body = {
+ Errors: [{ Reason: DisconnectReason[DisconnectReason.GameNotFound] }]
+ };
+ return;
+ }
+
+ // Refresh the room's filter-related metadata
+ // This allows hosts to update their room's visibility tags
+ const body = (ctx.request as any).body || {};
+
+ if (typeof body.privacy === "number") {
+ foundRoom.privacy = body.privacy === 1
+ ? RoomPrivacy.Public
+ : RoomPrivacy.Private;
+ }
+
+ if (typeof body.roomName === "string" && body.roomName.length > 0) {
+ foundRoom.setRoomNameOverride(body.roomName);
+ }
+
+ this.logger.info("Room %s refreshed filters (privacy=%s)", foundRoom,
+ foundRoom.privacy === RoomPrivacy.Public ? "public" : "private");
+
+ ctx.status = 200;
+ ctx.body = {
+ Success: true,
+ Game: this.getGameListing(ctx.socket.remoteAddress || "", foundRoom),
+ };
+ });
+
router.use((req, res) => {
this.logger.info("Bad request to %s", req.url);
});
diff --git a/src/Room.ts b/src/Room.ts
index d313f06c0..63de9d080 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -49,6 +49,7 @@ import {
EndGameIntent,
GameDataMessageTag,
GameMap,
+ GameMode,
GameOverReason,
GameState,
Hat,
@@ -83,11 +84,19 @@ import {
ClientBroadcastEvent,
ClientLeaveEvent,
EventTarget,
+ PlayerLevelChangedEvent,
+ PlayerMurderFailEvent,
+ PlayerTaskCompletedEvent,
+ PlayerTaskProgressEvent,
+ PlayerTaskStartedEvent,
RoomBeforeDestroyEvent,
RoomCreateEvent,
RoomDestroyEvent,
RoomGameEndEvent,
RoomGameStartEvent,
+ RoomHideAndSeekEndEvent,
+ RoomHideAndSeekStartEvent,
+ RoomHiderCaughtEvent,
RoomSelectHostEvent,
getPluginEventListeners
} from "./api";
@@ -104,6 +113,9 @@ import {
import { UnknownComponent } from "./components";
import { Logger } from "./Logger";
+import { GameOptionsValidator } from "./game/GameOptions";
+import { HideAndSeekManager } from "./game/modes/HideAndSeekManager";
+import { RoleManager } from "./game/roles/RoleManager";
import { fmtConfigurableLog } from "./util/fmtLogFormat";
import { Connection, logLanguages, logPlatforms } from "./Connection";
@@ -260,11 +272,19 @@ export enum RoomPrivacy {
export type RoomEvents = EventMapFromList<[
ClientBroadcastEvent,
ClientLeaveEvent,
+ PlayerLevelChangedEvent,
+ PlayerMurderFailEvent,
+ PlayerTaskCompletedEvent,
+ PlayerTaskProgressEvent,
+ PlayerTaskStartedEvent,
RoomBeforeDestroyEvent,
RoomCreateEvent,
RoomDestroyEvent,
RoomGameEndEvent,
RoomGameStartEvent,
+ RoomHideAndSeekEndEvent,
+ RoomHideAndSeekStartEvent,
+ RoomHiderCaughtEvent,
RoomSelectHostEvent
]>;
@@ -314,6 +334,16 @@ export class Room extends StatefulRoom {
privacy: RoomPrivacy;
+ /**
+ * The active game mode manager, if any (e.g., HideAndSeekManager).
+ */
+ gameModeManager: HideAndSeekManager | null;
+
+ /**
+ * The role manager for this room, handling role assignment and lifecycle.
+ */
+ roleManager: RoleManager;
+
protected roomNameOverride: string;
protected eventTargets: EventTarget[];
@@ -355,6 +385,8 @@ export class Room extends StatefulRoom {
this.roomNameOverride = "";
this.eventTargets = [];
+ this.gameModeManager = null;
+ this.roleManager = new RoleManager(this);
this.lastNetId = 100000;
@@ -365,6 +397,18 @@ export class Room extends StatefulRoom {
this.logger = new Logger(() => util.inspect(this.code, true, null, true), this.server.vorpal);
+ // Apply enforced settings from config (overrides host settings)
+ if (this.config.enforceSettings && Object.keys(this.config.enforceSettings).length > 0) {
+ this.settings.patch(this.config.enforceSettings);
+ this.logger.debug("Applied enforced settings to room");
+ }
+
+ // Apply default game mode if not set
+ const currentMode = this.settings.gameMode;
+ if (currentMode === undefined || currentMode === GameMode.None) {
+ (this.settings as any).gameMode = this.config.defaultGameMode || GameMode.Normal;
+ }
+
this.on("player.setname", async ev => {
if (ev.oldName) {
this.logger.info("%s changed their name from %s to %s",
@@ -409,10 +453,36 @@ export class Room extends StatefulRoom {
this.on("player.setlevel", async ev => {
const connection = this.getConnection(ev.player);
if (!connection) return;
+
+ const oldLevel = connection.playerLevel;
+ connection.playerLevel = (ev as any).newLevel ?? ev.player.playerLevel;
+
+ this.emit(new PlayerLevelChangedEvent(
+ this,
+ ev.player,
+ oldLevel,
+ connection.playerLevel
+ ));
+
await this.updateAuthorityForClient(this.getClientAwareAuthorityId(connection), connection);
});
this.on("player.syncsettings", async ev => {
+ if (!this.canMakeHostChanges(ev.player as Player)) {
+ this.logger.warn("%s attempted to change settings but is not the host", ev.player);
+ return;
+ }
+
+ const newSettings = (ev as any).newSettings || ev.settings;
+ if (newSettings) {
+ const validationResult = GameOptionsValidator.validateGameSettings(newSettings);
+ if (!validationResult.valid) {
+ this.logger.warn("%s sent invalid game settings: %s",
+ ev.player, validationResult.errors.join("; "));
+ return;
+ }
+ }
+
if (this.config.enforceSettings) {
ev.setSettings(this.config.enforceSettings);
}
@@ -425,9 +495,61 @@ export class Room extends StatefulRoom {
this.logger.info("Meeting started (%s's body was reported)", ev.body);
}
});
+
+ this.on("player.completetask", async ev => {
+ const taskType = ev.task?.taskType ?? 0;
+ const taskId = ev.message?.taskIdx ?? 0;
+ this.logger.info("%s completed task (idx: %s)",
+ ev.player, taskId);
+
+ // Route through role manager for role-specific behavior
+ this.roleManager.handleTaskComplete(
+ ev.player,
+ taskType,
+ taskId
+ );
+
+ this.emit(new PlayerTaskCompletedEvent(
+ this,
+ ev.player,
+ taskType,
+ taskId,
+ false
+ ));
+ });
+
+ this.on("player.checkmurder", async ev => {
+ if (!ev.isValid) {
+ this.emit(new PlayerMurderFailEvent(
+ this,
+ ev.player,
+ ev.victim,
+ "cooldown",
+ "Murder check determined murder is not valid"
+ ));
+ }
+ });
+
+ this.on("player.murder", async ev => {
+ this.logger.info("%s murdered %s",
+ ev.player, ev.victim);
+
+ // Route through role manager for role-specific kill behavior (e.g., Viper)
+ this.roleManager.handleKill(ev.player, ev.victim);
+ });
+
+ this.on("player.die", async ev => {
+ // Route through role manager for role-specific death behavior (e.g., Phantom)
+ this.roleManager.handleDeath(ev.player);
+ });
}
protected _reset() {
+ if (this.gameModeManager) {
+ this.gameModeManager.destroy();
+ this.gameModeManager = null;
+ }
+ this.roleManager.handleGameEnd();
this.players.clear();
this.networkedObjects.clear();
this.messageStream = [];
@@ -704,6 +826,15 @@ export class Room extends StatefulRoom {
}
async handleRpcMessage(message: RpcMessage, senderPlayer: Player) {
+ // Route through game mode manager first (may block certain RPCs)
+ if (this.gameModeManager) {
+ const handled = await this.gameModeManager.handleRpc(senderPlayer, message);
+ if (!handled) {
+ // Manager blocked this RPC (e.g., body report in H&S)
+ return true;
+ }
+ }
+
const component = this.networkedObjects.get(message.netId);
if (component) {
@@ -1411,6 +1542,12 @@ export class Room extends StatefulRoom {
this.connections.delete(leavingConnection.clientId);
leavingConnection.room = undefined;
+ // Notify game mode manager of disconnect
+ const leavingPlayer = this.players.get(leavingConnection.clientId) as Player | undefined;
+ if (leavingPlayer && this.gameModeManager) {
+ await this.gameModeManager.handlePlayerDisconnect(leavingPlayer);
+ }
+
await this.handleLeave(leavingConnection.clientId);
if (this.connections.size === 0) {
@@ -1490,10 +1627,25 @@ export class Room extends StatefulRoom {
await this.broadcastImmediate([], [new StartGameMessage(this.code.id)]);
+ // Initialize game mode-specific manager
+ if ((this.settings as any).gameMode === GameMode.HideNSeek ||
+ (this.settings as any).gameMode === GameMode.HideNSeekFools) {
+ this.gameModeManager = new HideAndSeekManager(this);
+ this.logger.info("Initialized Hide and Seek game mode manager");
+ }
+
if (this.isAuthoritative) {
await this.updateAllClientAwareAuthority();
await super.handleStartGame();
this.logger.info("Game started");
+
+ // Assign roles to players
+ this.roleManager.assignRoles();
+
+ // Start mode-specific logic after game is fully initialized
+ if (this.gameModeManager instanceof HideAndSeekManager) {
+ await this.gameModeManager.startGame();
+ }
}
}
diff --git a/src/WaterwayServer.ts b/src/WaterwayServer.ts
index 0612247a3..63ea487c2 100644
--- a/src/WaterwayServer.ts
+++ b/src/WaterwayServer.ts
@@ -87,6 +87,7 @@ import { LoadedPlugin, PluginLoader, WorkerPlugin } from "./handlers";
import i18n from "./i18n";
import { Matchmaker } from "./Matchmaker";
import { Logger } from "./Logger";
+import { GameOptionsValidator } from "./game/GameOptions";
const byteSizes = ["bytes", "kb", "mb", "gb", "tb"];
function formatBytes(bytes: number) {
@@ -249,6 +250,21 @@ export type LoggingConfig = {
export type ValidSearchTerm = "map" | "chat" | "chatType";
+export type FilterTagConfig = {
+ /**
+ * The internal name/key for this filter tag.
+ */
+ name: string;
+ /**
+ * The display name shown to users.
+ */
+ displayName: string;
+ /**
+ * Optional icon identifier for the tag.
+ */
+ icon?: string;
+};
+
export type GameListingConfig = {
/**
* Whether to ignore the privacy of a room, and return even private ones.
@@ -278,6 +294,12 @@ export type GameListingConfig = {
* @default false
*/
requireExactMatches: boolean;
+ /**
+ * Static filter tags to serve via the /api/filtertags endpoint.
+ * These appear as filter buttons in the client UI.
+ * @default []
+ */
+ filterTags: FilterTagConfig[];
}
export type ChatCommandConfig = {
@@ -373,8 +395,20 @@ export type RoomsConfig = StatefulRoomConfig & {
gameCodes: "v1" | "v2";
/**
* Enforce certain settings, preventing the host from changing them.
+ * These settings are applied on room creation and cannot be overridden.
*/
enforceSettings: Partial;
+ /**
+ * Allowed game modes for rooms on this server. An empty array means all
+ * game modes are allowed.
+ * @default []
+ */
+ allowedGameModes: number[];
+ /**
+ * The default game mode for new rooms.
+ * @default 1 (Normal)
+ */
+ defaultGameMode: number;
/**
* Options regarding room plugins.
*/
@@ -1507,6 +1541,14 @@ export class WaterwayServer extends EventEmitter {
if (ev.canceled)
return;
+ const validationResult = GameOptionsValidator.validateGameSettingsObject(message.gameSettings);
+ if (!validationResult.valid) {
+ this.logger.warn("%s created room with invalid game options: %s",
+ sender, validationResult.errors.join("; "));
+ await sender.disconnect("Invalid game options: " + validationResult.errors.join(", "));
+ return;
+ }
+
const room = await this.createRoom(ev.alteredRoomCode, message.gameSettings, sender);
this.logger.info("%s created room %s",
diff --git a/src/api/events/index.ts b/src/api/events/index.ts
index 36ab9a3c9..72e1b0942 100644
--- a/src/api/events/index.ts
+++ b/src/api/events/index.ts
@@ -1,5 +1,6 @@
export * from "./adaptor";
export * from "./client";
+export * from "./player";
export * from "./room";
export * from "./worker";
diff --git a/src/api/events/player/LevelChanged.ts b/src/api/events/player/LevelChanged.ts
new file mode 100644
index 000000000..6a1a830d7
--- /dev/null
+++ b/src/api/events/player/LevelChanged.ts
@@ -0,0 +1,30 @@
+import { BasicEvent } from "@skeldjs/events";
+import { Player } from "@skeldjs/au-core";
+
+import { Room } from "../../../Room";
+
+/**
+ * Emitted when a player's level changes (via SetLevel RPC).
+ */
+export class PlayerLevelChangedEvent extends BasicEvent {
+ static eventName = "player.levelchanged" as const;
+ eventName = "player.levelchanged" as const;
+
+ constructor(
+ public readonly room: Room,
+ /**
+ * The player whose level changed.
+ */
+ public readonly player: Player,
+ /**
+ * The player's previous level.
+ */
+ public readonly oldLevel: number,
+ /**
+ * The player's new level.
+ */
+ public readonly newLevel: number,
+ ) {
+ super();
+ }
+}
diff --git a/src/api/events/player/MurderFail.ts b/src/api/events/player/MurderFail.ts
new file mode 100644
index 000000000..3dbf393bb
--- /dev/null
+++ b/src/api/events/player/MurderFail.ts
@@ -0,0 +1,42 @@
+import { BasicEvent } from "@skeldjs/events";
+import { Player } from "@skeldjs/au-core";
+
+import { Room } from "../../../Room";
+
+/**
+ * Emitted when an impostor's murder attempt fails.
+ *
+ * Reasons for failure include:
+ * - "protected": The target was protected by a Guardian Angel.
+ * - "already_dead": The target was already killed.
+ * - "invulnerable": The target is in an invulnerable state.
+ * - "in_vent": The target is in a vent.
+ * - "cooldown": The killer's kill cooldown is not ready.
+ * - "not_impostor": The killer is not an impostor.
+ */
+export class PlayerMurderFailEvent extends BasicEvent {
+ static eventName = "player.murderfail" as const;
+ eventName = "player.murderfail" as const;
+
+ constructor(
+ public readonly room: Room,
+ /**
+ * The player who attempted the murder.
+ */
+ public readonly player: Player,
+ /**
+ * The intended target of the murder.
+ */
+ public readonly target: Player,
+ /**
+ * The reason the murder failed.
+ */
+ public readonly reason: "protected" | "already_dead" | "invulnerable" | "in_vent" | "cooldown" | "not_impostor" | string,
+ /**
+ * Optional additional details about the failure.
+ */
+ public readonly details?: string,
+ ) {
+ super();
+ }
+}
diff --git a/src/api/events/player/TaskCompleted.ts b/src/api/events/player/TaskCompleted.ts
new file mode 100644
index 000000000..e5687cb61
--- /dev/null
+++ b/src/api/events/player/TaskCompleted.ts
@@ -0,0 +1,34 @@
+import { BasicEvent } from "@skeldjs/events";
+import { Player } from "@skeldjs/au-core";
+
+import { Room } from "../../../Room";
+
+/**
+ * Emitted when a player completes a task.
+ */
+export class PlayerTaskCompletedEvent extends BasicEvent {
+ static eventName = "player.taskcompleted" as const;
+ eventName = "player.taskcompleted" as const;
+
+ constructor(
+ public readonly room: Room,
+ /**
+ * The player who completed the task.
+ */
+ public readonly player: Player,
+ /**
+ * The type of task (from TaskType enum).
+ */
+ public readonly taskType: number,
+ /**
+ * The unique task ID.
+ */
+ public readonly taskId: number,
+ /**
+ * Whether this was a long/multi-step task.
+ */
+ public readonly isLongTask: boolean,
+ ) {
+ super();
+ }
+}
diff --git a/src/api/events/player/TaskProgress.ts b/src/api/events/player/TaskProgress.ts
new file mode 100644
index 000000000..35c302261
--- /dev/null
+++ b/src/api/events/player/TaskProgress.ts
@@ -0,0 +1,30 @@
+import { BasicEvent } from "@skeldjs/events";
+import { Player } from "@skeldjs/au-core";
+
+import { Room } from "../../../Room";
+
+/**
+ * Emitted when a player's task progress updates.
+ */
+export class PlayerTaskProgressEvent extends BasicEvent {
+ static eventName = "player.taskprogress" as const;
+ eventName = "player.taskprogress" as const;
+
+ constructor(
+ public readonly room: Room,
+ /**
+ * The player whose task progressed.
+ */
+ public readonly player: Player,
+ /**
+ * The unique task ID.
+ */
+ public readonly taskId: number,
+ /**
+ * The current progress value (0-100 or game-specific).
+ */
+ public readonly progress: number,
+ ) {
+ super();
+ }
+}
diff --git a/src/api/events/player/TaskStarted.ts b/src/api/events/player/TaskStarted.ts
new file mode 100644
index 000000000..cd8162b02
--- /dev/null
+++ b/src/api/events/player/TaskStarted.ts
@@ -0,0 +1,30 @@
+import { BasicEvent } from "@skeldjs/events";
+import { Player } from "@skeldjs/au-core";
+
+import { Room } from "../../../Room";
+
+/**
+ * Emitted when a player starts a task.
+ */
+export class PlayerTaskStartedEvent extends BasicEvent {
+ static eventName = "player.taskstarted" as const;
+ eventName = "player.taskstarted" as const;
+
+ constructor(
+ public readonly room: Room,
+ /**
+ * The player who started the task.
+ */
+ public readonly player: Player,
+ /**
+ * The type of task (from TaskType enum).
+ */
+ public readonly taskType: number,
+ /**
+ * The unique task ID.
+ */
+ public readonly taskId: number,
+ ) {
+ super();
+ }
+}
diff --git a/src/api/events/player/index.ts b/src/api/events/player/index.ts
new file mode 100644
index 000000000..1f76b1778
--- /dev/null
+++ b/src/api/events/player/index.ts
@@ -0,0 +1,5 @@
+export * from "./LevelChanged";
+export * from "./MurderFail";
+export * from "./TaskCompleted";
+export * from "./TaskProgress";
+export * from "./TaskStarted";
diff --git a/src/api/events/room/HideAndSeekEnd.ts b/src/api/events/room/HideAndSeekEnd.ts
new file mode 100644
index 000000000..66187dbab
--- /dev/null
+++ b/src/api/events/room/HideAndSeekEnd.ts
@@ -0,0 +1,28 @@
+import { BasicEvent } from "@skeldjs/events";
+
+import { Room } from "../../../Room";
+
+/**
+ * Emitted when a Hide and Seek game ends.
+ */
+export class RoomHideAndSeekEndEvent extends BasicEvent {
+ static eventName = "room.hideandseekend" as const;
+ eventName = "room.hideandseekend" as const;
+
+ constructor(
+ public readonly room: Room,
+ /**
+ * Who won the game.
+ * - "seekers": The seeker killed all hiders.
+ * - "hiders": The hiders completed all tasks or survived the timer.
+ * - "none": The game ended without a winner (e.g., destroyed).
+ */
+ public readonly winner: "seekers" | "hiders" | "none",
+ /**
+ * The reason the game ended, for display/logging.
+ */
+ public readonly reason: string,
+ ) {
+ super();
+ }
+}
diff --git a/src/api/events/room/HideAndSeekStart.ts b/src/api/events/room/HideAndSeekStart.ts
new file mode 100644
index 000000000..1fb66199f
--- /dev/null
+++ b/src/api/events/room/HideAndSeekStart.ts
@@ -0,0 +1,31 @@
+import { BasicEvent } from "@skeldjs/events";
+import { Player } from "@skeldjs/au-core";
+
+import { Room } from "../../../Room";
+
+/**
+ * Emitted when a Hide and Seek game starts.
+ * Contains information about the seeker, hiders, and hide duration.
+ */
+export class RoomHideAndSeekStartEvent extends BasicEvent {
+ static eventName = "room.hideandseekstart" as const;
+ eventName = "room.hideandseekstart" as const;
+
+ constructor(
+ public readonly room: Room,
+ /**
+ * The player selected as the seeker (impostor role).
+ */
+ public readonly seekerPlayer: Player,
+ /**
+ * All players who are hiders (crewmate roles).
+ */
+ public readonly hiderPlayers: Player[],
+ /**
+ * The duration in seconds that hiders have to hide.
+ */
+ public readonly hideDuration: number,
+ ) {
+ super();
+ }
+}
diff --git a/src/api/events/room/HiderCaught.ts b/src/api/events/room/HiderCaught.ts
new file mode 100644
index 000000000..c733366d9
--- /dev/null
+++ b/src/api/events/room/HiderCaught.ts
@@ -0,0 +1,30 @@
+import { BasicEvent } from "@skeldjs/events";
+import { Player } from "@skeldjs/au-core";
+
+import { Room } from "../../../Room";
+
+/**
+ * Emitted when a hider is caught (killed) by the seeker in Hide and Seek mode.
+ */
+export class RoomHiderCaughtEvent extends BasicEvent {
+ static eventName = "room.hidercaught" as const;
+ eventName = "room.hidercaught" as const;
+
+ constructor(
+ public readonly room: Room,
+ /**
+ * The hider player that was caught.
+ */
+ public readonly hiderPlayer: Player,
+ /**
+ * The seeker player that caught the hider.
+ */
+ public readonly seekerPlayer: Player,
+ /**
+ * How many hiders remain alive.
+ */
+ public readonly remainingHiders: number,
+ ) {
+ super();
+ }
+}
diff --git a/src/api/events/room/index.ts b/src/api/events/room/index.ts
index 7d3ff078f..858a7a17d 100644
--- a/src/api/events/room/index.ts
+++ b/src/api/events/room/index.ts
@@ -4,4 +4,7 @@ export * from "./Create";
export * from "./Destroy";
export * from "./GameEnd";
export * from "./GameStart";
+export * from "./HideAndSeekEnd";
+export * from "./HideAndSeekStart";
+export * from "./HiderCaught";
export * from "./SelectHost";
diff --git a/src/game/GameOptions.ts b/src/game/GameOptions.ts
new file mode 100644
index 000000000..f9d082dbb
--- /dev/null
+++ b/src/game/GameOptions.ts
@@ -0,0 +1,595 @@
+import {
+ AllGameSettings,
+ AllRoleSettings,
+ GameSettings,
+ RoleSettings
+} from "@skeldjs/au-protocol";
+
+import {
+ GameMap,
+ GameKeyword,
+ GameMode,
+ KillDistance,
+ RoleType,
+ RulesPreset,
+ SpecialGameModes,
+ TaskBarMode,
+} from "@skeldjs/au-core";
+
+/**
+ * Result of validating game options. An empty errors array means valid.
+ */
+export interface ValidationResult {
+ valid: boolean;
+ errors: string[];
+}
+
+/**
+ * Valid ranges for numeric game options.
+ */
+export const GAME_OPTION_RANGES = {
+ playerSpeed: { min: 0.5, max: 3.0 },
+ crewmateVision: { min: 0.0, max: 5.0 },
+ impostorVision: { min: 0.0, max: 5.0 },
+ killCooldown: { min: 2.5, max: 60.0 },
+ commonTasks: { min: 0, max: 4 },
+ longTasks: { min: 0, max: 15 },
+ shortTasks: { min: 0, max: 23 },
+ numEmergencies: { min: 0, max: 10 },
+ numImpostors: { min: 1, max: 3 },
+ maxPlayers: { min: 4, max: 15 },
+ discussionTime: { min: 0, max: 300 },
+ votingTime: { min: 0, max: 300 },
+ emergencyCooldown: { min: 0, max: 60 },
+ crewmateVentUses: { min: 0, max: 30 },
+ hidingTime: { min: 10, max: 600 },
+ crewmateFlashlightSize: { min: 0.0, max: 5.0 },
+ impostorFlashlightSize: { min: 0.0, max: 5.0 },
+ finalHideTime: { min: 0, max: 300 },
+ finalSeekerSpeed: { min: 0.5, max: 3.0 },
+ maxPingTime: { min: 0, max: 30 },
+ crewmateTimeInVent: { min: 0, max: 60 },
+} as const;
+
+/**
+ * Valid ranges for role-specific settings.
+ */
+export const ROLE_OPTION_RANGES: Record = {
+ scientistCooldown: { min: 5, max: 60 },
+ scientistBatteryCharge: { min: 5, max: 30 },
+ engineerCooldown: { min: 5, max: 60 },
+ engineerInVentMaxTime: { min: 0, max: 60 },
+ guardianAngelCooldown: { min: 35, max: 120 },
+ guardianAngelPotectionDuration: { min: 5, max: 30 },
+ shapeshifterCooldown: { min: 5, max: 60 },
+ shapeshiftDuration: { min: 5, max: 30 },
+ noisemakerAlertDuration: { min: 1, max: 30 },
+ phantomCooldown: { min: 5, max: 60 },
+ phantomDuration: { min: 5, max: 60 },
+ trackerCooldown: { min: 5, max: 60 },
+ trackerDuration: { min: 5, max: 60 },
+ trackerDelay: { min: 1, max: 10 },
+ detectiveSuspectLimit: { min: 1, max: 10 },
+ viperDissolveTime: { min: 5, max: 60 },
+};
+
+/**
+ * Maximum number of players that can have a given role.
+ */
+const MAX_ROLE_PLAYERS = 15;
+
+/**
+ * All valid game map values.
+ */
+const VALID_GAME_MAPS: GameMap[] = [
+ GameMap.TheSkeld,
+ GameMap.MiraHQ,
+ GameMap.Polus,
+ GameMap.AprilFoolsTheSkeld,
+ GameMap.Airship,
+ GameMap.Fungle,
+];
+
+/**
+ * All valid kill distance values.
+ */
+const VALID_KILL_DISTANCES: KillDistance[] = [
+ KillDistance.Short,
+ KillDistance.Medium,
+ KillDistance.Long,
+];
+
+/**
+ * All valid task bar update modes.
+ */
+const VALID_TASKBAR_MODES: TaskBarMode[] = [
+ TaskBarMode.Normal,
+ TaskBarMode.MeetingOnly,
+ TaskBarMode.Invisible,
+];
+
+/**
+ * All valid game modes.
+ */
+const VALID_GAME_MODES: GameMode[] = [
+ GameMode.None,
+ GameMode.Normal,
+ GameMode.HideNSeek,
+ GameMode.NormalFools,
+ GameMode.HideNSeekFools,
+];
+
+/**
+ * All valid special game modes.
+ */
+const VALID_SPECIAL_MODES: SpecialGameModes[] = [
+ SpecialGameModes.None,
+ SpecialGameModes.AprilFools,
+];
+
+/**
+ * All valid rules presets.
+ */
+const VALID_RULES_PRESETS: RulesPreset[] = [
+ RulesPreset.Custom,
+ RulesPreset.Standard,
+];
+
+/**
+ * Validator class for game options.
+ *
+ * Provides static methods to validate individual settings and full game settings objects.
+ */
+export class GameOptionsValidator {
+ /**
+ * Check if a numeric value is within a valid range.
+ */
+ static inRange(value: number, min: number, max: number): boolean {
+ return value >= min && value <= max;
+ }
+
+ /**
+ * Check if a value is a valid member of an enum-like set.
+ */
+ static inEnum(value: T, validValues: readonly T[]): boolean {
+ return validValues.includes(value);
+ }
+
+ /**
+ * Validate role chance settings (maxPlayers and chance percentages).
+ */
+ static validateRoleChances(roleSettings: AllRoleSettings): string[] {
+ const errors: string[] = [];
+
+ if (!roleSettings.roleChances) return errors;
+
+ const roleChances = Object.entries(roleSettings.roleChances);
+ for (const [roleTypeStr, roleChance] of roleChances) {
+ const roleType = parseInt(roleTypeStr);
+
+ if (isNaN(roleType) || !(roleType in RoleType)) {
+ errors.push(`Invalid role type: ${roleTypeStr}`);
+ continue;
+ }
+
+ if (roleChance.maxPlayers < 0 || roleChance.maxPlayers > MAX_ROLE_PLAYERS) {
+ errors.push(
+ `${RoleType[roleType]}: maxPlayers (${roleChance.maxPlayers}) must be between 0 and ${MAX_ROLE_PLAYERS}`
+ );
+ }
+
+ if (roleChance.chance < 0 || roleChance.chance > 100) {
+ errors.push(
+ `${RoleType[roleType]}: chance (${roleChance.chance}) must be between 0 and 100`
+ );
+ }
+ }
+
+ return errors;
+ }
+
+ /**
+ * Validate numeric role settings against defined ranges.
+ */
+ static validateRoleSettingRanges(roleSettings: AllRoleSettings): string[] {
+ const errors: string[] = [];
+
+ for (const [key, range] of Object.entries(ROLE_OPTION_RANGES)) {
+ const value = (roleSettings as any)[key];
+ if (value !== undefined && !this.inRange(value, range.min, range.max)) {
+ errors.push(
+ `roleSettings.${key} (${value}) must be between ${range.min} and ${range.max}`
+ );
+ }
+ }
+
+ return errors;
+ }
+
+ /**
+ * Validate all role settings.
+ */
+ static validateRoleSettings(roleSettings: AllRoleSettings): string[] {
+ const errors: string[] = [];
+
+ if (!roleSettings) return errors;
+
+ errors.push(...this.validateRoleChances(roleSettings));
+ errors.push(...this.validateRoleSettingRanges(roleSettings));
+
+ return errors;
+ }
+
+ /**
+ * Validate settings specific to Normal game mode.
+ */
+ static validateNormalModeSettings(settings: Partial): string[] {
+ const errors: string[] = [];
+
+ if (settings.killCooldown !== undefined &&
+ !this.inRange(settings.killCooldown, GAME_OPTION_RANGES.killCooldown.min, GAME_OPTION_RANGES.killCooldown.max)) {
+ errors.push(
+ `killCooldown (${settings.killCooldown}) must be between ${GAME_OPTION_RANGES.killCooldown.min} and ${GAME_OPTION_RANGES.killCooldown.max}`
+ );
+ }
+
+ if (settings.numImpostors !== undefined &&
+ !this.inRange(settings.numImpostors, GAME_OPTION_RANGES.numImpostors.min, GAME_OPTION_RANGES.numImpostors.max)) {
+ errors.push(
+ `numImpostors (${settings.numImpostors}) must be between ${GAME_OPTION_RANGES.numImpostors.min} and ${GAME_OPTION_RANGES.numImpostors.max}`
+ );
+ }
+
+ if (settings.killDistance !== undefined &&
+ !this.inEnum(settings.killDistance, VALID_KILL_DISTANCES)) {
+ errors.push(`killDistance (${settings.killDistance}) is not a valid value`);
+ }
+
+ if (settings.discussionTime !== undefined &&
+ !this.inRange(settings.discussionTime, GAME_OPTION_RANGES.discussionTime.min, GAME_OPTION_RANGES.discussionTime.max)) {
+ errors.push(
+ `discussionTime (${settings.discussionTime}) must be between 0 and 300`
+ );
+ }
+
+ if (settings.votingTime !== undefined &&
+ !this.inRange(settings.votingTime, GAME_OPTION_RANGES.votingTime.min, GAME_OPTION_RANGES.votingTime.max)) {
+ errors.push(
+ `votingTime (${settings.votingTime}) must be between 0 and 300`
+ );
+ }
+
+ if (settings.emergencyCooldown !== undefined &&
+ !this.inRange(settings.emergencyCooldown, GAME_OPTION_RANGES.emergencyCooldown.min, GAME_OPTION_RANGES.emergencyCooldown.max)) {
+ errors.push(
+ `emergencyCooldown (${settings.emergencyCooldown}) must be between 0 and 60`
+ );
+ }
+
+ return errors;
+ }
+
+ /**
+ * Validate settings specific to Hide and Seek game mode.
+ */
+ static validateHideAndSeekSettings(settings: Partial): string[] {
+ const errors: string[] = [];
+
+ if (settings.crewmateVentUses !== undefined &&
+ !this.inRange(settings.crewmateVentUses, GAME_OPTION_RANGES.crewmateVentUses.min, GAME_OPTION_RANGES.crewmateVentUses.max)) {
+ errors.push(
+ `crewmateVentUses (${settings.crewmateVentUses}) must be between ${GAME_OPTION_RANGES.crewmateVentUses.min} and ${GAME_OPTION_RANGES.crewmateVentUses.max}`
+ );
+ }
+
+ if (settings.hidingTime !== undefined &&
+ !this.inRange(settings.hidingTime, GAME_OPTION_RANGES.hidingTime.min, GAME_OPTION_RANGES.hidingTime.max)) {
+ errors.push(
+ `hidingTime (${settings.hidingTime}) must be between ${GAME_OPTION_RANGES.hidingTime.min} and ${GAME_OPTION_RANGES.hidingTime.max}`
+ );
+ }
+
+ if (settings.crewmateFlashlightSize !== undefined &&
+ !this.inRange(settings.crewmateFlashlightSize, GAME_OPTION_RANGES.crewmateFlashlightSize.min, GAME_OPTION_RANGES.crewmateFlashlightSize.max)) {
+ errors.push(
+ `crewmateFlashlightSize (${settings.crewmateFlashlightSize}) must be between 0.0 and 5.0`
+ );
+ }
+
+ if (settings.impostorFlashlightSize !== undefined &&
+ !this.inRange(settings.impostorFlashlightSize, GAME_OPTION_RANGES.impostorFlashlightSize.min, GAME_OPTION_RANGES.impostorFlashlightSize.max)) {
+ errors.push(
+ `impostorFlashlightSize (${settings.impostorFlashlightSize}) must be between 0.0 and 5.0`
+ );
+ }
+
+ if (settings.finalHideTime !== undefined &&
+ !this.inRange(settings.finalHideTime, GAME_OPTION_RANGES.finalHideTime.min, GAME_OPTION_RANGES.finalHideTime.max)) {
+ errors.push(
+ `finalHideTime (${settings.finalHideTime}) must be between 0 and 300`
+ );
+ }
+
+ if (settings.finalSeekerSpeed !== undefined &&
+ !this.inRange(settings.finalSeekerSpeed, GAME_OPTION_RANGES.finalSeekerSpeed.min, GAME_OPTION_RANGES.finalSeekerSpeed.max)) {
+ errors.push(
+ `finalSeekerSpeed (${settings.finalSeekerSpeed}) must be between 0.5 and 3.0`
+ );
+ }
+
+ if (settings.maxPingTime !== undefined &&
+ !this.inRange(settings.maxPingTime, GAME_OPTION_RANGES.maxPingTime.min, GAME_OPTION_RANGES.maxPingTime.max)) {
+ errors.push(
+ `maxPingTime (${settings.maxPingTime}) must be between 0 and 30`
+ );
+ }
+
+ if (settings.crewmateTimeInVent !== undefined &&
+ !this.inRange(settings.crewmateTimeInVent, GAME_OPTION_RANGES.crewmateTimeInVent.min, GAME_OPTION_RANGES.crewmateTimeInVent.max)) {
+ errors.push(
+ `crewmateTimeInVent (${settings.crewmateTimeInVent}) must be between 0 and 60`
+ );
+ }
+
+ return errors;
+ }
+
+ /**
+ * Validate common settings that apply to all game modes.
+ */
+ static validateCommonSettings(settings: Partial): string[] {
+ const errors: string[] = [];
+
+ // Validate maxPlayers
+ if (settings.maxPlayers !== undefined &&
+ !this.inRange(settings.maxPlayers, GAME_OPTION_RANGES.maxPlayers.min, GAME_OPTION_RANGES.maxPlayers.max)) {
+ errors.push(
+ `maxPlayers (${settings.maxPlayers}) must be between ${GAME_OPTION_RANGES.maxPlayers.min} and ${GAME_OPTION_RANGES.maxPlayers.max}`
+ );
+ }
+
+ // Validate map
+ if (settings.map !== undefined && !this.inEnum(settings.map, VALID_GAME_MAPS)) {
+ errors.push(`map (${settings.map}) is not a valid game map`);
+ }
+
+ // Validate player speed
+ if (settings.playerSpeed !== undefined &&
+ !this.inRange(settings.playerSpeed, GAME_OPTION_RANGES.playerSpeed.min, GAME_OPTION_RANGES.playerSpeed.max)) {
+ errors.push(
+ `playerSpeed (${settings.playerSpeed}) must be between ${GAME_OPTION_RANGES.playerSpeed.min} and ${GAME_OPTION_RANGES.playerSpeed.max}`
+ );
+ }
+
+ // Validate vision ranges
+ if (settings.crewmateVision !== undefined &&
+ !this.inRange(settings.crewmateVision, GAME_OPTION_RANGES.crewmateVision.min, GAME_OPTION_RANGES.crewmateVision.max)) {
+ errors.push(
+ `crewmateVision (${settings.crewmateVision}) must be between 0.0 and 5.0`
+ );
+ }
+
+ if (settings.impostorVision !== undefined &&
+ !this.inRange(settings.impostorVision, GAME_OPTION_RANGES.impostorVision.min, GAME_OPTION_RANGES.impostorVision.max)) {
+ errors.push(
+ `impostorVision (${settings.impostorVision}) must be between 0.0 and 5.0`
+ );
+ }
+
+ // Validate task counts
+ if (settings.commonTasks !== undefined &&
+ !this.inRange(settings.commonTasks, GAME_OPTION_RANGES.commonTasks.min, GAME_OPTION_RANGES.commonTasks.max)) {
+ errors.push(
+ `commonTasks (${settings.commonTasks}) must be between 0 and 4`
+ );
+ }
+
+ if (settings.longTasks !== undefined &&
+ !this.inRange(settings.longTasks, GAME_OPTION_RANGES.longTasks.min, GAME_OPTION_RANGES.longTasks.max)) {
+ errors.push(
+ `longTasks (${settings.longTasks}) must be between 0 and 15`
+ );
+ }
+
+ if (settings.shortTasks !== undefined &&
+ !this.inRange(settings.shortTasks, GAME_OPTION_RANGES.shortTasks.min, GAME_OPTION_RANGES.shortTasks.max)) {
+ errors.push(
+ `shortTasks (${settings.shortTasks}) must be between 0 and 23`
+ );
+ }
+
+ // Validate numEmergencies
+ if (settings.numEmergencies !== undefined &&
+ !this.inRange(settings.numEmergencies, GAME_OPTION_RANGES.numEmergencies.min, GAME_OPTION_RANGES.numEmergencies.max)) {
+ errors.push(
+ `numEmergencies (${settings.numEmergencies}) must be between 0 and 10`
+ );
+ }
+
+ // Validate dependency: numEmergencies should not exceed maxPlayers
+ const maxPlayers = settings.maxPlayers ?? 10;
+ const numEmergencies = settings.numEmergencies;
+ if (numEmergencies !== undefined && maxPlayers !== undefined && numEmergencies > maxPlayers) {
+ errors.push(
+ `numEmergencies (${numEmergencies}) cannot exceed maxPlayers (${maxPlayers})`
+ );
+ }
+
+ // Validate game mode (on the GameSettings object, not in AllGameSettings)
+ if ((settings as any).gameMode !== undefined && !this.inEnum((settings as any).gameMode, VALID_GAME_MODES)) {
+ errors.push(`gameMode (${(settings as any).gameMode}) is not a valid game mode`);
+ }
+
+ // Validate special mode
+ if ((settings as any).specialMode !== undefined && !this.inEnum((settings as any).specialMode, VALID_SPECIAL_MODES)) {
+ errors.push(`specialMode (${(settings as any).specialMode}) is not a valid special mode`);
+ }
+
+ // Validate rules preset
+ if ((settings as any).rulesPreset !== undefined && !this.inEnum((settings as any).rulesPreset, VALID_RULES_PRESETS)) {
+ errors.push(`rulesPreset (${(settings as any).rulesPreset}) is not a valid rules preset`);
+ }
+
+ // Validate task bar updates
+ if (settings.taskbarUpdates !== undefined && !this.inEnum(settings.taskbarUpdates, VALID_TASKBAR_MODES)) {
+ errors.push(`taskbarUpdates (${settings.taskbarUpdates}) is not a valid task bar mode`);
+ }
+
+ return errors;
+ }
+
+ /**
+ * Perform a full validation of game settings.
+ *
+ * @param settings The game settings to validate.
+ * @returns A {@link ValidationResult} with valid flag and array of error messages.
+ */
+ static validateGameSettings(settings: Partial): ValidationResult {
+ // NOTE: When @skeldjs/au-protocol is updated with GameSettings.validate(),
+ // this method should delegate to it. For now, Waterway maintains its own
+ // validation that mirrors the SkeldJS protocol-level checks.
+
+ const errors: string[] = [];
+
+ errors.push(...this.validateCommonSettings(settings));
+
+ const gameMode = (settings as any).gameMode ?? GameMode.Normal;
+ switch (gameMode) {
+ case GameMode.Normal:
+ case GameMode.NormalFools:
+ errors.push(...this.validateNormalModeSettings(settings));
+ break;
+ case GameMode.HideNSeek:
+ case GameMode.HideNSeekFools:
+ errors.push(...this.validateHideAndSeekSettings(settings));
+ break;
+ }
+
+ if (settings.roleSettings) {
+ errors.push(...this.validateRoleSettings(settings.roleSettings as AllRoleSettings));
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors,
+ };
+ }
+
+ /**
+ * Validate a complete GameSettings object.
+ * Convenience method that calls validateGameSettings with the settings data.
+ */
+ static validateGameSettingsObject(settings: GameSettings): ValidationResult {
+ return this.validateGameSettings(settings as any);
+ }
+
+ /**
+ * Check whether role-specific settings have valid values for the given role type.
+ */
+ static validateSingleRole(roleType: RoleType, roleSettings: AllRoleSettings): string[] {
+ const errors: string[] = [];
+
+ switch (roleType) {
+ case RoleType.Scientist:
+ if (!this.inRange(roleSettings.scientistCooldown,
+ ROLE_OPTION_RANGES.scientistCooldown.min,
+ ROLE_OPTION_RANGES.scientistCooldown.max)) {
+ errors.push(`Scientist cooldown must be between 5 and 60`);
+ }
+ if (!this.inRange(roleSettings.scientistBatteryCharge,
+ ROLE_OPTION_RANGES.scientistBatteryCharge.min,
+ ROLE_OPTION_RANGES.scientistBatteryCharge.max)) {
+ errors.push(`Scientist battery charge must be between 5 and 30`);
+ }
+ break;
+ case RoleType.Engineer:
+ if (!this.inRange(roleSettings.engineerCooldown,
+ ROLE_OPTION_RANGES.engineerCooldown.min,
+ ROLE_OPTION_RANGES.engineerCooldown.max)) {
+ errors.push(`Engineer cooldown must be between 5 and 60`);
+ }
+ if (!this.inRange(roleSettings.engineerInVentMaxTime,
+ ROLE_OPTION_RANGES.engineerInVentMaxTime.min,
+ ROLE_OPTION_RANGES.engineerInVentMaxTime.max)) {
+ errors.push(`Engineer vent time must be between 0 and 60`);
+ }
+ break;
+ case RoleType.GuardianAngel:
+ if (!this.inRange(roleSettings.guardianAngelCooldown,
+ ROLE_OPTION_RANGES.guardianAngelCooldown.min,
+ ROLE_OPTION_RANGES.guardianAngelCooldown.max)) {
+ errors.push(`Guardian Angel cooldown must be between 35 and 120`);
+ }
+ if (!this.inRange(roleSettings.guardianAngelPotectionDuration,
+ ROLE_OPTION_RANGES.guardianAngelPotectionDuration.min,
+ ROLE_OPTION_RANGES.guardianAngelPotectionDuration.max)) {
+ errors.push(`Guardian Angel protection duration must be between 5 and 30`);
+ }
+ break;
+ case RoleType.Shapeshifter:
+ if (!this.inRange(roleSettings.shapeshifterCooldown,
+ ROLE_OPTION_RANGES.shapeshifterCooldown.min,
+ ROLE_OPTION_RANGES.shapeshifterCooldown.max)) {
+ errors.push(`Shapeshifter cooldown must be between 5 and 60`);
+ }
+ if (!this.inRange(roleSettings.shapeshiftDuration,
+ ROLE_OPTION_RANGES.shapeshiftDuration.min,
+ ROLE_OPTION_RANGES.shapeshiftDuration.max)) {
+ errors.push(`Shapeshift duration must be between 5 and 30`);
+ }
+ break;
+ case RoleType.Noisemaker:
+ if (!this.inRange(roleSettings.noisemakerAlertDuration,
+ ROLE_OPTION_RANGES.noisemakerAlertDuration.min,
+ ROLE_OPTION_RANGES.noisemakerAlertDuration.max)) {
+ errors.push(`Noisemaker alert duration must be between 1 and 30`);
+ }
+ break;
+ case RoleType.Phantom:
+ if (!this.inRange(roleSettings.phantomCooldown,
+ ROLE_OPTION_RANGES.phantomCooldown.min,
+ ROLE_OPTION_RANGES.phantomCooldown.max)) {
+ errors.push(`Phantom cooldown must be between 5 and 60`);
+ }
+ if (!this.inRange(roleSettings.phantomDuration,
+ ROLE_OPTION_RANGES.phantomDuration.min,
+ ROLE_OPTION_RANGES.phantomDuration.max)) {
+ errors.push(`Phantom duration must be between 5 and 60`);
+ }
+ break;
+ case RoleType.Tracker:
+ if (!this.inRange(roleSettings.trackerCooldown,
+ ROLE_OPTION_RANGES.trackerCooldown.min,
+ ROLE_OPTION_RANGES.trackerCooldown.max)) {
+ errors.push(`Tracker cooldown must be between 5 and 60`);
+ }
+ if (!this.inRange(roleSettings.trackerDuration,
+ ROLE_OPTION_RANGES.trackerDuration.min,
+ ROLE_OPTION_RANGES.trackerDuration.max)) {
+ errors.push(`Tracker duration must be between 5 and 60`);
+ }
+ if (!this.inRange(roleSettings.trackerDelay,
+ ROLE_OPTION_RANGES.trackerDelay.min,
+ ROLE_OPTION_RANGES.trackerDelay.max)) {
+ errors.push(`Tracker delay must be between 1 and 10`);
+ }
+ break;
+ case RoleType.Detective:
+ if (!this.inRange(roleSettings.detectiveSuspectLimit,
+ ROLE_OPTION_RANGES.detectiveSuspectLimit.min,
+ ROLE_OPTION_RANGES.detectiveSuspectLimit.max)) {
+ errors.push(`Detective suspect limit must be between 1 and 10`);
+ }
+ break;
+ case RoleType.Viper:
+ if (!this.inRange(roleSettings.viperDissolveTime,
+ ROLE_OPTION_RANGES.viperDissolveTime.min,
+ ROLE_OPTION_RANGES.viperDissolveTime.max)) {
+ errors.push(`Viper dissolve time must be between 5 and 60`);
+ }
+ break;
+ }
+
+ return errors;
+ }
+}
diff --git a/src/game/index.ts b/src/game/index.ts
new file mode 100644
index 000000000..cb1b91e93
--- /dev/null
+++ b/src/game/index.ts
@@ -0,0 +1 @@
+export * from "./GameOptions";
diff --git a/src/game/modes/HideAndSeekManager.ts b/src/game/modes/HideAndSeekManager.ts
new file mode 100644
index 000000000..114428de9
--- /dev/null
+++ b/src/game/modes/HideAndSeekManager.ts
@@ -0,0 +1,472 @@
+import {
+ GameMap,
+ GameMode,
+ GameOverReason,
+ GameState,
+ Player,
+ RoleType,
+ RpcMessageTag,
+} from "@skeldjs/au-core";
+
+import {
+ RpcMessage,
+ MurderPlayerMessage,
+ ReportDeadBodyMessage,
+ CompleteTaskMessage,
+ StartGameMessage,
+ EndGameMessage,
+ GameDataMessage,
+} from "@skeldjs/au-protocol";
+
+import { Room, RoomPrivacy } from "../../Room";
+import { RoomHideAndSeekEndEvent, RoomHideAndSeekStartEvent, RoomHiderCaughtEvent } from "../../api";
+import { Logger } from "../../Logger";
+
+/**
+ * Phases of a Hide and Seek game.
+ */
+export enum HideAndSeekPhase {
+ /** Game has not started yet (lobby). */
+ Lobby,
+ /** Hiders are hiding, seeker is waiting/blinded. */
+ Hiding,
+ /** Seeker is actively hunting hiders. */
+ Seeking,
+ /** Final hide countdown is active, seeker has speed/ping advantage. */
+ FinalHide,
+ /** Game has ended. */
+ Ended,
+}
+
+/**
+ * Manages the Hide and Seek game mode lifecycle.
+ *
+ * Game flow:
+ * 1. Game starts → Hiding phase (hiders hide, seeker waits)
+ * 2. Hiding timer expires → Seeking phase (seeker hunts)
+ * 3. Final hide timer (optional) → FinalHide phase (seeker gets advantages)
+ * 4. All hiders dead OR all tasks done → Game ends
+ */
+export class HideAndSeekManager {
+ /** Current phase of the game. */
+ phase: HideAndSeekPhase = HideAndSeekPhase.Lobby;
+
+ /** The player acting as the seeker (impostor). */
+ seeker: Player | null = null;
+
+ /** All hider players (crewmates). */
+ hiders: Player[] = [];
+
+ /** Timer handles for phase transitions. */
+ private _hideTimer: NodeJS.Timeout | null = null;
+ private _finalHideTimer: NodeJS.Timeout | null = null;
+ private _finalHideCheckTimer: NodeJS.Timeout | null = null;
+
+ /** Track completed tasks for win condition. */
+ private _totalTasks: number = 0;
+ private _completedTasks: Set = new Set();
+
+ /** Logger for this manager. */
+ logger: Logger;
+
+ constructor(
+ public readonly room: Room,
+ ) {
+ this.logger = new Logger(() => "HideAndSeek", room.server.vorpal);
+ }
+
+ /**
+ * Initialize and start the Hide and Seek game.
+ */
+ async startGame() {
+ const settings = this.room.settings;
+
+ // Select seeker
+ this.selectSeeker();
+
+ if (!this.seeker) {
+ this.logger.error("Could not select a seeker for Hide and Seek game");
+ await this.room.handleEndGame(GameOverReason.ImpostorDisconnect);
+ return;
+ }
+
+ // All other players are hiders
+ this.hiders = [];
+ for (const [, player] of this.room.players) {
+ if (player.clientId !== this.seeker.clientId) {
+ this.hiders.push(player);
+ }
+ }
+
+ if (this.hiders.length === 0) {
+ this.logger.error("No hiders available for Hide and Seek game");
+ await this.room.handleEndGame(GameOverReason.ImpostorDisconnect);
+ return;
+ }
+
+ // Roles are assigned by the RoleManager when the game starts
+ // Seekers and hiders are already handled via the game mode logic
+
+ // Start the hiding phase
+ this.phase = HideAndSeekPhase.Hiding;
+
+ const hideDuration = settings.hidingTime * 1000; // Convert seconds to ms
+
+ this.logger.info("Hide and Seek started: %s is the seeker, %s hiders. Hiding for %ss",
+ this.seeker, this.hiders.length, settings.hidingTime);
+
+ // Calculate total tasks for win condition
+ this._totalTasks = this.computeTotalTasks();
+
+ // Emit event
+ await this.room.emit(new RoomHideAndSeekStartEvent(
+ this.room,
+ this.seeker,
+ this.hiders,
+ settings.hidingTime
+ ));
+
+ // Send chat message
+ this.room.sendChat(
+ `Hide and Seek! ${this.seeker.username || "Someone"} is the seeker. ` +
+ `Hide for ${settings.hidingTime} seconds!`
+ );
+
+ // Set timer to end hiding phase
+ this._hideTimer = setTimeout(() => {
+ this.startSeekingPhase();
+ }, hideDuration);
+ }
+
+ /**
+ * Select the seeker from the players.
+ * Uses the configured seekerPlayerId if valid, otherwise picks randomly.
+ */
+ private selectSeeker() {
+ const settings = this.room.settings;
+
+ // If a specific seeker was configured and they exist, use them
+ if (settings.seekerPlayerId !== 0xffffffff) {
+ const configuredSeeker = this.room.players.get(settings.seekerPlayerId);
+ if (configuredSeeker && configuredSeeker.characterControl) {
+ this.seeker = configuredSeeker;
+ return;
+ }
+ }
+
+ // Otherwise pick a random player
+ const playerArray = [...this.room.players.values()].filter(
+ p => p.characterControl && p.inScene
+ );
+
+ if (playerArray.length === 0) {
+ this.seeker = null;
+ return;
+ }
+
+ const randomIndex = Math.floor(Math.random() * playerArray.length);
+ this.seeker = playerArray[randomIndex];
+ }
+
+ /**
+ * Transition from Hiding phase to Seeking phase.
+ */
+ private async startSeekingPhase() {
+ if (this.phase === HideAndSeekPhase.Ended) return;
+
+ this.phase = HideAndSeekPhase.Seeking;
+ this.logger.info("Hide and Seek: Seeking phase started");
+
+ // Enable flashlight if configured
+ if (this.room.settings.useFlashlight) {
+ // Flashlight behavior is handled by the client based on settings
+ }
+
+ this.room.sendChat(
+ "The seeker is now hunting! Run and finish your tasks!"
+ );
+
+ // Check if final hide is enabled
+ if (this.room.settings.finalHideSeekMap && this.room.settings.finalHideTime > 0) {
+ const seekingDuration = Math.max(
+ (this.room.settings.hidingTime * 1000) - this.room.settings.finalHideTime * 1000,
+ 30000 // At least 30 seconds of seeking
+ );
+
+ this._finalHideTimer = setTimeout(() => {
+ this.startFinalHidePhase();
+ }, seekingDuration);
+ }
+ }
+
+ /**
+ * Transition to Final Hide phase.
+ */
+ private async startFinalHidePhase() {
+ if (this.phase === HideAndSeekPhase.Ended) return;
+
+ this.phase = HideAndSeekPhase.FinalHide;
+ this.logger.info("Hide and Seek: Final Hide phase started");
+
+ // Seeker gets speed boost
+ if (this.seeker?.characterControl) {
+ // Final seeker speed is applied via settings
+ }
+
+ // Ping mechanic
+ if (this.room.settings.finalHidePing && this.seeker) {
+ this.startPingMechanic();
+ }
+
+ this.room.sendChat(
+ "Final Hide! The seeker is getting desperate..."
+ );
+
+ // Set timer for end of final hide
+ const finalHideMs = this.room.settings.finalHideTime * 1000;
+ this._finalHideCheckTimer = setTimeout(() => {
+ // Hiders win if they survive the final hide
+ this.endGame("hiders", "All hiders survived the final hide");
+ }, finalHideMs);
+ }
+
+ /**
+ * Start the ping mechanic for final hide.
+ * Periodically reveals hider positions to the seeker.
+ */
+ private startPingMechanic() {
+ if (!this.seeker || this.phase === HideAndSeekPhase.Ended) return;
+
+ const pingInterval = Math.min(this.room.settings.maxPingTime * 1000, 5000);
+
+ const pingTimer = setInterval(() => {
+ if (this.phase === HideAndSeekPhase.Ended || !this.seeker) {
+ clearInterval(pingTimer);
+ return;
+ }
+
+ // Broadcast a map-wide ping showing hider locations
+ // This would send position data to the seeker
+ // The exact protocol for this depends on the game version
+ this.logger.debug("Ping: revealing hider positions to seeker");
+ }, pingInterval);
+ }
+
+ /**
+ * Handle a murder RPC from the seeker.
+ */
+ async handleMurder(killer: Player, target: Player) {
+ // Only the seeker can kill
+ if (killer.clientId !== this.seeker?.clientId) {
+ this.logger.warn("Non-seeker %s attempted to kill in Hide and Seek", killer);
+ return false;
+ }
+
+ // Can only kill during Seeking or FinalHide phases
+ if (this.phase !== HideAndSeekPhase.Seeking && this.phase !== HideAndSeekPhase.FinalHide) {
+ this.logger.warn("Seeker attempted to kill during non-seeking phase: %s", HideAndSeekPhase[this.phase]);
+ return false;
+ }
+
+ // Kill the target via character control
+ // In Hide and Seek, the murder RPC is processed normally by the game engine
+ // The target's death is handled by the PlayerControl component
+
+ // Remove from hiders list
+ this.hiders = this.hiders.filter(h => h.clientId !== target.clientId);
+
+ this.logger.info("Seeker %s caught hider %s! (%s remaining)",
+ killer, target, this.hiders.length);
+
+ // Emit hider caught event
+ await this.room.emit(new RoomHiderCaughtEvent(
+ this.room,
+ target,
+ killer,
+ this.hiders.length
+ ));
+
+ // No body report in Hide and Seek — game continues immediately
+
+ // Check win condition: all hiders dead
+ if (this.hiders.length === 0) {
+ await this.endGame("seekers", "All hiders were caught");
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle task completion from a hider.
+ */
+ async handleTaskComplete(player: Player, taskType: number, taskId: number) {
+ if (this.phase === HideAndSeekPhase.Ended) return;
+
+ const taskKey = `${player.clientId}:${taskId}`;
+ this._completedTasks.add(taskKey);
+
+ this.logger.debug("Hider %s completed task %s (type %s). %s/%s total tasks done",
+ player, taskId, taskType,
+ this._completedTasks.size, this._totalTasks);
+
+ // Check win condition: all tasks completed
+ if (this._completedTasks.size >= this._totalTasks) {
+ await this.endGame("hiders", "All tasks completed");
+ }
+ }
+
+ /**
+ * Handle report dead body RPC.
+ * In Hide and Seek, body reports are disabled — meetings never happen.
+ */
+ handleReportBody(reporter: Player, bodyPlayerId: number): boolean {
+ // Body reports are disabled in Hide and Seek
+ this.logger.debug("Body report blocked in Hide and Seek mode (reporter: %s, body: %s)",
+ reporter, bodyPlayerId);
+ return false;
+ }
+
+ /**
+ * End the Hide and Seek game.
+ */
+ async endGame(winner: "seekers" | "hiders" | "none", reason: string) {
+ if (this.phase === HideAndSeekPhase.Ended) return;
+
+ this.phase = HideAndSeekPhase.Ended;
+
+ // Clear all timers
+ this.clearTimers();
+
+ this.logger.info("Hide and Seek ended: %s win — %s", winner, reason);
+
+ // Emit event
+ await this.room.emit(new RoomHideAndSeekEndEvent(
+ this.room,
+ winner,
+ reason
+ ));
+
+ // Send result chat
+ const winnerText = winner === "seekers"
+ ? "The Seeker wins!"
+ : winner === "hiders"
+ ? "The Hiders win!"
+ : "The game ended!";
+
+ this.room.sendChat(`${winnerText} ${reason}`);
+
+ // Determine game over reason
+ const gameOverReason = winner === "seekers"
+ ? GameOverReason.ImpostorByKill
+ : winner === "hiders"
+ ? GameOverReason.CrewmatesByTask
+ : GameOverReason.CrewmatesByVote;
+
+ await this.room.handleEndGame(gameOverReason);
+ }
+
+ /**
+ * Handle a player disconnecting during the game.
+ */
+ async handlePlayerDisconnect(player: Player) {
+ // If seeker disconnects, hiders win
+ if (player.clientId === this.seeker?.clientId) {
+ this.logger.info("Seeker disconnected, hiders win");
+ await this.endGame("hiders", "Seeker disconnected");
+ return;
+ }
+
+ // Remove from hiders list
+ this.hiders = this.hiders.filter(h => h.clientId !== player.clientId);
+
+ // Recalculate task total
+ this._totalTasks = this.computeTotalTasks();
+
+ // Check if all hiders are gone
+ if (this.hiders.length === 0) {
+ await this.endGame("seekers", "All hiders disconnected");
+ }
+ }
+
+ /**
+ * Handle RPC messages from players during the game.
+ * Returns true if the RPC was handled, false to pass through.
+ */
+ async handleRpc(player: Player, rpcMessage: RpcMessage): Promise {
+ switch (rpcMessage.child?.messageTag) {
+ case RpcMessageTag.ReportDeadBody:
+ // Body reports are disabled
+ return this.handleReportBody(player, (rpcMessage.child as any).bodyPlayerId ?? 0);
+
+ case RpcMessageTag.CompleteTask:
+ if (rpcMessage.child instanceof CompleteTaskMessage) {
+ await this.handleTaskComplete(
+ player,
+ 0, // taskType derived from taskIdx
+ rpcMessage.child.taskIdx
+ );
+ }
+ return true; // Still allow normal processing
+
+ case RpcMessageTag.MurderPlayer:
+ if (rpcMessage.child instanceof MurderPlayerMessage) {
+ const target = this.room.players.get(rpcMessage.child.victimNetId);
+ if (target) {
+ return await this.handleMurder(player, target);
+ }
+ }
+ return true;
+
+ case RpcMessageTag.StartMeeting:
+ // Meetings are disabled in Hide and Seek
+ this.logger.debug("Meeting start blocked in Hide and Seek mode");
+ return false;
+
+ default:
+ return true; // Allow other RPCs through
+ }
+ }
+
+ /**
+ * Calculate total number of tasks to be completed for hider win condition.
+ */
+ private computeTotalTasks(): number {
+ const settings = this.room.settings;
+
+ // Total tasks = common + long + short, per player
+ const tasksPerPlayer = (settings.commonTasks || 0)
+ + (settings.longTasks || 0)
+ + (settings.shortTasks || 0);
+
+ return tasksPerPlayer * this.hiders.length;
+ }
+
+ /**
+ * Clean up all timers.
+ */
+ private clearTimers() {
+ if (this._hideTimer) {
+ clearTimeout(this._hideTimer);
+ this._hideTimer = null;
+ }
+ if (this._finalHideTimer) {
+ clearTimeout(this._finalHideTimer);
+ this._finalHideTimer = null;
+ }
+ if (this._finalHideCheckTimer) {
+ clearTimeout(this._finalHideCheckTimer);
+ this._finalHideCheckTimer = null;
+ }
+ }
+
+ /**
+ * Clean up the manager. Call when the room is destroyed.
+ */
+ destroy() {
+ this.clearTimers();
+ this.seeker = null;
+ this.hiders = [];
+ this._completedTasks.clear();
+ this.phase = HideAndSeekPhase.Ended;
+ }
+}
diff --git a/src/game/modes/index.ts b/src/game/modes/index.ts
new file mode 100644
index 000000000..cf6416a3f
--- /dev/null
+++ b/src/game/modes/index.ts
@@ -0,0 +1 @@
+export * from "./HideAndSeekManager";
diff --git a/src/game/roles/BaseRole.ts b/src/game/roles/BaseRole.ts
new file mode 100644
index 000000000..da37e64be
--- /dev/null
+++ b/src/game/roles/BaseRole.ts
@@ -0,0 +1,120 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+
+/**
+ * Base class for all role implementations in Waterway.
+ *
+ * Each role overrides specific hooks to implement its unique behavior.
+ * Roles are assigned to players when a game starts based on role settings.
+ */
+export abstract class BaseRole {
+ /** The unique role type identifier from the Among Us protocol. */
+ abstract roleType: RoleType;
+
+ /** Which team this role belongs to (Crewmate or Impostor). */
+ abstract teamType: RoleTeamType;
+
+ /** Whether this role's ability is currently active. */
+ isActive: boolean = false;
+
+ /** The current cooldown timer for the role's ability (in milliseconds). */
+ currentCooldown: number = 0;
+
+ /** When the ability was last used (Unix timestamp in ms). */
+ lastAbilityUse: number = 0;
+
+ /** Whether this role's ability is on cooldown. */
+ get isOnCooldown(): boolean {
+ return this.currentCooldown > 0 && (Date.now() - this.lastAbilityUse) < this.currentCooldown;
+ }
+
+ constructor(
+ /** The room this role exists in. */
+ public readonly room: Room,
+ /** The player who has this role. */
+ public readonly player: Player,
+ ) {}
+
+ /**
+ * Called once when the role is assigned and the game starts.
+ * Use this to initialize role state, set up timers, etc.
+ */
+ onGameStart(): void {}
+
+ /**
+ * Called when the player with this role kills another player.
+ * Override in imposter roles.
+ *
+ * @param target The player being killed.
+ * @returns Whether the kill should proceed normally (true) or be handled by the role (false).
+ */
+ onKill(target: Player): boolean {
+ return true; // Allow normal kill behavior
+ }
+
+ /**
+ * Called when the player completes a task.
+ *
+ * @param taskType The type of task completed.
+ * @param taskId The unique task ID.
+ */
+ onTaskComplete(taskType: number, taskId: number): void {}
+
+ /**
+ * Called when a meeting starts.
+ */
+ onMeetingStart(): void {}
+
+ /**
+ * Called when the player dies.
+ * Return false to prevent the normal death behavior (e.g., Phantom).
+ */
+ onDeath(): boolean {
+ return true; // Allow normal death behavior
+ }
+
+ /**
+ * Called when the player uses their role ability.
+ * Subclasses override this to implement the ability.
+ *
+ * @param target Optional target player for targeted abilities.
+ * @returns Whether the ability was successfully used.
+ */
+ onAbilityUse(target?: Player): boolean {
+ return false;
+ }
+
+ /**
+ * Called every fixed update tick while the game is running.
+ * Use for cooldown management, periodic effects, etc.
+ */
+ onFixedUpdate(): void {}
+
+ /**
+ * Called when the game ends.
+ */
+ onGameEnd(): void {}
+
+ /**
+ * Start the cooldown for this role's ability.
+ * @param cooldownMs Cooldown duration in milliseconds.
+ */
+ protected startCooldown(cooldownMs: number): void {
+ this.currentCooldown = cooldownMs;
+ this.lastAbilityUse = Date.now();
+ }
+
+ /**
+ * Check if the ability can be used (not on cooldown and role is active).
+ */
+ canUseAbility(): boolean {
+ return this.isActive && !this.isOnCooldown;
+ }
+
+ /**
+ * Get a human-readable name for this role type.
+ */
+ getRoleName(): string {
+ return RoleType[this.roleType] || "Unknown";
+ }
+}
diff --git a/src/game/roles/DetectiveRole.ts b/src/game/roles/DetectiveRole.ts
new file mode 100644
index 000000000..9d74168f8
--- /dev/null
+++ b/src/game/roles/DetectiveRole.ts
@@ -0,0 +1,110 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+
+/**
+ * Detective Role
+ *
+ * Can inspect a player to see their "suspicion" level — whether they have
+ * killed someone or performed other suspicious actions.
+ *
+ * This is a Crewmate role.
+ */
+export class DetectiveRole extends BaseRole {
+ roleType = RoleType.Detective;
+ teamType = RoleTeamType.Crewmate;
+
+ /** How many suspects have been inspected this game. */
+ inspectedCount: number = 0;
+
+ /** Set of player IDs that have been inspected. */
+ inspectedPlayers: Set = new Set();
+
+ /** Records of player kills tracked by the detective. */
+ private _murderRecords: Map = new Map();
+
+ get suspectLimit(): number {
+ return this.room.settings.roleSettings.detectiveSuspectLimit || 3;
+ }
+
+ onGameStart(): void {
+ this.isActive = true;
+ this.inspectedCount = 0;
+ this.inspectedPlayers.clear();
+ this._murderRecords.clear();
+
+ this.room.logger.info("%s is the Detective (suspect limit: %s)",
+ this.player, this.suspectLimit);
+
+ // Listen for murders to track who kills
+ this.room.on("player.murder", (ev: any) => {
+ if (ev.player && ev.target) {
+ const killerId = ev.player.clientId;
+ const currentCount = this._murderRecords.get(killerId) || 0;
+ this._murderRecords.set(killerId, currentCount + 1);
+ }
+ });
+ }
+
+ /**
+ * Inspect a player to check their suspicion level.
+ */
+ onAbilityUse(target?: Player): boolean {
+ if (!target) {
+ this.room.logger.warn("%s (Detective) attempted to inspect but no target specified", this.player);
+ return false;
+ }
+
+ if (this.inspectedPlayers.has(target.clientId)) {
+ this.room.logger.warn("%s (Detective) already inspected %s", this.player, target);
+ return false;
+ }
+
+ if (this.inspectedCount >= this.suspectLimit) {
+ this.room.logger.warn("%s (Detective) has reached the suspect limit (%s)",
+ this.player, this.suspectLimit);
+ return false;
+ }
+
+ // Perform the inspection
+ this.inspectedCount++;
+ this.inspectedPlayers.add(target.clientId);
+
+ const kills = this._murderRecords.get(target.clientId) || 0;
+ const targetInfo = target.getPlayerInfo();
+ const isImpostor = targetInfo?.isImpostor || false;
+
+ this.room.logger.info("%s (Detective) inspected %s: %s kills, isImpostor=%s",
+ this.player, target, kills, isImpostor);
+
+ // Send the inspection result to the detective
+ let resultMessage: string;
+ if (kills > 0) {
+ resultMessage = `Detective report: ${target.username || "Player"} has committed ${kills} murder(s)! Highly suspicious!`;
+ } else if (isImpostor) {
+ resultMessage = `Detective report: ${target.username || "Player"} appears suspicious but no kills detected.`;
+ } else {
+ resultMessage = `Detective report: ${target.username || "Player"} appears innocent.`;
+ }
+
+ this.room.sendChat(resultMessage, { targets: [this.player] });
+
+ return true;
+ }
+
+ /**
+ * Get the number of remaining inspections available.
+ */
+ get remainingInspections(): number {
+ return Math.max(0, this.suspectLimit - this.inspectedCount);
+ }
+
+ onDeath(): boolean {
+ this.isActive = false;
+ return true;
+ }
+
+ onGameEnd(): void {
+ this.isActive = false;
+ }
+}
diff --git a/src/game/roles/NoisemakerRole.ts b/src/game/roles/NoisemakerRole.ts
new file mode 100644
index 000000000..568252552
--- /dev/null
+++ b/src/game/roles/NoisemakerRole.ts
@@ -0,0 +1,84 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+
+/**
+ * Noisemaker Role
+ *
+ * When this player completes a task, they emit a noise alert that reveals
+ * their position to nearby players (and optionally to impostors).
+ *
+ * This is a Crewmate role.
+ */
+export class NoisemakerRole extends BaseRole {
+ roleType = RoleType.Noisemaker;
+ teamType = RoleTeamType.Crewmate;
+
+ /** Duration of the alert in seconds. */
+ get alertDuration(): number {
+ return this.room.settings.roleSettings.noisemakerAlertDuration || 10;
+ }
+
+ /** Whether impostors also see the alert. */
+ get impostorAlert(): boolean {
+ return this.room.settings.roleSettings.noisemakerImpostorAlert !== false;
+ }
+
+ onGameStart(): void {
+ this.isActive = true;
+ this.room.logger.info("%s is the Noisemaker (alert duration: %ss, impostor alert: %s)",
+ this.player, this.alertDuration, this.impostorAlert);
+ }
+
+ onTaskComplete(taskType: number, taskId: number): void {
+ if (!this.isActive) return;
+
+ this.room.logger.info("%s (Noisemaker) completed task %s, triggering alert!",
+ this.player, taskId);
+
+ // Broadcast the alert to nearby players
+ this.triggerAlert();
+ }
+
+ /**
+ * Trigger the noise alert, revealing this player's position.
+ */
+ private triggerAlert(): void {
+ const characterControl = this.player.characterControl;
+ if (!characterControl) return;
+
+ // The alert is broadcast to players based on proximity
+ // In the Among Us protocol, this is typically done by sending
+ // a position snap/sync to relevant players
+
+ // Determine who should see the alert
+ const recipients: Player[] = [];
+
+ for (const [, otherPlayer] of this.room.players) {
+ if (otherPlayer.clientId === this.player.clientId) continue;
+
+ const otherInfo = otherPlayer.getPlayerInfo();
+ if (!otherInfo) continue;
+
+ // Impostors can see if configured, crewmates always see
+ if (otherInfo.isImpostor && !this.impostorAlert) continue;
+
+ recipients.push(otherPlayer);
+ }
+
+ // Send a chat message to indicate the alert
+ this.room.sendChat(
+ `🔔 Noisemaker alert! ${this.player.username || "Someone"} revealed their position!`
+ );
+
+ this.room.logger.debug("Noisemaker alert sent to %s players", recipients.length);
+
+ // Start cooldown after alert
+ this.startCooldown(this.alertDuration * 1000);
+ }
+
+ onDeath(): boolean {
+ this.isActive = false;
+ return true;
+ }
+}
diff --git a/src/game/roles/PhantomRole.ts b/src/game/roles/PhantomRole.ts
new file mode 100644
index 000000000..4f547af06
--- /dev/null
+++ b/src/game/roles/PhantomRole.ts
@@ -0,0 +1,110 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+
+/**
+ * Phantom Role
+ *
+ * When this player is killed, instead of dying immediately, they enter a
+ * "vanished" ghost state where they can continue moving invisibly for a
+ * limited duration. After the duration expires, they properly die.
+ *
+ * This is an Impostor role.
+ */
+export class PhantomRole extends BaseRole {
+ roleType = RoleType.Phantom;
+ teamType = RoleTeamType.Impostor;
+
+ /** Whether the phantom is currently in the vanished state. */
+ isVanished: boolean = false;
+
+ /** Timer for the vanish duration. */
+ private _vanishTimer: NodeJS.Timeout | null = null;
+
+ /** Timer for the cooldown between vanish uses. */
+ private _cooldownTimer: NodeJS.Timeout | null = null;
+
+ get cooldown(): number {
+ return this.room.settings.roleSettings.phantomCooldown || 15;
+ }
+
+ get duration(): number {
+ return this.room.settings.roleSettings.phantomDuration || 30;
+ }
+
+ onGameStart(): void {
+ this.isActive = true;
+ this.room.logger.info("%s is the Phantom (duration: %ss, cooldown: %ss)",
+ this.player, this.duration, this.cooldown);
+ }
+
+ /**
+ * The Phantom doesn't die immediately. Instead, they enter a vanished state.
+ */
+ onDeath(): boolean {
+ if (!this.isActive) return true; // Normal death
+
+ // Enter vanished state instead of dying
+ this.isVanished = true;
+ this.room.logger.info("%s (Phantom) entered vanished state for %ss",
+ this.player, this.duration);
+
+ // Notify the player
+ this.room.sendChat(
+ `${this.player.username || "Someone"} has vanished into the shadows...`,
+ { targets: [...this.room.players.values()] }
+ );
+
+ // Set a timer to properly die after the duration
+ this._vanishTimer = setTimeout(() => {
+ this.endVanish();
+ }, this.duration * 1000);
+
+ // Disable the ability until cooldown
+ this.isActive = false;
+ this._cooldownTimer = setTimeout(() => {
+ this.isActive = true;
+ this.room.logger.debug("%s Phantom ability ready again", this.player);
+ }, this.cooldown * 1000);
+
+ return false; // Prevent normal death
+ }
+
+ /**
+ * End the vanished state and properly kill the phantom.
+ */
+ private endVanish(): void {
+ if (!this.isVanished) return;
+
+ this.isVanished = false;
+ this.room.logger.info("%s (Phantom) vanished state ended", this.player);
+
+ // Clear timer
+ if (this._vanishTimer) {
+ clearTimeout(this._vanishTimer);
+ this._vanishTimer = null;
+ }
+
+ // The player actually dies now via the normal game engine mechanics
+ }
+
+ /**
+ * Force-end the vanish state (e.g., game ends).
+ */
+ forceEndVanish(): void {
+ if (this._vanishTimer) {
+ clearTimeout(this._vanishTimer);
+ this._vanishTimer = null;
+ }
+ this.isVanished = false;
+ }
+
+ onGameEnd(): void {
+ this.forceEndVanish();
+ if (this._cooldownTimer) {
+ clearTimeout(this._cooldownTimer);
+ this._cooldownTimer = null;
+ }
+ this.isActive = false;
+ }
+}
diff --git a/src/game/roles/RoleManager.ts b/src/game/roles/RoleManager.ts
new file mode 100644
index 000000000..caf71927e
--- /dev/null
+++ b/src/game/roles/RoleManager.ts
@@ -0,0 +1,242 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+import { NoisemakerRole } from "./NoisemakerRole";
+import { PhantomRole } from "./PhantomRole";
+import { TrackerRole } from "./TrackerRole";
+import { DetectiveRole } from "./DetectiveRole";
+import { ViperRole } from "./ViperRole";
+
+/**
+ * Maps role types to their implementing classes.
+ */
+const ROLE_CONSTRUCTORS: Partial) => BaseRole>> = {
+ [RoleType.Noisemaker]: NoisemakerRole,
+ [RoleType.Phantom]: PhantomRole,
+ [RoleType.Tracker]: TrackerRole,
+ [RoleType.Detective]: DetectiveRole,
+ [RoleType.Viper]: ViperRole,
+};
+
+/**
+ * Manages role assignment and lifecycle for a room.
+ *
+ * When a game starts, the RoleManager reads the room's role settings
+ * and assigns roles to players based on the configured chances and
+ * maximum player counts.
+ */
+export class RoleManager {
+ /** All active role instances, keyed by player client ID. */
+ activeRoles: Map = new Map();
+
+ constructor(public readonly room: Room) {}
+
+ /**
+ * Assign roles to players based on the room's role settings.
+ * Called when the game starts.
+ */
+ assignRoles(): void {
+ const settings = this.room.settings;
+ const roleChances = settings.roleSettings.roleChances;
+
+ if (!roleChances) return;
+
+ const availablePlayers = [...this.room.players.values()].filter(
+ p => p.characterControl && p.inScene
+ );
+
+ // Track how many of each role we've assigned
+ const assignedCounts: Partial> = {};
+
+ for (const [roleTypeStr, roleChance] of Object.entries(roleChances)) {
+ const roleType = parseInt(roleTypeStr) as RoleType;
+
+ // Skip if no chance or no constructor for this role
+ if (roleChance.chance <= 0) continue;
+ if (!ROLE_CONSTRUCTORS[roleType]) continue;
+
+ const RoleCtor = ROLE_CONSTRUCTORS[roleType]!;
+ assignedCounts[roleType] = 0;
+
+ for (const player of availablePlayers) {
+ // Check max players limit for this role
+ if (assignedCounts[roleType]! >= roleChance.maxPlayers) break;
+
+ // Skip players that already have a role
+ if (this.activeRoles.has(player.clientId)) continue;
+
+ // Skip players who are already the standard impostor (for crewmate roles)
+ const playerInfo = player.getPlayerInfo();
+ const isImpostor = playerInfo?.isImpostor || false;
+ const isCrewmateRole = [
+ RoleType.Scientist,
+ RoleType.Engineer,
+ RoleType.GuardianAngel,
+ RoleType.Noisemaker,
+ RoleType.Tracker,
+ RoleType.Detective,
+ ].includes(roleType);
+
+ const isImpostorRole = [
+ RoleType.Shapeshifter,
+ RoleType.Phantom,
+ RoleType.Viper,
+ ].includes(roleType);
+
+ // Crewmate roles should not be assigned to impostors
+ if (isCrewmateRole && isImpostor) continue;
+ // Impostor roles should not be assigned to crewmates
+ if (isImpostorRole && !isImpostor) continue;
+
+ // Roll the dice
+ const roll = Math.random() * 100;
+ if (roll <= roleChance.chance) {
+ this.assignRoleToPlayer(player, RoleCtor, roleType);
+ assignedCounts[roleType]!++;
+ }
+ }
+ }
+
+ // Log assignment summary
+ const assignedCount = this.activeRoles.size;
+ if (assignedCount > 0) {
+ this.room.logger.info("Assigned %s role(s) to players", assignedCount);
+ for (const [playerId, role] of this.activeRoles) {
+ const player = this.room.players.get(playerId);
+ this.room.logger.info(" %s → %s", player, role.getRoleName());
+ }
+ }
+ }
+
+ /**
+ * Create and assign a role to a specific player.
+ */
+ assignRoleToPlayer(
+ player: Player,
+ RoleCtor: new (room: Room, player: Player) => BaseRole,
+ roleType: RoleType
+ ): BaseRole {
+ const role = new RoleCtor(this.room, player);
+ this.activeRoles.set(player.clientId, role);
+
+ // The player's role is tracked by this manager
+ // The client-facing role is set via PlayerControl.setRole RPC
+ if (player.characterControl) {
+ (player.characterControl as any).setRole(roleType);
+ }
+
+ // Initialize the role
+ role.onGameStart();
+
+ return role;
+ }
+
+ /**
+ * Get the role instance for a player, if any.
+ */
+ getRoleForPlayer(player: Player): BaseRole | null {
+ return this.activeRoles.get(player.clientId) || null;
+ }
+
+ /**
+ * Check if a player has a specific role type.
+ */
+ playerHasRole(player: Player, roleType: RoleType): boolean {
+ const role = this.activeRoles.get(player.clientId);
+ return role?.roleType === roleType;
+ }
+
+ /**
+ * Called when a player with a role kills someone.
+ * Routes to the appropriate role's onKill handler.
+ */
+ handleKill(killer: Player, target: Player): boolean {
+ const role = this.activeRoles.get(killer.clientId);
+ if (!role) return true; // No special role, allow normal kill
+
+ return role.onKill(target);
+ }
+
+ /**
+ * Called when a player with a role completes a task.
+ * Routes to the appropriate role's onTaskComplete handler.
+ */
+ handleTaskComplete(player: Player, taskType: number, taskId: number): void {
+ const role = this.activeRoles.get(player.clientId);
+ if (!role) return;
+
+ role.onTaskComplete(taskType, taskId);
+ }
+
+ /**
+ * Called when a player with a role dies.
+ * Routes to the appropriate role's onDeath handler.
+ */
+ handleDeath(player: Player): boolean {
+ const role = this.activeRoles.get(player.clientId);
+ if (!role) return true; // No special role, allow normal death
+
+ const result = role.onDeath();
+
+ // If the role allowed normal death, clean up
+ if (result) {
+ this.activeRoles.delete(player.clientId);
+ }
+
+ return result;
+ }
+
+ /**
+ * Called when a meeting starts.
+ */
+ handleMeetingStart(): void {
+ for (const [, role] of this.activeRoles) {
+ role.onMeetingStart();
+ }
+ }
+
+ /**
+ * Called every fixed update tick.
+ */
+ handleFixedUpdate(): void {
+ for (const [, role] of this.activeRoles) {
+ role.onFixedUpdate();
+ }
+ }
+
+ /**
+ * Called when the game ends.
+ * Cleans up all active roles.
+ */
+ handleGameEnd(): void {
+ for (const [, role] of this.activeRoles) {
+ role.onGameEnd();
+ }
+ this.activeRoles.clear();
+ }
+
+ /**
+ * Called when a player uses their role ability.
+ */
+ handleAbilityUse(player: Player, target?: Player): boolean {
+ const role = this.activeRoles.get(player.clientId);
+ if (!role) return false;
+
+ return role.onAbilityUse(target);
+ }
+
+ /**
+ * Get all active role instances.
+ */
+ getAllRoles(): BaseRole[] {
+ return [...this.activeRoles.values()];
+ }
+
+ /**
+ * Register a custom role constructor.
+ * Allows plugins to add new roles.
+ */
+ static registerRole(roleType: RoleType, ctor: new (room: Room, player: Player) => BaseRole): void {
+ ROLE_CONSTRUCTORS[roleType] = ctor;
+ }
+}
diff --git a/src/game/roles/TrackerRole.ts b/src/game/roles/TrackerRole.ts
new file mode 100644
index 000000000..79c8af96e
--- /dev/null
+++ b/src/game/roles/TrackerRole.ts
@@ -0,0 +1,132 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+
+/**
+ * Tracker Role
+ *
+ * Can mark a target player and receive periodic position updates
+ * (arrow indicators pointing to the target's direction).
+ *
+ * This is a Crewmate role.
+ */
+export class TrackerRole extends BaseRole {
+ roleType = RoleType.Tracker;
+ teamType = RoleTeamType.Crewmate;
+
+ /** The currently tracked player, if any. */
+ trackedTarget: Player | null = null;
+
+ /** Timer for periodic position updates. */
+ private _trackingTimer: NodeJS.Timeout | null = null;
+
+ /** How long the tracking has been active. */
+ private _trackingElapsed: number = 0;
+
+ get cooldown(): number {
+ return this.room.settings.roleSettings.trackerCooldown || 15;
+ }
+
+ get duration(): number {
+ return this.room.settings.roleSettings.trackerDuration || 30;
+ }
+
+ get delay(): number {
+ return this.room.settings.roleSettings.trackerDelay || 1;
+ }
+
+ onGameStart(): void {
+ this.isActive = true;
+ this.room.logger.info("%s is the Tracker (duration: %ss, cooldown: %ss, delay: %ss)",
+ this.player, this.duration, this.cooldown, this.delay);
+ }
+
+ /**
+ * Use the tracking ability on a target player.
+ */
+ onAbilityUse(target?: Player): boolean {
+ if (!target) {
+ this.room.logger.warn("%s (Tracker) attempted to track but no target specified", this.player);
+ return false;
+ }
+
+ if (!this.canUseAbility()) {
+ this.room.logger.warn("%s (Tracker) ability on cooldown", this.player);
+ return false;
+ }
+
+ if (target.clientId === this.player.clientId) {
+ this.room.logger.warn("%s (Tracker) attempted to track themselves", this.player);
+ return false;
+ }
+
+ // Start tracking
+ this.trackedTarget = target;
+ this._trackingElapsed = 0;
+ this.startCooldown(this.cooldown * 1000);
+
+ this.room.logger.info("%s (Tracker) is now tracking %s", this.player, target);
+
+ this.room.sendChat(
+ `Tracker is on the hunt...`,
+ { targets: [this.player] }
+ );
+
+ // Start periodic position updates
+ this._trackingTimer = setInterval(() => {
+ this.sendPositionUpdate();
+ this._trackingElapsed += this.delay;
+
+ // Stop tracking after duration expires
+ if (this._trackingElapsed >= this.duration) {
+ this.stopTracking();
+ }
+ }, this.delay * 1000);
+
+ return true;
+ }
+
+ /**
+ * Send a position update about the tracked target to this player.
+ */
+ private sendPositionUpdate(): void {
+ if (!this.trackedTarget || !this.trackedTarget.characterControl) {
+ this.stopTracking();
+ return;
+ }
+
+ // In the actual game, this would send an arrow/indicator RPC
+ // For now, we log the position update
+ this.room.logger.debug("%s (Tracker) received position update for %s",
+ this.player, this.trackedTarget);
+ }
+
+ /**
+ * Stop tracking the current target.
+ */
+ private stopTracking(): void {
+ if (this._trackingTimer) {
+ clearInterval(this._trackingTimer);
+ this._trackingTimer = null;
+ }
+
+ if (this.trackedTarget) {
+ this.room.logger.info("%s (Tracker) stopped tracking %s",
+ this.player, this.trackedTarget);
+ }
+
+ this.trackedTarget = null;
+ this._trackingElapsed = 0;
+ }
+
+ onDeath(): boolean {
+ this.stopTracking();
+ this.isActive = false;
+ return true;
+ }
+
+ onGameEnd(): void {
+ this.stopTracking();
+ this.isActive = false;
+ }
+}
diff --git a/src/game/roles/ViperRole.ts b/src/game/roles/ViperRole.ts
new file mode 100644
index 000000000..eaf0ddfd5
--- /dev/null
+++ b/src/game/roles/ViperRole.ts
@@ -0,0 +1,134 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+
+/**
+ * Viper Role
+ *
+ * When this impostor kills a player, the target doesn't die immediately.
+ * Instead, they become "poisoned" and die after a dissolve delay.
+ * This creates confusion as the kill is delayed and may happen during
+ * a meeting or while the viper is elsewhere (providing an alibi).
+ *
+ * This is an Impostor role.
+ */
+export class ViperRole extends BaseRole {
+ roleType = RoleType.Viper;
+ teamType = RoleTeamType.Impostor;
+
+ /** Set of poisoned player IDs awaiting dissolve. */
+ private _poisonedPlayers: Set = new Set();
+
+ /** Timers for each poisoned player. */
+ private _poisonTimers: Map = new Map();
+
+ get dissolveTime(): number {
+ return this.room.settings.roleSettings.viperDissolveTime || 15;
+ }
+
+ onGameStart(): void {
+ this.isActive = true;
+ this._poisonedPlayers.clear();
+ this._poisonTimers.clear();
+
+ this.room.logger.info("%s is the Viper (dissolve time: %ss)",
+ this.player, this.dissolveTime);
+ }
+
+ /**
+ * When the Viper kills, the target is poisoned instead of dying immediately.
+ */
+ onKill(target: Player): boolean {
+ if (!this.isActive) return true; // Normal kill if ability inactive
+
+ this.room.logger.info("%s (Viper) poisoned %s (dissolve in %ss)",
+ this.player, target, this.dissolveTime);
+
+ // Mark as poisoned
+ this._poisonedPlayers.add(target.clientId);
+
+ // Don't kill immediately — the dissolve timer will handle death
+ // But we still need to prevent the normal kill from processing
+ // The actual kill happens after the dissolve time
+
+ // Send a subtle message to the target
+ this.room.sendChat(
+ `You feel a sharp sting... something is wrong...`,
+ { targets: [target] }
+ );
+
+ // Set the dissolve timer
+ const timer = setTimeout(() => {
+ this.dissolveTarget(target);
+ }, this.dissolveTime * 1000);
+
+ this._poisonTimers.set(target.clientId, timer);
+
+ return false; // Prevent normal kill — Viper handles it
+ }
+
+ /**
+ * Actually kill the poisoned target after the dissolve time.
+ */
+ private dissolveTarget(target: Player): void {
+ if (!this._poisonedPlayers.has(target.clientId)) return;
+
+ this._poisonedPlayers.delete(target.clientId);
+ this._poisonTimers.delete(target.clientId);
+
+ // Check if the target is already dead
+ const playerInfo = target.getPlayerInfo();
+ if (playerInfo?.isDead) {
+ this.room.logger.debug("%s (Viper) target %s was already dead, skipping dissolve",
+ this.player, target);
+ return;
+ }
+
+ this.room.logger.info("%s (Viper) poison dissolved on %s", this.player, target);
+
+ // Send death message
+ this.room.sendChat(
+ `${target.username || "Someone"} succumbed to poison!`
+ );
+
+ // The target dies via the normal game engine mechanics
+ // Poison mechanic is tracked by this role for timing
+ }
+
+ /**
+ * Check if a player is currently poisoned by this Viper.
+ */
+ isPoisoned(playerId: number): boolean {
+ return this._poisonedPlayers.has(playerId);
+ }
+
+ /**
+ * Get all currently poisoned player IDs.
+ */
+ getPoisonedPlayers(): number[] {
+ return [...this._poisonedPlayers];
+ }
+
+ onDeath(): boolean {
+ // Clear all poison when the Viper dies
+ this.clearAllPoisons();
+ this.isActive = false;
+ return true;
+ }
+
+ onGameEnd(): void {
+ this.clearAllPoisons();
+ this.isActive = false;
+ }
+
+ /**
+ * Clear all active poisons (e.g., game over, Viper dies).
+ */
+ private clearAllPoisons(): void {
+ for (const [, timer] of this._poisonTimers) {
+ clearTimeout(timer);
+ }
+ this._poisonTimers.clear();
+ this._poisonedPlayers.clear();
+ }
+}
diff --git a/src/game/roles/index.ts b/src/game/roles/index.ts
new file mode 100644
index 000000000..bc16b4456
--- /dev/null
+++ b/src/game/roles/index.ts
@@ -0,0 +1,7 @@
+export * from "./BaseRole";
+export * from "./DetectiveRole";
+export * from "./NoisemakerRole";
+export * from "./PhantomRole";
+export * from "./RoleManager";
+export * from "./TrackerRole";
+export * from "./ViperRole";
diff --git a/yarn.lock b/yarn.lock
index eb26087cd..dc8d30e29 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1,2984 +1,1619 @@
-# This file is generated by running "yarn install" inside your project.
-# Manual changes might be lost - proceed with caution!
-
-__metadata:
- version: 8
- cacheKey: 10
-
-"@cspotcode/source-map-support@npm:^0.8.0":
- version: 0.8.1
- resolution: "@cspotcode/source-map-support@npm:0.8.1"
- dependencies:
- "@jridgewell/trace-mapping": "npm:0.3.9"
- checksum: 10/b6e38a1712fab242c86a241c229cf562195aad985d0564bd352ac404be583029e89e93028ffd2c251d2c407ecac5fb0cbdca94a2d5c10f29ac806ede0508b3ff
- languageName: node
- linkType: hard
-
-"@gerrit0/mini-shiki@npm:^3.12.0":
- version: 3.13.1
- resolution: "@gerrit0/mini-shiki@npm:3.13.1"
- dependencies:
- "@shikijs/engine-oniguruma": "npm:^3.13.0"
- "@shikijs/langs": "npm:^3.13.0"
- "@shikijs/themes": "npm:^3.13.0"
- "@shikijs/types": "npm:^3.13.0"
- "@shikijs/vscode-textmate": "npm:^10.0.2"
- checksum: 10/e7606dae2846cd69defb5443af38a9edd70eea241753459ffdd237a082f70200096bd501bc988fb0b49485924f2a61fed2374f89be8ef5d09f34859ba938db0b
- languageName: node
- linkType: hard
-
-"@hapi/bourne@npm:^3.0.0":
- version: 3.0.0
- resolution: "@hapi/bourne@npm:3.0.0"
- checksum: 10/b3b5d7bdf511fe27b7b8b01b9457f125646665bef72a78848c69170efdea19c2b72522246a87ede6cd811e51e7a556ceff194e46fb1393c6c8c796431c1810b6
- languageName: node
- linkType: hard
-
-"@isaacs/cliui@npm:^8.0.2":
- version: 8.0.2
- resolution: "@isaacs/cliui@npm:8.0.2"
- dependencies:
- string-width: "npm:^5.1.2"
- string-width-cjs: "npm:string-width@^4.2.0"
- strip-ansi: "npm:^7.0.1"
- strip-ansi-cjs: "npm:strip-ansi@^6.0.1"
- wrap-ansi: "npm:^8.1.0"
- wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0"
- checksum: 10/e9ed5fd27c3aec1095e3a16e0c0cf148d1fee55a38665c35f7b3f86a9b5d00d042ddaabc98e8a1cb7463b9378c15f22a94eb35e99469c201453eb8375191f243
- languageName: node
- linkType: hard
-
-"@isaacs/fs-minipass@npm:^4.0.0":
- version: 4.0.1
- resolution: "@isaacs/fs-minipass@npm:4.0.1"
- dependencies:
- minipass: "npm:^7.0.4"
- checksum: 10/4412e9e6713c89c1e66d80bb0bb5a2a93192f10477623a27d08f228ba0316bb880affabc5bfe7f838f58a34d26c2c190da726e576cdfc18c49a72e89adabdcf5
- languageName: node
- linkType: hard
-
-"@jridgewell/resolve-uri@npm:^3.0.3":
- version: 3.1.2
- resolution: "@jridgewell/resolve-uri@npm:3.1.2"
- checksum: 10/97106439d750a409c22c8bff822d648f6a71f3aa9bc8e5129efdc36343cd3096ddc4eeb1c62d2fe48e9bdd4db37b05d4646a17114ecebd3bbcacfa2de51c3c1d
- languageName: node
- linkType: hard
-
-"@jridgewell/sourcemap-codec@npm:^1.4.10":
- version: 1.5.5
- resolution: "@jridgewell/sourcemap-codec@npm:1.5.5"
- checksum: 10/5d9d207b462c11e322d71911e55e21a4e2772f71ffe8d6f1221b8eb5ae6774458c1d242f897fb0814e8714ca9a6b498abfa74dfe4f434493342902b1a48b33a5
- languageName: node
- linkType: hard
-
-"@jridgewell/trace-mapping@npm:0.3.9":
- version: 0.3.9
- resolution: "@jridgewell/trace-mapping@npm:0.3.9"
- dependencies:
- "@jridgewell/resolve-uri": "npm:^3.0.3"
- "@jridgewell/sourcemap-codec": "npm:^1.4.10"
- checksum: 10/83deafb8e7a5ca98993c2c6eeaa93c270f6f647a4c0dc00deb38c9cf9b2d3b7bf15e8839540155247ef034a052c0ec4466f980bf0c9e2ab63b97d16c0cedd3ff
- languageName: node
- linkType: hard
-
-"@koa/router@npm:^14.0.0":
- version: 14.0.0
- resolution: "@koa/router@npm:14.0.0"
- dependencies:
- debug: "npm:^4.4.1"
- http-errors: "npm:^2.0.0"
- koa-compose: "npm:^4.1.0"
- path-to-regexp: "npm:^8.2.0"
- checksum: 10/f5f9bedd4c163ad376bcf9626ebb13f35febc44c1f81545ee5efaceb67324e3caf476f9d2a966b4590cac41ab9994b1bcb11f050afbdccd6343f27f31758ff68
- languageName: node
- linkType: hard
-
-"@noble/hashes@npm:^1.1.5":
- version: 1.8.0
- resolution: "@noble/hashes@npm:1.8.0"
- checksum: 10/474b7f56bc6fb2d5b3a42132561e221b0ea4f91e590f4655312ca13667840896b34195e2b53b7f097ec080a1fdd3b58d902c2a8d0fbdf51d2e238b53808a177e
- languageName: node
- linkType: hard
-
-"@npmcli/agent@npm:^3.0.0":
- version: 3.0.0
- resolution: "@npmcli/agent@npm:3.0.0"
- dependencies:
- agent-base: "npm:^7.1.0"
- http-proxy-agent: "npm:^7.0.0"
- https-proxy-agent: "npm:^7.0.1"
- lru-cache: "npm:^10.0.1"
- socks-proxy-agent: "npm:^8.0.3"
- checksum: 10/775c9a7eb1f88c195dfb3bce70c31d0fe2a12b28b754e25c08a3edb4bc4816bfedb7ac64ef1e730579d078ca19dacf11630e99f8f3c3e0fd7b23caa5fd6d30a6
- languageName: node
- linkType: hard
-
-"@npmcli/fs@npm:^4.0.0":
- version: 4.0.0
- resolution: "@npmcli/fs@npm:4.0.0"
- dependencies:
- semver: "npm:^7.3.5"
- checksum: 10/405c4490e1ff11cf299775449a3c254a366a4b1ffc79d87159b0ee7d5558ac9f6a2f8c0735fd6ff3873cef014cb1a44a5f9127cb6a1b2dbc408718cca9365b5a
- languageName: node
- linkType: hard
-
-"@paralleldrive/cuid2@npm:^2.2.2":
- version: 2.2.2
- resolution: "@paralleldrive/cuid2@npm:2.2.2"
- dependencies:
- "@noble/hashes": "npm:^1.1.5"
- checksum: 10/40ee269d6e47b4fed7706a2e4da7c27c3c668ebc969110d6d112277b6b16a67cce0503b53b9943f2c55035a72d225f77ea5541e03396d6429eec9252137a53b7
- languageName: node
- linkType: hard
-
-"@pkgjs/parseargs@npm:^0.11.0":
- version: 0.11.0
- resolution: "@pkgjs/parseargs@npm:0.11.0"
- checksum: 10/115e8ceeec6bc69dff2048b35c0ab4f8bbee12d8bb6c1f4af758604586d802b6e669dcb02dda61d078de42c2b4ddce41b3d9e726d7daa6b4b850f4adbf7333ff
- languageName: node
- linkType: hard
-
-"@schemastore/package@npm:^0.0.6":
- version: 0.0.6
- resolution: "@schemastore/package@npm:0.0.6"
- checksum: 10/4f65a8ed9409494f012cb41c601e564695d3d6623f98359cc08158212a18e534e4233147bd04776b92378cbcfea6853dd2abcc7615aea02f74c5a0a77d1ae6c1
- languageName: node
- linkType: hard
-
-"@shikijs/engine-oniguruma@npm:^3.13.0":
- version: 3.13.0
- resolution: "@shikijs/engine-oniguruma@npm:3.13.0"
- dependencies:
- "@shikijs/types": "npm:3.13.0"
- "@shikijs/vscode-textmate": "npm:^10.0.2"
- checksum: 10/bc7f1c69640ccece9a3ac3f3adc9cebde9c24bcd722747cc464c338a32725af2b9087a42646cd0f56fbea98d99414918c558f2ca0beadc13db0a740183e7707f
- languageName: node
- linkType: hard
-
-"@shikijs/langs@npm:^3.13.0":
- version: 3.13.0
- resolution: "@shikijs/langs@npm:3.13.0"
- dependencies:
- "@shikijs/types": "npm:3.13.0"
- checksum: 10/2a0478246ce61745d9012cf5051c9790ef5afad6c89fe5716be4a81d04d7dfd9ffc90af0bad0be0061111bfc001a3bc0c55ffd4fc0b82bc1c09a4c33d7b593ca
- languageName: node
- linkType: hard
-
-"@shikijs/themes@npm:^3.13.0":
- version: 3.13.0
- resolution: "@shikijs/themes@npm:3.13.0"
- dependencies:
- "@shikijs/types": "npm:3.13.0"
- checksum: 10/4ab0ecf1ddb35e387cef22e267cdf3ed7548a84f24ad58885d19401f75cc01a5a10152ee0196222e01cead8202f20012615aa0076f798599f7dfc2bb7beaa617
- languageName: node
- linkType: hard
-
-"@shikijs/types@npm:3.13.0, @shikijs/types@npm:^3.13.0":
- version: 3.13.0
- resolution: "@shikijs/types@npm:3.13.0"
- dependencies:
- "@shikijs/vscode-textmate": "npm:^10.0.2"
- "@types/hast": "npm:^3.0.4"
- checksum: 10/a57cc95847edd84134166de1f11a044e356581834b2dfd75dcd67f2221aef6f20f505839c090a76b7c8e0a79d7d5cee829de50d261f363a633ae9afca29546c4
- languageName: node
- linkType: hard
-
-"@shikijs/vscode-textmate@npm:^10.0.2":
- version: 10.0.2
- resolution: "@shikijs/vscode-textmate@npm:10.0.2"
- checksum: 10/d924cba8a01cd9ca12f56ba097d628fdb81455abb85884c8d8a5ae85b628a37dd5907e7691019b97107bd6608c866adf91ba04a1c3bba391281c88e386c044ea
- languageName: node
- linkType: hard
-
-"@skeldjs/au-client@npm:^3.0.3":
- version: 3.0.3
- resolution: "@skeldjs/au-client@npm:3.0.3"
- checksum: 10/d83bcbf37568863ba874f19a631fd45db3737383689b33885f7a8261a83f486ee3f39bae7eee61d54f27da3c79037caa9aa5d9caf688f52150908f0ac2d4e110
- languageName: node
- linkType: hard
-
-"@skeldjs/au-constants@npm:3.0.3, @skeldjs/au-constants@npm:^3.0.3":
- version: 3.0.3
- resolution: "@skeldjs/au-constants@npm:3.0.3"
- checksum: 10/47f6738bbdcc8c752ebd6814a38c67b561b7021f54d31c6b0046c3abe9d05d89325d7a5217c3be19b91c0ad93faca29ef12a99e90a517d5a1904b8d0755b3d0e
- languageName: node
- linkType: hard
-
-"@skeldjs/au-core@npm:^3.0.3":
- version: 3.0.3
- resolution: "@skeldjs/au-core@npm:3.0.3"
- dependencies:
- "@skeldjs/au-constants": "npm:3.0.3"
- "@skeldjs/au-protocol": "npm:3.0.3"
- "@skeldjs/events": "npm:3.0.3"
- "@skeldjs/hazel": "npm:3.0.3"
- checksum: 10/74a1b68a23a88137f155c8a3be154f1ef17f42d70f4cf4ddd908a4d878db8332efd5d6a9a8841e35995ea1a8f22e40893d5258e0e4e1a18bae0fdec71f08a58e
- languageName: node
- linkType: hard
-
-"@skeldjs/au-protocol@npm:3.0.3, @skeldjs/au-protocol@npm:^3.0.3":
- version: 3.0.3
- resolution: "@skeldjs/au-protocol@npm:3.0.3"
- dependencies:
- "@skeldjs/au-constants": "npm:3.0.3"
- "@skeldjs/hazel": "npm:3.0.3"
- checksum: 10/4c937b8bd7567359e6cd170fc190b6cba5dff39112b245339fb9d7d28ab03625184d51b4c812474e6c58f2157621212011020766987fe8c7eff1c3e0e5ba7e5a
- languageName: node
- linkType: hard
-
-"@skeldjs/au-text@npm:^3.0.3":
- version: 3.0.3
- resolution: "@skeldjs/au-text@npm:3.0.3"
- checksum: 10/5f2a948d8b1ec0be3175f0837cb9995c9e8b23d8fd46968200f979c0191e74045d549cc993bdf5507abf121282955607a1067985e94f9863150809bae0b88103
- languageName: node
- linkType: hard
-
-"@skeldjs/events@npm:3.0.3, @skeldjs/events@npm:^3.0.3":
- version: 3.0.3
- resolution: "@skeldjs/events@npm:3.0.3"
- checksum: 10/9c6aa038e8ed7432906c3928dd5198f07eda63ace7668e093135c080c88a69280bdceda0c685ba4eaeff2f4cdac934e0e97dd45068ed93c2960087d669316293
- languageName: node
- linkType: hard
-
-"@skeldjs/hazel@npm:3.0.3, @skeldjs/hazel@npm:^3.0.3":
- version: 3.0.3
- resolution: "@skeldjs/hazel@npm:3.0.3"
- checksum: 10/fe09f5b4649b82be2a41a8b45fa6d1325e8e1726999c6d0426f8853e8e7b2b79275484dd7fdc34afe4a0791c5d4b05b6c3b3ee5e8f913b865431c24215fcfc6b
- languageName: node
- linkType: hard
-
-"@skeldjs/waterway@workspace:.":
- version: 0.0.0-use.local
- resolution: "@skeldjs/waterway@workspace:."
- dependencies:
- "@koa/router": "npm:^14.0.0"
- "@schemastore/package": "npm:^0.0.6"
- "@skeldjs/au-client": "npm:^3.0.3"
- "@skeldjs/au-constants": "npm:^3.0.3"
- "@skeldjs/au-core": "npm:^3.0.3"
- "@skeldjs/au-protocol": "npm:^3.0.3"
- "@skeldjs/au-text": "npm:^3.0.3"
- "@skeldjs/events": "npm:^3.0.3"
- "@skeldjs/hazel": "npm:^3.0.3"
- "@types/koa": "npm:^2"
- "@types/koa__router": "npm:^12"
- "@types/minimatch": "npm:^3.0.5"
- "@types/node": "npm:^24.7.0"
- "@types/prompts": "npm:^2.0.14"
- "@types/triple-beam": "npm:^1.3.2"
- "@types/vorpal": "npm:^1.12.0"
- "@types/yargs": "npm:^17.0.0"
- chalk: "npm:^4.1.1"
- chokidar: "npm:^3.5.2"
- compare-versions: "npm:^3.6.0"
- dotenv: "npm:^10.0.0"
- koa: "npm:^3.0.3"
- koa-body: "npm:^6.0.1"
- minimatch: "npm:^3.0.4"
- pinst: "npm:^2.1.6"
- prompts: "npm:^2.4.2"
- query-registry: "npm:^2.5.0"
- reflect-metadata: "npm:^0.1.13"
- resolve-from: "npm:^5.0.0"
- resolve-pkg: "npm:^2.0.0"
- triple-beam: "npm:^1.3.0"
- ts-node: "npm:^10.0.0"
- typedoc: "npm:^0.28.13"
- typedoc-plugin-external-module-map: "npm:^2.2.0"
- typescript: "npm:^5.9.3"
- vorpal: "npm:^1.12.0"
- bin:
- waterway: ./dist/bin/pkg.js
- languageName: unknown
- linkType: soft
-
-"@tsconfig/node10@npm:^1.0.7":
- version: 1.0.11
- resolution: "@tsconfig/node10@npm:1.0.11"
- checksum: 10/51fe47d55fe1b80ec35e6e5ed30a13665fd3a531945350aa74a14a1e82875fb60b350c2f2a5e72a64831b1b6bc02acb6760c30b3738b54954ec2dea82db7a267
- languageName: node
- linkType: hard
-
-"@tsconfig/node12@npm:^1.0.7":
- version: 1.0.11
- resolution: "@tsconfig/node12@npm:1.0.11"
- checksum: 10/5ce29a41b13e7897a58b8e2df11269c5395999e588b9a467386f99d1d26f6c77d1af2719e407621412520ea30517d718d5192a32403b8dfcc163bf33e40a338a
- languageName: node
- linkType: hard
-
-"@tsconfig/node14@npm:^1.0.0":
- version: 1.0.3
- resolution: "@tsconfig/node14@npm:1.0.3"
- checksum: 10/19275fe80c4c8d0ad0abed6a96dbf00642e88b220b090418609c4376e1cef81bf16237bf170ad1b341452feddb8115d8dd2e5acdfdea1b27422071163dc9ba9d
- languageName: node
- linkType: hard
-
-"@tsconfig/node16@npm:^1.0.2":
- version: 1.0.4
- resolution: "@tsconfig/node16@npm:1.0.4"
- checksum: 10/202319785901f942a6e1e476b872d421baec20cf09f4b266a1854060efbf78cde16a4d256e8bc949d31e6cd9a90f1e8ef8fb06af96a65e98338a2b6b0de0a0ff
- languageName: node
- linkType: hard
-
-"@types/accepts@npm:*":
- version: 1.3.7
- resolution: "@types/accepts@npm:1.3.7"
- dependencies:
- "@types/node": "npm:*"
- checksum: 10/7678cf74976e16093aff6e6f9755826faf069ac1e30179276158ce46ea246348ff22ca6bdd46cef08428881337d9ceefbf00bab08a7731646eb9fc9449d6a1e7
- languageName: node
- linkType: hard
-
-"@types/body-parser@npm:*":
- version: 1.19.6
- resolution: "@types/body-parser@npm:1.19.6"
- dependencies:
- "@types/connect": "npm:*"
- "@types/node": "npm:*"
- checksum: 10/33041e88eae00af2cfa0827e951e5f1751eafab2a8b6fce06cd89ef368a988907996436b1325180edaeddd1c0c7d0d0d4c20a6c9ff294a91e0039a9db9e9b658
- languageName: node
- linkType: hard
-
-"@types/co-body@npm:^6.1.0":
- version: 6.1.3
- resolution: "@types/co-body@npm:6.1.3"
- dependencies:
- "@types/node": "npm:*"
- "@types/qs": "npm:*"
- checksum: 10/e93fdc177f69ee0535cf401783258e4255f5eb8235c58b5a2a5a8958cf341fadf3d0bf2c75907ed6b7d188ce2c2f2cf9593a71d4eef12900beba54ebbbdd5cc1
- languageName: node
- linkType: hard
-
-"@types/connect@npm:*":
- version: 3.4.38
- resolution: "@types/connect@npm:3.4.38"
- dependencies:
- "@types/node": "npm:*"
- checksum: 10/7eb1bc5342a9604facd57598a6c62621e244822442976c443efb84ff745246b10d06e8b309b6e80130026a396f19bf6793b7cecd7380169f369dac3bfc46fb99
- languageName: node
- linkType: hard
-
-"@types/content-disposition@npm:*":
- version: 0.5.9
- resolution: "@types/content-disposition@npm:0.5.9"
- checksum: 10/d895703c0027ca6c4c0c1ede363909dbb590deec3b206a84b88a49c8b2840bcbd31b4ddeb2e1e6caa1af00801cc79c5b69a5c75a8152f2959810b10fe75a4e1f
- languageName: node
- linkType: hard
-
-"@types/cookies@npm:*":
- version: 0.9.1
- resolution: "@types/cookies@npm:0.9.1"
- dependencies:
- "@types/connect": "npm:*"
- "@types/express": "npm:*"
- "@types/keygrip": "npm:*"
- "@types/node": "npm:*"
- checksum: 10/512635d55e25fb113412a8f0edc59a00f68ab79646ce19ebd95f8e97815f9ea506b69ec61da12e024565bb03d238fbb7983805c53a6f8d92684c45869a9ac7cf
- languageName: node
- linkType: hard
-
-"@types/express-serve-static-core@npm:^5.0.0":
- version: 5.1.0
- resolution: "@types/express-serve-static-core@npm:5.1.0"
- dependencies:
- "@types/node": "npm:*"
- "@types/qs": "npm:*"
- "@types/range-parser": "npm:*"
- "@types/send": "npm:*"
- checksum: 10/c0b5b7ebc15b222f51e5705da2b8a5180335bf70927cc83c065784331aa9291984db1bfa4a14f5ba31b538dcb543561d9280046051fa4c9b7256eb971293e735
- languageName: node
- linkType: hard
-
-"@types/express@npm:*":
- version: 5.0.3
- resolution: "@types/express@npm:5.0.3"
- dependencies:
- "@types/body-parser": "npm:*"
- "@types/express-serve-static-core": "npm:^5.0.0"
- "@types/serve-static": "npm:*"
- checksum: 10/bb6f10c14c8e3cce07f79ee172688aa9592852abd7577b663cd0c2054307f172c2b2b36468c918fed0d4ac359b99695807b384b3da6157dfa79acbac2226b59b
- languageName: node
- linkType: hard
-
-"@types/formidable@npm:^2.0.5":
- version: 2.0.6
- resolution: "@types/formidable@npm:2.0.6"
- dependencies:
- "@types/node": "npm:*"
- checksum: 10/808a9bc11276e3bd44a8b9d20f4d567ef0e452dcff7fa6ce2575b769bab049ccc4225d02ddd80f0f4de34aa2d2e7242e036bc547811667868860e8dc632d8b16
- languageName: node
- linkType: hard
-
-"@types/hast@npm:^3.0.4":
- version: 3.0.4
- resolution: "@types/hast@npm:3.0.4"
- dependencies:
- "@types/unist": "npm:*"
- checksum: 10/732920d81bb7605895776841b7658b4d8cc74a43a8fa176017cc0fb0ecc1a4c82a2b75a4fe6b71aa262b649d3fb62858c6789efa3793ea1d40269953af96ecb5
- languageName: node
- linkType: hard
-
-"@types/http-assert@npm:*":
- version: 1.5.6
- resolution: "@types/http-assert@npm:1.5.6"
- checksum: 10/dfe1010164ba633859d90a50c4c53e69a38a16972061ef614acc1b0bdb7e53a1c923a11b4169a4a7eedc20b2303962d761727a212ae099717327cf4f38293817
- languageName: node
- linkType: hard
-
-"@types/http-errors@npm:*, @types/http-errors@npm:^2":
- version: 2.0.5
- resolution: "@types/http-errors@npm:2.0.5"
- checksum: 10/a88da669366bc483e8f3b3eb3d34ada5f8d13eeeef851b1204d77e2ba6fc42aba4566d877cca5c095204a3f4349b87fe397e3e21288837bdd945dd514120755b
- languageName: node
- linkType: hard
-
-"@types/keygrip@npm:*":
- version: 1.0.6
- resolution: "@types/keygrip@npm:1.0.6"
- checksum: 10/d157f60bf920492347791d2b26d530d5069ce05796549fbacd4c24d66ffbebbcb0ab67b21e7a1b80a593b9fd4b67dc4843dec04c12bbc2e0fddfb8577a826c41
- languageName: node
- linkType: hard
-
-"@types/koa-compose@npm:*":
- version: 3.2.8
- resolution: "@types/koa-compose@npm:3.2.8"
- dependencies:
- "@types/koa": "npm:*"
- checksum: 10/95c32bdee738ac7c10439bbf6342ca3b9f0aafd7e8118739eac7fb0fa703a23cfe4c88f63e13a69a16fbde702e0bcdc62b272aa734325fc8efa7e5625479752e
- languageName: node
- linkType: hard
-
-"@types/koa@npm:*":
- version: 3.0.0
- resolution: "@types/koa@npm:3.0.0"
- dependencies:
- "@types/accepts": "npm:*"
- "@types/content-disposition": "npm:*"
- "@types/cookies": "npm:*"
- "@types/http-assert": "npm:*"
- "@types/http-errors": "npm:^2"
- "@types/keygrip": "npm:*"
- "@types/koa-compose": "npm:*"
- "@types/node": "npm:*"
- checksum: 10/db461810b71afe7b73dc849c0832669d1d838edd15b81b0c07d37d32545620dfb0efc19cc120485f5e06a805a151cd89696eb53a5bfad8e223d7e593080069cb
- languageName: node
- linkType: hard
-
-"@types/koa@npm:^2, @types/koa@npm:^2.13.5":
- version: 2.15.0
- resolution: "@types/koa@npm:2.15.0"
- dependencies:
- "@types/accepts": "npm:*"
- "@types/content-disposition": "npm:*"
- "@types/cookies": "npm:*"
- "@types/http-assert": "npm:*"
- "@types/http-errors": "npm:*"
- "@types/keygrip": "npm:*"
- "@types/koa-compose": "npm:*"
- "@types/node": "npm:*"
- checksum: 10/2be9dff1ef66bf15b037386c188893761a8fb46390a5e1d2a2031d9e1ba4473e40ddfbd625980a504bd804d7148b3e230c18e240503f33eac3b6e5e830645d30
- languageName: node
- linkType: hard
-
-"@types/koa__router@npm:^12":
- version: 12.0.4
- resolution: "@types/koa__router@npm:12.0.4"
- dependencies:
- "@types/koa": "npm:*"
- checksum: 10/c01311980bf9a921b77cca5a93cc85522a6d13fe49575e6190fa80407a60237e7351d99a399316dda3119641d498f5d8236b905cd3b4f54fad2c0839ab655dd4
- languageName: node
- linkType: hard
-
-"@types/mime@npm:^1":
- version: 1.3.5
- resolution: "@types/mime@npm:1.3.5"
- checksum: 10/e29a5f9c4776f5229d84e525b7cd7dd960b51c30a0fb9a028c0821790b82fca9f672dab56561e2acd9e8eed51d431bde52eafdfef30f643586c4162f1aecfc78
- languageName: node
- linkType: hard
-
-"@types/minimatch@npm:^3.0.5":
- version: 3.0.5
- resolution: "@types/minimatch@npm:3.0.5"
- checksum: 10/c41d136f67231c3131cf1d4ca0b06687f4a322918a3a5adddc87ce90ed9dbd175a3610adee36b106ae68c0b92c637c35e02b58c8a56c424f71d30993ea220b92
- languageName: node
- linkType: hard
-
-"@types/node@npm:*, @types/node@npm:^24.7.0":
- version: 24.7.2
- resolution: "@types/node@npm:24.7.2"
- dependencies:
- undici-types: "npm:~7.14.0"
- checksum: 10/62073022f7fc11363683e6ce82332193129c3053e7f973127a83b0daa5d755ecf5806d310f5a7c78375e6c3d309aa15b4ae87c4c1797a9eec091425d36098b60
- languageName: node
- linkType: hard
-
-"@types/node@npm:^20.14.14":
- version: 20.19.21
- resolution: "@types/node@npm:20.19.21"
- dependencies:
- undici-types: "npm:~6.21.0"
- checksum: 10/5e460265afe6b08e40a60b7b353bfe03836a9f73f5fae9ef90d5fa746f2173cbb4b20874adb611c711d4246f905d92f409339f4e5268048307306c7ecf5a16a7
- languageName: node
- linkType: hard
-
-"@types/prompts@npm:^2.0.14":
- version: 2.4.9
- resolution: "@types/prompts@npm:2.4.9"
- dependencies:
- "@types/node": "npm:*"
- kleur: "npm:^3.0.3"
- checksum: 10/69b8372f4c790b45fea16a46ff8d1bcc71b14579481776b67bd6263637118a7ecb1f12e1311506c29fadc81bf618dc64f1a91f903cfd5be67a0455a227b3e462
- languageName: node
- linkType: hard
-
-"@types/qs@npm:*":
- version: 6.14.0
- resolution: "@types/qs@npm:6.14.0"
- checksum: 10/1909205514d22b3cbc7c2314e2bd8056d5f05dfb21cf4377f0730ee5e338ea19957c41735d5e4806c746176563f50005bbab602d8358432e25d900bdf4970826
- languageName: node
- linkType: hard
-
-"@types/range-parser@npm:*":
- version: 1.2.7
- resolution: "@types/range-parser@npm:1.2.7"
- checksum: 10/95640233b689dfbd85b8c6ee268812a732cf36d5affead89e806fe30da9a430767af8ef2cd661024fd97e19d61f3dec75af2df5e80ec3bea000019ab7028629a
- languageName: node
- linkType: hard
-
-"@types/send@npm:*":
- version: 1.2.0
- resolution: "@types/send@npm:1.2.0"
- dependencies:
- "@types/node": "npm:*"
- checksum: 10/11b4a178902deae8743e92a12ce0821200c285a75fade3b44ca5ee66f65997cd9ce86006622f0a1adaef8fa5272ac06bf464fab34089e36dca8e57197ff32544
- languageName: node
- linkType: hard
-
-"@types/send@npm:<1":
- version: 0.17.5
- resolution: "@types/send@npm:0.17.5"
- dependencies:
- "@types/mime": "npm:^1"
- "@types/node": "npm:*"
- checksum: 10/b68ae8f9ba9328a4f276cd010914ed43b96371fbf34c7aa08a9111bff36661810bb14b96647e4a92e319dbd2689dc107fb0f9194ec3fa9335c162dc134026240
- languageName: node
- linkType: hard
-
-"@types/serve-static@npm:*":
- version: 1.15.9
- resolution: "@types/serve-static@npm:1.15.9"
- dependencies:
- "@types/http-errors": "npm:*"
- "@types/node": "npm:*"
- "@types/send": "npm:<1"
- checksum: 10/5b7a24c1e5fb474ae539165451dae4e3e85e104d9935562de9c5f0c6b2557395aefe6fdbaf094178ea94d58fd9f26e9605a51ccaf101655fcda18971840c06ad
- languageName: node
- linkType: hard
-
-"@types/triple-beam@npm:^1.3.2":
- version: 1.3.5
- resolution: "@types/triple-beam@npm:1.3.5"
- checksum: 10/519b6a1b30d4571965c9706ad5400a200b94e4050feca3e7856e3ea7ac00ec9903e32e9a10e2762d0f7e472d5d03e5f4b29c16c0bd8c1f77c8876c683b2231f1
- languageName: node
- linkType: hard
-
-"@types/unist@npm:*":
- version: 3.0.3
- resolution: "@types/unist@npm:3.0.3"
- checksum: 10/96e6453da9e075aaef1dc22482463898198acdc1eeb99b465e65e34303e2ec1e3b1ed4469a9118275ec284dc98019f63c3f5d49422f0e4ac707e5ab90fb3b71a
- languageName: node
- linkType: hard
-
-"@types/vorpal@npm:^1.12.0":
- version: 1.12.8
- resolution: "@types/vorpal@npm:1.12.8"
- checksum: 10/69822ba025aa179af3bb0a1b8f18d73ba5db92c499a66e96f241cfe2fb97e8072478ef435a107f946dd4fd6d7d2533d637f9edc9c33256897d5834d5ce27ac67
- languageName: node
- linkType: hard
-
-"@types/yargs-parser@npm:*":
- version: 21.0.3
- resolution: "@types/yargs-parser@npm:21.0.3"
- checksum: 10/a794eb750e8ebc6273a51b12a0002de41343ffe46befef460bdbb57262d187fdf608bc6615b7b11c462c63c3ceb70abe2564c8dd8ee0f7628f38a314f74a9b9b
- languageName: node
- linkType: hard
-
-"@types/yargs@npm:^17.0.0":
- version: 17.0.33
- resolution: "@types/yargs@npm:17.0.33"
- dependencies:
- "@types/yargs-parser": "npm:*"
- checksum: 10/16f6681bf4d99fb671bf56029141ed01db2862e3db9df7fc92d8bea494359ac96a1b4b1c35a836d1e95e665fb18ad753ab2015fc0db663454e8fd4e5d5e2ef91
- languageName: node
- linkType: hard
-
-"abbrev@npm:^3.0.0":
- version: 3.0.1
- resolution: "abbrev@npm:3.0.1"
- checksum: 10/ebd2c149dda6f543b66ce3779ea612151bb3aa9d0824f169773ee9876f1ca5a4e0adbcccc7eed048c04da7998e1825e2aa76fcca92d9e67dea50ac2b0a58dc2e
- languageName: node
- linkType: hard
-
-"accepts@npm:^1.3.8":
- version: 1.3.8
- resolution: "accepts@npm:1.3.8"
- dependencies:
- mime-types: "npm:~2.1.34"
- negotiator: "npm:0.6.3"
- checksum: 10/67eaaa90e2917c58418e7a9b89392002d2b1ccd69bcca4799135d0c632f3b082f23f4ae4ddeedbced5aa59bcc7bdf4699c69ebed4593696c922462b7bc5744d6
- languageName: node
- linkType: hard
-
-"acorn-walk@npm:^8.1.1":
- version: 8.3.4
- resolution: "acorn-walk@npm:8.3.4"
- dependencies:
- acorn: "npm:^8.11.0"
- checksum: 10/871386764e1451c637bb8ab9f76f4995d408057e9909be6fb5ad68537ae3375d85e6a6f170b98989f44ab3ff6c74ad120bc2779a3d577606e7a0cd2b4efcaf77
- languageName: node
- linkType: hard
-
-"acorn@npm:^8.11.0, acorn@npm:^8.4.1":
- version: 8.15.0
- resolution: "acorn@npm:8.15.0"
- bin:
- acorn: bin/acorn
- checksum: 10/77f2de5051a631cf1729c090e5759148459cdb76b5f5c70f890503d629cf5052357b0ce783c0f976dd8a93c5150f59f6d18df1def3f502396a20f81282482fa4
- languageName: node
- linkType: hard
-
-"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
- version: 7.1.4
- resolution: "agent-base@npm:7.1.4"
- checksum: 10/79bef167247789f955aaba113bae74bf64aa1e1acca4b1d6bb444bdf91d82c3e07e9451ef6a6e2e35e8f71a6f97ce33e3d855a5328eb9fad1bc3cc4cfd031ed8
- languageName: node
- linkType: hard
-
-"ansi-escapes@npm:^1.0.0, ansi-escapes@npm:^1.1.0":
- version: 1.4.0
- resolution: "ansi-escapes@npm:1.4.0"
- checksum: 10/287f18ea70cde710dbb83b6b6c4e1d62fcb962b951a601d976df69478a4ebdff6305691e3befb9053d740060544929732b8bade7a9781611dcd2b997e6bda3d6
- languageName: node
- linkType: hard
-
-"ansi-regex@npm:^2.0.0":
- version: 2.1.1
- resolution: "ansi-regex@npm:2.1.1"
- checksum: 10/190abd03e4ff86794f338a31795d262c1dfe8c91f7e01d04f13f646f1dcb16c5800818f886047876f1272f065570ab86b24b99089f8b68a0e11ff19aed4ca8f1
- languageName: node
- linkType: hard
-
-"ansi-regex@npm:^5.0.1":
- version: 5.0.1
- resolution: "ansi-regex@npm:5.0.1"
- checksum: 10/2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b
- languageName: node
- linkType: hard
-
-"ansi-regex@npm:^6.0.1":
- version: 6.2.2
- resolution: "ansi-regex@npm:6.2.2"
- checksum: 10/9b17ce2c6daecc75bcd5966b9ad672c23b184dc3ed9bf3c98a0702f0d2f736c15c10d461913568f2cf527a5e64291c7473358885dd493305c84a1cfed66ba94f
- languageName: node
- linkType: hard
-
-"ansi-styles@npm:^2.2.1":
- version: 2.2.1
- resolution: "ansi-styles@npm:2.2.1"
- checksum: 10/ebc0e00381f2a29000d1dac8466a640ce11943cef3bda3cd0020dc042e31e1058ab59bf6169cd794a54c3a7338a61ebc404b7c91e004092dd20e028c432c9c2c
- languageName: node
- linkType: hard
-
-"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0":
- version: 4.3.0
- resolution: "ansi-styles@npm:4.3.0"
- dependencies:
- color-convert: "npm:^2.0.1"
- checksum: 10/b4494dfbfc7e4591b4711a396bd27e540f8153914123dccb4cdbbcb514015ada63a3809f362b9d8d4f6b17a706f1d7bea3c6f974b15fa5ae76b5b502070889ff
- languageName: node
- linkType: hard
-
-"ansi-styles@npm:^6.1.0":
- version: 6.2.3
- resolution: "ansi-styles@npm:6.2.3"
- checksum: 10/c49dad7639f3e48859bd51824c93b9eb0db628afc243c51c3dd2410c4a15ede1a83881c6c7341aa2b159c4f90c11befb38f2ba848c07c66c9f9de4bcd7cb9f30
- languageName: node
- linkType: hard
-
-"anymatch@npm:~3.1.2":
- version: 3.1.3
- resolution: "anymatch@npm:3.1.3"
- dependencies:
- normalize-path: "npm:^3.0.0"
- picomatch: "npm:^2.0.4"
- checksum: 10/3e044fd6d1d26545f235a9fe4d7a534e2029d8e59fa7fd9f2a6eb21230f6b5380ea1eaf55136e60cbf8e613544b3b766e7a6fa2102e2a3a117505466e3025dc2
- languageName: node
- linkType: hard
-
-"arg@npm:^4.1.0":
- version: 4.1.3
- resolution: "arg@npm:4.1.3"
- checksum: 10/969b491082f20cad166649fa4d2073ea9e974a4e5ac36247ca23d2e5a8b3cb12d60e9ff70a8acfe26d76566c71fd351ee5e6a9a6595157eb36f92b1fd64e1599
- languageName: node
- linkType: hard
-
-"argparse@npm:^2.0.1":
- version: 2.0.1
- resolution: "argparse@npm:2.0.1"
- checksum: 10/18640244e641a417ec75a9bd38b0b2b6b95af5199aa241b131d4b2fb206f334d7ecc600bd194861610a5579084978bfcbb02baa399dbe442d56d0ae5e60dbaef
- languageName: node
- linkType: hard
-
-"asap@npm:^2.0.0":
- version: 2.0.6
- resolution: "asap@npm:2.0.6"
- checksum: 10/b244c0458c571945e4b3be0b14eb001bea5596f9868cc50cc711dc03d58a7e953517d3f0dad81ccde3ff37d1f074701fa76a6f07d41aaa992d7204a37b915dda
- languageName: node
- linkType: hard
-
-"async-function@npm:^1.0.0":
- version: 1.0.0
- resolution: "async-function@npm:1.0.0"
- checksum: 10/1a09379937d846f0ce7614e75071c12826945d4e417db634156bf0e4673c495989302f52186dfa9767a1d9181794554717badd193ca2bbab046ef1da741d8efd
- languageName: node
- linkType: hard
-
-"async-generator-function@npm:^1.0.0":
- version: 1.0.0
- resolution: "async-generator-function@npm:1.0.0"
- checksum: 10/3d49e7acbeee9e84537f4cb0e0f91893df8eba976759875ae8ee9e3d3c82f6ecdebdb347c2fad9926b92596d93cdfc78ecc988bcdf407e40433e8e8e6fe5d78e
- languageName: node
- linkType: hard
-
-"babel-polyfill@npm:^6.3.14":
- version: 6.26.0
- resolution: "babel-polyfill@npm:6.26.0"
- dependencies:
- babel-runtime: "npm:^6.26.0"
- core-js: "npm:^2.5.0"
- regenerator-runtime: "npm:^0.10.5"
- checksum: 10/2abfe4bf4af39c7b0c42af8ccce14897aefbde6547a227e36f4f12ba5795e8603d2964cc72ceb59086b5a69fafcb00b0deda5c1055e373c3bef76dcc517d6d0d
- languageName: node
- linkType: hard
-
-"babel-runtime@npm:^6.26.0":
- version: 6.26.0
- resolution: "babel-runtime@npm:6.26.0"
- dependencies:
- core-js: "npm:^2.4.0"
- regenerator-runtime: "npm:^0.11.0"
- checksum: 10/2cdf0f083b9598a43cdb11cbf1e7060584079a9a2230f06aec997ba81e887ef17fdcb5ad813a484ee099e06d2de0cea832bdd3011c06325acb284284c754ee8f
- languageName: node
- linkType: hard
-
-"balanced-match@npm:^1.0.0":
- version: 1.0.2
- resolution: "balanced-match@npm:1.0.2"
- checksum: 10/9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65
- languageName: node
- linkType: hard
-
-"binary-extensions@npm:^2.0.0":
- version: 2.3.0
- resolution: "binary-extensions@npm:2.3.0"
- checksum: 10/bcad01494e8a9283abf18c1b967af65ee79b0c6a9e6fcfafebfe91dbe6e0fc7272bafb73389e198b310516ae04f7ad17d79aacf6cb4c0d5d5202a7e2e52c7d98
- languageName: node
- linkType: hard
-
-"brace-expansion@npm:^1.1.7":
- version: 1.1.12
- resolution: "brace-expansion@npm:1.1.12"
- dependencies:
- balanced-match: "npm:^1.0.0"
- concat-map: "npm:0.0.1"
- checksum: 10/12cb6d6310629e3048cadb003e1aca4d8c9bb5c67c3c321bafdd7e7a50155de081f78ea3e0ed92ecc75a9015e784f301efc8132383132f4f7904ad1ac529c562
- languageName: node
- linkType: hard
-
-"brace-expansion@npm:^2.0.1":
- version: 2.0.2
- resolution: "brace-expansion@npm:2.0.2"
- dependencies:
- balanced-match: "npm:^1.0.0"
- checksum: 10/01dff195e3646bc4b0d27b63d9bab84d2ebc06121ff5013ad6e5356daa5a9d6b60fa26cf73c74797f2dc3fbec112af13578d51f75228c1112b26c790a87b0488
- languageName: node
- linkType: hard
-
-"braces@npm:~3.0.2":
- version: 3.0.3
- resolution: "braces@npm:3.0.3"
- dependencies:
- fill-range: "npm:^7.1.1"
- checksum: 10/fad11a0d4697a27162840b02b1fad249c1683cbc510cd5bf1a471f2f8085c046d41094308c577a50a03a579dd99d5a6b3724c4b5e8b14df2c4443844cfcda2c6
- languageName: node
- linkType: hard
-
-"builtins@npm:^5.0.0":
- version: 5.1.0
- resolution: "builtins@npm:5.1.0"
- dependencies:
- semver: "npm:^7.0.0"
- checksum: 10/60aa9969f69656bf6eab82cd74b23ab805f112ae46a54b912bccc1533875760f2d2ce95e0a7d13144e35ada9f0386f17ed4961908bc9434b5a5e21375b1902b2
- languageName: node
- linkType: hard
-
-"bytes@npm:3.1.2":
- version: 3.1.2
- resolution: "bytes@npm:3.1.2"
- checksum: 10/a10abf2ba70c784471d6b4f58778c0beeb2b5d405148e66affa91f23a9f13d07603d0a0354667310ae1d6dc141474ffd44e2a074be0f6e2254edb8fc21445388
- languageName: node
- linkType: hard
-
-"cacache@npm:^19.0.1":
- version: 19.0.1
- resolution: "cacache@npm:19.0.1"
- dependencies:
- "@npmcli/fs": "npm:^4.0.0"
- fs-minipass: "npm:^3.0.0"
- glob: "npm:^10.2.2"
- lru-cache: "npm:^10.0.1"
- minipass: "npm:^7.0.3"
- minipass-collect: "npm:^2.0.1"
- minipass-flush: "npm:^1.0.5"
- minipass-pipeline: "npm:^1.2.4"
- p-map: "npm:^7.0.2"
- ssri: "npm:^12.0.0"
- tar: "npm:^7.4.3"
- unique-filename: "npm:^4.0.0"
- checksum: 10/ea026b27b13656330c2bbaa462a88181dcaa0435c1c2e705db89b31d9bdf7126049d6d0445ba746dca21454a0cfdf1d6f47fd39d34c8c8435296b30bc5738a13
- languageName: node
- linkType: hard
-
-"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2":
- version: 1.0.2
- resolution: "call-bind-apply-helpers@npm:1.0.2"
- dependencies:
- es-errors: "npm:^1.3.0"
- function-bind: "npm:^1.1.2"
- checksum: 10/00482c1f6aa7cfb30fb1dbeb13873edf81cfac7c29ed67a5957d60635a56b2a4a480f1016ddbdb3395cc37900d46037fb965043a51c5c789ffeab4fc535d18b5
- languageName: node
- linkType: hard
-
-"call-bound@npm:^1.0.2":
- version: 1.0.4
- resolution: "call-bound@npm:1.0.4"
- dependencies:
- call-bind-apply-helpers: "npm:^1.0.2"
- get-intrinsic: "npm:^1.3.0"
- checksum: 10/ef2b96e126ec0e58a7ff694db43f4d0d44f80e641370c21549ed911fecbdbc2df3ebc9bddad918d6bbdefeafb60bb3337902006d5176d72bcd2da74820991af7
- languageName: node
- linkType: hard
-
-"chalk@npm:^1.0.0, chalk@npm:^1.1.0":
- version: 1.1.3
- resolution: "chalk@npm:1.1.3"
- dependencies:
- ansi-styles: "npm:^2.2.1"
- escape-string-regexp: "npm:^1.0.2"
- has-ansi: "npm:^2.0.0"
- strip-ansi: "npm:^3.0.0"
- supports-color: "npm:^2.0.0"
- checksum: 10/abcf10da02afde04cc615f06c4bdb3ffc70d2bfbf37e0df03bb88b7459a9411dab4d01210745b773abc936031530a20355f1facc4bee1bbf08613d8fdcfb3aeb
- languageName: node
- linkType: hard
-
-"chalk@npm:^4.1.1":
- version: 4.1.2
- resolution: "chalk@npm:4.1.2"
- dependencies:
- ansi-styles: "npm:^4.1.0"
- supports-color: "npm:^7.1.0"
- checksum: 10/cb3f3e594913d63b1814d7ca7c9bafbf895f75fbf93b92991980610dfd7b48500af4e3a5d4e3a8f337990a96b168d7eb84ee55efdce965e2ee8efc20f8c8f139
- languageName: node
- linkType: hard
-
-"chokidar@npm:^3.5.2":
- version: 3.6.0
- resolution: "chokidar@npm:3.6.0"
- dependencies:
- anymatch: "npm:~3.1.2"
- braces: "npm:~3.0.2"
- fsevents: "npm:~2.3.2"
- glob-parent: "npm:~5.1.2"
- is-binary-path: "npm:~2.1.0"
- is-glob: "npm:~4.0.1"
- normalize-path: "npm:~3.0.0"
- readdirp: "npm:~3.6.0"
- dependenciesMeta:
- fsevents:
- optional: true
- checksum: 10/c327fb07704443f8d15f7b4a7ce93b2f0bc0e6cea07ec28a7570aa22cd51fcf0379df589403976ea956c369f25aa82d84561947e227cd925902e1751371658df
- languageName: node
- linkType: hard
-
-"chownr@npm:^3.0.0":
- version: 3.0.0
- resolution: "chownr@npm:3.0.0"
- checksum: 10/b63cb1f73d171d140a2ed8154ee6566c8ab775d3196b0e03a2a94b5f6a0ce7777ee5685ca56849403c8d17bd457a6540672f9a60696a6137c7a409097495b82c
- languageName: node
- linkType: hard
-
-"cli-cursor@npm:^1.0.1, cli-cursor@npm:^1.0.2":
- version: 1.0.2
- resolution: "cli-cursor@npm:1.0.2"
- dependencies:
- restore-cursor: "npm:^1.0.1"
- checksum: 10/e3b4400d5e925ed11c7596f82e80e170693f69ac6f0f21da2a400043c37548dd780f985a1a5ef1ffb038e36fc6711d1d4f066b104eed851ae76e34bd883cf2bf
- languageName: node
- linkType: hard
-
-"cli-width@npm:^1.0.1":
- version: 1.1.1
- resolution: "cli-width@npm:1.1.1"
- checksum: 10/b85b74a9b6ec288a9bb594dbac7358374a07c8038544f2aebb0a248073ee485ab68c7eb11ebf162031aca4f8cafac931eefa9476cbf321fe5ee961fc6f3497a7
- languageName: node
- linkType: hard
-
-"co-body@npm:^6.1.0":
- version: 6.2.0
- resolution: "co-body@npm:6.2.0"
- dependencies:
- "@hapi/bourne": "npm:^3.0.0"
- inflation: "npm:^2.0.0"
- qs: "npm:^6.5.2"
- raw-body: "npm:^2.3.3"
- type-is: "npm:^1.6.16"
- checksum: 10/644761ad8abbcbc15f0a76634b17abda928fec01aa7bfdee23f4e65c0d49c6ea63738d1ed7fca1f92a52bd76cd08f8031d788a65ab00842744d50f03536c7b36
- languageName: node
- linkType: hard
-
-"code-point-at@npm:^1.0.0":
- version: 1.1.0
- resolution: "code-point-at@npm:1.1.0"
- checksum: 10/17d5666611f9b16d64fdf48176d9b7fb1c7d1c1607a189f7e600040a11a6616982876af148230336adb7d8fe728a559f743a4e29db3747e3b1a32fa7f4529681
- languageName: node
- linkType: hard
-
-"color-convert@npm:^2.0.1":
- version: 2.0.1
- resolution: "color-convert@npm:2.0.1"
- dependencies:
- color-name: "npm:~1.1.4"
- checksum: 10/fa00c91b4332b294de06b443923246bccebe9fab1b253f7fe1772d37b06a2269b4039a85e309abe1fe11b267b11c08d1d0473fda3badd6167f57313af2887a64
- languageName: node
- linkType: hard
-
-"color-name@npm:~1.1.4":
- version: 1.1.4
- resolution: "color-name@npm:1.1.4"
- checksum: 10/b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610
- languageName: node
- linkType: hard
-
-"compare-versions@npm:^3.6.0":
- version: 3.6.0
- resolution: "compare-versions@npm:3.6.0"
- checksum: 10/7492a50cdaa2c27f5254eee7c4b38856e1c164991bab3d98d7fd067fe4b570d47123ecb92523b78338be86aa221668fd3868bfe8caa5587dc3ebbe1a03d52b5d
- languageName: node
- linkType: hard
-
-"concat-map@npm:0.0.1":
- version: 0.0.1
- resolution: "concat-map@npm:0.0.1"
- checksum: 10/9680699c8e2b3af0ae22592cb764acaf973f292a7b71b8a06720233011853a58e256c89216a10cbe889727532fd77f8bcd49a760cedfde271b8e006c20e079f2
- languageName: node
- linkType: hard
-
-"content-disposition@npm:~0.5.4":
- version: 0.5.4
- resolution: "content-disposition@npm:0.5.4"
- dependencies:
- safe-buffer: "npm:5.2.1"
- checksum: 10/b7f4ce176e324f19324be69b05bf6f6e411160ac94bc523b782248129eb1ef3be006f6cff431aaea5e337fe5d176ce8830b8c2a1b721626ead8933f0cbe78720
- languageName: node
- linkType: hard
-
-"content-type@npm:^1.0.5":
- version: 1.0.5
- resolution: "content-type@npm:1.0.5"
- checksum: 10/585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662
- languageName: node
- linkType: hard
-
-"cookies@npm:~0.9.1":
- version: 0.9.1
- resolution: "cookies@npm:0.9.1"
- dependencies:
- depd: "npm:~2.0.0"
- keygrip: "npm:~1.1.0"
- checksum: 10/4816461a38d907b20f3fb7a2bc4741fe580e7a195f3e248ef7025cb3be56a07638a0f4e72553a5f535554ca30172c8a3245c63ac72c9737cec034e9a47773392
- languageName: node
- linkType: hard
-
-"core-js@npm:^2.4.0, core-js@npm:^2.5.0":
- version: 2.6.12
- resolution: "core-js@npm:2.6.12"
- checksum: 10/7c624eb00a59c74c769d5d80f751f3bf1fc6201205b6562f27286ad5e00bbca1483f2f7eb0c2854b86f526ef5c7dc958b45f2ff536f8a31b8e9cb1a13a96efca
- languageName: node
- linkType: hard
-
-"create-require@npm:^1.1.0":
- version: 1.1.1
- resolution: "create-require@npm:1.1.1"
- checksum: 10/a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff
- languageName: node
- linkType: hard
-
-"cross-spawn@npm:^7.0.6":
- version: 7.0.6
- resolution: "cross-spawn@npm:7.0.6"
- dependencies:
- path-key: "npm:^3.1.0"
- shebang-command: "npm:^2.0.0"
- which: "npm:^2.0.1"
- checksum: 10/0d52657d7ae36eb130999dffff1168ec348687b48dd38e2ff59992ed916c88d328cf1d07ff4a4a10bc78de5e1c23f04b306d569e42f7a2293915c081e4dfee86
- languageName: node
- linkType: hard
-
-"debug@npm:4, debug@npm:^4.3.4, debug@npm:^4.4.1":
- version: 4.4.3
- resolution: "debug@npm:4.4.3"
- dependencies:
- ms: "npm:^2.1.3"
- peerDependenciesMeta:
- supports-color:
- optional: true
- checksum: 10/9ada3434ea2993800bd9a1e320bd4aa7af69659fb51cca685d390949434bc0a8873c21ed7c9b852af6f2455a55c6d050aa3937d52b3c69f796dab666f762acad
- languageName: node
- linkType: hard
-
-"deep-equal@npm:~1.0.1":
- version: 1.0.1
- resolution: "deep-equal@npm:1.0.1"
- checksum: 10/cbecc071afb2891334ced9e9de5834889b9a9992ae8d8369b7eb74c513529eb6d1f6c04d4e2b5f34d8386f7816cd7a6cda45edff847695faea45e43c23973f45
- languageName: node
- linkType: hard
-
-"delegates@npm:^1.0.0":
- version: 1.0.0
- resolution: "delegates@npm:1.0.0"
- checksum: 10/a51744d9b53c164ba9c0492471a1a2ffa0b6727451bdc89e31627fdf4adda9d51277cfcbfb20f0a6f08ccb3c436f341df3e92631a3440226d93a8971724771fd
- languageName: node
- linkType: hard
-
-"depd@npm:2.0.0, depd@npm:~2.0.0":
- version: 2.0.0
- resolution: "depd@npm:2.0.0"
- checksum: 10/c0c8ff36079ce5ada64f46cc9d6fd47ebcf38241105b6e0c98f412e8ad91f084bcf906ff644cc3a4bd876ca27a62accb8b0fff72ea6ed1a414b89d8506f4a5ca
- languageName: node
- linkType: hard
-
-"depd@npm:~1.1.2":
- version: 1.1.2
- resolution: "depd@npm:1.1.2"
- checksum: 10/2ed6966fc14463a9e85451db330ab8ba041efed0b9a1a472dbfc6fbf2f82bab66491915f996b25d8517dddc36c8c74e24c30879b34877f3c4410733444a51d1d
- languageName: node
- linkType: hard
-
-"destroy@npm:^1.2.0":
- version: 1.2.0
- resolution: "destroy@npm:1.2.0"
- checksum: 10/0acb300b7478a08b92d810ab229d5afe0d2f4399272045ab22affa0d99dbaf12637659411530a6fcd597a9bdac718fc94373a61a95b4651bbc7b83684a565e38
- languageName: node
- linkType: hard
-
-"dezalgo@npm:^1.0.4":
- version: 1.0.4
- resolution: "dezalgo@npm:1.0.4"
- dependencies:
- asap: "npm:^2.0.0"
- wrappy: "npm:1"
- checksum: 10/895389c6aead740d2ab5da4d3466d20fa30f738010a4d3f4dcccc9fc645ca31c9d10b7e1804ae489b1eb02c7986f9f1f34ba132d409b043082a86d9a4e745624
- languageName: node
- linkType: hard
-
-"diff@npm:^4.0.1":
- version: 4.0.2
- resolution: "diff@npm:4.0.2"
- checksum: 10/ec09ec2101934ca5966355a229d77afcad5911c92e2a77413efda5455636c4cf2ce84057e2d7715227a2eeeda04255b849bd3ae3a4dd22eb22e86e76456df069
- languageName: node
- linkType: hard
-
-"dotenv@npm:^10.0.0":
- version: 10.0.0
- resolution: "dotenv@npm:10.0.0"
- checksum: 10/55f701ae213e3afe3f4232fae5edfb6e0c49f061a363ff9f1c5a0c2bf3fb990a6e49aeada11b2a116efb5fdc3bc3f1ef55ab330be43033410b267f7c0809a9dc
- languageName: node
- linkType: hard
-
-"dunder-proto@npm:^1.0.1":
- version: 1.0.1
- resolution: "dunder-proto@npm:1.0.1"
- dependencies:
- call-bind-apply-helpers: "npm:^1.0.1"
- es-errors: "npm:^1.3.0"
- gopd: "npm:^1.2.0"
- checksum: 10/5add88a3d68d42d6e6130a0cac450b7c2edbe73364bbd2fc334564418569bea97c6943a8fcd70e27130bf32afc236f30982fc4905039b703f23e9e0433c29934
- languageName: node
- linkType: hard
-
-"eastasianwidth@npm:^0.2.0":
- version: 0.2.0
- resolution: "eastasianwidth@npm:0.2.0"
- checksum: 10/9b1d3e1baefeaf7d70799db8774149cef33b97183a6addceeba0cf6b85ba23ee2686f302f14482006df32df75d32b17c509c143a3689627929e4a8efaf483952
- languageName: node
- linkType: hard
-
-"ee-first@npm:1.1.1":
- version: 1.1.1
- resolution: "ee-first@npm:1.1.1"
- checksum: 10/1b4cac778d64ce3b582a7e26b218afe07e207a0f9bfe13cc7395a6d307849cfe361e65033c3251e00c27dd060cab43014c2d6b2647676135e18b77d2d05b3f4f
- languageName: node
- linkType: hard
-
-"emoji-regex@npm:^8.0.0":
- version: 8.0.0
- resolution: "emoji-regex@npm:8.0.0"
- checksum: 10/c72d67a6821be15ec11997877c437491c313d924306b8da5d87d2a2bcc2cec9903cb5b04ee1a088460501d8e5b44f10df82fdc93c444101a7610b80c8b6938e1
- languageName: node
- linkType: hard
-
-"emoji-regex@npm:^9.2.2":
- version: 9.2.2
- resolution: "emoji-regex@npm:9.2.2"
- checksum: 10/915acf859cea7131dac1b2b5c9c8e35c4849e325a1d114c30adb8cd615970f6dca0e27f64f3a4949d7d6ed86ecd79a1c5c63f02e697513cddd7b5835c90948b8
- languageName: node
- linkType: hard
-
-"encodeurl@npm:^2.0.0":
- version: 2.0.0
- resolution: "encodeurl@npm:2.0.0"
- checksum: 10/abf5cd51b78082cf8af7be6785813c33b6df2068ce5191a40ca8b1afe6a86f9230af9a9ce694a5ce4665955e5c1120871826df9c128a642e09c58d592e2807fe
- languageName: node
- linkType: hard
-
-"encoding@npm:^0.1.13":
- version: 0.1.13
- resolution: "encoding@npm:0.1.13"
- dependencies:
- iconv-lite: "npm:^0.6.2"
- checksum: 10/bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f
- languageName: node
- linkType: hard
-
-"entities@npm:^4.4.0":
- version: 4.5.0
- resolution: "entities@npm:4.5.0"
- checksum: 10/ede2a35c9bce1aeccd055a1b445d41c75a14a2bb1cd22e242f20cf04d236cdcd7f9c859eb83f76885327bfae0c25bf03303665ee1ce3d47c5927b98b0e3e3d48
- languageName: node
- linkType: hard
-
-"env-paths@npm:^2.2.0":
- version: 2.2.1
- resolution: "env-paths@npm:2.2.1"
- checksum: 10/65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e
- languageName: node
- linkType: hard
-
-"err-code@npm:^2.0.2":
- version: 2.0.3
- resolution: "err-code@npm:2.0.3"
- checksum: 10/1d20d825cdcce8d811bfbe86340f4755c02655a7feb2f13f8c880566d9d72a3f6c92c192a6867632e490d6da67b678271f46e01044996a6443e870331100dfdd
- languageName: node
- linkType: hard
-
-"es-define-property@npm:^1.0.1":
- version: 1.0.1
- resolution: "es-define-property@npm:1.0.1"
- checksum: 10/f8dc9e660d90919f11084db0a893128f3592b781ce967e4fccfb8f3106cb83e400a4032c559184ec52ee1dbd4b01e7776c7cd0b3327b1961b1a4a7008920fe78
- languageName: node
- linkType: hard
-
-"es-errors@npm:^1.3.0":
- version: 1.3.0
- resolution: "es-errors@npm:1.3.0"
- checksum: 10/96e65d640156f91b707517e8cdc454dd7d47c32833aa3e85d79f24f9eb7ea85f39b63e36216ef0114996581969b59fe609a94e30316b08f5f4df1d44134cf8d5
- languageName: node
- linkType: hard
-
-"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1":
- version: 1.1.1
- resolution: "es-object-atoms@npm:1.1.1"
- dependencies:
- es-errors: "npm:^1.3.0"
- checksum: 10/54fe77de288451dae51c37bfbfe3ec86732dc3778f98f3eb3bdb4bf48063b2c0b8f9c93542656986149d08aa5be3204286e2276053d19582b76753f1a2728867
- languageName: node
- linkType: hard
-
-"escape-html@npm:^1.0.3":
- version: 1.0.3
- resolution: "escape-html@npm:1.0.3"
- checksum: 10/6213ca9ae00d0ab8bccb6d8d4e0a98e76237b2410302cf7df70aaa6591d509a2a37ce8998008cbecae8fc8ffaadf3fb0229535e6a145f3ce0b211d060decbb24
- languageName: node
- linkType: hard
-
-"escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5":
- version: 1.0.5
- resolution: "escape-string-regexp@npm:1.0.5"
- checksum: 10/6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410
- languageName: node
- linkType: hard
-
-"exit-hook@npm:^1.0.0":
- version: 1.1.1
- resolution: "exit-hook@npm:1.1.1"
- checksum: 10/1b4f16da7c202cd336ca07acb052922639182b4e2f1ad4007ed481bb774ce93469f505dec1371d9cd580ac54146a9fd260f053b0e4a48fa87c49fa3dc4a3f144
- languageName: node
- linkType: hard
-
-"exponential-backoff@npm:^3.1.1":
- version: 3.1.3
- resolution: "exponential-backoff@npm:3.1.3"
- checksum: 10/ca25962b4bbab943b7c4ed0b5228e263833a5063c65e1cdeac4be9afad350aae5466e8e619b5051f4f8d37b2144a2d6e8fcc771b6cc82934f7dade2f964f652c
- languageName: node
- linkType: hard
-
-"fdir@npm:^6.5.0":
- version: 6.5.0
- resolution: "fdir@npm:6.5.0"
- peerDependencies:
- picomatch: ^3 || ^4
- peerDependenciesMeta:
- picomatch:
- optional: true
- checksum: 10/14ca1c9f0a0e8f4f2e9bf4e8551065a164a09545dae548c12a18d238b72e51e5a7b39bd8e5494b56463a0877672d0a6c1ef62c6fa0677db1b0c847773be939b1
- languageName: node
- linkType: hard
-
-"figures@npm:^1.3.5":
- version: 1.7.0
- resolution: "figures@npm:1.7.0"
- dependencies:
- escape-string-regexp: "npm:^1.0.5"
- object-assign: "npm:^4.1.0"
- checksum: 10/3a815f8a3b488f818e661694112b4546ddff799aa6a07c864c46dadff923af74021f84d42ded402432a98c3208acebf2d096f3a7cc3d1a7b19a2cdc9cbcaea2e
- languageName: node
- linkType: hard
-
-"fill-range@npm:^7.1.1":
- version: 7.1.1
- resolution: "fill-range@npm:7.1.1"
- dependencies:
- to-regex-range: "npm:^5.0.1"
- checksum: 10/a7095cb39e5bc32fada2aa7c7249d3f6b01bd1ce461a61b0adabacccabd9198500c6fb1f68a7c851a657e273fce2233ba869638897f3d7ed2e87a2d89b4436ea
- languageName: node
- linkType: hard
-
-"foreground-child@npm:^3.1.0":
- version: 3.3.1
- resolution: "foreground-child@npm:3.3.1"
- dependencies:
- cross-spawn: "npm:^7.0.6"
- signal-exit: "npm:^4.0.1"
- checksum: 10/427b33f997a98073c0424e5c07169264a62cda806d8d2ded159b5b903fdfc8f0a1457e06b5fc35506497acb3f1e353f025edee796300209ac6231e80edece835
- languageName: node
- linkType: hard
-
-"formidable@npm:^2.0.1":
- version: 2.1.5
- resolution: "formidable@npm:2.1.5"
- dependencies:
- "@paralleldrive/cuid2": "npm:^2.2.2"
- dezalgo: "npm:^1.0.4"
- once: "npm:^1.4.0"
- qs: "npm:^6.11.0"
- checksum: 10/ee96de12e91d63fe86479ffe5bf59004bb3f43e00ce7ccecd1b1ff10b5d1a89a19b1ede727e1fe57ef596c377b9f9300212a5f7bab14fd28f3c4ffe12dbb4cc7
- languageName: node
- linkType: hard
-
-"fresh@npm:~0.5.2":
- version: 0.5.2
- resolution: "fresh@npm:0.5.2"
- checksum: 10/64c88e489b5d08e2f29664eb3c79c705ff9a8eb15d3e597198ef76546d4ade295897a44abb0abd2700e7ef784b2e3cbf1161e4fbf16f59129193fd1030d16da1
- languageName: node
- linkType: hard
-
-"fromentries@npm:^1.3.2":
- version: 1.3.2
- resolution: "fromentries@npm:1.3.2"
- checksum: 10/10d6e07d289db102c0c1eaf5c3e3fa55ddd6b50033d7de16d99a7cd89f1e1a302dfadb26457031f9bb5d2ed95a179aaf0396092dde5abcae06e8a2f0476826be
- languageName: node
- linkType: hard
-
-"fs-minipass@npm:^3.0.0":
- version: 3.0.3
- resolution: "fs-minipass@npm:3.0.3"
- dependencies:
- minipass: "npm:^7.0.3"
- checksum: 10/af143246cf6884fe26fa281621d45cfe111d34b30535a475bfa38dafe343dadb466c047a924ffc7d6b7b18265df4110224ce3803806dbb07173bf2087b648d7f
- languageName: node
- linkType: hard
-
-"fsevents@npm:~2.3.2":
- version: 2.3.3
- resolution: "fsevents@npm:2.3.3"
- dependencies:
- node-gyp: "npm:latest"
- checksum: 10/4c1ade961ded57cdbfbb5cac5106ec17bc8bccd62e16343c569a0ceeca83b9dfef87550b4dc5cbb89642da412b20c5071f304c8c464b80415446e8e155a038c0
- conditions: os=darwin
- languageName: node
- linkType: hard
-
-"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin":
- version: 2.3.3
- resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"
- dependencies:
- node-gyp: "npm:latest"
- conditions: os=darwin
- languageName: node
- linkType: hard
-
-"function-bind@npm:^1.1.2":
- version: 1.1.2
- resolution: "function-bind@npm:1.1.2"
- checksum: 10/185e20d20f10c8d661d59aac0f3b63b31132d492e1b11fcc2a93cb2c47257ebaee7407c38513efd2b35cafdf972d9beb2ea4593c1e0f3bf8f2744836928d7454
- languageName: node
- linkType: hard
-
-"generator-function@npm:^2.0.0":
- version: 2.0.1
- resolution: "generator-function@npm:2.0.1"
- checksum: 10/eb7e7eb896c5433f3d40982b2ccacdb3dd990dd3499f14040e002b5d54572476513be8a2e6f9609f6e41ab29f2c4469307611ddbfc37ff4e46b765c326663805
- languageName: node
- linkType: hard
-
-"get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.3.0":
- version: 1.3.1
- resolution: "get-intrinsic@npm:1.3.1"
- dependencies:
- async-function: "npm:^1.0.0"
- async-generator-function: "npm:^1.0.0"
- call-bind-apply-helpers: "npm:^1.0.2"
- es-define-property: "npm:^1.0.1"
- es-errors: "npm:^1.3.0"
- es-object-atoms: "npm:^1.1.1"
- function-bind: "npm:^1.1.2"
- generator-function: "npm:^2.0.0"
- get-proto: "npm:^1.0.1"
- gopd: "npm:^1.2.0"
- has-symbols: "npm:^1.1.0"
- hasown: "npm:^2.0.2"
- math-intrinsics: "npm:^1.1.0"
- checksum: 10/bb579dda84caa4a3a41611bdd483dade7f00f246f2a7992eb143c5861155290df3fdb48a8406efa3dfb0b434e2c8fafa4eebd469e409d0439247f85fc3fa2cc1
- languageName: node
- linkType: hard
-
-"get-proto@npm:^1.0.1":
- version: 1.0.1
- resolution: "get-proto@npm:1.0.1"
- dependencies:
- dunder-proto: "npm:^1.0.1"
- es-object-atoms: "npm:^1.0.0"
- checksum: 10/4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b
- languageName: node
- linkType: hard
-
-"glob-parent@npm:~5.1.2":
- version: 5.1.2
- resolution: "glob-parent@npm:5.1.2"
- dependencies:
- is-glob: "npm:^4.0.1"
- checksum: 10/32cd106ce8c0d83731966d31517adb766d02c3812de49c30cfe0675c7c0ae6630c11214c54a5ae67aca882cf738d27fd7768f21aa19118b9245950554be07247
- languageName: node
- linkType: hard
-
-"glob@npm:^10.2.2":
- version: 10.4.5
- resolution: "glob@npm:10.4.5"
- dependencies:
- foreground-child: "npm:^3.1.0"
- jackspeak: "npm:^3.1.2"
- minimatch: "npm:^9.0.4"
- minipass: "npm:^7.1.2"
- package-json-from-dist: "npm:^1.0.0"
- path-scurry: "npm:^1.11.1"
- bin:
- glob: dist/esm/bin.mjs
- checksum: 10/698dfe11828b7efd0514cd11e573eaed26b2dff611f0400907281ce3eab0c1e56143ef9b35adc7c77ecc71fba74717b510c7c223d34ca8a98ec81777b293d4ac
- languageName: node
- linkType: hard
-
-"gopd@npm:^1.2.0":
- version: 1.2.0
- resolution: "gopd@npm:1.2.0"
- checksum: 10/94e296d69f92dc1c0768fcfeecfb3855582ab59a7c75e969d5f96ce50c3d201fd86d5a2857c22565764d5bb8a816c7b1e58f133ec318cd56274da36c5e3fb1a1
- languageName: node
- linkType: hard
-
-"graceful-fs@npm:^4.2.6":
- version: 4.2.11
- resolution: "graceful-fs@npm:4.2.11"
- checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2
- languageName: node
- linkType: hard
-
-"has-ansi@npm:^2.0.0":
- version: 2.0.0
- resolution: "has-ansi@npm:2.0.0"
- dependencies:
- ansi-regex: "npm:^2.0.0"
- checksum: 10/1b51daa0214440db171ff359d0a2d17bc20061164c57e76234f614c91dbd2a79ddd68dfc8ee73629366f7be45a6df5f2ea9de83f52e1ca24433f2cc78c35d8ec
- languageName: node
- linkType: hard
-
-"has-flag@npm:^4.0.0":
- version: 4.0.0
- resolution: "has-flag@npm:4.0.0"
- checksum: 10/261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad
- languageName: node
- linkType: hard
-
-"has-symbols@npm:^1.1.0":
- version: 1.1.0
- resolution: "has-symbols@npm:1.1.0"
- checksum: 10/959385c98696ebbca51e7534e0dc723ada325efa3475350951363cce216d27373e0259b63edb599f72eb94d6cde8577b4b2375f080b303947e560f85692834fa
- languageName: node
- linkType: hard
-
-"hasown@npm:^2.0.2":
- version: 2.0.2
- resolution: "hasown@npm:2.0.2"
- dependencies:
- function-bind: "npm:^1.1.2"
- checksum: 10/7898a9c1788b2862cf0f9c345a6bec77ba4a0c0983c7f19d610c382343d4f98fa260686b225dfb1f88393a66679d2ec58ee310c1d6868c081eda7918f32cc70a
- languageName: node
- linkType: hard
-
-"http-assert@npm:^1.5.0":
- version: 1.5.0
- resolution: "http-assert@npm:1.5.0"
- dependencies:
- deep-equal: "npm:~1.0.1"
- http-errors: "npm:~1.8.0"
- checksum: 10/69c9b3c14cf8b2822916360a365089ce936c883c49068f91c365eccba5c141a9964d19fdda589150a480013bf503bf37d8936c732e9635819339e730ab0e7527
- languageName: node
- linkType: hard
-
-"http-cache-semantics@npm:^4.1.1":
- version: 4.2.0
- resolution: "http-cache-semantics@npm:4.2.0"
- checksum: 10/4efd2dfcfeea9d5e88c84af450b9980be8a43c2c8179508b1c57c7b4421c855f3e8efe92fa53e0b3f4a43c85824ada930eabbc306d1b3beab750b6dcc5187693
- languageName: node
- linkType: hard
-
-"http-errors@npm:2.0.0, http-errors@npm:^2.0.0":
- version: 2.0.0
- resolution: "http-errors@npm:2.0.0"
- dependencies:
- depd: "npm:2.0.0"
- inherits: "npm:2.0.4"
- setprototypeof: "npm:1.2.0"
- statuses: "npm:2.0.1"
- toidentifier: "npm:1.0.1"
- checksum: 10/0e7f76ee8ff8a33e58a3281a469815b893c41357378f408be8f6d4aa7d1efafb0da064625518e7078381b6a92325949b119dc38fcb30bdbc4e3a35f78c44c439
- languageName: node
- linkType: hard
-
-"http-errors@npm:~1.8.0":
- version: 1.8.1
- resolution: "http-errors@npm:1.8.1"
- dependencies:
- depd: "npm:~1.1.2"
- inherits: "npm:2.0.4"
- setprototypeof: "npm:1.2.0"
- statuses: "npm:>= 1.5.0 < 2"
- toidentifier: "npm:1.0.1"
- checksum: 10/76fc491bd8df2251e21978e080d5dae20d9736cfb29bb72b5b76ec1bcebb1c14f0f58a3a128dd89288934379d2173cfb0421c571d54103e93dd65ef6243d64d8
- languageName: node
- linkType: hard
-
-"http-proxy-agent@npm:^7.0.0":
- version: 7.0.2
- resolution: "http-proxy-agent@npm:7.0.2"
- dependencies:
- agent-base: "npm:^7.1.0"
- debug: "npm:^4.3.4"
- checksum: 10/d062acfa0cb82beeb558f1043c6ba770ea892b5fb7b28654dbc70ea2aeea55226dd34c02a294f6c1ca179a5aa483c4ea641846821b182edbd9cc5d89b54c6848
- languageName: node
- linkType: hard
-
-"https-proxy-agent@npm:^7.0.1":
- version: 7.0.6
- resolution: "https-proxy-agent@npm:7.0.6"
- dependencies:
- agent-base: "npm:^7.1.2"
- debug: "npm:4"
- checksum: 10/784b628cbd55b25542a9d85033bdfd03d4eda630fb8b3c9477959367f3be95dc476ed2ecbb9836c359c7c698027fc7b45723a302324433590f45d6c1706e8c13
- languageName: node
- linkType: hard
-
-"iconv-lite@npm:0.4.24":
- version: 0.4.24
- resolution: "iconv-lite@npm:0.4.24"
- dependencies:
- safer-buffer: "npm:>= 2.1.2 < 3"
- checksum: 10/6d3a2dac6e5d1fb126d25645c25c3a1209f70cceecc68b8ef51ae0da3cdc078c151fade7524a30b12a3094926336831fca09c666ef55b37e2c69638b5d6bd2e3
- languageName: node
- linkType: hard
-
-"iconv-lite@npm:^0.6.2":
- version: 0.6.3
- resolution: "iconv-lite@npm:0.6.3"
- dependencies:
- safer-buffer: "npm:>= 2.1.2 < 3.0.0"
- checksum: 10/24e3292dd3dadaa81d065c6f8c41b274a47098150d444b96e5f53b4638a9a71482921ea6a91a1f59bb71d9796de25e04afd05919fa64c360347ba65d3766f10f
- languageName: node
- linkType: hard
-
-"imurmurhash@npm:^0.1.4":
- version: 0.1.4
- resolution: "imurmurhash@npm:0.1.4"
- checksum: 10/2d30b157a91fe1c1d7c6f653cbf263f039be6c5bfa959245a16d4ee191fc0f2af86c08545b6e6beeb041c56b574d2d5b9f95343d378ab49c0f37394d541e7fc8
- languageName: node
- linkType: hard
-
-"in-publish@npm:^2.0.0":
- version: 2.0.1
- resolution: "in-publish@npm:2.0.1"
- bin:
- in-install: in-install.js
- in-publish: in-publish.js
- not-in-install: not-in-install.js
- not-in-publish: not-in-publish.js
- checksum: 10/e02c5812669f37adaf4c3cea43854aae0746e8987a8a455dbcb03e4cb83059aba60e522cf053fc027ab943123e71ccf5121bc2924dc6d8b73f34020849414e20
- languageName: node
- linkType: hard
-
-"inflation@npm:^2.0.0":
- version: 2.1.0
- resolution: "inflation@npm:2.1.0"
- checksum: 10/80c1b5d9ec408105a85f0623c824d668ddf0cadafd8d9716c0737990e5a712ae5f7d6bb0ff216b6648eccb9c6ac69fe06c0d8c58456d168db5bf550c89dd74ed
- languageName: node
- linkType: hard
-
-"inherits@npm:2.0.4":
- version: 2.0.4
- resolution: "inherits@npm:2.0.4"
- checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521
- languageName: node
- linkType: hard
-
-"inquirer@npm:0.11.0":
- version: 0.11.0
- resolution: "inquirer@npm:0.11.0"
- dependencies:
- ansi-escapes: "npm:^1.1.0"
- ansi-regex: "npm:^2.0.0"
- chalk: "npm:^1.0.0"
- cli-cursor: "npm:^1.0.1"
- cli-width: "npm:^1.0.1"
- figures: "npm:^1.3.5"
- lodash: "npm:^3.3.1"
- readline2: "npm:^1.0.1"
- run-async: "npm:^0.1.0"
- rx-lite: "npm:^3.1.2"
- strip-ansi: "npm:^3.0.0"
- through: "npm:^2.3.6"
- checksum: 10/3274e6aa1c570d219e4be5a02f7ae758dc99ad7b90a54239749721be5e66fb1371b55be69b8155995cdb476667890d96089a6419e069634f5d8aa95ac719cff3
- languageName: node
- linkType: hard
-
-"ip-address@npm:^10.0.1":
- version: 10.0.1
- resolution: "ip-address@npm:10.0.1"
- checksum: 10/09731acda32cd8e14c46830c137e7e5940f47b36d63ffb87c737331270287d631cf25aa95570907a67d3f919fdb25f4470c404eda21e62f22e0a55927f4dd0fb
- languageName: node
- linkType: hard
-
-"is-binary-path@npm:~2.1.0":
- version: 2.1.0
- resolution: "is-binary-path@npm:2.1.0"
- dependencies:
- binary-extensions: "npm:^2.0.0"
- checksum: 10/078e51b4f956c2c5fd2b26bb2672c3ccf7e1faff38e0ebdba45612265f4e3d9fc3127a1fa8370bbf09eab61339203c3d3b7af5662cbf8be4030f8fac37745b0e
- languageName: node
- linkType: hard
-
-"is-extglob@npm:^2.1.1":
- version: 2.1.1
- resolution: "is-extglob@npm:2.1.1"
- checksum: 10/df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85
- languageName: node
- linkType: hard
-
-"is-fullwidth-code-point@npm:^1.0.0":
- version: 1.0.0
- resolution: "is-fullwidth-code-point@npm:1.0.0"
- dependencies:
- number-is-nan: "npm:^1.0.0"
- checksum: 10/4d46a7465a66a8aebcc5340d3b63a56602133874af576a9ca42c6f0f4bd787a743605771c5f246db77da96605fefeffb65fc1dbe862dcc7328f4b4d03edf5a57
- languageName: node
- linkType: hard
-
-"is-fullwidth-code-point@npm:^3.0.0":
- version: 3.0.0
- resolution: "is-fullwidth-code-point@npm:3.0.0"
- checksum: 10/44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348
- languageName: node
- linkType: hard
-
-"is-glob@npm:^4.0.1, is-glob@npm:~4.0.1":
- version: 4.0.3
- resolution: "is-glob@npm:4.0.3"
- dependencies:
- is-extglob: "npm:^2.1.1"
- checksum: 10/3ed74f2b0cdf4f401f38edb0442ddfde3092d79d7d35c9919c86641efdbcbb32e45aa3c0f70ce5eecc946896cd5a0f26e4188b9f2b881876f7cb6c505b82da11
- languageName: node
- linkType: hard
-
-"is-number@npm:^7.0.0":
- version: 7.0.0
- resolution: "is-number@npm:7.0.0"
- checksum: 10/6a6c3383f68afa1e05b286af866017c78f1226d43ac8cb064e115ff9ed85eb33f5c4f7216c96a71e4dfea289ef52c5da3aef5bbfade8ffe47a0465d70c0c8e86
- languageName: node
- linkType: hard
-
-"isexe@npm:^2.0.0":
- version: 2.0.0
- resolution: "isexe@npm:2.0.0"
- checksum: 10/7c9f715c03aff08f35e98b1fadae1b9267b38f0615d501824f9743f3aab99ef10e303ce7db3f186763a0b70a19de5791ebfc854ff884d5a8c4d92211f642ec92
- languageName: node
- linkType: hard
-
-"isexe@npm:^3.1.1":
- version: 3.1.1
- resolution: "isexe@npm:3.1.1"
- checksum: 10/7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e
- languageName: node
- linkType: hard
-
-"isomorphic-unfetch@npm:^3.1.0":
- version: 3.1.0
- resolution: "isomorphic-unfetch@npm:3.1.0"
- dependencies:
- node-fetch: "npm:^2.6.1"
- unfetch: "npm:^4.2.0"
- checksum: 10/4e760d9a3f94b42c59fe5c6b53202469cecd864875dcac927668b1f43eb57698422a0086fadde47f7815752c4f4e30ecf1ce9a0eb09c44a871a2484dbc580b39
- languageName: node
- linkType: hard
-
-"jackspeak@npm:^3.1.2":
- version: 3.4.3
- resolution: "jackspeak@npm:3.4.3"
- dependencies:
- "@isaacs/cliui": "npm:^8.0.2"
- "@pkgjs/parseargs": "npm:^0.11.0"
- dependenciesMeta:
- "@pkgjs/parseargs":
- optional: true
- checksum: 10/96f8786eaab98e4bf5b2a5d6d9588ea46c4d06bbc4f2eb861fdd7b6b182b16f71d8a70e79820f335d52653b16d4843b29dd9cdcf38ae80406756db9199497cf3
- languageName: node
- linkType: hard
-
-"keygrip@npm:~1.1.0":
- version: 1.1.0
- resolution: "keygrip@npm:1.1.0"
- dependencies:
- tsscmp: "npm:1.0.6"
- checksum: 10/078cd16a463d187121f0a27c1c9c95c52ad392b620f823431689f345a0501132cee60f6e96914b07d570105af470b96960402accd6c48a0b1f3cd8fac4fa2cae
- languageName: node
- linkType: hard
-
-"kleur@npm:^3.0.3":
- version: 3.0.3
- resolution: "kleur@npm:3.0.3"
- checksum: 10/0c0ecaf00a5c6173d25059c7db2113850b5457016dfa1d0e3ef26da4704fbb186b4938d7611246d86f0ddf1bccf26828daa5877b1f232a65e7373d0122a83e7f
- languageName: node
- linkType: hard
-
-"koa-body@npm:^6.0.1":
- version: 6.0.1
- resolution: "koa-body@npm:6.0.1"
- dependencies:
- "@types/co-body": "npm:^6.1.0"
- "@types/formidable": "npm:^2.0.5"
- "@types/koa": "npm:^2.13.5"
- co-body: "npm:^6.1.0"
- formidable: "npm:^2.0.1"
- zod: "npm:^3.19.1"
- checksum: 10/d241d4d228117da43ccd485babe9f8e221188360faef93f936f85ced03d8df900b1bd3af0f2e26b8e514f66361373078ef8501b50089b20e19c578566d25a239
- languageName: node
- linkType: hard
-
-"koa-compose@npm:^4.1.0":
- version: 4.1.0
- resolution: "koa-compose@npm:4.1.0"
- checksum: 10/46cb16792d96425e977c2ae4e5cb04930280740e907242ec9c25e3fb8b4a1d7b54451d7432bc24f40ec62255edea71894d2ceeb8238501842b4e48014f2e83db
- languageName: node
- linkType: hard
-
-"koa@npm:^3.0.3":
- version: 3.1.1
- resolution: "koa@npm:3.1.1"
- dependencies:
- accepts: "npm:^1.3.8"
- content-disposition: "npm:~0.5.4"
- content-type: "npm:^1.0.5"
- cookies: "npm:~0.9.1"
- delegates: "npm:^1.0.0"
- destroy: "npm:^1.2.0"
- encodeurl: "npm:^2.0.0"
- escape-html: "npm:^1.0.3"
- fresh: "npm:~0.5.2"
- http-assert: "npm:^1.5.0"
- http-errors: "npm:^2.0.0"
- koa-compose: "npm:^4.1.0"
- mime-types: "npm:^3.0.1"
- on-finished: "npm:^2.4.1"
- parseurl: "npm:^1.3.3"
- statuses: "npm:^2.0.1"
- type-is: "npm:^2.0.1"
- vary: "npm:^1.1.2"
- checksum: 10/b9f53e98752e73d2d3ed2df28a8062387e116d7053f3d655815ea7f1bae672f4f6afe41d6ff5f6cf429aad76aea4fd4424655bdcf6f8291a658108fe3aa2cf43
- languageName: node
- linkType: hard
-
-"linkify-it@npm:^5.0.0":
- version: 5.0.0
- resolution: "linkify-it@npm:5.0.0"
- dependencies:
- uc.micro: "npm:^2.0.0"
- checksum: 10/ef3b7609dda6ec0c0be8a7b879cea195f0d36387b0011660cd6711bba0ad82137f59b458b7e703ec74f11d88e7c1328e2ad9b855a8500c0ded67461a8c4519e6
- languageName: node
- linkType: hard
-
-"lodash@npm:^3.3.1":
- version: 3.10.1
- resolution: "lodash@npm:3.10.1"
- checksum: 10/3fa24526ced39885889d5609239d14c4145b3736f103fcdde247524203bfeb38e641653df7bdcc172b316a9775ca32787dda09430872ce906f017a699c92fad8
- languageName: node
- linkType: hard
-
-"lodash@npm:^4.5.1":
- version: 4.17.21
- resolution: "lodash@npm:4.17.21"
- checksum: 10/c08619c038846ea6ac754abd6dd29d2568aa705feb69339e836dfa8d8b09abbb2f859371e86863eda41848221f9af43714491467b5b0299122431e202bb0c532
- languageName: node
- linkType: hard
-
-"log-update@npm:^1.0.2":
- version: 1.0.2
- resolution: "log-update@npm:1.0.2"
- dependencies:
- ansi-escapes: "npm:^1.0.0"
- cli-cursor: "npm:^1.0.2"
- checksum: 10/eb8389778092093ec65f36f6a81dd599d0196b74176f07668fcf2bbeb805e36548b438655060e14dcfb910c47f2ef2ff9984c50be9aabeaa772d8aa448a374aa
- languageName: node
- linkType: hard
-
-"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
- version: 10.4.3
- resolution: "lru-cache@npm:10.4.3"
- checksum: 10/e6e90267360476720fa8e83cc168aa2bf0311f3f2eea20a6ba78b90a885ae72071d9db132f40fda4129c803e7dcec3a6b6a6fbb44ca90b081630b810b5d6a41a
- languageName: node
- linkType: hard
-
-"lunr@npm:^2.3.9":
- version: 2.3.9
- resolution: "lunr@npm:2.3.9"
- checksum: 10/f2f6db34c046f5a767782fe2454e6dd69c75ba3c5cf5c1cb9cacca2313a99c2ba78ff8fa67dac866fb7c4ffd5f22e06684793f5f15ba14bddb598b94513d54bf
- languageName: node
- linkType: hard
-
-"make-error@npm:^1.1.1, make-error@npm:^1.3.6":
- version: 1.3.6
- resolution: "make-error@npm:1.3.6"
- checksum: 10/b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402
- languageName: node
- linkType: hard
-
-"make-fetch-happen@npm:^14.0.3":
- version: 14.0.3
- resolution: "make-fetch-happen@npm:14.0.3"
- dependencies:
- "@npmcli/agent": "npm:^3.0.0"
- cacache: "npm:^19.0.1"
- http-cache-semantics: "npm:^4.1.1"
- minipass: "npm:^7.0.2"
- minipass-fetch: "npm:^4.0.0"
- minipass-flush: "npm:^1.0.5"
- minipass-pipeline: "npm:^1.2.4"
- negotiator: "npm:^1.0.0"
- proc-log: "npm:^5.0.0"
- promise-retry: "npm:^2.0.1"
- ssri: "npm:^12.0.0"
- checksum: 10/fce0385840b6d86b735053dfe941edc2dd6468fda80fe74da1eeff10cbd82a75760f406194f2bc2fa85b99545b2bc1f84c08ddf994b21830775ba2d1a87e8bdf
- languageName: node
- linkType: hard
-
-"markdown-it@npm:^14.1.0":
- version: 14.1.0
- resolution: "markdown-it@npm:14.1.0"
- dependencies:
- argparse: "npm:^2.0.1"
- entities: "npm:^4.4.0"
- linkify-it: "npm:^5.0.0"
- mdurl: "npm:^2.0.0"
- punycode.js: "npm:^2.3.1"
- uc.micro: "npm:^2.1.0"
- bin:
- markdown-it: bin/markdown-it.mjs
- checksum: 10/f34f921be178ed0607ba9e3e27c733642be445e9bb6b1dba88da7aafe8ba1bc5d2f1c3aa8f3fc33b49a902da4e4c08c2feadfafb290b8c7dda766208bb6483a9
- languageName: node
- linkType: hard
-
-"math-intrinsics@npm:^1.1.0":
- version: 1.1.0
- resolution: "math-intrinsics@npm:1.1.0"
- checksum: 10/11df2eda46d092a6035479632e1ec865b8134bdfc4bd9e571a656f4191525404f13a283a515938c3a8de934dbfd9c09674d9da9fa831e6eb7e22b50b197d2edd
- languageName: node
- linkType: hard
-
-"mdurl@npm:^2.0.0":
- version: 2.0.0
- resolution: "mdurl@npm:2.0.0"
- checksum: 10/1720349d4a53e401aa993241368e35c0ad13d816ad0b28388928c58ca9faa0cf755fa45f18ccbf64f4ce54a845a50ddce5c84e4016897b513096a68dac4b0158
- languageName: node
- linkType: hard
-
-"media-typer@npm:0.3.0":
- version: 0.3.0
- resolution: "media-typer@npm:0.3.0"
- checksum: 10/38e0984db39139604756903a01397e29e17dcb04207bb3e081412ce725ab17338ecc47220c1b186b6bbe79a658aad1b0d41142884f5a481f36290cdefbe6aa46
- languageName: node
- linkType: hard
-
-"media-typer@npm:^1.1.0":
- version: 1.1.0
- resolution: "media-typer@npm:1.1.0"
- checksum: 10/a58dd60804df73c672942a7253ccc06815612326dc1c0827984b1a21704466d7cde351394f47649e56cf7415e6ee2e26e000e81b51b3eebb5a93540e8bf93cbd
- languageName: node
- linkType: hard
-
-"mime-db@npm:1.52.0":
- version: 1.52.0
- resolution: "mime-db@npm:1.52.0"
- checksum: 10/54bb60bf39e6f8689f6622784e668a3d7f8bed6b0d886f5c3c446cb3284be28b30bf707ed05d0fe44a036f8469976b2629bbea182684977b084de9da274694d7
- languageName: node
- linkType: hard
-
-"mime-db@npm:^1.54.0":
- version: 1.54.0
- resolution: "mime-db@npm:1.54.0"
- checksum: 10/9e7834be3d66ae7f10eaa69215732c6d389692b194f876198dca79b2b90cbf96688d9d5d05ef7987b20f749b769b11c01766564264ea5f919c88b32a29011311
- languageName: node
- linkType: hard
-
-"mime-types@npm:^3.0.0, mime-types@npm:^3.0.1":
- version: 3.0.1
- resolution: "mime-types@npm:3.0.1"
- dependencies:
- mime-db: "npm:^1.54.0"
- checksum: 10/fa1d3a928363723a8046c346d87bf85d35014dae4285ad70a3ff92bd35957992b3094f8417973cfe677330916c6ef30885109624f1fb3b1e61a78af509dba120
- languageName: node
- linkType: hard
-
-"mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
- version: 2.1.35
- resolution: "mime-types@npm:2.1.35"
- dependencies:
- mime-db: "npm:1.52.0"
- checksum: 10/89aa9651b67644035de2784a6e665fc685d79aba61857e02b9c8758da874a754aed4a9aced9265f5ed1171fd934331e5516b84a7f0218031b6fa0270eca1e51a
- languageName: node
- linkType: hard
-
-"minimatch@npm:^3.0.4":
- version: 3.1.2
- resolution: "minimatch@npm:3.1.2"
- dependencies:
- brace-expansion: "npm:^1.1.7"
- checksum: 10/e0b25b04cd4ec6732830344e5739b13f8690f8a012d73445a4a19fbc623f5dd481ef7a5827fde25954cd6026fede7574cc54dc4643c99d6c6b653d6203f94634
- languageName: node
- linkType: hard
-
-"minimatch@npm:^9.0.4, minimatch@npm:^9.0.5":
- version: 9.0.5
- resolution: "minimatch@npm:9.0.5"
- dependencies:
- brace-expansion: "npm:^2.0.1"
- checksum: 10/dd6a8927b063aca6d910b119e1f2df6d2ce7d36eab91de83167dd136bb85e1ebff97b0d3de1cb08bd1f7e018ca170b4962479fefab5b2a69e2ae12cb2edc8348
- languageName: node
- linkType: hard
-
-"minimist@npm:^1.2.0":
- version: 1.2.8
- resolution: "minimist@npm:1.2.8"
- checksum: 10/908491b6cc15a6c440ba5b22780a0ba89b9810e1aea684e253e43c4e3b8d56ec1dcdd7ea96dde119c29df59c936cde16062159eae4225c691e19c70b432b6e6f
- languageName: node
- linkType: hard
-
-"minipass-collect@npm:^2.0.1":
- version: 2.0.1
- resolution: "minipass-collect@npm:2.0.1"
- dependencies:
- minipass: "npm:^7.0.3"
- checksum: 10/b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342
- languageName: node
- linkType: hard
-
-"minipass-fetch@npm:^4.0.0":
- version: 4.0.1
- resolution: "minipass-fetch@npm:4.0.1"
- dependencies:
- encoding: "npm:^0.1.13"
- minipass: "npm:^7.0.3"
- minipass-sized: "npm:^1.0.3"
- minizlib: "npm:^3.0.1"
- dependenciesMeta:
- encoding:
- optional: true
- checksum: 10/7ddfebdbb87d9866e7b5f7eead5a9e3d9d507992af932a11d275551f60006cf7d9178e66d586dbb910894f3e3458d27c0ddf93c76e94d49d0a54a541ddc1263d
- languageName: node
- linkType: hard
-
-"minipass-flush@npm:^1.0.5":
- version: 1.0.5
- resolution: "minipass-flush@npm:1.0.5"
- dependencies:
- minipass: "npm:^3.0.0"
- checksum: 10/56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf
- languageName: node
- linkType: hard
-
-"minipass-pipeline@npm:^1.2.4":
- version: 1.2.4
- resolution: "minipass-pipeline@npm:1.2.4"
- dependencies:
- minipass: "npm:^3.0.0"
- checksum: 10/b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b
- languageName: node
- linkType: hard
-
-"minipass-sized@npm:^1.0.3":
- version: 1.0.3
- resolution: "minipass-sized@npm:1.0.3"
- dependencies:
- minipass: "npm:^3.0.0"
- checksum: 10/40982d8d836a52b0f37049a0a7e5d0f089637298e6d9b45df9c115d4f0520682a78258905e5c8b180fb41b593b0a82cc1361d2c74b45f7ada66334f84d1ecfdd
- languageName: node
- linkType: hard
-
-"minipass@npm:^3.0.0":
- version: 3.3.6
- resolution: "minipass@npm:3.3.6"
- dependencies:
- yallist: "npm:^4.0.0"
- checksum: 10/a5c6ef069f70d9a524d3428af39f2b117ff8cd84172e19b754e7264a33df460873e6eb3d6e55758531580970de50ae950c496256bb4ad3691a2974cddff189f0
- languageName: node
- linkType: hard
-
-"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2":
- version: 7.1.2
- resolution: "minipass@npm:7.1.2"
- checksum: 10/c25f0ee8196d8e6036661104bacd743785b2599a21de5c516b32b3fa2b83113ac89a2358465bc04956baab37ffb956ae43be679b2262bf7be15fce467ccd7950
- languageName: node
- linkType: hard
-
-"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0":
- version: 3.1.0
- resolution: "minizlib@npm:3.1.0"
- dependencies:
- minipass: "npm:^7.1.2"
- checksum: 10/f47365cc2cb7f078cbe7e046eb52655e2e7e97f8c0a9a674f4da60d94fb0624edfcec9b5db32e8ba5a99a5f036f595680ae6fe02a262beaa73026e505cc52f99
- languageName: node
- linkType: hard
-
-"ms@npm:^2.1.3":
- version: 2.1.3
- resolution: "ms@npm:2.1.3"
- checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d
- languageName: node
- linkType: hard
-
-"mute-stream@npm:0.0.5":
- version: 0.0.5
- resolution: "mute-stream@npm:0.0.5"
- checksum: 10/a2a3b25fa0e6adf3516f51b1d8bd0cc48e256fc9384c3c5bd3a5c3635e0823d6dee4c28909a3f2a9d8782d7d8daa3a8c081163d881e4d7f30ff17e7d2eabde76
- languageName: node
- linkType: hard
-
-"negotiator@npm:0.6.3":
- version: 0.6.3
- resolution: "negotiator@npm:0.6.3"
- checksum: 10/2723fb822a17ad55c93a588a4bc44d53b22855bf4be5499916ca0cab1e7165409d0b288ba2577d7b029f10ce18cf2ed8e703e5af31c984e1e2304277ef979837
- languageName: node
- linkType: hard
-
-"negotiator@npm:^1.0.0":
- version: 1.0.0
- resolution: "negotiator@npm:1.0.0"
- checksum: 10/b5734e87295324fabf868e36fb97c84b7d7f3156ec5f4ee5bf6e488079c11054f818290fc33804cef7b1ee21f55eeb14caea83e7dafae6492a409b3e573153e5
- languageName: node
- linkType: hard
-
-"node-fetch@npm:^2.6.1":
- version: 2.7.0
- resolution: "node-fetch@npm:2.7.0"
- dependencies:
- whatwg-url: "npm:^5.0.0"
- peerDependencies:
- encoding: ^0.1.0
- peerDependenciesMeta:
- encoding:
- optional: true
- checksum: 10/b24f8a3dc937f388192e59bcf9d0857d7b6940a2496f328381641cb616efccc9866e89ec43f2ec956bbd6c3d3ee05524ce77fe7b29ccd34692b3a16f237d6676
- languageName: node
- linkType: hard
-
-"node-gyp@npm:latest":
- version: 11.5.0
- resolution: "node-gyp@npm:11.5.0"
- dependencies:
- env-paths: "npm:^2.2.0"
- exponential-backoff: "npm:^3.1.1"
- graceful-fs: "npm:^4.2.6"
- make-fetch-happen: "npm:^14.0.3"
- nopt: "npm:^8.0.0"
- proc-log: "npm:^5.0.0"
- semver: "npm:^7.3.5"
- tar: "npm:^7.4.3"
- tinyglobby: "npm:^0.2.12"
- which: "npm:^5.0.0"
- bin:
- node-gyp: bin/node-gyp.js
- checksum: 10/15a600b626116e1e528c49f73027c5ff84dbf6986df77b0fb61d6eb079ab4230c39f245295cb67f0590e6541a848cbd267e00c5769e8fb8bf88a5cca3701b551
- languageName: node
- linkType: hard
-
-"node-localstorage@npm:^0.6.0":
- version: 0.6.0
- resolution: "node-localstorage@npm:0.6.0"
- checksum: 10/9bc98d8ddcdf651c25ab35f730c133b13d03accd3334563845281aa1287254f6ca862ec56a337ea740cb5458fc7791aaeee8004aabf80dffd112dd8e1270b4a0
- languageName: node
- linkType: hard
-
-"nopt@npm:^8.0.0":
- version: 8.1.0
- resolution: "nopt@npm:8.1.0"
- dependencies:
- abbrev: "npm:^3.0.0"
- bin:
- nopt: bin/nopt.js
- checksum: 10/26ab456c51a96f02a9e5aa8d1b80ef3219f2070f3f3528a040e32fb735b1e651e17bdf0f1476988d3a46d498f35c65ed662d122f340d38ce4a7e71dd7b20c4bc
- languageName: node
- linkType: hard
-
-"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0":
- version: 3.0.0
- resolution: "normalize-path@npm:3.0.0"
- checksum: 10/88eeb4da891e10b1318c4b2476b6e2ecbeb5ff97d946815ffea7794c31a89017c70d7f34b3c2ebf23ef4e9fc9fb99f7dffe36da22011b5b5c6ffa34f4873ec20
- languageName: node
- linkType: hard
-
-"number-is-nan@npm:^1.0.0":
- version: 1.0.1
- resolution: "number-is-nan@npm:1.0.1"
- checksum: 10/13656bc9aa771b96cef209ffca31c31a03b507ca6862ba7c3f638a283560620d723d52e626d57892c7fff475f4c36ac07f0600f14544692ff595abff214b9ffb
- languageName: node
- linkType: hard
-
-"object-assign@npm:^4.1.0":
- version: 4.1.1
- resolution: "object-assign@npm:4.1.1"
- checksum: 10/fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f
- languageName: node
- linkType: hard
-
-"object-inspect@npm:^1.13.3":
- version: 1.13.4
- resolution: "object-inspect@npm:1.13.4"
- checksum: 10/aa13b1190ad3e366f6c83ad8a16ed37a19ed57d267385aa4bfdccda833d7b90465c057ff6c55d035a6b2e52c1a2295582b294217a0a3a1ae7abdd6877ef781fb
- languageName: node
- linkType: hard
-
-"on-finished@npm:^2.4.1":
- version: 2.4.1
- resolution: "on-finished@npm:2.4.1"
- dependencies:
- ee-first: "npm:1.1.1"
- checksum: 10/8e81472c5028125c8c39044ac4ab8ba51a7cdc19a9fbd4710f5d524a74c6d8c9ded4dd0eed83f28d3d33ac1d7a6a439ba948ccb765ac6ce87f30450a26bfe2ea
- languageName: node
- linkType: hard
-
-"once@npm:^1.3.0, once@npm:^1.4.0":
- version: 1.4.0
- resolution: "once@npm:1.4.0"
- dependencies:
- wrappy: "npm:1"
- checksum: 10/cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68
- languageName: node
- linkType: hard
-
-"onetime@npm:^1.0.0":
- version: 1.1.0
- resolution: "onetime@npm:1.1.0"
- checksum: 10/751f45ddeba213600c215280cb937efd7b530b498277c9605f9ee0e5e2022b3463e23fd6d1800e793e128301adee12ed4aba41242e50fbc0d631a9e171aef361
- languageName: node
- linkType: hard
-
-"p-map@npm:^7.0.2":
- version: 7.0.3
- resolution: "p-map@npm:7.0.3"
- checksum: 10/2ef48ccfc6dd387253d71bf502604f7893ed62090b2c9d73387f10006c342606b05233da0e4f29388227b61eb5aeface6197e166520c465c234552eeab2fe633
- languageName: node
- linkType: hard
-
-"package-json-from-dist@npm:^1.0.0":
- version: 1.0.1
- resolution: "package-json-from-dist@npm:1.0.1"
- checksum: 10/58ee9538f2f762988433da00e26acc788036914d57c71c246bf0be1b60cdbd77dd60b6a3e1a30465f0b248aeb80079e0b34cb6050b1dfa18c06953bb1cbc7602
- languageName: node
- linkType: hard
-
-"parseurl@npm:^1.3.3":
- version: 1.3.3
- resolution: "parseurl@npm:1.3.3"
- checksum: 10/407cee8e0a3a4c5cd472559bca8b6a45b82c124e9a4703302326e9ab60fc1081442ada4e02628efef1eb16197ddc7f8822f5a91fd7d7c86b51f530aedb17dfa2
- languageName: node
- linkType: hard
-
-"path-key@npm:^3.1.0":
- version: 3.1.1
- resolution: "path-key@npm:3.1.1"
- checksum: 10/55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020
- languageName: node
- linkType: hard
-
-"path-scurry@npm:^1.11.1":
- version: 1.11.1
- resolution: "path-scurry@npm:1.11.1"
- dependencies:
- lru-cache: "npm:^10.2.0"
- minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0"
- checksum: 10/5e8845c159261adda6f09814d7725683257fcc85a18f329880ab4d7cc1d12830967eae5d5894e453f341710d5484b8fdbbd4d75181b4d6e1eb2f4dc7aeadc434
- languageName: node
- linkType: hard
-
-"path-to-regexp@npm:^8.2.0":
- version: 8.3.0
- resolution: "path-to-regexp@npm:8.3.0"
- checksum: 10/568f148fc64f5fd1ecebf44d531383b28df924214eabf5f2570dce9587a228e36c37882805ff02d71c6209b080ea3ee6a4d2b712b5df09741b67f1f3cf91e55a
- languageName: node
- linkType: hard
-
-"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1":
- version: 2.3.1
- resolution: "picomatch@npm:2.3.1"
- checksum: 10/60c2595003b05e4535394d1da94850f5372c9427ca4413b71210f437f7b2ca091dbd611c45e8b37d10036fa8eade25c1b8951654f9d3973bfa66a2ff4d3b08bc
- languageName: node
- linkType: hard
-
-"picomatch@npm:^4.0.3":
- version: 4.0.3
- resolution: "picomatch@npm:4.0.3"
- checksum: 10/57b99055f40b16798f2802916d9c17e9744e620a0db136554af01d19598b96e45e2f00014c91d1b8b13874b80caa8c295b3d589a3f72373ec4aaf54baa5962d5
- languageName: node
- linkType: hard
-
-"pinst@npm:^2.1.6":
- version: 2.1.6
- resolution: "pinst@npm:2.1.6"
- dependencies:
- fromentries: "npm:^1.3.2"
- bin:
- pinst: bin.js
- checksum: 10/b6449d527ec6c7b76056a5282a80ff4d2498d4e882e846eb8837ec291ad1b08784756c6d9de0f1d96b70bd657e50b52db3ae2a683c6245690e12c2c7eb199d0f
- languageName: node
- linkType: hard
-
-"proc-log@npm:^5.0.0":
- version: 5.0.0
- resolution: "proc-log@npm:5.0.0"
- checksum: 10/35610bdb0177d3ab5d35f8827a429fb1dc2518d9e639f2151ac9007f01a061c30e0c635a970c9b00c39102216160f6ec54b62377c92fac3b7bfc2ad4b98d195c
- languageName: node
- linkType: hard
-
-"promise-retry@npm:^2.0.1":
- version: 2.0.1
- resolution: "promise-retry@npm:2.0.1"
- dependencies:
- err-code: "npm:^2.0.2"
- retry: "npm:^0.12.0"
- checksum: 10/96e1a82453c6c96eef53a37a1d6134c9f2482f94068f98a59145d0986ca4e497bf110a410adf73857e588165eab3899f0ebcf7b3890c1b3ce802abc0d65967d4
- languageName: node
- linkType: hard
-
-"prompts@npm:^2.4.2":
- version: 2.4.2
- resolution: "prompts@npm:2.4.2"
- dependencies:
- kleur: "npm:^3.0.3"
- sisteransi: "npm:^1.0.5"
- checksum: 10/c52536521a4d21eff4f2f2aa4572446cad227464066365a7167e52ccf8d9839c099f9afec1aba0eed3d5a2514b3e79e0b3e7a1dc326b9acde6b75d27ed74b1a9
- languageName: node
- linkType: hard
-
-"punycode.js@npm:^2.3.1":
- version: 2.3.1
- resolution: "punycode.js@npm:2.3.1"
- checksum: 10/f0e946d1edf063f9e3d30a32ca86d8ff90ed13ca40dad9c75d37510a04473340cfc98db23a905cc1e517b1e9deb0f6021dce6f422ace235c60d3c9ac47c5a16a
- languageName: node
- linkType: hard
-
-"qs@npm:^6.11.0, qs@npm:^6.5.2":
- version: 6.14.0
- resolution: "qs@npm:6.14.0"
- dependencies:
- side-channel: "npm:^1.1.0"
- checksum: 10/a60e49bbd51c935a8a4759e7505677b122e23bf392d6535b8fc31c1e447acba2c901235ecb192764013cd2781723dc1f61978b5fdd93cc31d7043d31cdc01974
- languageName: node
- linkType: hard
-
-"query-registry@npm:^2.5.0":
- version: 2.6.0
- resolution: "query-registry@npm:2.6.0"
- dependencies:
- isomorphic-unfetch: "npm:^3.1.0"
- make-error: "npm:^1.3.6"
- tiny-lru: "npm:^8.0.2"
- url-join: "npm:4.0.1"
- validate-npm-package-name: "npm:^4.0.0"
- checksum: 10/2d12a4f82339946c61bc57977c8669d344affd824ba426f1bb1800458c0c6a7c4722385f4dbb42c54bc3e3e1e09dc4cbb0bfe41a59d8c308963fcc1dafee6280
- languageName: node
- linkType: hard
-
-"raw-body@npm:^2.3.3":
- version: 2.5.2
- resolution: "raw-body@npm:2.5.2"
- dependencies:
- bytes: "npm:3.1.2"
- http-errors: "npm:2.0.0"
- iconv-lite: "npm:0.4.24"
- unpipe: "npm:1.0.0"
- checksum: 10/863b5171e140546a4d99f349b720abac4410338e23df5e409cfcc3752538c9caf947ce382c89129ba976f71894bd38b5806c774edac35ebf168d02aa1ac11a95
- languageName: node
- linkType: hard
-
-"readdirp@npm:~3.6.0":
- version: 3.6.0
- resolution: "readdirp@npm:3.6.0"
- dependencies:
- picomatch: "npm:^2.2.1"
- checksum: 10/196b30ef6ccf9b6e18c4e1724b7334f72a093d011a99f3b5920470f0b3406a51770867b3e1ae9711f227ef7a7065982f6ee2ce316746b2cb42c88efe44297fe7
- languageName: node
- linkType: hard
-
-"readline2@npm:^1.0.1":
- version: 1.0.1
- resolution: "readline2@npm:1.0.1"
- dependencies:
- code-point-at: "npm:^1.0.0"
- is-fullwidth-code-point: "npm:^1.0.0"
- mute-stream: "npm:0.0.5"
- checksum: 10/7ac8ffa917af89f042bb24f695b1333158d83e26f398108f6d4ce7ca3ab6bccb6fa32623d9254ea1dc5420db7e6ce0b0fc527108645ababf6e280d8db3fe8a89
- languageName: node
- linkType: hard
-
-"reflect-metadata@npm:^0.1.13":
- version: 0.1.14
- resolution: "reflect-metadata@npm:0.1.14"
- checksum: 10/fcab9c17ec3b9fea0e2f748c2129aceb57c24af6d8d13842b8a77c8c79dde727d7456ce293e76e8d7b267d1dbf93eea4c5b3c9101299a789a075824f2e40f1ee
- languageName: node
- linkType: hard
-
-"regenerator-runtime@npm:^0.10.5":
- version: 0.10.5
- resolution: "regenerator-runtime@npm:0.10.5"
- checksum: 10/a10d9a2510ee0ec2603f2fc316bff0233b7f41702dc69a19b6a23442395a7be9247668f06e5b7a81577d0e3ef677a11c8c63b4edd7a16f1550e5b8fb22173346
- languageName: node
- linkType: hard
-
-"regenerator-runtime@npm:^0.11.0":
- version: 0.11.1
- resolution: "regenerator-runtime@npm:0.11.1"
- checksum: 10/64e62d78594c227e7d5269811bca9e4aa6451332adaae8c79a30cab0fa98733b1ad90bdb9d038095c340c6fad3b414a49a8d9e0b6b424ab7ff8f94f35704f8a2
- languageName: node
- linkType: hard
-
-"resolve-from@npm:^5.0.0":
- version: 5.0.0
- resolution: "resolve-from@npm:5.0.0"
- checksum: 10/be18a5e4d76dd711778664829841cde690971d02b6cbae277735a09c1c28f407b99ef6ef3cd585a1e6546d4097b28df40ed32c4a287b9699dcf6d7f208495e23
- languageName: node
- linkType: hard
-
-"resolve-pkg@npm:^2.0.0":
- version: 2.0.0
- resolution: "resolve-pkg@npm:2.0.0"
- dependencies:
- resolve-from: "npm:^5.0.0"
- checksum: 10/4a14cc38effed20ff362c8f377719af9a45ebe27ee07d79d4802b4568858cd96033f4edc3a2add7fd27e361d24101a042047297a9ef9476696ba16b72e0a05fc
- languageName: node
- linkType: hard
-
-"restore-cursor@npm:^1.0.1":
- version: 1.0.1
- resolution: "restore-cursor@npm:1.0.1"
- dependencies:
- exit-hook: "npm:^1.0.0"
- onetime: "npm:^1.0.0"
- checksum: 10/e40bd1a540d69970341fc734dfada908815a44f91903211f34d32c47da33f6e7824bbc97f6e76aff387137d6b2a1ada3d3d2dc1b654b8accdc8ed5721c46cbfa
- languageName: node
- linkType: hard
-
-"retry@npm:^0.12.0":
- version: 0.12.0
- resolution: "retry@npm:0.12.0"
- checksum: 10/1f914879f97e7ee931ad05fe3afa629bd55270fc6cf1c1e589b6a99fab96d15daad0fa1a52a00c729ec0078045fe3e399bd4fd0c93bcc906957bdc17f89cb8e6
- languageName: node
- linkType: hard
-
-"run-async@npm:^0.1.0":
- version: 0.1.0
- resolution: "run-async@npm:0.1.0"
- dependencies:
- once: "npm:^1.3.0"
- checksum: 10/66fd3ada4036a77a70fbf5063d66bf88df77fa9cbf20516115a6a09431ba66621f353e6fefecd10f9cb6a3345b5fe007a438dbf3f6020fbfd5732634cd4d3e15
- languageName: node
- linkType: hard
-
-"rx-lite@npm:^3.1.2":
- version: 3.1.2
- resolution: "rx-lite@npm:3.1.2"
- checksum: 10/e11d3b1a044e0fe5af82b923dee68aa83b193bf3ad8128cf70e033cbc414f175011a644419c25fe62f75a6f20a1f2aee3b407847dae129fa8df1198b618fb1b2
- languageName: node
- linkType: hard
-
-"safe-buffer@npm:5.2.1":
- version: 5.2.1
- resolution: "safe-buffer@npm:5.2.1"
- checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451
- languageName: node
- linkType: hard
-
-"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0":
- version: 2.1.2
- resolution: "safer-buffer@npm:2.1.2"
- checksum: 10/7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83
- languageName: node
- linkType: hard
-
-"semver@npm:^7.0.0, semver@npm:^7.3.5":
- version: 7.7.3
- resolution: "semver@npm:7.7.3"
- bin:
- semver: bin/semver.js
- checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9
- languageName: node
- linkType: hard
-
-"setprototypeof@npm:1.2.0":
- version: 1.2.0
- resolution: "setprototypeof@npm:1.2.0"
- checksum: 10/fde1630422502fbbc19e6844346778f99d449986b2f9cdcceb8326730d2f3d9964dbcb03c02aaadaefffecd0f2c063315ebea8b3ad895914bf1afc1747fc172e
- languageName: node
- linkType: hard
-
-"shebang-command@npm:^2.0.0":
- version: 2.0.0
- resolution: "shebang-command@npm:2.0.0"
- dependencies:
- shebang-regex: "npm:^3.0.0"
- checksum: 10/6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa
- languageName: node
- linkType: hard
-
-"shebang-regex@npm:^3.0.0":
- version: 3.0.0
- resolution: "shebang-regex@npm:3.0.0"
- checksum: 10/1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222
- languageName: node
- linkType: hard
-
-"side-channel-list@npm:^1.0.0":
- version: 1.0.0
- resolution: "side-channel-list@npm:1.0.0"
- dependencies:
- es-errors: "npm:^1.3.0"
- object-inspect: "npm:^1.13.3"
- checksum: 10/603b928997abd21c5a5f02ae6b9cc36b72e3176ad6827fab0417ead74580cc4fb4d5c7d0a8a2ff4ead34d0f9e35701ed7a41853dac8a6d1a664fcce1a044f86f
- languageName: node
- linkType: hard
-
-"side-channel-map@npm:^1.0.1":
- version: 1.0.1
- resolution: "side-channel-map@npm:1.0.1"
- dependencies:
- call-bound: "npm:^1.0.2"
- es-errors: "npm:^1.3.0"
- get-intrinsic: "npm:^1.2.5"
- object-inspect: "npm:^1.13.3"
- checksum: 10/5771861f77feefe44f6195ed077a9e4f389acc188f895f570d56445e251b861754b547ea9ef73ecee4e01fdada6568bfe9020d2ec2dfc5571e9fa1bbc4a10615
- languageName: node
- linkType: hard
-
-"side-channel-weakmap@npm:^1.0.2":
- version: 1.0.2
- resolution: "side-channel-weakmap@npm:1.0.2"
- dependencies:
- call-bound: "npm:^1.0.2"
- es-errors: "npm:^1.3.0"
- get-intrinsic: "npm:^1.2.5"
- object-inspect: "npm:^1.13.3"
- side-channel-map: "npm:^1.0.1"
- checksum: 10/a815c89bc78c5723c714ea1a77c938377ea710af20d4fb886d362b0d1f8ac73a17816a5f6640f354017d7e292a43da9c5e876c22145bac00b76cfb3468001736
- languageName: node
- linkType: hard
-
-"side-channel@npm:^1.1.0":
- version: 1.1.0
- resolution: "side-channel@npm:1.1.0"
- dependencies:
- es-errors: "npm:^1.3.0"
- object-inspect: "npm:^1.13.3"
- side-channel-list: "npm:^1.0.0"
- side-channel-map: "npm:^1.0.1"
- side-channel-weakmap: "npm:^1.0.2"
- checksum: 10/7d53b9db292c6262f326b6ff3bc1611db84ece36c2c7dc0e937954c13c73185b0406c56589e2bb8d071d6fee468e14c39fb5d203ee39be66b7b8174f179afaba
- languageName: node
- linkType: hard
-
-"signal-exit@npm:^4.0.1":
- version: 4.1.0
- resolution: "signal-exit@npm:4.1.0"
- checksum: 10/c9fa63bbbd7431066174a48ba2dd9986dfd930c3a8b59de9c29d7b6854ec1c12a80d15310869ea5166d413b99f041bfa3dd80a7947bcd44ea8e6eb3ffeabfa1f
- languageName: node
- linkType: hard
-
-"sisteransi@npm:^1.0.5":
- version: 1.0.5
- resolution: "sisteransi@npm:1.0.5"
- checksum: 10/aba6438f46d2bfcef94cf112c835ab395172c75f67453fe05c340c770d3c402363018ae1ab4172a1026a90c47eaccf3af7b6ff6fa749a680c2929bd7fa2b37a4
- languageName: node
- linkType: hard
-
-"smart-buffer@npm:^4.2.0":
- version: 4.2.0
- resolution: "smart-buffer@npm:4.2.0"
- checksum: 10/927484aa0b1640fd9473cee3e0a0bcad6fce93fd7bbc18bac9ad0c33686f5d2e2c422fba24b5899c184524af01e11dd2bd051c2bf2b07e47aff8ca72cbfc60d2
- languageName: node
- linkType: hard
-
-"socks-proxy-agent@npm:^8.0.3":
- version: 8.0.5
- resolution: "socks-proxy-agent@npm:8.0.5"
- dependencies:
- agent-base: "npm:^7.1.2"
- debug: "npm:^4.3.4"
- socks: "npm:^2.8.3"
- checksum: 10/ee99e1dacab0985b52cbe5a75640be6e604135e9489ebdc3048635d186012fbaecc20fbbe04b177dee434c319ba20f09b3e7dfefb7d932466c0d707744eac05c
- languageName: node
- linkType: hard
-
-"socks@npm:^2.8.3":
- version: 2.8.7
- resolution: "socks@npm:2.8.7"
- dependencies:
- ip-address: "npm:^10.0.1"
- smart-buffer: "npm:^4.2.0"
- checksum: 10/d19366c95908c19db154f329bbe94c2317d315dc933a7c2b5101e73f32a555c84fb199b62174e1490082a593a4933d8d5a9b297bde7d1419c14a11a965f51356
- languageName: node
- linkType: hard
-
-"ssri@npm:^12.0.0":
- version: 12.0.0
- resolution: "ssri@npm:12.0.0"
- dependencies:
- minipass: "npm:^7.0.3"
- checksum: 10/7024c1a6e39b3f18aa8f1c8290e884fe91b0f9ca5a6c6d410544daad54de0ba664db879afe16412e187c6c292fd60b937f047ee44292e5c2af2dcc6d8e1a9b48
- languageName: node
- linkType: hard
-
-"statuses@npm:2.0.1":
- version: 2.0.1
- resolution: "statuses@npm:2.0.1"
- checksum: 10/18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb
- languageName: node
- linkType: hard
-
-"statuses@npm:>= 1.5.0 < 2":
- version: 1.5.0
- resolution: "statuses@npm:1.5.0"
- checksum: 10/c469b9519de16a4bb19600205cffb39ee471a5f17b82589757ca7bd40a8d92ebb6ed9f98b5a540c5d302ccbc78f15dc03cc0280dd6e00df1335568a5d5758a5c
- languageName: node
- linkType: hard
-
-"statuses@npm:^2.0.1":
- version: 2.0.2
- resolution: "statuses@npm:2.0.2"
- checksum: 10/6927feb50c2a75b2a4caab2c565491f7a93ad3d8dbad7b1398d52359e9243a20e2ebe35e33726dee945125ef7a515e9097d8a1b910ba2bbd818265a2f6c39879
- languageName: node
- linkType: hard
-
-"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0":
- version: 4.2.3
- resolution: "string-width@npm:4.2.3"
- dependencies:
- emoji-regex: "npm:^8.0.0"
- is-fullwidth-code-point: "npm:^3.0.0"
- strip-ansi: "npm:^6.0.1"
- checksum: 10/e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb
- languageName: node
- linkType: hard
-
-"string-width@npm:^1.0.1":
- version: 1.0.2
- resolution: "string-width@npm:1.0.2"
- dependencies:
- code-point-at: "npm:^1.0.0"
- is-fullwidth-code-point: "npm:^1.0.0"
- strip-ansi: "npm:^3.0.0"
- checksum: 10/5c79439e95bc3bd7233a332c5f5926ab2ee90b23816ed4faa380ce3b2576d7800b0a5bb15ae88ed28737acc7ea06a518c2eef39142dd727adad0e45c776cd37e
- languageName: node
- linkType: hard
-
-"string-width@npm:^5.0.1, string-width@npm:^5.1.2":
- version: 5.1.2
- resolution: "string-width@npm:5.1.2"
- dependencies:
- eastasianwidth: "npm:^0.2.0"
- emoji-regex: "npm:^9.2.2"
- strip-ansi: "npm:^7.0.1"
- checksum: 10/7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193
- languageName: node
- linkType: hard
-
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1":
- version: 6.0.1
- resolution: "strip-ansi@npm:6.0.1"
- dependencies:
- ansi-regex: "npm:^5.0.1"
- checksum: 10/ae3b5436d34fadeb6096367626ce987057713c566e1e7768818797e00ac5d62023d0f198c4e681eae9e20701721980b26a64a8f5b91238869592a9c6800719a2
- languageName: node
- linkType: hard
-
-"strip-ansi@npm:^3.0.0, strip-ansi@npm:^3.0.1":
- version: 3.0.1
- resolution: "strip-ansi@npm:3.0.1"
- dependencies:
- ansi-regex: "npm:^2.0.0"
- checksum: 10/9b974de611ce5075c70629c00fa98c46144043db92ae17748fb780f706f7a789e9989fd10597b7c2053ae8d1513fd707816a91f1879b2f71e6ac0b6a863db465
- languageName: node
- linkType: hard
-
-"strip-ansi@npm:^7.0.1":
- version: 7.1.2
- resolution: "strip-ansi@npm:7.1.2"
- dependencies:
- ansi-regex: "npm:^6.0.1"
- checksum: 10/db0e3f9654e519c8a33c50fc9304d07df5649388e7da06d3aabf66d29e5ad65d5e6315d8519d409c15b32fa82c1df7e11ed6f8cd50b0e4404463f0c9d77c8d0b
- languageName: node
- linkType: hard
-
-"supports-color@npm:^2.0.0":
- version: 2.0.0
- resolution: "supports-color@npm:2.0.0"
- checksum: 10/d2957d19e782a806abc3e8616b6648cc1e70c3ebe94fb1c2d43160686f6d79cd7c9f22c4853bc4a362d89d1c249ab6d429788c5f6c83b3086e6d763024bf4581
- languageName: node
- linkType: hard
-
-"supports-color@npm:^7.1.0":
- version: 7.2.0
- resolution: "supports-color@npm:7.2.0"
- dependencies:
- has-flag: "npm:^4.0.0"
- checksum: 10/c8bb7afd564e3b26b50ca6ee47572c217526a1389fe018d00345856d4a9b08ffbd61fadaf283a87368d94c3dcdb8f5ffe2650a5a65863e21ad2730ca0f05210a
- languageName: node
- linkType: hard
-
-"tar@npm:^7.4.3":
- version: 7.5.1
- resolution: "tar@npm:7.5.1"
- dependencies:
- "@isaacs/fs-minipass": "npm:^4.0.0"
- chownr: "npm:^3.0.0"
- minipass: "npm:^7.1.2"
- minizlib: "npm:^3.1.0"
- yallist: "npm:^5.0.0"
- checksum: 10/4848cd2fa2fcaf0734cf54e14bc685056eb43a74d7cc7f954c3ac88fea88c85d95b1d7896619f91aab6f2234c5eec731c18aaa201a78fcf86985bdc824ed7a00
- languageName: node
- linkType: hard
-
-"through@npm:^2.3.6":
- version: 2.3.8
- resolution: "through@npm:2.3.8"
- checksum: 10/5da78346f70139a7d213b65a0106f3c398d6bc5301f9248b5275f420abc2c4b1e77c2abc72d218dedc28c41efb2e7c312cb76a7730d04f9c2d37d247da3f4198
- languageName: node
- linkType: hard
-
-"tiny-lru@npm:^8.0.2":
- version: 8.0.2
- resolution: "tiny-lru@npm:8.0.2"
- checksum: 10/74b193d83b9edbe690b4e97e3cfd66c8a66d593c33b2c7133f132adacbf0442afbef82c679848208010137175aac3db9e68f350bef68cc299c0f2eb589554ffb
- languageName: node
- linkType: hard
-
-"tinyglobby@npm:^0.2.12":
- version: 0.2.15
- resolution: "tinyglobby@npm:0.2.15"
- dependencies:
- fdir: "npm:^6.5.0"
- picomatch: "npm:^4.0.3"
- checksum: 10/d72bd826a8b0fa5fa3929e7fe5ba48fceb2ae495df3a231b6c5408cd7d8c00b58ab5a9c2a76ba56a62ee9b5e083626f1f33599734bed1ffc4b792406408f0ca2
- languageName: node
- linkType: hard
-
-"to-regex-range@npm:^5.0.1":
- version: 5.0.1
- resolution: "to-regex-range@npm:5.0.1"
- dependencies:
- is-number: "npm:^7.0.0"
- checksum: 10/10dda13571e1f5ad37546827e9b6d4252d2e0bc176c24a101252153ef435d83696e2557fe128c4678e4e78f5f01e83711c703eef9814eb12dab028580d45980a
- languageName: node
- linkType: hard
-
-"toidentifier@npm:1.0.1":
- version: 1.0.1
- resolution: "toidentifier@npm:1.0.1"
- checksum: 10/952c29e2a85d7123239b5cfdd889a0dde47ab0497f0913d70588f19c53f7e0b5327c95f4651e413c74b785147f9637b17410ac8c846d5d4a20a5a33eb6dc3a45
- languageName: node
- linkType: hard
-
-"tr46@npm:~0.0.3":
- version: 0.0.3
- resolution: "tr46@npm:0.0.3"
- checksum: 10/8f1f5aa6cb232f9e1bdc86f485f916b7aa38caee8a778b378ffec0b70d9307873f253f5cbadbe2955ece2ac5c83d0dc14a77513166ccd0a0c7fe197e21396695
- languageName: node
- linkType: hard
-
-"triple-beam@npm:^1.3.0":
- version: 1.4.1
- resolution: "triple-beam@npm:1.4.1"
- checksum: 10/2e881a3e8e076b6f2b85b9ec9dd4a900d3f5016e6d21183ed98e78f9abcc0149e7d54d79a3f432b23afde46b0885bdcdcbff789f39bc75de796316961ec07f61
- languageName: node
- linkType: hard
-
-"ts-node@npm:^10.0.0":
- version: 10.9.2
- resolution: "ts-node@npm:10.9.2"
- dependencies:
- "@cspotcode/source-map-support": "npm:^0.8.0"
- "@tsconfig/node10": "npm:^1.0.7"
- "@tsconfig/node12": "npm:^1.0.7"
- "@tsconfig/node14": "npm:^1.0.0"
- "@tsconfig/node16": "npm:^1.0.2"
- acorn: "npm:^8.4.1"
- acorn-walk: "npm:^8.1.1"
- arg: "npm:^4.1.0"
- create-require: "npm:^1.1.0"
- diff: "npm:^4.0.1"
- make-error: "npm:^1.1.1"
- v8-compile-cache-lib: "npm:^3.0.1"
- yn: "npm:3.1.1"
- peerDependencies:
- "@swc/core": ">=1.2.50"
- "@swc/wasm": ">=1.2.50"
- "@types/node": "*"
- typescript: ">=2.7"
- peerDependenciesMeta:
- "@swc/core":
- optional: true
- "@swc/wasm":
- optional: true
- bin:
- ts-node: dist/bin.js
- ts-node-cwd: dist/bin-cwd.js
- ts-node-esm: dist/bin-esm.js
- ts-node-script: dist/bin-script.js
- ts-node-transpile-only: dist/bin-transpile.js
- ts-script: dist/bin-script-deprecated.js
- checksum: 10/a91a15b3c9f76ac462f006fa88b6bfa528130dcfb849dd7ef7f9d640832ab681e235b8a2bc58ecde42f72851cc1d5d4e22c901b0c11aa51001ea1d395074b794
- languageName: node
- linkType: hard
-
-"tsscmp@npm:1.0.6":
- version: 1.0.6
- resolution: "tsscmp@npm:1.0.6"
- checksum: 10/850405080ea3ecb158e9e01bc4e87c9edb94a829d8ad8747f30ba103fcc41a287d7949ab84d7b27c36294036a2c9878f050db15b73a1a1961abfb7688b82ac53
- languageName: node
- linkType: hard
-
-"type-is@npm:^1.6.16":
- version: 1.6.18
- resolution: "type-is@npm:1.6.18"
- dependencies:
- media-typer: "npm:0.3.0"
- mime-types: "npm:~2.1.24"
- checksum: 10/0bd9eeae5efd27d98fd63519f999908c009e148039d8e7179a074f105362d4fcc214c38b24f6cda79c87e563cbd12083a4691381ed28559220d4a10c2047bed4
- languageName: node
- linkType: hard
-
-"type-is@npm:^2.0.1":
- version: 2.0.1
- resolution: "type-is@npm:2.0.1"
- dependencies:
- content-type: "npm:^1.0.5"
- media-typer: "npm:^1.1.0"
- mime-types: "npm:^3.0.0"
- checksum: 10/bacdb23c872dacb7bd40fbd9095e6b2fca2895eedbb689160c05534d7d4810a7f4b3fd1ae87e96133c505958f6d602967a68db5ff577b85dd6be76eaa75d58af
- languageName: node
- linkType: hard
-
-"typedoc-plugin-external-module-map@npm:^2.2.0":
- version: 2.2.0
- resolution: "typedoc-plugin-external-module-map@npm:2.2.0"
- dependencies:
- "@types/node": "npm:^20.14.14"
- peerDependencies:
- typedoc: ">=0.27 <2.0"
- checksum: 10/bf669f336d5680f2dc41c1dd0d8f7cff26623499784d85935100a104282068f93eff94dfb9811fccf99c693f18494509fa7c95f209f9f7f03991bafb58306b2c
- languageName: node
- linkType: hard
-
-"typedoc@npm:^0.28.13":
- version: 0.28.14
- resolution: "typedoc@npm:0.28.14"
- dependencies:
- "@gerrit0/mini-shiki": "npm:^3.12.0"
- lunr: "npm:^2.3.9"
- markdown-it: "npm:^14.1.0"
- minimatch: "npm:^9.0.5"
- yaml: "npm:^2.8.1"
- peerDependencies:
- typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x
- bin:
- typedoc: bin/typedoc
- checksum: 10/6b2bb87c85de96475f60ba2a92e0cb7169fef138cce4d91c949267ca9268c4fc97c2db9702f306ad5e594aa5244cc9aa563791ec31dd1b1d5288cf1ab08aa7a0
- languageName: node
- linkType: hard
-
-"typescript@npm:^5.9.3":
- version: 5.9.3
- resolution: "typescript@npm:5.9.3"
- bin:
- tsc: bin/tsc
- tsserver: bin/tsserver
- checksum: 10/c089d9d3da2729fd4ac517f9b0e0485914c4b3c26f80dc0cffcb5de1719a17951e92425d55db59515c1a7ddab65808466debb864d0d56dcf43f27007d0709594
- languageName: node
- linkType: hard
-
-"typescript@patch:typescript@npm%3A^5.9.3#optional!builtin":
- version: 5.9.3
- resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5"
- bin:
- tsc: bin/tsc
- tsserver: bin/tsserver
- checksum: 10/696e1b017bc2635f4e0c94eb4435357701008e2f272f553d06e35b494b8ddc60aa221145e286c28ace0c89ee32827a28c2040e3a69bdc108b1a5dc8fb40b72e3
- languageName: node
- linkType: hard
-
-"uc.micro@npm:^2.0.0, uc.micro@npm:^2.1.0":
- version: 2.1.0
- resolution: "uc.micro@npm:2.1.0"
- checksum: 10/37197358242eb9afe367502d4638ac8c5838b78792ab218eafe48287b0ed28aaca268ec0392cc5729f6c90266744de32c06ae938549aee041fc93b0f9672d6b2
- languageName: node
- linkType: hard
-
-"undici-types@npm:~6.21.0":
- version: 6.21.0
- resolution: "undici-types@npm:6.21.0"
- checksum: 10/ec8f41aa4359d50f9b59fa61fe3efce3477cc681908c8f84354d8567bb3701fafdddf36ef6bff307024d3feb42c837cf6f670314ba37fc8145e219560e473d14
- languageName: node
- linkType: hard
-
-"undici-types@npm:~7.14.0":
- version: 7.14.0
- resolution: "undici-types@npm:7.14.0"
- checksum: 10/0f8709b21437697af35801e33bddbe9992e0cf1771959c41850b1946f63822b825e03ce99f44bf19e4f5c3ccc5166e0be59f541565c36ce86163dc2c5870bc62
- languageName: node
- linkType: hard
-
-"unfetch@npm:^4.2.0":
- version: 4.2.0
- resolution: "unfetch@npm:4.2.0"
- checksum: 10/d4924178060b6828d858acef3ce2baea69acd3f3f9e2429fd503a0ed0d2b1ed0ee107786aceadfd167ce884fad12d22b5288eb865a3ea036979b8358b8555c9a
- languageName: node
- linkType: hard
-
-"unique-filename@npm:^4.0.0":
- version: 4.0.0
- resolution: "unique-filename@npm:4.0.0"
- dependencies:
- unique-slug: "npm:^5.0.0"
- checksum: 10/6a62094fcac286b9ec39edbd1f8f64ff92383baa430af303dfed1ffda5e47a08a6b316408554abfddd9730c78b6106bef4ca4d02c1231a735ddd56ced77573df
- languageName: node
- linkType: hard
-
-"unique-slug@npm:^5.0.0":
- version: 5.0.0
- resolution: "unique-slug@npm:5.0.0"
- dependencies:
- imurmurhash: "npm:^0.1.4"
- checksum: 10/beafdf3d6f44990e0a5ce560f8f881b4ee811be70b6ba0db25298c31c8cf525ed963572b48cd03be1c1349084f9e339be4241666d7cf1ebdad20598d3c652b27
- languageName: node
- linkType: hard
-
-"unpipe@npm:1.0.0":
- version: 1.0.0
- resolution: "unpipe@npm:1.0.0"
- checksum: 10/4fa18d8d8d977c55cb09715385c203197105e10a6d220087ec819f50cb68870f02942244f1017565484237f1f8c5d3cd413631b1ae104d3096f24fdfde1b4aa2
- languageName: node
- linkType: hard
-
-"url-join@npm:4.0.1":
- version: 4.0.1
- resolution: "url-join@npm:4.0.1"
- checksum: 10/b53b256a9a36ed6b0f6768101e78ca97f32d7b935283fd29ce19d0bbfb6f88aa80aa6c03fd87f2f8978ab463a6539f597a63051e7086f3379685319a7495f709
- languageName: node
- linkType: hard
-
-"v8-compile-cache-lib@npm:^3.0.1":
- version: 3.0.1
- resolution: "v8-compile-cache-lib@npm:3.0.1"
- checksum: 10/88d3423a52b6aaf1836be779cab12f7016d47ad8430dffba6edf766695e6d90ad4adaa3d8eeb512cc05924f3e246c4a4ca51e089dccf4402caa536b5e5be8961
- languageName: node
- linkType: hard
-
-"validate-npm-package-name@npm:^4.0.0":
- version: 4.0.0
- resolution: "validate-npm-package-name@npm:4.0.0"
- dependencies:
- builtins: "npm:^5.0.0"
- checksum: 10/a32fd537bad17fcb59cfd58ae95a414d443866020d448ec3b22e8d40550cb585026582a57efbe1f132b882eea4da8ac38ee35f7be0dd72988a3cb55d305a20c1
- languageName: node
- linkType: hard
-
-"vary@npm:^1.1.2":
- version: 1.1.2
- resolution: "vary@npm:1.1.2"
- checksum: 10/31389debef15a480849b8331b220782230b9815a8e0dbb7b9a8369559aed2e9a7800cd904d4371ea74f4c3527db456dc8e7ac5befce5f0d289014dbdf47b2242
- languageName: node
- linkType: hard
-
-"vorpal@npm:^1.12.0":
- version: 1.12.0
- resolution: "vorpal@npm:1.12.0"
- dependencies:
- babel-polyfill: "npm:^6.3.14"
- chalk: "npm:^1.1.0"
- in-publish: "npm:^2.0.0"
- inquirer: "npm:0.11.0"
- lodash: "npm:^4.5.1"
- log-update: "npm:^1.0.2"
- minimist: "npm:^1.2.0"
- node-localstorage: "npm:^0.6.0"
- strip-ansi: "npm:^3.0.0"
- wrap-ansi: "npm:^2.0.0"
- checksum: 10/7bc1f40863a7fdcbd6c8a8cf463a59bac5c433c2a836ff86e8574676fa8a2ca17d19806d4aa24cf5a23a3350b59fe9c9a4a430d806ccb245dbb47ebad834d181
- languageName: node
- linkType: hard
-
-"webidl-conversions@npm:^3.0.0":
- version: 3.0.1
- resolution: "webidl-conversions@npm:3.0.1"
- checksum: 10/b65b9f8d6854572a84a5c69615152b63371395f0c5dcd6729c45789052296df54314db2bc3e977df41705eacb8bc79c247cee139a63fa695192f95816ed528ad
- languageName: node
- linkType: hard
-
-"whatwg-url@npm:^5.0.0":
- version: 5.0.0
- resolution: "whatwg-url@npm:5.0.0"
- dependencies:
- tr46: "npm:~0.0.3"
- webidl-conversions: "npm:^3.0.0"
- checksum: 10/f95adbc1e80820828b45cc671d97da7cd5e4ef9deb426c31bcd5ab00dc7103042291613b3ef3caec0a2335ed09e0d5ed026c940755dbb6d404e2b27f940fdf07
- languageName: node
- linkType: hard
-
-"which@npm:^2.0.1":
- version: 2.0.2
- resolution: "which@npm:2.0.2"
- dependencies:
- isexe: "npm:^2.0.0"
- bin:
- node-which: ./bin/node-which
- checksum: 10/4782f8a1d6b8fc12c65e968fea49f59752bf6302dc43036c3bf87da718a80710f61a062516e9764c70008b487929a73546125570acea95c5b5dcc8ac3052c70f
- languageName: node
- linkType: hard
-
-"which@npm:^5.0.0":
- version: 5.0.0
- resolution: "which@npm:5.0.0"
- dependencies:
- isexe: "npm:^3.1.1"
- bin:
- node-which: bin/which.js
- checksum: 10/6ec99e89ba32c7e748b8a3144e64bfc74aa63e2b2eacbb61a0060ad0b961eb1a632b08fb1de067ed59b002cec3e21de18299216ebf2325ef0f78e0f121e14e90
- languageName: node
- linkType: hard
-
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
- version: 7.0.0
- resolution: "wrap-ansi@npm:7.0.0"
- dependencies:
- ansi-styles: "npm:^4.0.0"
- string-width: "npm:^4.1.0"
- strip-ansi: "npm:^6.0.0"
- checksum: 10/cebdaeca3a6880da410f75209e68cd05428580de5ad24535f22696d7d9cab134d1f8498599f344c3cf0fb37c1715807a183778d8c648d6cc0cb5ff2bb4236540
- languageName: node
- linkType: hard
-
-"wrap-ansi@npm:^2.0.0":
- version: 2.1.0
- resolution: "wrap-ansi@npm:2.1.0"
- dependencies:
- string-width: "npm:^1.0.1"
- strip-ansi: "npm:^3.0.1"
- checksum: 10/cf66d33f62f2edf0aac52685da98194e47ddf4ceb81d9f98f294b46ffbbf8662caa72a905b343aeab8d6a16cade982be5fc45df99235b07f781ebf68f051ca98
- languageName: node
- linkType: hard
-
-"wrap-ansi@npm:^8.1.0":
- version: 8.1.0
- resolution: "wrap-ansi@npm:8.1.0"
- dependencies:
- ansi-styles: "npm:^6.1.0"
- string-width: "npm:^5.0.1"
- strip-ansi: "npm:^7.0.1"
- checksum: 10/7b1e4b35e9bb2312d2ee9ee7dc95b8cb5f8b4b5a89f7dde5543fe66c1e3715663094defa50d75454ac900bd210f702d575f15f3f17fa9ec0291806d2578d1ddf
- languageName: node
- linkType: hard
-
-"wrappy@npm:1":
- version: 1.0.2
- resolution: "wrappy@npm:1.0.2"
- checksum: 10/159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5
- languageName: node
- linkType: hard
-
-"yallist@npm:^4.0.0":
- version: 4.0.0
- resolution: "yallist@npm:4.0.0"
- checksum: 10/4cb02b42b8a93b5cf50caf5d8e9beb409400a8a4d85e83bb0685c1457e9ac0b7a00819e9f5991ac25ffabb56a78e2f017c1acc010b3a1babfe6de690ba531abd
- languageName: node
- linkType: hard
-
-"yallist@npm:^5.0.0":
- version: 5.0.0
- resolution: "yallist@npm:5.0.0"
- checksum: 10/1884d272d485845ad04759a255c71775db0fac56308764b4c77ea56a20d56679fad340213054c8c9c9c26fcfd4c4b2a90df993b7e0aaf3cdb73c618d1d1a802a
- languageName: node
- linkType: hard
-
-"yaml@npm:^2.8.1":
- version: 2.8.1
- resolution: "yaml@npm:2.8.1"
- bin:
- yaml: bin.mjs
- checksum: 10/eae07b3947d405012672ec17ce27348aea7d1fa0534143355d24a43a58f5e05652157ea2182c4fe0604f0540be71f99f1173f9d61018379404507790dff17665
- languageName: node
- linkType: hard
-
-"yn@npm:3.1.1":
- version: 3.1.1
- resolution: "yn@npm:3.1.1"
- checksum: 10/2c487b0e149e746ef48cda9f8bad10fc83693cd69d7f9dcd8be4214e985de33a29c9e24f3c0d6bcf2288427040a8947406ab27f7af67ee9456e6b84854f02dd6
- languageName: node
- linkType: hard
-
-"zod@npm:^3.19.1":
- version: 3.25.76
- resolution: "zod@npm:3.25.76"
- checksum: 10/f0c963ec40cd96858451d1690404d603d36507c1fc9682f2dae59ab38b578687d542708a7fdbf645f77926f78c9ed558f57c3d3aa226c285f798df0c4da16995
- languageName: node
- linkType: hard
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@cspotcode/source-map-support@^0.8.0":
+ version "0.8.1"
+ resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz"
+ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
+ dependencies:
+ "@jridgewell/trace-mapping" "0.3.9"
+
+"@gerrit0/mini-shiki@^3.23.0":
+ version "3.23.0"
+ resolved "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz"
+ integrity sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==
+ dependencies:
+ "@shikijs/engine-oniguruma" "^3.23.0"
+ "@shikijs/langs" "^3.23.0"
+ "@shikijs/themes" "^3.23.0"
+ "@shikijs/types" "^3.23.0"
+ "@shikijs/vscode-textmate" "^10.0.2"
+
+"@hapi/bourne@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz"
+ integrity sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==
+
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.1.2"
+ resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
+ integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.5.5"
+ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz"
+ integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
+
+"@jridgewell/trace-mapping@0.3.9":
+ version "0.3.9"
+ resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz"
+ integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@koa/router@^14.0.0":
+ version "14.0.0"
+ resolved "https://registry.npmjs.org/@koa/router/-/router-14.0.0.tgz"
+ integrity sha512-LBSu5K0qAaaQcXX/0WIB9PGDevyCxxpnc1uq13vV/CgObaVxuis5hKl3Eboq/8gcb6ebnkAStW9NB/Em2eYyFA==
+ dependencies:
+ debug "^4.4.1"
+ http-errors "^2.0.0"
+ koa-compose "^4.1.0"
+ path-to-regexp "^8.2.0"
+
+"@noble/hashes@^1.1.5":
+ version "1.8.0"
+ resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz"
+ integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==
+
+"@paralleldrive/cuid2@^2.2.2":
+ version "2.3.1"
+ resolved "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz"
+ integrity sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==
+ dependencies:
+ "@noble/hashes" "^1.1.5"
+
+"@schemastore/package@^0.0.6":
+ version "0.0.6"
+ resolved "https://registry.npmjs.org/@schemastore/package/-/package-0.0.6.tgz"
+ integrity sha512-uNloNHoyHttSSdeuEkkSC+mdxJXMKlcUPOMb//qhQbIQijXg8x54VmAw3jm6GJZQ5DBtIqGBd66zEQCDCChQVA==
+
+"@shikijs/engine-oniguruma@^3.23.0":
+ version "3.23.0"
+ resolved "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz"
+ integrity sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==
+ dependencies:
+ "@shikijs/types" "3.23.0"
+ "@shikijs/vscode-textmate" "^10.0.2"
+
+"@shikijs/langs@^3.23.0":
+ version "3.23.0"
+ resolved "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz"
+ integrity sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==
+ dependencies:
+ "@shikijs/types" "3.23.0"
+
+"@shikijs/themes@^3.23.0":
+ version "3.23.0"
+ resolved "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz"
+ integrity sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==
+ dependencies:
+ "@shikijs/types" "3.23.0"
+
+"@shikijs/types@^3.23.0", "@shikijs/types@3.23.0":
+ version "3.23.0"
+ resolved "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz"
+ integrity sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==
+ dependencies:
+ "@shikijs/vscode-textmate" "^10.0.2"
+ "@types/hast" "^3.0.4"
+
+"@shikijs/vscode-textmate@^10.0.2":
+ version "10.0.2"
+ resolved "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz"
+ integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==
+
+"@skeldjs/au-client@^3.0.3":
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/@skeldjs/au-client/-/au-client-3.0.3.tgz"
+ integrity sha512-ZvjcLCjQcljl2EUtPIUL5uqG1pyjvU1rgL02CxkGc6IFNW3VF+rDHJZHjjsun7gMzyoTOTP+zqTUf/7/5KQQKw==
+
+"@skeldjs/au-constants@^3.0.3", "@skeldjs/au-constants@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/@skeldjs/au-constants/-/au-constants-3.0.3.tgz"
+ integrity sha512-A8xD3XuX5eyrw+HWuqNNrMhw1TQ78Vdq8Zypg5cgYxWe12hgpqf22X9XgOkru1M/943u1xmkQoNvASDD0/j38g==
+
+"@skeldjs/au-core@^3.0.3":
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/@skeldjs/au-core/-/au-core-3.0.3.tgz"
+ integrity sha512-Mobo6sn8CjLDmo9DT1YTQlQMtVzuTQy9+obK5e4vVhhXSDfebl4avk6A+6l9ZW/FqgQfZOARmFpV30vZGdJ7Cg==
+ dependencies:
+ "@skeldjs/au-constants" "3.0.3"
+ "@skeldjs/au-protocol" "3.0.3"
+ "@skeldjs/events" "3.0.3"
+ "@skeldjs/hazel" "3.0.3"
+
+"@skeldjs/au-protocol@^3.0.3", "@skeldjs/au-protocol@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/@skeldjs/au-protocol/-/au-protocol-3.0.3.tgz"
+ integrity sha512-35KuC7ZOgfMlwn2NSfpabC0di76NAYsqjV2oWhdrmxvXcXPsOgoXx1R4K8V8dsh5Chj9odCysyjUOMbsLkI5mQ==
+ dependencies:
+ "@skeldjs/au-constants" "3.0.3"
+ "@skeldjs/hazel" "3.0.3"
+
+"@skeldjs/au-text@^3.0.3":
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/@skeldjs/au-text/-/au-text-3.0.3.tgz"
+ integrity sha512-00fKMnEViEPFNOF6q+0eM7IXvYgXcqIDhZGNTbKwTLqWhhwWp0QJgXBBRws4otoAd0/jiyS/zFYq+WzA3Vh57Q==
+
+"@skeldjs/events@^3.0.3", "@skeldjs/events@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/@skeldjs/events/-/events-3.0.3.tgz"
+ integrity sha512-ZONbKbfq1aIY8JxSFWBmhxuWcn7mvvEvxM1OUO/e/+0zwQYqWCN5Jwv2u+rumXqPoerVBT1kd/I6JZ2kXaM5/A==
+
+"@skeldjs/hazel@^3.0.3", "@skeldjs/hazel@3.0.3":
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/@skeldjs/hazel/-/hazel-3.0.3.tgz"
+ integrity sha512-AQCsgcJVBGXSLd1mkgyvpttL3n5QUg7919pc18gmdIWGh8XJ9yl3XoGmg5uUCZXVcpQ21RfrrdL98zEfQQyumQ==
+
+"@tsconfig/node10@^1.0.7":
+ version "1.0.12"
+ resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz"
+ integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==
+
+"@tsconfig/node12@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz"
+ integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
+
+"@tsconfig/node14@^1.0.0":
+ version "1.0.3"
+ resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz"
+ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
+
+"@tsconfig/node16@^1.0.2":
+ version "1.0.4"
+ resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz"
+ integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
+
+"@types/accepts@*":
+ version "1.3.7"
+ resolved "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz"
+ integrity sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/body-parser@*":
+ version "1.19.6"
+ resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz"
+ integrity sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==
+ dependencies:
+ "@types/connect" "*"
+ "@types/node" "*"
+
+"@types/co-body@^6.1.0":
+ version "6.1.3"
+ resolved "https://registry.npmjs.org/@types/co-body/-/co-body-6.1.3.tgz"
+ integrity sha512-UhuhrQ5hclX6UJctv5m4Rfp52AfG9o9+d9/HwjxhVB5NjXxr5t9oKgJxN8xRHgr35oo8meUEHUPFWiKg6y71aA==
+ dependencies:
+ "@types/node" "*"
+ "@types/qs" "*"
+
+"@types/connect@*":
+ version "3.4.38"
+ resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz"
+ integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==
+ dependencies:
+ "@types/node" "*"
+
+"@types/content-disposition@*":
+ version "0.5.9"
+ resolved "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.9.tgz"
+ integrity sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==
+
+"@types/cookies@*":
+ version "0.9.2"
+ resolved "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.2.tgz"
+ integrity sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==
+ dependencies:
+ "@types/connect" "*"
+ "@types/express" "*"
+ "@types/keygrip" "*"
+ "@types/node" "*"
+
+"@types/express-serve-static-core@^5.0.0":
+ version "5.1.1"
+ resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz"
+ integrity sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==
+ dependencies:
+ "@types/node" "*"
+ "@types/qs" "*"
+ "@types/range-parser" "*"
+ "@types/send" "*"
+
+"@types/express@*":
+ version "5.0.6"
+ resolved "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz"
+ integrity sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==
+ dependencies:
+ "@types/body-parser" "*"
+ "@types/express-serve-static-core" "^5.0.0"
+ "@types/serve-static" "^2"
+
+"@types/formidable@^2.0.5":
+ version "2.0.6"
+ resolved "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.6.tgz"
+ integrity sha512-L4HcrA05IgQyNYJj6kItuIkXrInJvsXTPC5B1i64FggWKKqSL+4hgt7asiSNva75AoLQjq29oPxFfU4GAQ6Z2w==
+ dependencies:
+ "@types/node" "*"
+
+"@types/hast@^3.0.4":
+ version "3.0.4"
+ resolved "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz"
+ integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==
+ dependencies:
+ "@types/unist" "*"
+
+"@types/http-assert@*":
+ version "1.5.6"
+ resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.6.tgz"
+ integrity sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==
+
+"@types/http-errors@*":
+ version "2.0.5"
+ resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz"
+ integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==
+
+"@types/keygrip@*":
+ version "1.0.6"
+ resolved "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz"
+ integrity sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==
+
+"@types/koa__router@^12":
+ version "12.0.5"
+ resolved "https://registry.npmjs.org/@types/koa__router/-/koa__router-12.0.5.tgz"
+ integrity sha512-1HeLxuDn4n5it1yZYCSyOYXo++73zT0ffoviXnPxbwbxLbvDFEvWD9ZzpRiIpK4oKR0pi+K+Mk/ZjyROjW3HSw==
+ dependencies:
+ "@types/koa" "*"
+
+"@types/koa-compose@*":
+ version "3.2.9"
+ resolved "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.9.tgz"
+ integrity sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==
+ dependencies:
+ "@types/koa" "*"
+
+"@types/koa@*", "@types/koa@^2", "@types/koa@^2.13.5":
+ version "2.15.2"
+ resolved "https://registry.npmjs.org/@types/koa/-/koa-2.15.2.tgz"
+ integrity sha512-CB+iyjjh1uS5N6/CKwXvw0qA7USMS2WVc4Tjf660yCjhdvqzNr8gdFcIawB41zGGptOQ+d1fnpaQWIIUXYxR3w==
+ dependencies:
+ "@types/accepts" "*"
+ "@types/content-disposition" "*"
+ "@types/cookies" "*"
+ "@types/http-assert" "*"
+ "@types/http-errors" "*"
+ "@types/keygrip" "*"
+ "@types/koa-compose" "*"
+ "@types/node" "*"
+
+"@types/minimatch@^3.0.5":
+ version "3.0.5"
+ resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz"
+ integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
+
+"@types/node@*", "@types/node@^24.7.0":
+ version "24.13.2"
+ resolved "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz"
+ integrity sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==
+ dependencies:
+ undici-types "~7.18.0"
+
+"@types/node@^20.14.14":
+ version "20.19.43"
+ resolved "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz"
+ integrity sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==
+ dependencies:
+ undici-types "~6.21.0"
+
+"@types/prompts@^2.0.14":
+ version "2.4.9"
+ resolved "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz"
+ integrity sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==
+ dependencies:
+ "@types/node" "*"
+ kleur "^3.0.3"
+
+"@types/qs@*":
+ version "6.15.1"
+ resolved "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz"
+ integrity sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==
+
+"@types/range-parser@*":
+ version "1.2.7"
+ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz"
+ integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
+
+"@types/send@*":
+ version "1.2.1"
+ resolved "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz"
+ integrity sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/serve-static@^2":
+ version "2.2.0"
+ resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz"
+ integrity sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==
+ dependencies:
+ "@types/http-errors" "*"
+ "@types/node" "*"
+
+"@types/triple-beam@^1.3.2":
+ version "1.3.5"
+ resolved "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz"
+ integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==
+
+"@types/unist@*":
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz"
+ integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==
+
+"@types/vorpal@^1.12.0":
+ version "1.12.8"
+ resolved "https://registry.npmjs.org/@types/vorpal/-/vorpal-1.12.8.tgz"
+ integrity sha512-Qt+Yxa1q6QCaYMxZFXlyPOF3ktIscTelNr1AFYuKM7/Dhlki4gvc476uFyA/hYvskSA6V8W+55x9FjlbAPcYdQ==
+
+"@types/yargs-parser@*":
+ version "21.0.3"
+ resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz"
+ integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==
+
+"@types/yargs@^17.0.0":
+ version "17.0.35"
+ resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz"
+ integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==
+ dependencies:
+ "@types/yargs-parser" "*"
+
+accepts@^1.3.8:
+ version "1.3.8"
+ resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz"
+ integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
+ dependencies:
+ mime-types "~2.1.34"
+ negotiator "0.6.3"
+
+acorn-walk@^8.1.1:
+ version "8.3.5"
+ resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz"
+ integrity sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==
+ dependencies:
+ acorn "^8.11.0"
+
+acorn@^8.11.0, acorn@^8.4.1:
+ version "8.17.0"
+ resolved "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz"
+ integrity sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==
+
+ansi-escapes@^1.0.0, ansi-escapes@^1.1.0:
+ version "1.4.0"
+ resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz"
+ integrity sha512-wiXutNjDUlNEDWHcYH3jtZUhd3c4/VojassD8zHdHCY13xbZy2XbW+NKQwA0tWGBVzDA9qEzYwfoSsWmviidhw==
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz"
+ integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==
+
+ansi-styles@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz"
+ integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==
+
+ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+anymatch@~3.1.2:
+ version "3.1.3"
+ resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz"
+ integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+ dependencies:
+ normalize-path "^3.0.0"
+ picomatch "^2.0.4"
+
+arg@^4.1.0:
+ version "4.1.3"
+ resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz"
+ integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+asap@^2.0.0:
+ version "2.0.6"
+ resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz"
+ integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
+
+babel-polyfill@^6.3.14:
+ version "6.26.0"
+ resolved "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz"
+ integrity sha512-F2rZGQnAdaHWQ8YAoeRbukc7HS9QgdgeyJ0rQDd485v9opwuPvjpPFcOOT/WmkKTdgy9ESgSPXDcTNpzrGr6iQ==
+ dependencies:
+ babel-runtime "^6.26.0"
+ core-js "^2.5.0"
+ regenerator-runtime "^0.10.5"
+
+babel-runtime@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz"
+ integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==
+ dependencies:
+ core-js "^2.4.0"
+ regenerator-runtime "^0.11.0"
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+balanced-match@^4.0.2:
+ version "4.0.4"
+ resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz"
+ integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==
+
+binary-extensions@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz"
+ integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
+
+brace-expansion@^1.1.7:
+ version "1.1.15"
+ resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz"
+ integrity sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+brace-expansion@^5.0.5:
+ version "5.0.6"
+ resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz"
+ integrity sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==
+ dependencies:
+ balanced-match "^4.0.2"
+
+braces@~3.0.2:
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz"
+ integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+ dependencies:
+ fill-range "^7.1.1"
+
+builtins@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz"
+ integrity sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==
+ dependencies:
+ semver "^7.0.0"
+
+bytes@~3.1.2:
+ version "3.1.2"
+ resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz"
+ integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
+call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz"
+ integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
+ dependencies:
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+
+call-bound@^1.0.2:
+ version "1.0.4"
+ resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz"
+ integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
+ dependencies:
+ call-bind-apply-helpers "^1.0.2"
+ get-intrinsic "^1.3.0"
+
+chalk@^1.0.0:
+ version "1.1.3"
+ resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
+ integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==
+ dependencies:
+ ansi-styles "^2.2.1"
+ escape-string-regexp "^1.0.2"
+ has-ansi "^2.0.0"
+ strip-ansi "^3.0.0"
+ supports-color "^2.0.0"
+
+chalk@^1.1.0:
+ version "1.1.3"
+ resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
+ integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==
+ dependencies:
+ ansi-styles "^2.2.1"
+ escape-string-regexp "^1.0.2"
+ has-ansi "^2.0.0"
+ strip-ansi "^3.0.0"
+ supports-color "^2.0.0"
+
+chalk@^4.1.1:
+ version "4.1.2"
+ resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+chokidar@^3.5.2:
+ version "3.6.0"
+ resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
+ integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
+ dependencies:
+ anymatch "~3.1.2"
+ braces "~3.0.2"
+ glob-parent "~5.1.2"
+ is-binary-path "~2.1.0"
+ is-glob "~4.0.1"
+ normalize-path "~3.0.0"
+ readdirp "~3.6.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+cli-cursor@^1.0.1, cli-cursor@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz"
+ integrity sha512-25tABq090YNKkF6JH7lcwO0zFJTRke4Jcq9iX2nr/Sz0Cjjv4gckmwlW6Ty/aoyFd6z3ysR2hMGC2GFugmBo6A==
+ dependencies:
+ restore-cursor "^1.0.1"
+
+cli-width@^1.0.1:
+ version "1.1.1"
+ resolved "https://registry.npmjs.org/cli-width/-/cli-width-1.1.1.tgz"
+ integrity sha512-eMU2akIeEIkCxGXUNmDnJq1KzOIiPnJ+rKqRe6hcxE3vIOPvpMrBYOn/Bl7zNlYJj/zQxXquAnozHUCf9Whnsg==
+
+co-body@^6.1.0:
+ version "6.2.0"
+ resolved "https://registry.npmjs.org/co-body/-/co-body-6.2.0.tgz"
+ integrity sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==
+ dependencies:
+ "@hapi/bourne" "^3.0.0"
+ inflation "^2.0.0"
+ qs "^6.5.2"
+ raw-body "^2.3.3"
+ type-is "^1.6.16"
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz"
+ integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+compare-versions@^3.6.0:
+ version "3.6.0"
+ resolved "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz"
+ integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+ integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+content-disposition@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz"
+ integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==
+
+content-type@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz"
+ integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
+
+content-type@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz"
+ integrity sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==
+
+cookies@~0.9.1:
+ version "0.9.1"
+ resolved "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz"
+ integrity sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==
+ dependencies:
+ depd "~2.0.0"
+ keygrip "~1.1.0"
+
+core-js@^2.4.0, core-js@^2.5.0:
+ version "2.6.12"
+ resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz"
+ integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
+
+create-require@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
+ integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
+
+debug@^4.4.1:
+ version "4.4.3"
+ resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
+ integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
+ dependencies:
+ ms "^2.1.3"
+
+deep-equal@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz"
+ integrity sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==
+
+delegates@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz"
+ integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
+
+depd@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"
+ integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
+
+depd@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
+ integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
+destroy@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
+ integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
+
+dezalgo@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz"
+ integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==
+ dependencies:
+ asap "^2.0.0"
+ wrappy "1"
+
+diff@^4.0.1:
+ version "4.0.4"
+ resolved "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz"
+ integrity sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==
+
+dotenv@^10.0.0:
+ version "10.0.0"
+ resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz"
+ integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
+
+dunder-proto@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz"
+ integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
+ dependencies:
+ call-bind-apply-helpers "^1.0.1"
+ es-errors "^1.3.0"
+ gopd "^1.2.0"
+
+ee-first@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
+ integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
+
+encodeurl@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz"
+ integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
+
+entities@^4.4.0:
+ version "4.5.0"
+ resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz"
+ integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+
+es-define-property@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz"
+ integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
+
+es-errors@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz"
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
+es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz"
+ integrity sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==
+ dependencies:
+ es-errors "^1.3.0"
+
+escape-html@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"
+ integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
+ integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+exit-hook@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz"
+ integrity sha512-MsG3prOVw1WtLXAZbM3KiYtooKR1LvxHh3VHsVtIy0uiUu8usxgB/94DP2HxtD/661lLdB6yzQ09lGJSQr6nkg==
+
+figures@^1.3.5:
+ version "1.7.0"
+ resolved "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz"
+ integrity sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==
+ dependencies:
+ escape-string-regexp "^1.0.5"
+ object-assign "^4.1.0"
+
+fill-range@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz"
+ integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+formidable@^2.0.1:
+ version "2.1.5"
+ resolved "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz"
+ integrity sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==
+ dependencies:
+ "@paralleldrive/cuid2" "^2.2.2"
+ dezalgo "^1.0.4"
+ once "^1.4.0"
+ qs "^6.11.0"
+
+fresh@~0.5.2:
+ version "0.5.2"
+ resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
+ integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
+
+fromentries@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz"
+ integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==
+
+function-bind@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
+ integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
+ integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
+ dependencies:
+ call-bind-apply-helpers "^1.0.2"
+ es-define-property "^1.0.1"
+ es-errors "^1.3.0"
+ es-object-atoms "^1.1.1"
+ function-bind "^1.1.2"
+ get-proto "^1.0.1"
+ gopd "^1.2.0"
+ has-symbols "^1.1.0"
+ hasown "^2.0.2"
+ math-intrinsics "^1.1.0"
+
+get-proto@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz"
+ integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
+ dependencies:
+ dunder-proto "^1.0.1"
+ es-object-atoms "^1.0.0"
+
+glob-parent@~5.1.2:
+ version "5.1.2"
+ resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+gopd@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz"
+ integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
+
+has-ansi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz"
+ integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==
+ dependencies:
+ ansi-regex "^2.0.0"
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-symbols@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz"
+ integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
+
+hasown@^2.0.2:
+ version "2.0.4"
+ resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz"
+ integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==
+ dependencies:
+ function-bind "^1.1.2"
+
+http-assert@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz"
+ integrity sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==
+ dependencies:
+ deep-equal "~1.0.1"
+ http-errors "~1.8.0"
+
+http-errors@^2.0.0, http-errors@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz"
+ integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==
+ dependencies:
+ depd "~2.0.0"
+ inherits "~2.0.4"
+ setprototypeof "~1.2.0"
+ statuses "~2.0.2"
+ toidentifier "~1.0.1"
+
+http-errors@~1.8.0:
+ version "1.8.1"
+ resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz"
+ integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==
+ dependencies:
+ depd "~1.1.2"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses ">= 1.5.0 < 2"
+ toidentifier "1.0.1"
+
+iconv-lite@~0.4.24:
+ version "0.4.24"
+ resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+in-publish@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz"
+ integrity sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==
+
+inflation@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/inflation/-/inflation-2.1.0.tgz"
+ integrity sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==
+
+inherits@~2.0.4, inherits@2.0.4:
+ version "2.0.4"
+ resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inquirer@0.11.0:
+ version "0.11.0"
+ resolved "https://registry.npmjs.org/inquirer/-/inquirer-0.11.0.tgz"
+ integrity sha512-LIwC+g/fJbmKhDm341+RqDIV4jPf/n3pMway9xg8Ovt6CCQo1ozXhmuKTcoNIWhWJJKsSGZP+Rnuq7JgM7mE2A==
+ dependencies:
+ ansi-escapes "^1.1.0"
+ ansi-regex "^2.0.0"
+ chalk "^1.0.0"
+ cli-cursor "^1.0.1"
+ cli-width "^1.0.1"
+ figures "^1.3.5"
+ lodash "^3.3.1"
+ readline2 "^1.0.1"
+ run-async "^0.1.0"
+ rx-lite "^3.1.2"
+ strip-ansi "^3.0.0"
+ through "^2.3.6"
+
+is-binary-path@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz"
+ integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+ dependencies:
+ binary-extensions "^2.0.0"
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz"
+ integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-glob@^4.0.1, is-glob@~4.0.1:
+ version "4.0.3"
+ resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+isomorphic-unfetch@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz"
+ integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==
+ dependencies:
+ node-fetch "^2.6.1"
+ unfetch "^4.2.0"
+
+keygrip@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz"
+ integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==
+ dependencies:
+ tsscmp "1.0.6"
+
+kleur@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz"
+ integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
+
+koa-body@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.npmjs.org/koa-body/-/koa-body-6.0.1.tgz"
+ integrity sha512-M8ZvMD8r+kPHy28aWP9VxL7kY8oPWA+C7ZgCljrCMeaU7uX6wsIQgDHskyrAr9sw+jqnIXyv4Mlxri5R4InIJg==
+ dependencies:
+ "@types/co-body" "^6.1.0"
+ "@types/formidable" "^2.0.5"
+ "@types/koa" "^2.13.5"
+ co-body "^6.1.0"
+ formidable "^2.0.1"
+ zod "^3.19.1"
+
+koa-compose@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz"
+ integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==
+
+koa@^3.0.3:
+ version "3.2.1"
+ resolved "https://registry.npmjs.org/koa/-/koa-3.2.1.tgz"
+ integrity sha512-e7IpWJrnanNUroVK2taAgMxoEZvHLXdQiNjeExSu/DEIWm83jaKGBgb7tLmu2rMYpA027qFB3iLR/k3AVpFRnA==
+ dependencies:
+ accepts "^1.3.8"
+ content-disposition "~1.0.1"
+ content-type "^1.0.5"
+ cookies "~0.9.1"
+ delegates "^1.0.0"
+ destroy "^1.2.0"
+ encodeurl "^2.0.0"
+ escape-html "^1.0.3"
+ fresh "~0.5.2"
+ http-assert "^1.5.0"
+ http-errors "^2.0.0"
+ koa-compose "^4.1.0"
+ mime-types "^3.0.1"
+ on-finished "^2.4.1"
+ parseurl "^1.3.3"
+ statuses "^2.0.1"
+ type-is "^2.0.1"
+ vary "^1.1.2"
+
+linkify-it@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz"
+ integrity sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==
+ dependencies:
+ uc.micro "^2.0.0"
+
+lodash@^3.3.1:
+ version "3.10.1"
+ resolved "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz"
+ integrity sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ==
+
+lodash@^4.5.1:
+ version "4.18.1"
+ resolved "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz"
+ integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==
+
+log-update@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz"
+ integrity sha512-4vSow8gbiGnwdDNrpy1dyNaXWKSCIPop0EHdE8GrnngHoJujM3QhvHUN/igsYCgPoHo7pFOezlJ61Hlln0KHyA==
+ dependencies:
+ ansi-escapes "^1.0.0"
+ cli-cursor "^1.0.2"
+
+lunr@^2.3.9:
+ version "2.3.9"
+ resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz"
+ integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==
+
+make-error@^1.1.1, make-error@^1.3.6:
+ version "1.3.6"
+ resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz"
+ integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
+
+markdown-it@^14.1.1:
+ version "14.2.0"
+ resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz"
+ integrity sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==
+ dependencies:
+ argparse "^2.0.1"
+ entities "^4.4.0"
+ linkify-it "^5.0.1"
+ mdurl "^2.0.0"
+ punycode.js "^2.3.1"
+ uc.micro "^2.1.0"
+
+math-intrinsics@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz"
+ integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
+
+mdurl@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz"
+ integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==
+
+media-typer@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz"
+ integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==
+
+media-typer@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
+ integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
+
+mime-db@^1.54.0:
+ version "1.54.0"
+ resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz"
+ integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==
+
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^3.0.0, mime-types@^3.0.1:
+ version "3.0.2"
+ resolved "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz"
+ integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==
+ dependencies:
+ mime-db "^1.54.0"
+
+mime-types@~2.1.24:
+ version "2.1.35"
+ resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mime-types@~2.1.34:
+ version "2.1.35"
+ resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+minimatch@^10.2.5:
+ version "10.2.5"
+ resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz"
+ integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==
+ dependencies:
+ brace-expansion "^5.0.5"
+
+minimatch@^3.0.4:
+ version "3.1.5"
+ resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz"
+ integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@^1.2.0:
+ version "1.2.8"
+ resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
+ integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
+ms@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+mute-stream@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz"
+ integrity sha512-EbrziT4s8cWPmzr47eYVW3wimS4HsvlnV5ri1xw1aR6JQo/OrJX5rkl32K/QQHdxeabJETtfeaROGhd8W7uBgg==
+
+negotiator@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+
+node-fetch@^2.6.1:
+ version "2.7.0"
+ resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
+ integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
+ dependencies:
+ whatwg-url "^5.0.0"
+
+node-localstorage@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.npmjs.org/node-localstorage/-/node-localstorage-0.6.0.tgz"
+ integrity sha512-t9dKMce8qUs2KK02ZiBgzZSykUxc+5UcML7/20a62ruHwfh7+bNQvrH/auxY5gFNexTwAFdr+DbptxlLq4+7qQ==
+
+normalize-path@^3.0.0, normalize-path@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
+ integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz"
+ integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==
+
+object-assign@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
+ integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
+
+object-inspect@^1.13.3, object-inspect@^1.13.4:
+ version "1.13.4"
+ resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz"
+ integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
+
+on-finished@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz"
+ integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
+ dependencies:
+ ee-first "1.1.1"
+
+once@^1.3.0, once@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+onetime@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz"
+ integrity sha512-GZ+g4jayMqzCRMgB2sol7GiCLjKfS1PINkjmx8spcKce1LiVqcbQreXwqs2YAFXC6R03VIG28ZS31t8M866v6A==
+
+parseurl@^1.3.3:
+ version "1.3.3"
+ resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
+ integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+path-to-regexp@^8.2.0:
+ version "8.4.2"
+ resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz"
+ integrity sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==
+
+picomatch@^2.0.4, picomatch@^2.2.1:
+ version "2.3.2"
+ resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz"
+ integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==
+
+pinst@^2.1.6:
+ version "2.1.6"
+ resolved "https://registry.npmjs.org/pinst/-/pinst-2.1.6.tgz"
+ integrity sha512-B4dYmf6nEXg1NpDSB+orYWvKa5Kfmz5KzWC29U59dpVM4S/+xp0ak/JMEsw04UQTNNKps7klu0BUalr343Gt9g==
+ dependencies:
+ fromentries "^1.3.2"
+
+prompts@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz"
+ integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
+ dependencies:
+ kleur "^3.0.3"
+ sisteransi "^1.0.5"
+
+punycode.js@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz"
+ integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==
+
+qs@^6.11.0, qs@^6.5.2:
+ version "6.15.2"
+ resolved "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz"
+ integrity sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==
+ dependencies:
+ side-channel "^1.1.0"
+
+query-registry@^2.5.0:
+ version "2.6.0"
+ resolved "https://registry.npmjs.org/query-registry/-/query-registry-2.6.0.tgz"
+ integrity sha512-Z5oNq7qH0g96qBTx2jAvS0X71hKP4tETtSJKEl6BdihzYqh9QKiJQBMT7qIQuzxR9lxfiso+aXCFhZ+EcAoppQ==
+ dependencies:
+ isomorphic-unfetch "^3.1.0"
+ make-error "^1.3.6"
+ tiny-lru "^8.0.2"
+ url-join "4.0.1"
+ validate-npm-package-name "^4.0.0"
+
+raw-body@^2.3.3:
+ version "2.5.3"
+ resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz"
+ integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==
+ dependencies:
+ bytes "~3.1.2"
+ http-errors "~2.0.1"
+ iconv-lite "~0.4.24"
+ unpipe "~1.0.0"
+
+readdirp@~3.6.0:
+ version "3.6.0"
+ resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz"
+ integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+ dependencies:
+ picomatch "^2.2.1"
+
+readline2@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz"
+ integrity sha512-8/td4MmwUB6PkZUbV25uKz7dfrmjYWxsW8DVfibWdlHRk/l/DfHKn4pU+dfcoGLFgWOdyGCzINRQD7jn+Bv+/g==
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ mute-stream "0.0.5"
+
+reflect-metadata@^0.1.13:
+ version "0.1.14"
+ resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz"
+ integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==
+
+regenerator-runtime@^0.10.5:
+ version "0.10.5"
+ resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz"
+ integrity sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==
+
+regenerator-runtime@^0.11.0:
+ version "0.11.1"
+ resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz"
+ integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
+resolve-pkg@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-2.0.0.tgz"
+ integrity sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ==
+ dependencies:
+ resolve-from "^5.0.0"
+
+restore-cursor@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz"
+ integrity sha512-reSjH4HuiFlxlaBaFCiS6O76ZGG2ygKoSlCsipKdaZuKSPx/+bt9mULkn4l0asVzbEfQQmXRg6Wp6gv6m0wElw==
+ dependencies:
+ exit-hook "^1.0.0"
+ onetime "^1.0.0"
+
+run-async@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz"
+ integrity sha512-qOX+w+IxFgpUpJfkv2oGN0+ExPs68F4sZHfaRRx4dDexAQkG83atugKVEylyT5ARees3HBbfmuvnjbrd8j9Wjw==
+ dependencies:
+ once "^1.3.0"
+
+rx-lite@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz"
+ integrity sha512-1I1+G2gteLB8Tkt8YI1sJvSIfa0lWuRtC8GjvtyPBcLSF5jBCCJJqKrpER5JU5r6Bhe+i9/pK3VMuUcXu0kdwQ==
+
+"safer-buffer@>= 2.1.2 < 3":
+ version "2.1.2"
+ resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+semver@^7.0.0:
+ version "7.8.4"
+ resolved "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz"
+ integrity sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==
+
+setprototypeof@~1.2.0, setprototypeof@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
+side-channel-list@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz"
+ integrity sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==
+ dependencies:
+ es-errors "^1.3.0"
+ object-inspect "^1.13.4"
+
+side-channel-map@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz"
+ integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
+ dependencies:
+ call-bound "^1.0.2"
+ es-errors "^1.3.0"
+ get-intrinsic "^1.2.5"
+ object-inspect "^1.13.3"
+
+side-channel-weakmap@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz"
+ integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
+ dependencies:
+ call-bound "^1.0.2"
+ es-errors "^1.3.0"
+ get-intrinsic "^1.2.5"
+ object-inspect "^1.13.3"
+ side-channel-map "^1.0.1"
+
+side-channel@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz"
+ integrity sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==
+ dependencies:
+ es-errors "^1.3.0"
+ object-inspect "^1.13.4"
+ side-channel-list "^1.0.1"
+ side-channel-map "^1.0.1"
+ side-channel-weakmap "^1.0.2"
+
+sisteransi@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz"
+ integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
+
+statuses@^2.0.1, statuses@~2.0.2:
+ version "2.0.2"
+ resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz"
+ integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==
+
+"statuses@>= 1.5.0 < 2":
+ version "1.5.0"
+ resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz"
+ integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
+
+string-width@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz"
+ integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz"
+ integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==
+ dependencies:
+ ansi-regex "^2.0.0"
+
+supports-color@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
+ integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+through@^2.3.6:
+ version "2.3.8"
+ resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
+ integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+
+tiny-lru@^8.0.2:
+ version "8.0.2"
+ resolved "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz"
+ integrity sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+toidentifier@~1.0.1, toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
+tr46@~0.0.3:
+ version "0.0.3"
+ resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
+ integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+
+triple-beam@^1.3.0:
+ version "1.4.1"
+ resolved "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz"
+ integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==
+
+ts-node@^10.0.0:
+ version "10.9.2"
+ resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz"
+ integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
+ dependencies:
+ "@cspotcode/source-map-support" "^0.8.0"
+ "@tsconfig/node10" "^1.0.7"
+ "@tsconfig/node12" "^1.0.7"
+ "@tsconfig/node14" "^1.0.0"
+ "@tsconfig/node16" "^1.0.2"
+ acorn "^8.4.1"
+ acorn-walk "^8.1.1"
+ arg "^4.1.0"
+ create-require "^1.1.0"
+ diff "^4.0.1"
+ make-error "^1.1.1"
+ v8-compile-cache-lib "^3.0.1"
+ yn "3.1.1"
+
+tsscmp@1.0.6:
+ version "1.0.6"
+ resolved "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz"
+ integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==
+
+type-is@^1.6.16:
+ version "1.6.18"
+ resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz"
+ integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
+ dependencies:
+ media-typer "0.3.0"
+ mime-types "~2.1.24"
+
+type-is@^2.0.1:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz"
+ integrity sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==
+ dependencies:
+ content-type "^2.0.0"
+ media-typer "^1.1.0"
+ mime-types "^3.0.0"
+
+typedoc-plugin-external-module-map@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.npmjs.org/typedoc-plugin-external-module-map/-/typedoc-plugin-external-module-map-2.2.0.tgz"
+ integrity sha512-wWou92pJOMJk6cpUpbIEsBjtZKGLnu9UsVamXhndhkyBBvOBxaS+dx8USPDYByuy1Xx05V36IF+GOLqjPTpfyw==
+ dependencies:
+ "@types/node" "^20.14.14"
+
+typedoc@^0.28.13, "typedoc@>=0.27 <2.0":
+ version "0.28.19"
+ resolved "https://registry.npmjs.org/typedoc/-/typedoc-0.28.19.tgz"
+ integrity sha512-wKh+lhdmMFivMlc6vRRcMGXeGEHGU2g8a2CkPTJjJlwRf1iXbimWIPcFolCqe4E0d/FRtGszpIrsp3WLpDB8Pw==
+ dependencies:
+ "@gerrit0/mini-shiki" "^3.23.0"
+ lunr "^2.3.9"
+ markdown-it "^14.1.1"
+ minimatch "^10.2.5"
+ yaml "^2.8.3"
+
+typescript@^5.9.3, typescript@>=2.7, "typescript@5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x":
+ version "5.9.3"
+ resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz"
+ integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
+
+uc.micro@^2.0.0, uc.micro@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz"
+ integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==
+
+undici-types@~6.21.0:
+ version "6.21.0"
+ resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz"
+ integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
+
+undici-types@~7.18.0:
+ version "7.18.2"
+ resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz"
+ integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==
+
+unfetch@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz"
+ integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==
+
+unpipe@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
+ integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
+url-join@4.0.1:
+ version "4.0.1"
+ resolved "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz"
+ integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
+
+v8-compile-cache-lib@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"
+ integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
+
+validate-npm-package-name@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz"
+ integrity sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==
+ dependencies:
+ builtins "^5.0.0"
+
+vary@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
+ integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
+
+vorpal@^1.12.0:
+ version "1.12.0"
+ resolved "https://registry.npmjs.org/vorpal/-/vorpal-1.12.0.tgz"
+ integrity sha512-lYEhd75l75P3D1LKpm4KqdOSpNyNdDJ9ixEZmC5ZAZUKGy6JNexfMdQ9SNaT5pCHuzuXXRJQedJ+CdqNg/D4Kw==
+ dependencies:
+ babel-polyfill "^6.3.14"
+ chalk "^1.1.0"
+ in-publish "^2.0.0"
+ inquirer "0.11.0"
+ lodash "^4.5.1"
+ log-update "^1.0.2"
+ minimist "^1.2.0"
+ node-localstorage "^0.6.0"
+ strip-ansi "^3.0.0"
+ wrap-ansi "^2.0.0"
+
+webidl-conversions@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
+ integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
+
+whatwg-url@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
+ integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
+ dependencies:
+ tr46 "~0.0.3"
+ webidl-conversions "^3.0.0"
+
+wrap-ansi@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz"
+ integrity sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+yaml@^2.8.3:
+ version "2.9.0"
+ resolved "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz"
+ integrity sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==
+
+yn@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz"
+ integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
+
+zod@^3.19.1:
+ version "3.25.76"
+ resolved "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz"
+ integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==
From 5ba8e6e9a5e768490e729216f5aa14f20c37d963 Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Fri, 19 Jun 2026 05:09:39 +0800
Subject: [PATCH 02/17] New Version Add and New GitHub URL
---
Dockerfile | 62 ++++++++++++++++++++++++++++++++++++++
bin/createDefaultConfig.ts | 15 ++++++++-
2 files changed, 76 insertions(+), 1 deletion(-)
create mode 100644 Dockerfile
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000..f70792495
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,62 @@
+# ── Stage 1: Build SkeldJS from source ──
+FROM node:22-alpine AS skeldjs-builder
+
+WORKDIR /skeldjs
+RUN apk add --no-cache git
+
+# Clone the SkeldJS fork
+ARG SKELDJS_REPO=https://github.com/Empostor/SkeldJS
+ARG SKELDJS_BRANCH=main
+RUN git clone --depth 1 --branch ${SKELDJS_BRANCH} ${SKELDJS_REPO} .
+
+# Install dependencies & build all packages
+RUN corepack enable && yarn install
+RUN yarn build
+
+# ── Stage 2: Build Waterway with local SkeldJS ──
+FROM node:22-alpine AS builder
+
+WORKDIR /app
+
+# Copy SkeldJS built packages from previous stage
+COPY --from=skeldjs-builder /skeldjs/packages /skeldjs/packages
+COPY --from=skeldjs-builder /skeldjs/package.json /skeldjs/
+COPY --from=skeldjs-builder /skeldjs/tsconfig.json /skeldjs/
+
+# Copy Waterway source
+COPY package.json tsconfig.json .yarnrc.yml ./
+COPY .yarn/ .yarn/
+COPY bin/ bin/
+COPY src/ src/
+
+# Enable yarn
+RUN corepack enable
+
+# Rewrite @skeldjs dependencies to use local linked packages
+# This replaces npm registry deps with file: references to the locally built SkeldJS
+RUN sed -i \
+ -e 's|"@skeldjs/au-client": "[^"]*"|"@skeldjs/au-client": "link:/skeldjs/packages/client"|' \
+ -e 's|"@skeldjs/au-constants": "[^"]*"|"@skeldjs/au-constants": "link:/skeldjs/packages/constant"|' \
+ -e 's|"@skeldjs/au-core": "[^"]*"|"@skeldjs/au-core": "link:/skeldjs/packages/core"|' \
+ -e 's|"@skeldjs/au-protocol": "[^"]*"|"@skeldjs/au-protocol": "link:/skeldjs/packages/protocol"|' \
+ -e 's|"@skeldjs/au-text": "[^"]*"|"@skeldjs/au-text": "link:/skeldjs/packages/text"|' \
+ -e 's|"@skeldjs/events": "[^"]*"|"@skeldjs/events": "link:/skeldjs/packages/events"|' \
+ -e 's|"@skeldjs/hazel": "[^"]*"|"@skeldjs/hazel": "link:/skeldjs/packages/hazel"|' \
+ package.json
+
+# Install and build Waterway
+RUN yarn install
+RUN yarn build
+
+# ── Stage 3: Production image ──
+FROM node:22-alpine
+
+WORKDIR /app
+
+COPY --from=builder /app/dist /app/dist
+COPY --from=builder /app/node_modules /app/node_modules
+COPY --from=builder /app/package.json /app/
+
+EXPOSE 22023 22123
+
+CMD ["node", "dist/bin/bootstrap"]
diff --git a/bin/createDefaultConfig.ts b/bin/createDefaultConfig.ts
index 374755883..2a7c4f893 100644
--- a/bin/createDefaultConfig.ts
+++ b/bin/createDefaultConfig.ts
@@ -9,7 +9,20 @@ export function createDefaultConfig(): WaterwayConfig {
autoUpdate: false,
exitConfirmation: true,
defaultLanguage: "en",
- acceptedVersions: ["2025.7.15"],
+ acceptedVersions: [
+ "2025.7.15", // 17.0.0 (2025-09-09)
+ "2025.9.12", // 17.0.1 (2025-10-14)
+ "2025.10.9", // 17.1 (2025-11-18)
+ "2025.11.6", // 17.1.1 (2025-12-03, mobile only)
+ "2025.12.8", // 17.1.2 (2025-12-11, mobile only)
+ "2025.11.5", // 17.2 (2026-02-17, build 6630)
+ "2026.1.22", // 17.2 (2026-02-19, build 6686, hotfix)
+ "2026.2.2", // 17.2.2 (2026-03-17, build 6768)
+ "2026.1.12", // 17.3 (2026-03-31, build 6803)
+ "2026.3.17", // 17.3.1 (2026-04-08, build 6841, mobile only)
+ "2026.3.18", // 17.4 (2026-06-05, build 7044, pc only)
+ "2026.4.23", // 17.4 (2026-06-05, build 7045, mobile only)
+ ],
matchmaker: {
port: 22023
},
From dbae59c3a8341ad8d8562cfc94b4449755345f09 Mon Sep 17 00:00:00 2001
From: Fang-Kaui <145177672+FangkuaiYa@users.noreply.github.com>
Date: Fri, 19 Jun 2026 05:26:06 +0800
Subject: [PATCH 03/17] Modify Dockerfile for flexible SkeldJS cloning
Updated Dockerfile to allow branch specification for cloning SkeldJS.
---
Dockerfile | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index f70792495..7a5436297 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,14 +4,19 @@ FROM node:22-alpine AS skeldjs-builder
WORKDIR /skeldjs
RUN apk add --no-cache git
-# Clone the SkeldJS fork
+# Clone the SkeldJS fork — uses default branch (master)
+# To specify a branch: docker build --build-arg SKELDJS_REF=my-branch .
ARG SKELDJS_REPO=https://github.com/Empostor/SkeldJS
-ARG SKELDJS_BRANCH=main
-RUN git clone --depth 1 --branch ${SKELDJS_BRANCH} ${SKELDJS_REPO} .
+ARG SKELDJS_REF=
+RUN if [ -n "${SKELDJS_REF}" ]; then \
+ git clone --depth 1 --branch "${SKELDJS_REF}" ${SKELDJS_REPO} . ; \
+ else \
+ git clone --depth 1 ${SKELDJS_REPO} . ; \
+ fi
-# Install dependencies & build all packages
+# Install dependencies & build all packages (SkeldJS monorepo uses build-all)
RUN corepack enable && yarn install
-RUN yarn build
+RUN yarn build-all
# ── Stage 2: Build Waterway with local SkeldJS ──
FROM node:22-alpine AS builder
From 73da79ba41d03b3f75677faeb1e8015c54036d6e Mon Sep 17 00:00:00 2001
From: Fang-Kaui <145177672+FangkuaiYa@users.noreply.github.com>
Date: Fri, 19 Jun 2026 05:37:06 +0800
Subject: [PATCH 04/17] Add files via upload
---
src/index.ts | 41 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/src/index.ts b/src/index.ts
index 85af84794..7053b62f8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -14,6 +14,43 @@ export * from "./WaterwayServer";
export * from "./i18n";
-export * from "@skeldjs/au-core";
export * from "@skeldjs/au-protocol";
-export * from "@skeldjs/hazel";
\ No newline at end of file
+export * from "@skeldjs/hazel";
+
+// Re-export from @skeldjs/au-core explicitly to avoid ambiguity
+// with Waterway's own Player* events (PlayerLevelChangedEvent, etc.)
+export type { Player, PlayerControl, NetworkedPlayerInfo, MeetingHud, NetworkedObject } from "@skeldjs/au-core";
+export type { PlayerJoinEvent, PlayerResolvable, PlayerSceneChangeEvent } from "@skeldjs/au-core";
+export type { PlayerSetAuthoritativeEvent, RoomEndGameIntentEvent, RoomFixedUpdateEvent } from "@skeldjs/au-core";
+export type { StatefulRoom, StatefulRoomEvents } from "@skeldjs/au-core";
+
+export {
+ AlterGameTag,
+ Color,
+ colorData,
+ CustomNetworkTransform,
+ DataState,
+ DisconnectReason,
+ EndGameIntent,
+ GameDataMessageTag,
+ GameMap,
+ GameMode,
+ GameOverReason,
+ GameState,
+ Hat,
+ KillDistance,
+ Language,
+ Platform,
+ QuickChatMode,
+ RoleType,
+ RoleTeamType,
+ RpcMessageTag,
+ SendOption,
+ Skin,
+ SpawnFlag,
+ SpawnType,
+ SpecialOwnerId,
+ SystemType,
+ TaskBarMode,
+ Visor,
+} from "@skeldjs/au-core";
From df4395c342f38ab5b4121032beba8d603a14ce79 Mon Sep 17 00:00:00 2001
From: Fang-Kaui <145177672+FangkuaiYa@users.noreply.github.com>
Date: Fri, 19 Jun 2026 05:37:42 +0800
Subject: [PATCH 05/17] Refactor Dockerfile for improved clarity and structure
---
Dockerfile | 47 ++++++++++++++++++++++++-----------------------
1 file changed, 24 insertions(+), 23 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 7a5436297..870a96b71 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,53 +4,54 @@ FROM node:22-alpine AS skeldjs-builder
WORKDIR /skeldjs
RUN apk add --no-cache git
-# Clone the SkeldJS fork — uses default branch (master)
-# To specify a branch: docker build --build-arg SKELDJS_REF=my-branch .
+# Clone the SkeldJS fork — uses default branch
+# To force re-clone: docker build --build-arg CACHEBUST=$(date +%s) .
+# To specify a branch: docker build --build-arg SKELDJS_REF=my-branch .
ARG SKELDJS_REPO=https://github.com/Empostor/SkeldJS
ARG SKELDJS_REF=
+ARG CACHEBUST=1
RUN if [ -n "${SKELDJS_REF}" ]; then \
git clone --depth 1 --branch "${SKELDJS_REF}" ${SKELDJS_REPO} . ; \
else \
git clone --depth 1 ${SKELDJS_REPO} . ; \
fi
-# Install dependencies & build all packages (SkeldJS monorepo uses build-all)
+# Install dependencies & build all packages
RUN corepack enable && yarn install
RUN yarn build-all
-# ── Stage 2: Build Waterway with local SkeldJS ──
+# ── Stage 2: Build Waterway ──
FROM node:22-alpine AS builder
WORKDIR /app
-# Copy SkeldJS built packages from previous stage
-COPY --from=skeldjs-builder /skeldjs/packages /skeldjs/packages
-COPY --from=skeldjs-builder /skeldjs/package.json /skeldjs/
-COPY --from=skeldjs-builder /skeldjs/tsconfig.json /skeldjs/
-
# Copy Waterway source
COPY package.json tsconfig.json .yarnrc.yml ./
COPY .yarn/ .yarn/
COPY bin/ bin/
COPY src/ src/
-# Enable yarn
+# Enable yarn, install only third-party deps (SkeldJS from npm as placeholder)
RUN corepack enable
+RUN yarn install
-# Rewrite @skeldjs dependencies to use local linked packages
-# This replaces npm registry deps with file: references to the locally built SkeldJS
-RUN sed -i \
- -e 's|"@skeldjs/au-client": "[^"]*"|"@skeldjs/au-client": "link:/skeldjs/packages/client"|' \
- -e 's|"@skeldjs/au-constants": "[^"]*"|"@skeldjs/au-constants": "link:/skeldjs/packages/constant"|' \
- -e 's|"@skeldjs/au-core": "[^"]*"|"@skeldjs/au-core": "link:/skeldjs/packages/core"|' \
- -e 's|"@skeldjs/au-protocol": "[^"]*"|"@skeldjs/au-protocol": "link:/skeldjs/packages/protocol"|' \
- -e 's|"@skeldjs/au-text": "[^"]*"|"@skeldjs/au-text": "link:/skeldjs/packages/text"|' \
- -e 's|"@skeldjs/events": "[^"]*"|"@skeldjs/events": "link:/skeldjs/packages/events"|' \
- -e 's|"@skeldjs/hazel": "[^"]*"|"@skeldjs/hazel": "link:/skeldjs/packages/hazel"|' \
- package.json
+# Overwrite npm SkeldJS with locally-built packages
+COPY --from=skeldjs-builder /skeldjs/packages/client/dist /app/node_modules/@skeldjs/au-client/dist
+COPY --from=skeldjs-builder /skeldjs/packages/client/package.json /app/node_modules/@skeldjs/au-client/
+COPY --from=skeldjs-builder /skeldjs/packages/constant/dist /app/node_modules/@skeldjs/au-constants/dist
+COPY --from=skeldjs-builder /skeldjs/packages/constant/package.json /app/node_modules/@skeldjs/au-constants/
+COPY --from=skeldjs-builder /skeldjs/packages/core/dist /app/node_modules/@skeldjs/au-core/dist
+COPY --from=skeldjs-builder /skeldjs/packages/core/package.json /app/node_modules/@skeldjs/au-core/
+COPY --from=skeldjs-builder /skeldjs/packages/protocol/dist /app/node_modules/@skeldjs/au-protocol/dist
+COPY --from=skeldjs-builder /skeldjs/packages/protocol/package.json /app/node_modules/@skeldjs/au-protocol/
+COPY --from=skeldjs-builder /skeldjs/packages/text/dist /app/node_modules/@skeldjs/au-text/dist
+COPY --from=skeldjs-builder /skeldjs/packages/text/package.json /app/node_modules/@skeldjs/au-text/
+COPY --from=skeldjs-builder /skeldjs/packages/events/dist /app/node_modules/@skeldjs/events/dist
+COPY --from=skeldjs-builder /skeldjs/packages/events/package.json /app/node_modules/@skeldjs/events/
+COPY --from=skeldjs-builder /skeldjs/packages/hazel/dist /app/node_modules/@skeldjs/hazel/dist
+COPY --from=skeldjs-builder /skeldjs/packages/hazel/package.json /app/node_modules/@skeldjs/hazel/
-# Install and build Waterway
-RUN yarn install
+# Build Waterway with local SkeldJS
RUN yarn build
# ── Stage 3: Production image ──
From 62c4fedd3e7a3c4deb4cfe19af78eb0b342729d6 Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Fri, 19 Jun 2026 06:19:31 +0800
Subject: [PATCH 06/17] 1 +fix: room privacy inverted on join, missing
QuickChat in game listing 2 + 3 +=== [FIX] Room Privacy
Inverted (src/Room.ts) === 4 + - finishJoiningGame() sent
AlterGame(ChangePrivacy, 1) when room was Private 5 + because the
ternary mapped RoomPrivacy.Private(=1) -> value 1, but in the 6 +
Among Us protocol, value 1 means Public. 7 + - Result: every
newly-joined player received an initial AlterGame telling them 8 +
the room was public when it was actually private. Manual toggle by host
9 + fixed it because handleAlterGameMessage() (correct path) was then
used. 10 + - Fix: change ternary from "RoomPrivacy.Private ? 1 : 0" to
11 + "RoomPrivacy.Public ? 1 : 0" 12 + 13 +=== [FIX]
Missing QuickChat in HTTP Matchmaker (src/Matchmaker.ts) === 14 + -
GameListingJson type was missing the QuickChat field that Among Us clients
15 + read from the HTTP /api/games response to determine chat mode.
16 + - getGameListing() now sets QuickChat from the host connection's
chatMode. 17 + - Without this, joining clients defaulted to
QuickChatOnly display. 18 + 19 +=== [FIX] Missing Language field
in UDP GameListing (src/WaterwayServer.ts) === 20 + - UDP game listing
now passes room.settings.keywords as Language to the 21 +
GameListing constructor (11th arg, cast to any for npm compat until 22
+ SkeldJS protocol update is published).
---
changelog.txt | 23 +++++++++++++++++++++++
src/Matchmaker.ts | 4 ++++
src/Room.ts | 2 +-
src/WaterwayServer.ts | 9 ++++++---
4 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/changelog.txt b/changelog.txt
index 8f1433171..f56660ef7 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,26 @@
+fix: room privacy inverted on join, missing QuickChat in game listing
+
+=== [FIX] Room Privacy Inverted (src/Room.ts) ===
+ - finishJoiningGame() sent AlterGame(ChangePrivacy, 1) when room was Private
+ because the ternary mapped RoomPrivacy.Private(=1) -> value 1, but in the
+ Among Us protocol, value 1 means Public.
+ - Result: every newly-joined player received an initial AlterGame telling them
+ the room was public when it was actually private. Manual toggle by host
+ fixed it because handleAlterGameMessage() (correct path) was then used.
+ - Fix: change ternary from "RoomPrivacy.Private ? 1 : 0" to
+ "RoomPrivacy.Public ? 1 : 0"
+
+=== [FIX] Missing QuickChat in HTTP Matchmaker (src/Matchmaker.ts) ===
+ - GameListingJson type was missing the QuickChat field that Among Us clients
+ read from the HTTP /api/games response to determine chat mode.
+ - getGameListing() now sets QuickChat from the host connection's chatMode.
+ - Without this, joining clients defaulted to QuickChatOnly display.
+
+=== [FIX] Missing Language field in UDP GameListing (src/WaterwayServer.ts) ===
+ - UDP game listing now passes room.settings.keywords as Language to the
+ GameListing constructor (11th arg, cast to any for npm compat until
+ SkeldJS protocol update is published).
+
feat: game options validation, Hide & Seek manager, role system, API events, matchmaker improvements, and config updates
=== Game Options Validation (src/game/GameOptions.ts) [NEW] ===
diff --git a/src/Matchmaker.ts b/src/Matchmaker.ts
index 3f550b039..988d3b269 100644
--- a/src/Matchmaker.ts
+++ b/src/Matchmaker.ts
@@ -27,6 +27,7 @@ export type GameListingJson = {
Platform: number;
HostPlatformName: string;
Language: number;
+ QuickChat: number;
Options: string;
}
@@ -375,6 +376,9 @@ export class Matchmaker {
Platform: room.playerAuthority?.platform.platformTag || Platform.Unknown,
HostPlatformName: room.playerAuthority?.platform.platformName || "UNKNOWN",
Language: room.settings.keywords,
+ QuickChat: room.playerAuthority
+ ? (room.getConnection(room.playerAuthority)?.chatMode ?? QuickChatMode.FreeChat)
+ : QuickChatMode.FreeChat,
Options: settingsWriter.toString("base64"),
};
return gameListing;
diff --git a/src/Room.ts b/src/Room.ts
index 63de9d080..ed6ba593b 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -1416,7 +1416,7 @@ export class Room extends StatefulRoom {
new AlterGameMessage(
this.code.id,
AlterGameTag.ChangePrivacy,
- this.privacy === RoomPrivacy.Private ? 1 : 0
+ this.privacy === RoomPrivacy.Public ? 1 : 0
)
]
)
diff --git a/src/WaterwayServer.ts b/src/WaterwayServer.ts
index 63ea487c2..0d5e69500 100644
--- a/src/WaterwayServer.ts
+++ b/src/WaterwayServer.ts
@@ -1704,7 +1704,9 @@ export class WaterwayServer extends EventEmitter {
continue;
const roomAge = Math.floor((Date.now() - room.createdAt) / 1000);
- const gameListing = new GameListing(
+ // NOTE: The 11-arg GameListing(language) will work once SkeldJS
+ // with the Language field lands in node_modules (via Docker COPY or npm publish).
+ const gameListing = new (GameListing as any)(
room.code.id,
listingIp,
this.config.socket.port,
@@ -1717,8 +1719,9 @@ export class WaterwayServer extends EventEmitter {
room.playerAuthority?.platform || new PlatformSpecificData(
Platform.Unknown,
"UNKNOWN"
- )
- );
+ ),
+ room.settings.keywords
+ ) as GameListing;
if (ignoreSearchTerms === true) {
gamesAndRelevance.push([0, gameListing]);
From e4c2e179bd4e1cc7f212bafbcc8ca2f6e62320a8 Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Fri, 19 Jun 2026 06:54:03 +0800
Subject: [PATCH 07/17] Fix
---
changelog.txt | 10 +++++++++-
src/Room.ts | 14 +++++++++++---
2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/changelog.txt b/changelog.txt
index f56660ef7..62bc603ff 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,4 +1,4 @@
-fix: room privacy inverted on join, missing QuickChat in game listing
+fix: game restart state corruption, room privacy inverted, missing QuickChat
=== [FIX] Room Privacy Inverted (src/Room.ts) ===
- finishJoiningGame() sent AlterGame(ChangePrivacy, 1) when room was Private
@@ -16,6 +16,14 @@ fix: room privacy inverted on join, missing QuickChat in game listing
- getGameListing() now sets QuickChat from the host connection's chatMode.
- Without this, joining clients defaulted to QuickChatOnly display.
+=== [FIX] Game restart state corruption (src/Room.ts) ===
+ - _reset() was not being called between games, causing Duplicate NetID
+ crash because lastNetId kept incrementing across games.
+ - handleStartGame() now calls _reset() when gameState === Ended.
+ - _reset() no longer clears this.players — lobby connections must persist.
+ - _reset() now resets: lastNetId→100000, networkedObjects, objectList,
+ roleManager, gameModeManager, ownershipGuards, messageStream, settings.
+
=== [FIX] Missing Language field in UDP GameListing (src/WaterwayServer.ts) ===
- UDP game listing now passes room.settings.keywords as Language to the
GameListing constructor (11th arg, cast to any for npm compat until
diff --git a/src/Room.ts b/src/Room.ts
index ed6ba593b..988c7f9da 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -550,13 +550,16 @@ export class Room extends StatefulRoom {
this.gameModeManager = null;
}
this.roleManager.handleGameEnd();
- this.players.clear();
+ // Don't clear this.players — lobby connections stay
+ // Clear only game-specific object state (players/connections stay)
this.networkedObjects.clear();
+ this.objectList.length = 0;
this.messageStream = [];
- this.authorityId = 0;
this.settings = new GameSettings;
this.startGameCounter = -1;
this.privacy = RoomPrivacy.Private;
+ this.lastNetId = 100000;
+ this.ownershipGuards.clear();
}
async emit(
@@ -1608,8 +1611,13 @@ export class Room extends StatefulRoom {
}
async handleStartGame(startedBy?: Player) {
+ // Reset room state if restarting after a previous game ended
+ if (this.gameState === GameState.Ended) {
+ this._reset();
+ }
+
this.gameState = GameState.Started;
-
+
if (startedBy) {
this.logger.info("Player %s requested game start, to be managed by %s",
startedBy, this.isAuthoritative ? "server" : (this.playerAuthority || "unknown player"));
From aa59f2d76e0c0a2f51f477e249f15c8e88a887f7 Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Sat, 20 Jun 2026 21:23:02 +0800
Subject: [PATCH 08/17] Fix alignment with Impostor (C# Among Us Server),
implementing account system, role completion, and RPC fixes.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- handleRpcMessage(): In SaaH mode, block the original message broadcast after receiving ReportDeadBody
- The server has already initiated StartMeeting + MeetingHud spawn via startMeetingWithAuth
- Prevent the client from receiving duplicate ReportDeadBody + StartMeeting, eliminating double report UI
- Fix the issue where non-host players cannot enter the meeting
- handleRpcMessage(): Add import for ReportDeadBodyMessage
- processFixedUpdate(): Call roleManager.handleFixedUpdate() to drive role timers
- processFixedUpdate(): Call gameModeManager's fixed update (type-safe check)
- player.startmeeting event: Add roleManager.handleMeetingStart() call
- Add ReportDeadBodyMessage to @skeldjs/au-protocol imports
- Three-level cache matching strategy: (IP + username) → username only → IP only
- Support expiration cleanup, manual removal, on-demand lookup
- Default TTL: 10 minutes
- Storage: puid, friendCode, username, ip, clientVersion, expiresAt
- New fields: puid: string, friendCode: string, isAuthenticated: boolean
- Constructor initializes them as empty
- disconnect() cleanup resets all authentication fields
- Parse JWT from Authorization: Bearer header
- extractPuidFromJwt(): Base64URL-decode JWT payload, extract sub claim
- Call Innersloth backend to obtain FriendCode
- fetchFriendCodeFromBackend(): HTTPS GET backend.innersloth.com/api/user/username
- Support both JSON:API and flat response formats
- 15-second timeout
- generateFallbackFriendCode(): Generate stable fallback code based on SHA-256 hash of PUID
- Store in AuthCache for UDP handshake matching
- Support X-Real-IP / X-Forwarded-For reverse proxy headers
- Preserve body.Puid fallback path (legacy)
- New imports: https, AuthCache
- handleHello(): After client authentication, look up AuthCache by (remoteIP, username)
- On match: set connection.puid, connection.friendCode, connection.isAuthenticated
- Remove from cache after match (single-use)
- Three-tier fallback: IP+username → username → IP
- handleJoin(): Pass friendCode and puid parameters when constructing Player
- handleRemoteJoin(): PlayerJoinData includes puid and friendCode
- Crewmate role, passive ability
- Client automatically displays Vitals UI after recognizing via SetRole RPC
- No special server-side logic
- Crewmate role, can use vents
- Track ventUsesRemaining (from roleSettings.engineerVentUses / crewmateVentUses)
- onVentUse(): Check and decrement usage count
- Crewmate role, can shield alive players
- Shield duration from guardianAngelProtectionDuration setting
- Cooldown from guardianAngelCooldown setting
- onAbilityUse(): Set protection state via PlayerControl
- onFixedUpdate(): Drive cooldown timer
- onDeath(): Remove current protection
- Impostor role, can shapeshift into other players' appearances
- Shift duration from shapeshifterDuration setting
- Cooldown from shapeshifterCooldown setting
- onAbilityUse(): Save original appearance → apply target appearance
- revertTransform(): Restore original appearance → push PlayerInfo update
- onFixedUpdate(): Drive shapeshift timer and cooldown
- Add Scientist, Engineer, GuardianAngel, Shapeshifter to ROLE_CONSTRUCTORS
- Add Crewmate to isCrewmateRole
- Add Impostor to isImpostorRole
- Add ScientistRole, EngineerRole, GuardianAngelRole, ShapeshifterRole exports
- onDeath(): Trigger vanishWithAuth() RPC instead of just sending chat message
- endVanish(): Trigger appearWithAuth() RPC + causeToDie for official death
- onFixedUpdate(): Drive vanish timer and cooldown
- Remove all setTimeout, use fixed update timers instead
- onKill(): Poison delay, track poisoned players and remaining time via Map
- onFixedUpdate(): Countdown dissolution, call causeToDie
- dissolveTarget(): Execute dissolution kill
- Remove setTimeout, use fixed update timers instead
- onFixedUpdate(): Drive tracking duration, position update interval, cooldown
- sendPositionUpdate(): Read target CustomNetworkTransform to get position
- Remove setInterval, use fixed update instead
- onFixedUpdate(): Drive alarm effect timer and cooldown
- Remove setTimeout dependency
- onFixedUpdate(): Drive cooldown timer
- onGameStart(): Register player.murder listener to track kill records
- Remove setInterval dependency
- Among Us native /cmd chat command handler
- Built-in commands:
- /cmd help — Show available commands
- /cmd list — List room players (with FriendCode)
- /cmd kick — Kick a player
- /cmd ban — Ban a player
- /cmd room — Set room name
- /cmd public — Make room public
- /cmd private — Make room private
- CmdContext: Supports reply (visible only to sender) and broadcast (entire room)
- Player lookup: Supports fuzzy username matching and exact ClientID matching
- Authentication requirement: kick/ban/room/public/private require isAuthenticated
- Extensible: register() method for plugins to register custom commands
- Add cmdHandler: CmdHandler field and instantiate it
- handleRpcMessage(): Detect /cmd messages from non-authority players
- Call cmdHandler.handleMessage() to process the command
- Suppress broadcast (aligns with Impostor /cmd being host-only visible)
- player.chat event: Skip /cmd messages (avoid duplicate processing with CmdHandler)
---
changelog.txt | 180 ------------------
src/AuthCache.ts | 180 ++++++++++++++++++
src/Connection.ts | 21 ++
src/Matchmaker.ts | 192 +++++++++++++++++--
src/Room.ts | 60 +++++-
src/WaterwayServer.ts | 23 +++
src/game/roles/DetectiveRole.ts | 49 +++--
src/game/roles/EngineerRole.ts | 62 ++++++
src/game/roles/GuardianAngelRole.ts | 117 ++++++++++++
src/game/roles/NoisemakerRole.ts | 74 +++++---
src/game/roles/PhantomRole.ts | 100 +++++-----
src/game/roles/RoleManager.ts | 10 +
src/game/roles/ScientistRole.ts | 33 ++++
src/game/roles/ShapeshifterRole.ts | 179 ++++++++++++++++++
src/game/roles/TrackerRole.ts | 92 +++++----
src/game/roles/ViperRole.ts | 73 +++----
src/game/roles/index.ts | 4 +
src/handlers/CmdHandler.ts | 284 ++++++++++++++++++++++++++++
src/handlers/index.ts | 1 +
19 files changed, 1368 insertions(+), 366 deletions(-)
delete mode 100644 changelog.txt
create mode 100644 src/AuthCache.ts
create mode 100644 src/game/roles/EngineerRole.ts
create mode 100644 src/game/roles/GuardianAngelRole.ts
create mode 100644 src/game/roles/ScientistRole.ts
create mode 100644 src/game/roles/ShapeshifterRole.ts
create mode 100644 src/handlers/CmdHandler.ts
diff --git a/changelog.txt b/changelog.txt
deleted file mode 100644
index 62bc603ff..000000000
--- a/changelog.txt
+++ /dev/null
@@ -1,180 +0,0 @@
-fix: game restart state corruption, room privacy inverted, missing QuickChat
-
-=== [FIX] Room Privacy Inverted (src/Room.ts) ===
- - finishJoiningGame() sent AlterGame(ChangePrivacy, 1) when room was Private
- because the ternary mapped RoomPrivacy.Private(=1) -> value 1, but in the
- Among Us protocol, value 1 means Public.
- - Result: every newly-joined player received an initial AlterGame telling them
- the room was public when it was actually private. Manual toggle by host
- fixed it because handleAlterGameMessage() (correct path) was then used.
- - Fix: change ternary from "RoomPrivacy.Private ? 1 : 0" to
- "RoomPrivacy.Public ? 1 : 0"
-
-=== [FIX] Missing QuickChat in HTTP Matchmaker (src/Matchmaker.ts) ===
- - GameListingJson type was missing the QuickChat field that Among Us clients
- read from the HTTP /api/games response to determine chat mode.
- - getGameListing() now sets QuickChat from the host connection's chatMode.
- - Without this, joining clients defaulted to QuickChatOnly display.
-
-=== [FIX] Game restart state corruption (src/Room.ts) ===
- - _reset() was not being called between games, causing Duplicate NetID
- crash because lastNetId kept incrementing across games.
- - handleStartGame() now calls _reset() when gameState === Ended.
- - _reset() no longer clears this.players — lobby connections must persist.
- - _reset() now resets: lastNetId→100000, networkedObjects, objectList,
- roleManager, gameModeManager, ownershipGuards, messageStream, settings.
-
-=== [FIX] Missing Language field in UDP GameListing (src/WaterwayServer.ts) ===
- - UDP game listing now passes room.settings.keywords as Language to the
- GameListing constructor (11th arg, cast to any for npm compat until
- SkeldJS protocol update is published).
-
-feat: game options validation, Hide & Seek manager, role system, API events, matchmaker improvements, and config updates
-
-=== Game Options Validation (src/game/GameOptions.ts) [NEW] ===
- + GameOptionsValidator.validateGameSettings() — validates all game settings
- + Numeric range checks: playerSpeed, killCooldown, vision, tasks, timers
- + Enum checks: GameMap, GameKeyword, KillDistance, TaskBarMode, GameMode,
- RulesPreset
- + Dependency check: numEmergencies <= maxPlayers
- + Role settings validation for all 9 roles
- + Used in WaterwayServer.handleHostGameMessage() on room creation
- + Used in Room constructor on player.syncsettings RPC
-
-=== Hide & Seek Game Mode Manager (src/game/modes/HideAndSeekManager.ts) [NEW] ===
- + Full lifecycle: Hiding -> Seeking -> FinalHide -> Ended
- + Automatic seeker selection (configured playerId or random)
- + Hider tracking with task-completion win condition
- + Body reports and meetings disabled during gameplay
- + Final hide phase: seeker speed boost, position ping mechanic
- + Player disconnect handling:
- - Seeker leaves -> hiders win
- - All hiders leave -> seeker wins
- + Integrated into Room.handleStartGame() and Room.handleRpcMessage()
- + Clean teardown on destroy / game end
-
-=== Role System (src/game/roles/) [NEW] ===
-
- [NEW] BaseRole.ts — server-side role base class
- + Cooldown management (startCooldown, canUseAbility, isOnCooldown)
- + Hooks: onGameStart, onKill, onTaskComplete, onDeath, onMeetingStart,
- onAbilityUse, onFixedUpdate, onGameEnd
- + roleType, teamType, isActive, currentCooldown, lastAbilityUse
-
- [NEW] NoisemakerRole.ts — crewmate, emits alert on task completion
- + onTaskComplete(): broadcasts position reveal to nearby players
- + Configurable alert duration and impostor visibility
-
- [NEW] PhantomRole.ts — impostor, vanishes on death
- + onDeath(): enters invisible ghost state instead of dying
- + Vanish duration + cooldown from role settings
- + Auto-dies after vanish expires
-
- [NEW] TrackerRole.ts — crewmate, tracks a target
- + onAbilityUse(target): starts periodic position updates
- + Tracking duration, cooldown, and update delay from role settings
- + Auto-stops after duration
-
- [NEW] DetectiveRole.ts — crewmate, inspects suspects
- + onAbilityUse(target): checks kill count, reports suspicion level
- + suspectLimit from role settings
- + Tracks murder records during game
-
- [NEW] ViperRole.ts — impostor, delayed poison kills
- + onKill(target): applies poison instead of instant kill
- + Target dies after viperDissolveTime seconds
- + All poisons cleared on Viper death or game end
-
- [NEW] RoleManager.ts — role assignment and lifecycle
- + assignRoles(): reads roleChances from settings, rolls dice per player
- + Respects maxPlayers limits and team constraints
- + Routes game events (kill, task, death, meeting) to correct role
- + Plugin-extensible via static registerRole()
-
-=== New API Events (src/api/events/) [NEW] ===
-
- [NEW] room/HideAndSeekStart.ts — room.hideandseekstart
- Fields: room, seekerPlayer, hiderPlayers, hideDuration
-
- [NEW] room/HiderCaught.ts — room.hidercaught
- Fields: room, hiderPlayer, seekerPlayer, remainingHiders
-
- [NEW] room/HideAndSeekEnd.ts — room.hideandseekend
- Fields: room, winner ("seekers"|"hiders"|"none"), reason
-
- [NEW] player/TaskStarted.ts — player.taskstarted
- Fields: room, player, taskType, taskId
-
- [NEW] player/TaskProgress.ts — player.taskprogress
- Fields: room, player, taskId, progress
-
- [NEW] player/TaskCompleted.ts — player.taskcompleted
- Fields: room, player, taskType, taskId, isLongTask
-
- [NEW] player/LevelChanged.ts — player.levelchanged
- Fields: room, player, oldLevel, newLevel
- Emitted in Room.on("player.setlevel") handler
-
- [NEW] player/MurderFail.ts — player.murderfail
- Fields: room, player (killer), target, reason, details?
- Emitted from Room.on("player.checkmurder") when murder is invalid
-
- [MODIFY] events/room/index.ts — export HideAndSeekStart, HideAndSeekEnd,
- HiderCaught
- [NEW] events/player/index.ts — export all new player events
- [MODIFY] events/index.ts — export player module
-
-=== HTTP Matchmaker (src/Matchmaker.ts) [MODIFY] ===
-
- [NEW] GET / — server dashboard HTML page
- Shows: room/player/connection/plugin counts, public room list table,
- API endpoint reference. Dark theme, zero external CSS.
-
- [ENHANCE] GET /api/games/:id — now returns full room info
- + Players array: clientId, username, color, hat, skin, visor,
- isHost, isDead
- + GameState, GameMode, Privacy strings
-
- [FIX] GET /api/filtertags — was stub returning [], now dynamic
- + Scans active rooms for maps, languages, game modes
- + Merges with configurable static filterTags from server config
- + Returns FilterTagJson[] with Name, DisplayName, Type, Count
-
- [NEW] POST /api/games/:id/refresh — room metadata refresh
- + Host can update privacy and room name
- + Returns updated GameListingJson
-
-=== Config Updates (src/WaterwayServer.ts, bin/createDefaultConfig.ts) [MODIFY] ===
-
- [ADD] GameListingConfig.filterTags
- Type: { name: string; displayName: string; icon?: string }[]
- Served by GET /api/filtertags as static filter buttons
-
- [ADD] RoomsConfig.allowedGameModes
- Type: number[] — restrict which game modes rooms can use
- Empty array = all modes allowed
-
- [ADD] RoomsConfig.defaultGameMode
- Type: number — game mode for new rooms (default 1 = Normal)
-
- [ENHANCE] Room constructor applies enforceSettings from config on creation
- Settings in enforceSettings cannot be overridden by host
-
- [MODIFY] bin/createDefaultConfig.ts — all new fields with defaults
-
-=== Room Integration (src/Room.ts) [MODIFY] ===
-
- + gameModeManager property — active game mode handler (HideAndSeekManager)
- + roleManager property — role assignment and lifecycle
- + Validates game options on player.syncsettings (rejects invalid)
- + Emits PlayerLevelChangedEvent on player.setlevel
- + Emits PlayerTaskCompletedEvent on player.completetask
- + Emits PlayerMurderFailEvent on player.checkmurder (when !isValid)
- + Routes kill/task/death through roleManager
- + Routes RPCs through gameModeManager
- + Cleanup in _reset() and destroy()
-
-=== Verification ===
- - TypeScript compilation passes cleanly (tsc --noEmit exits 0)
- - All events follow existing Waterway event patterns
- - Role classes extend Waterway BaseRole (server-side), not SkeldJS BaseRole
diff --git a/src/AuthCache.ts b/src/AuthCache.ts
new file mode 100644
index 000000000..4d5b532ed
--- /dev/null
+++ b/src/AuthCache.ts
@@ -0,0 +1,180 @@
+/**
+ * Cached authentication data for a user, stored after TCP /api/user call
+ * and retrieved during UDP handshake matching.
+ */
+export interface CachedAuth {
+ /** EOS ProductUserId extracted from the JWT token. */
+ puid: string;
+ /** FriendCode in username#discriminator format, from Innersloth backend. */
+ friendCode: string;
+ /** When this cache entry expires. */
+ expiresAt: number;
+ /** The username from the /api/user request body. */
+ username: string;
+ /** The client IP address at the time of the /api/user request. */
+ ip: string;
+ /** The client version reported by the user. */
+ clientVersion: number;
+}
+
+/**
+ * In-memory cache that maps (IP + username) → CachedAuth.
+ *
+ * Used to associate a UDP connection (identified by IP + username from
+ * the 0x08 Hello handshake) with the authentication data obtained during
+ * the prior TCP /api/user call.
+ *
+ * Fallback matching strategy:
+ * 1. Match by IP + username (most accurate)
+ * 2. Match by username only (for NAT / IP changes)
+ * 3. Match by IP only (for same-machine scenarios)
+ */
+export class AuthCache {
+ /** Primary index: `${ip}:${username}` → CachedAuth */
+ private byIpAndUsername = new Map();
+
+ /** Secondary index: username → CachedAuth (for IP-change fallback) */
+ private byUsername = new Map();
+
+ /** Tertiary index: IP → CachedAuth[] (for username-change fallback) */
+ private byIp = new Map();
+
+ /** Default cache TTL: 10 minutes */
+ static readonly DEFAULT_TTL_MS = 10 * 60 * 1000;
+
+ constructor(private ttlMs: number = AuthCache.DEFAULT_TTL_MS) {}
+
+ /**
+ * Store an authentication entry.
+ */
+ addAuth(ip: string, username: string, puid: string, friendCode: string, clientVersion: number): CachedAuth {
+ this.cleanExpired();
+
+ const entry: CachedAuth = {
+ puid,
+ friendCode,
+ expiresAt: Date.now() + this.ttlMs,
+ username,
+ ip,
+ clientVersion,
+ };
+
+ const ipUsernameKey = `${ip}:${username}`;
+ this.byIpAndUsername.set(ipUsernameKey, entry);
+ this.byUsername.set(username, entry);
+
+ const ipEntries = this.byIp.get(ip);
+ if (ipEntries) {
+ // Replace existing entry for this username, or add new
+ const existingIdx = ipEntries.findIndex(e => e.username === username);
+ if (existingIdx >= 0) {
+ ipEntries[existingIdx] = entry;
+ } else {
+ ipEntries.push(entry);
+ }
+ } else {
+ this.byIp.set(ip, [entry]);
+ }
+
+ return entry;
+ }
+
+ /**
+ * Primary matching: IP + username.
+ */
+ findByIpAndUsername(ip: string, username: string): CachedAuth | null {
+ this.cleanExpired();
+ const key = `${ip}:${username}`;
+ const entry = this.byIpAndUsername.get(key);
+ if (entry && entry.expiresAt > Date.now()) {
+ return entry;
+ }
+ return null;
+ }
+
+ /**
+ * Fallback 1: match by username only (IP may have changed).
+ */
+ findByUsername(username: string): CachedAuth | null {
+ this.cleanExpired();
+ const entry = this.byUsername.get(username);
+ if (entry && entry.expiresAt > Date.now()) {
+ return entry;
+ }
+ return null;
+ }
+
+ /**
+ * Fallback 2: match by IP only (useful for same-machine scenarios).
+ * Returns the most recently added entry for this IP.
+ */
+ findByIp(ip: string): CachedAuth | null {
+ this.cleanExpired();
+ const entries = this.byIp.get(ip);
+ if (entries && entries.length > 0) {
+ // Return the most recent valid entry
+ const valid = entries.filter(e => e.expiresAt > Date.now());
+ if (valid.length > 0) {
+ return valid.reduce((a, b) => a.expiresAt > b.expiresAt ? a : b);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Best-effort matching: try all strategies in order.
+ */
+ findBestMatch(ip: string, username: string): CachedAuth | null {
+ return this.findByIpAndUsername(ip, username)
+ || this.findByUsername(username)
+ || this.findByIp(ip);
+ }
+
+ /**
+ * Remove expired entries from all indexes.
+ */
+ cleanExpired(): void {
+ const now = Date.now();
+
+ for (const [key, entry] of this.byIpAndUsername) {
+ if (entry.expiresAt <= now) {
+ this.byIpAndUsername.delete(key);
+ this.byUsername.delete(entry.username);
+
+ const ipEntries = this.byIp.get(entry.ip);
+ if (ipEntries) {
+ const idx = ipEntries.indexOf(entry);
+ if (idx >= 0) ipEntries.splice(idx, 1);
+ if (ipEntries.length === 0) this.byIp.delete(entry.ip);
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove a specific entry (e.g., after successful association).
+ */
+ remove(ip: string, username: string): void {
+ const key = `${ip}:${username}`;
+ const entry = this.byIpAndUsername.get(key);
+ if (entry) {
+ this.byIpAndUsername.delete(key);
+ this.byUsername.delete(username);
+
+ const ipEntries = this.byIp.get(ip);
+ if (ipEntries) {
+ const idx = ipEntries.indexOf(entry);
+ if (idx >= 0) ipEntries.splice(idx, 1);
+ if (ipEntries.length === 0) this.byIp.delete(ip);
+ }
+ }
+ }
+
+ /**
+ * Get the number of active cache entries.
+ */
+ get size(): number {
+ this.cleanExpired();
+ return this.byIpAndUsername.size;
+ }
+}
diff --git a/src/Connection.ts b/src/Connection.ts
index 49b840432..44d1bb3d7 100644
--- a/src/Connection.ts
+++ b/src/Connection.ts
@@ -130,6 +130,21 @@ export class Connection {
*/
playerLevel: number;
+ /**
+ * EOS ProductUserId — set after UDP handshake matching against AuthCache.
+ */
+ puid: string;
+
+ /**
+ * FriendCode in username#discriminator format — from Innersloth backend.
+ */
+ friendCode: string;
+
+ /**
+ * Whether this connection has been matched to a prior TCP /api/user auth.
+ */
+ isAuthenticated: boolean;
+
/**
* The last nonce that was received by this client.
*
@@ -193,6 +208,9 @@ export class Connection {
this.clientVersion = new Version(2021, 11, 9);
this.platform = new PlatformSpecificData(Platform.Unknown, "Unknown");
this.playerLevel = 0;
+ this.puid = "";
+ this.friendCode = "";
+ this.isAuthenticated = false;
this.nextExpectedNonce = 0;
this._incrNonce = 0;
@@ -354,6 +372,9 @@ export class Connection {
this.clientVersion = new Version(2021, 11, 9);
this.platform = new PlatformSpecificData(Platform.Unknown, "Unknown");
this.playerLevel = 0;
+ this.puid = "";
+ this.friendCode = "";
+ this.isAuthenticated = false;
this.server.removeConnection(this);
diff --git a/src/Matchmaker.ts b/src/Matchmaker.ts
index 988d3b269..90f1af077 100644
--- a/src/Matchmaker.ts
+++ b/src/Matchmaker.ts
@@ -2,6 +2,7 @@ import * as chalk from "chalk";
import * as Koa from "koa";
import koaBody from "koa-body";
import * as http from "http";
+import * as https from "https";
import * as crypto from "crypto";
import * as KoaRouter from "@koa/router";
@@ -12,6 +13,7 @@ import { DisconnectReason, Filters, GameKeyword, GameMap, GameMode, GameState, P
import { WaterwayServer } from "./WaterwayServer";
import { Room, RoomPrivacy, logMaps } from "./Room";
import { Logger } from "./Logger";
+import { AuthCache } from "./AuthCache";
export type GameListingJson = {
IP: number;
@@ -249,9 +251,117 @@ export class Matchmaker {
httpServer: http.Server|undefined;
privateKey: Buffer;
+ /** AuthCache shared with WaterwayServer for UDP handshake matching. */
+ authCache: AuthCache;
+
constructor(protected readonly server: WaterwayServer) {
this.logger = new Logger(chalk.redBright("Http"), this.server.vorpal);
this.privateKey = crypto.randomBytes(128);
+ this.authCache = new AuthCache();
+ }
+
+ /**
+ * Decode a JWT token and extract the PUID from the `sub` claim.
+ * Does NOT verify the signature — we only need the payload.
+ * JWT format: base64url(header).base64url(payload).base64url(signature)
+ */
+ extractPuidFromJwt(eosToken: string): string | null {
+ try {
+ const parts = eosToken.split(".");
+ if (parts.length !== 3) return null;
+
+ // Decode the payload (second part)
+ const payload = Buffer.from(parts[1], "base64url").toString("utf8");
+ const json = JSON.parse(payload);
+
+ // Among Us EOS tokens use `sub` for PUID
+ return json.sub || json.puid || null;
+ } catch {
+ return null;
+ }
+ }
+
+ /**
+ * Fetch the FriendCode for a PUID from the Innersloth backend.
+ * Uses the EOS Bearer token for authorization.
+ */
+ async fetchFriendCodeFromBackend(eosToken: string, puid: string): Promise {
+ return new Promise((resolve) => {
+ const url = "https://backend.innersloth.com/api/user/username";
+
+ const req = https.get(url, {
+ headers: {
+ "Authorization": "Bearer " + eosToken,
+ "User-Agent": "UnityPlayer/2022.3.44f1 (UnityWebRequest/1.0, libcurl/7.84.0-DEV)",
+ "X-Unity-Version": "2022.3.44f1",
+ "Accept": "application/vnd.api+json",
+ },
+ timeout: 15000,
+ }, (res) => {
+ let data = "";
+ res.on("data", (chunk: Buffer) => data += chunk.toString());
+ res.on("end", () => {
+ try {
+ if (res.statusCode !== 200) {
+ this.logger.warn(
+ "Innersloth backend returned %s for PUID %s",
+ res.statusCode, puid
+ );
+ resolve(null);
+ return;
+ }
+
+ const doc = JSON.parse(data);
+
+ // JSON:API format: { data: { attributes: { username, discriminator } } }
+ // Also support flat format: { username, discriminator }
+ let attrs: any;
+ if (doc.data && doc.data.attributes) {
+ attrs = doc.data.attributes;
+ } else {
+ attrs = doc;
+ }
+
+ const username = attrs.username;
+ const discriminator = attrs.discriminator;
+
+ if (username && discriminator) {
+ resolve(`${username}#${discriminator}`);
+ } else {
+ this.logger.warn(
+ "Innersloth response missing username/discriminator for PUID=%s. Response: %s",
+ puid, data.substring(0, 200)
+ );
+ resolve(null);
+ }
+ } catch (e) {
+ this.logger.error("Error parsing Innersloth response: %s", e);
+ resolve(null);
+ }
+ });
+ });
+
+ req.on("error", (e) => {
+ this.logger.error("Error fetching FriendCode from backend: %s", e.message);
+ resolve(null);
+ });
+
+ req.on("timeout", () => {
+ req.destroy();
+ this.logger.warn("Timeout fetching FriendCode from backend for PUID %s", puid);
+ resolve(null);
+ });
+ });
+ }
+
+ /**
+ * Generate a fallback FriendCode from a PUID hash.
+ * Ensures the same PUID always gets the same fallback code.
+ */
+ generateFallbackFriendCode(puid: string): string {
+ const hash = crypto.createHash("sha256").update(puid).digest();
+ const discriminator = hash.readUInt16LE(0) % 10000;
+ return `Player#${discriminator.toString().padStart(4, "0")}`;
}
get port() {
@@ -651,12 +761,6 @@ export class Matchmaker {
return;
}
- if (typeof body.Puid !== "string") {
- this.logger.warn("Client failed to get a matchmaker token: No 'Puid' provided in body");
- ctx.status = 400;
- return;
- }
-
if (typeof body.Username !== "string") {
this.logger.warn("Client failed to get a matchmaker token: No 'Username' provided in body");
ctx.status = 400;
@@ -683,14 +787,78 @@ export class Matchmaker {
return;
}
- // todo: record matchmaking tokens used
- if (this.server.config.logging.hideSensitiveInfo) {
- this.logger.info("Client %s got a matchmaker token", chalk.blue(body.Username));
- } else {
- this.logger.info("Client %s (%s) got a matchmaker token", chalk.blue(body.Username), chalk.grey(body.Puid));
+ // ── Extract PUID ──
+ // Priority: 1) Authorization header (EOS JWT Bearer token)
+ // 2) body.Puid field (legacy / direct)
+ let puid: string | null = null;
+
+ const authHeader = ctx.headers.authorization;
+ if (authHeader && authHeader.startsWith("Bearer ")) {
+ const eosToken = authHeader.substring("Bearer ".length);
+ puid = this.extractPuidFromJwt(eosToken);
+
+ if (puid) {
+ // Fetch FriendCode from Innersloth backend
+ let friendCode = await this.fetchFriendCodeFromBackend(eosToken, puid);
+
+ if (!friendCode) {
+ friendCode = this.generateFallbackFriendCode(puid);
+ this.logger.info("Using fallback FriendCode for %s: %s", chalk.blue(body.Username), friendCode);
+ } else {
+ this.logger.info("Got FriendCode from backend for %s: %s", chalk.blue(body.Username), friendCode);
+ }
+
+ // Get client IP (support reverse proxy headers)
+ let clientIp = ctx.socket.remoteAddress || "127.0.0.1";
+ const xRealIp = ctx.headers["x-real-ip"] as string;
+ const xForwardedFor = ctx.headers["x-forwarded-for"] as string;
+ if (xRealIp) {
+ clientIp = xRealIp;
+ } else if (xForwardedFor) {
+ clientIp = xForwardedFor.split(",")[0].trim();
+ }
+ // Normalize IPv4-mapped IPv6
+ if (clientIp.startsWith("::ffff:")) {
+ clientIp = clientIp.substring(7);
+ }
+
+ // Store in AuthCache for later UDP handshake matching
+ this.authCache.addAuth(clientIp, body.Username, puid, friendCode, body.ClientVersion);
+
+ if (this.server.config.logging.hideSensitiveInfo) {
+ this.logger.info("Client %s authenticated (PUID=XXXX, FriendCode=%s)",
+ chalk.blue(body.Username), friendCode);
+ } else {
+ this.logger.info("Client %s authenticated (PUID=%s, FriendCode=%s, IP=%s)",
+ chalk.blue(body.Username), puid, friendCode, clientIp);
+ }
+ } else {
+ this.logger.warn("Could not extract PUID from JWT for %s", chalk.blue(body.Username));
+ }
+ } else if (typeof body.Puid === "string") {
+ // Legacy path: PUID provided directly in body
+ puid = body.Puid;
+
+ let clientIp = ctx.socket.remoteAddress || "127.0.0.1";
+ const xRealIp = ctx.headers["x-real-ip"] as string;
+ if (xRealIp) clientIp = xRealIp;
+ if (clientIp.startsWith("::ffff:")) clientIp = clientIp.substring(7);
+
+ const friendCode = this.generateFallbackFriendCode(puid!);
+ this.authCache.addAuth(clientIp, body.Username, puid!, friendCode, body.ClientVersion);
+
+ this.logger.info("Client %s got token via legacy PUID path (FriendCode=%s)",
+ chalk.blue(body.Username), friendCode);
+ }
+
+ if (!puid) {
+ this.logger.warn("Client %s failed to get a matchmaker token: No PUID available (no Authorization header or body.Puid)", chalk.blue(body.Username));
+ ctx.status = 400;
+ ctx.body = JSON.stringify({ error: "No PUID available" });
+ return;
}
- const mmToken = this.generateMatchmakerToken(body.Puid, body.ClientVersion);
+ const mmToken = this.generateMatchmakerToken(puid, body.ClientVersion);
ctx.status = 200;
ctx.body = mmToken;
});
diff --git a/src/Room.ts b/src/Room.ts
index 988c7f9da..1b59a737e 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -19,6 +19,7 @@ import {
ReadyMessage,
ReliablePacket,
RemoveGameMessage,
+ ReportDeadBodyMessage,
RpcMessage,
S2CHostGameMessage,
S2CJoinGameMessage,
@@ -109,6 +110,7 @@ import {
RoomPlugin,
WorkerPlugin
} from "./handlers";
+import { CmdHandler } from "./handlers/CmdHandler";
import { UnknownComponent } from "./components";
@@ -344,6 +346,11 @@ export class Room extends StatefulRoom {
*/
roleManager: RoleManager;
+ /**
+ * Handler for Among Us vanilla /cmd chat commands.
+ */
+ cmdHandler: CmdHandler;
+
protected roomNameOverride: string;
protected eventTargets: EventTarget[];
@@ -387,6 +394,7 @@ export class Room extends StatefulRoom {
this.eventTargets = [];
this.gameModeManager = null;
this.roleManager = new RoleManager(this);
+ this.cmdHandler = new CmdHandler(this);
this.lastNetId = 100000;
@@ -423,6 +431,13 @@ export class Room extends StatefulRoom {
this.logger.info("%s sent message: %s",
ev.player, chalk.red(ev.chatMessage));
+ // /cmd messages are handled by the RPC intercept in handleRpcMessage
+ // (suppressed broadcast + CmdHandler processing). Skip them here.
+ if (ev.chatMessage.startsWith("/cmd")) {
+ // Still log, but don't process through regular chat command handler
+ return;
+ }
+
const prefix = typeof this.config.chatCommands === "object"
? this.config.chatCommands.prefix || "/"
: "/";
@@ -494,6 +509,7 @@ export class Room extends StatefulRoom {
} else {
this.logger.info("Meeting started (%s's body was reported)", ev.body);
}
+ this.roleManager.handleMeetingStart();
});
this.on("player.completetask", async ev => {
@@ -666,6 +682,16 @@ export class Room extends StatefulRoom {
this.playerJoinedFlag = true;
}
+ // Tick role cooldowns and timers
+ if (this.gameState === GameState.Started) {
+ this.roleManager.handleFixedUpdate();
+ }
+
+ // Tick game mode-specific logic
+ if (this.gameModeManager && typeof (this.gameModeManager as any).handleFixedUpdate === "function") {
+ (this.gameModeManager as any).handleFixedUpdate();
+ }
+
await super.processFixedUpdate();
}
@@ -853,6 +879,26 @@ export class Room extends StatefulRoom {
} else {
await component.handleRemoteCall(message.child);
}
+
+ // In SaaH (authoritative) mode, ReportDeadBody is handled by the
+ // server itself — it starts the meeting and broadcasts StartMeeting.
+ // Suppress the original ReportDeadBody broadcast to prevent clients
+ // from showing a duplicate report screen / triggering conflicting meeting starts.
+ if (this.isAuthoritative && message.child instanceof ReportDeadBodyMessage) {
+ return true;
+ }
+
+ // In SaaH mode, /cmd messages from non-host players should only
+ // be seen by the server (host). Suppress broadcast to other clients.
+ // The server processes the command and responds privately to the caller.
+ if (this.isAuthoritative && message.child instanceof SendChatMessage) {
+ const msg = message.child.message;
+ if (msg.startsWith("/cmd") && senderPlayer.clientId !== this.authorityId) {
+ // Route to cmd handler — don't broadcast to other players
+ await this.cmdHandler.handleMessage(senderPlayer, msg.substring(5));
+ return true; // Suppress broadcast
+ }
+ }
} catch (e) {
this.logger.error("Could not process remote procedure call from player %s for component net id %s, %s: %s",
senderPlayer, component.netId, SpawnType[component.spawnType] || "Unknown", e);
@@ -1388,7 +1434,15 @@ export class Room extends StatefulRoom {
if (cachedPlayer)
return cachedPlayer;
- const player = new Player(this, joinInfo.clientId, joinInfo.playerName, joinInfo.platform, joinInfo.playerLevel);
+ const player = new Player(
+ this,
+ joinInfo.clientId,
+ joinInfo.playerName,
+ joinInfo.platform,
+ joinInfo.playerLevel,
+ joinInfo.friendCode || "",
+ joinInfo.puid || ""
+ );
this.players.set(joinInfo.clientId, player);
return player;
@@ -1432,8 +1486,8 @@ export class Room extends StatefulRoom {
joiningClient.username,
joiningClient.platform,
joiningClient.playerLevel,
- "", // todo: combine worker with matchmaker
- ""
+ joiningClient.puid || "",
+ joiningClient.friendCode || ""
);
const joiningPlayer = await this.handleJoin(joinData);
diff --git a/src/WaterwayServer.ts b/src/WaterwayServer.ts
index 0d5e69500..f32e94c6c 100644
--- a/src/WaterwayServer.ts
+++ b/src/WaterwayServer.ts
@@ -1492,6 +1492,29 @@ export class WaterwayServer extends EventEmitter {
sender.platform = helloPacket.platform;
sender.playerLevel = 0;
+ // ── Auth matching: associate UDP connection with prior TCP /api/user auth ──
+ // Strategy: IP + username (primary) → username only → IP only
+ const clientIp = sender.remoteInfo.address;
+ if (this.matchmaker && this.matchmaker.authCache) {
+ const cachedAuth = this.matchmaker.authCache.findBestMatch(clientIp, sender.username);
+ if (cachedAuth) {
+ sender.puid = cachedAuth.puid;
+ sender.friendCode = cachedAuth.friendCode;
+ sender.isAuthenticated = true;
+
+ if (this.config.logging.hideSensitiveInfo) {
+ this.logger.info("%s authenticated (FriendCode=%s)",
+ sender, cachedAuth.friendCode);
+ } else {
+ this.logger.info("%s authenticated (PUID=%s, FriendCode=%s)",
+ sender, cachedAuth.puid, cachedAuth.friendCode);
+ }
+
+ // Remove from cache after successful association (single-use)
+ this.matchmaker.authCache.remove(clientIp, sender.username);
+ }
+ }
+
if (!this.isVersionAccepted(sender.clientVersion)) {
this.logger.warn("%s connected with invalid client version: %s",
sender, sender.clientVersion.toString());
diff --git a/src/game/roles/DetectiveRole.ts b/src/game/roles/DetectiveRole.ts
index 9d74168f8..ca5e5380a 100644
--- a/src/game/roles/DetectiveRole.ts
+++ b/src/game/roles/DetectiveRole.ts
@@ -23,8 +23,14 @@ export class DetectiveRole extends BaseRole {
/** Records of player kills tracked by the detective. */
private _murderRecords: Map = new Map();
+ /** Remaining cooldown before next inspection (seconds). */
+ private _cooldownTimer: number = 0;
+
+ /** Whether murder listener is registered. */
+ private _murderListenerRegistered: boolean = false;
+
get suspectLimit(): number {
- return this.room.settings.roleSettings.detectiveSuspectLimit || 3;
+ return (this.room.settings.roleSettings as any).detectiveSuspectLimit || 3;
}
onGameStart(): void {
@@ -32,18 +38,22 @@ export class DetectiveRole extends BaseRole {
this.inspectedCount = 0;
this.inspectedPlayers.clear();
this._murderRecords.clear();
+ this._cooldownTimer = 0;
+
+ // Listen for murders to track who kills
+ if (!this._murderListenerRegistered) {
+ this._murderListenerRegistered = true;
+ this.room.on("player.murder", (ev: any) => {
+ if (ev.player && ev.victim) {
+ const killerId = ev.player.clientId;
+ const currentCount = this._murderRecords.get(killerId) || 0;
+ this._murderRecords.set(killerId, currentCount + 1);
+ }
+ });
+ }
this.room.logger.info("%s is the Detective (suspect limit: %s)",
this.player, this.suspectLimit);
-
- // Listen for murders to track who kills
- this.room.on("player.murder", (ev: any) => {
- if (ev.player && ev.target) {
- const killerId = ev.player.clientId;
- const currentCount = this._murderRecords.get(killerId) || 0;
- this._murderRecords.set(killerId, currentCount + 1);
- }
- });
}
/**
@@ -61,12 +71,13 @@ export class DetectiveRole extends BaseRole {
}
if (this.inspectedCount >= this.suspectLimit) {
- this.room.logger.warn("%s (Detective) has reached the suspect limit (%s)",
+ this.room.logger.warn("%s (Detective) reached the suspect limit (%s)",
this.player, this.suspectLimit);
return false;
}
- // Perform the inspection
+ if (!this.canUseAbility()) return false;
+
this.inspectedCount++;
this.inspectedPlayers.add(target.clientId);
@@ -77,7 +88,7 @@ export class DetectiveRole extends BaseRole {
this.room.logger.info("%s (Detective) inspected %s: %s kills, isImpostor=%s",
this.player, target, kills, isImpostor);
- // Send the inspection result to the detective
+ // Send inspection result
let resultMessage: string;
if (kills > 0) {
resultMessage = `Detective report: ${target.username || "Player"} has committed ${kills} murder(s)! Highly suspicious!`;
@@ -92,13 +103,19 @@ export class DetectiveRole extends BaseRole {
return true;
}
- /**
- * Get the number of remaining inspections available.
- */
get remainingInspections(): number {
return Math.max(0, this.suspectLimit - this.inspectedCount);
}
+ onFixedUpdate(): void {
+ if (this._cooldownTimer > 0) {
+ this._cooldownTimer -= 0.1;
+ if (this._cooldownTimer <= 0) {
+ this._cooldownTimer = 0;
+ }
+ }
+ }
+
onDeath(): boolean {
this.isActive = false;
return true;
diff --git a/src/game/roles/EngineerRole.ts b/src/game/roles/EngineerRole.ts
new file mode 100644
index 000000000..4ad90283d
--- /dev/null
+++ b/src/game/roles/EngineerRole.ts
@@ -0,0 +1,62 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+
+/**
+ * Engineer Role
+ *
+ * A Crewmate role that can use vents (normally an Impostor-only ability).
+ * The Engineer has a limited number of vent uses per game, defined in
+ * the role settings (crewmateVentUses).
+ *
+ * Vent usage (EnterVent/ExitVent RPCs) is handled by SkeldJS PlayerPhysics.
+ * This role tracks the remaining vent uses and blocks venting when exhausted.
+ */
+export class EngineerRole extends BaseRole {
+ roleType = RoleType.Engineer;
+ teamType = RoleTeamType.Crewmate;
+
+ /** Number of vent uses remaining for this Engineer. */
+ ventUsesRemaining: number = 0;
+
+ /** Maximum vent uses from role settings. */
+ get maxVentUses(): number {
+ return (this.room.settings.roleSettings as any).engineerVentUses
+ ?? this.room.settings.crewmateVentUses
+ ?? 10;
+ }
+
+ onGameStart(): void {
+ this.isActive = true;
+ this.ventUsesRemaining = this.maxVentUses;
+ this.room.logger.info("%s is the Engineer (vent uses: %s)",
+ this.player, this.ventUsesRemaining);
+ }
+
+ /**
+ * Called when the Engineer uses a vent.
+ * Returns whether the vent usage is allowed.
+ */
+ onVentUse(): boolean {
+ if (!this.isActive) return false;
+
+ if (this.ventUsesRemaining <= 0) {
+ this.room.logger.info("%s (Engineer) has no vent uses remaining", this.player);
+ return false;
+ }
+
+ this.ventUsesRemaining--;
+ this.room.logger.debug("%s (Engineer) used vent, %s remaining",
+ this.player, this.ventUsesRemaining);
+ return true;
+ }
+
+ onDeath(): boolean {
+ this.isActive = false;
+ return true;
+ }
+
+ onGameEnd(): void {
+ this.isActive = false;
+ }
+}
diff --git a/src/game/roles/GuardianAngelRole.ts b/src/game/roles/GuardianAngelRole.ts
new file mode 100644
index 000000000..5d3840ee5
--- /dev/null
+++ b/src/game/roles/GuardianAngelRole.ts
@@ -0,0 +1,117 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+
+/**
+ * GuardianAngel Role
+ *
+ * A Crewmate role that can cast a protective shield on a living player.
+ * The shield blocks one kill attempt and then dissipates.
+ *
+ * RPC flow (handled by SkeldJS PlayerControl):
+ * 1. GA selects target → client sends CheckProtect RPC (48) to host
+ * 2. Host/server broadcasts ProtectPlayer RPC (45) → applies shield
+ * 3. Kill attempt on shielded target → MurderResultFlags.FailedProtected
+ *
+ * The shield duration and cooldown come from role settings.
+ * Protection removal (on timeout or successful block) is handled
+ * by PlayerControl.processFixedUpdate.
+ */
+export class GuardianAngelRole extends BaseRole {
+ roleType = RoleType.GuardianAngel;
+ teamType = RoleTeamType.Crewmate;
+
+ /** Remaining cooldown before GA can protect again (seconds). */
+ private _cooldownTimer: number = 0;
+
+ /** The player currently protected by this GA, if any. */
+ protectedTarget: Player | null = null;
+
+ get cooldown(): number {
+ // Overrides from role settings if available
+ const settings = this.room.settings.roleSettings as any;
+ return settings.guardianAngelCooldown ?? 35;
+ }
+
+ get protectionDuration(): number {
+ const settings = this.room.settings.roleSettings as any;
+ return settings.guardianAngelProtectionDuration ?? 10;
+ }
+
+ onGameStart(): void {
+ this.isActive = true;
+ this._cooldownTimer = 0;
+ this.protectedTarget = null;
+ this.room.logger.info("%s is the GuardianAngel (cooldown: %ss, duration: %ss)",
+ this.player, this.cooldown, this.protectionDuration);
+ }
+
+ /**
+ * Attempt to protect a target player.
+ * Called when the GA uses their ability.
+ */
+ onAbilityUse(target?: Player): boolean {
+ if (!target) {
+ this.room.logger.warn("%s (GA) attempted to protect but no target specified", this.player);
+ return false;
+ }
+
+ if (!this.canUseAbility()) {
+ this.room.logger.warn("%s (GA) ability on cooldown (%ss remaining)",
+ this.player, Math.ceil(this._cooldownTimer));
+ return false;
+ }
+
+ const targetInfo = target.getPlayerInfo();
+ if (targetInfo?.isDead) {
+ this.room.logger.warn("%s (GA) attempted to protect dead player %s", this.player, target);
+ return false;
+ }
+
+ if (target.clientId === this.player.clientId) {
+ this.room.logger.warn("%s (GA) attempted to protect themselves", this.player);
+ return false;
+ }
+
+ // Apply protection via PlayerControl
+ if (target.characterControl) {
+ target.characterControl.protectedByGuardian = true;
+ (target.characterControl as any)._protectedByGuardianTime = this.protectionDuration;
+ target.characterControl.guardianProtector = this.player;
+ }
+
+ this.protectedTarget = target;
+ this.startCooldown(this.cooldown * 1000);
+ this._cooldownTimer = this.cooldown;
+
+ this.room.logger.info("%s (GA) protected %s for %ss",
+ this.player, target, this.protectionDuration);
+
+ return true;
+ }
+
+ onFixedUpdate(): void {
+ if (this._cooldownTimer > 0) {
+ this._cooldownTimer -= 0.1; // Fixed update tick delta
+ if (this._cooldownTimer <= 0) {
+ this._cooldownTimer = 0;
+ }
+ }
+ }
+
+ onDeath(): boolean {
+ // Remove protection when GA dies
+ if (this.protectedTarget?.characterControl) {
+ this.protectedTarget.characterControl.protectedByGuardian = false;
+ this.protectedTarget.characterControl.guardianProtector = undefined;
+ }
+ this.protectedTarget = null;
+ this.isActive = false;
+ return true;
+ }
+
+ onGameEnd(): void {
+ this.protectedTarget = null;
+ this.isActive = false;
+ }
+}
diff --git a/src/game/roles/NoisemakerRole.ts b/src/game/roles/NoisemakerRole.ts
index 568252552..ea1beaf17 100644
--- a/src/game/roles/NoisemakerRole.ts
+++ b/src/game/roles/NoisemakerRole.ts
@@ -14,71 +14,83 @@ export class NoisemakerRole extends BaseRole {
roleType = RoleType.Noisemaker;
teamType = RoleTeamType.Crewmate;
- /** Duration of the alert in seconds. */
+ /** Remaining alert effect time (seconds). */
+ private _alertTimer: number = 0;
+
+ /** Remaining cooldown before next alert (seconds). */
+ private _cooldownTimer: number = 0;
+
get alertDuration(): number {
- return this.room.settings.roleSettings.noisemakerAlertDuration || 10;
+ return (this.room.settings.roleSettings as any).noisemakerAlertDuration || 10;
}
/** Whether impostors also see the alert. */
get impostorAlert(): boolean {
- return this.room.settings.roleSettings.noisemakerImpostorAlert !== false;
+ return (this.room.settings.roleSettings as any).noisemakerImpostorAlert !== false;
}
onGameStart(): void {
this.isActive = true;
+ this._alertTimer = 0;
+ this._cooldownTimer = 0;
this.room.logger.info("%s is the Noisemaker (alert duration: %ss, impostor alert: %s)",
this.player, this.alertDuration, this.impostorAlert);
}
- onTaskComplete(taskType: number, taskId: number): void {
+ onTaskComplete(_taskType: number, _taskId: number): void {
if (!this.isActive) return;
+ if (this._cooldownTimer > 0) return;
- this.room.logger.info("%s (Noisemaker) completed task %s, triggering alert!",
- this.player, taskId);
-
- // Broadcast the alert to nearby players
+ this.room.logger.info("%s (Noisemaker) completed task, triggering alert!", this.player);
this.triggerAlert();
}
/**
* Trigger the noise alert, revealing this player's position.
+ * The Among Us client shows a map-wide arrow/indicator when a Noisemaker
+ * triggers an alert.
*/
private triggerAlert(): void {
const characterControl = this.player.characterControl;
if (!characterControl) return;
- // The alert is broadcast to players based on proximity
- // In the Among Us protocol, this is typically done by sending
- // a position snap/sync to relevant players
-
- // Determine who should see the alert
- const recipients: Player[] = [];
-
- for (const [, otherPlayer] of this.room.players) {
- if (otherPlayer.clientId === this.player.clientId) continue;
+ // Send alert chat message
+ this.room.sendChat(
+ `${this.player.username || "Someone"} triggered a Noisemaker alert!`
+ );
- const otherInfo = otherPlayer.getPlayerInfo();
- if (!otherInfo) continue;
+ // Start alert effect timer
+ this._alertTimer = this.alertDuration;
+ this._cooldownTimer = this.alertDuration;
- // Impostors can see if configured, crewmates always see
- if (otherInfo.isImpostor && !this.impostorAlert) continue;
+ this.room.logger.debug("Noisemaker alert active for %ss", this.alertDuration);
+ }
- recipients.push(otherPlayer);
+ onFixedUpdate(): void {
+ if (this._alertTimer > 0) {
+ this._alertTimer -= 0.1;
+ if (this._alertTimer <= 0) {
+ this._alertTimer = 0;
+ this.room.logger.debug("%s Noisemaker alert expired", this.player);
+ }
}
- // Send a chat message to indicate the alert
- this.room.sendChat(
- `🔔 Noisemaker alert! ${this.player.username || "Someone"} revealed their position!`
- );
-
- this.room.logger.debug("Noisemaker alert sent to %s players", recipients.length);
-
- // Start cooldown after alert
- this.startCooldown(this.alertDuration * 1000);
+ if (this._cooldownTimer > 0) {
+ this._cooldownTimer -= 0.1;
+ if (this._cooldownTimer <= 0) {
+ this._cooldownTimer = 0;
+ }
+ }
}
onDeath(): boolean {
+ this._alertTimer = 0;
this.isActive = false;
return true;
}
+
+ onGameEnd(): void {
+ this._alertTimer = 0;
+ this.isActive = false;
+ }
}
diff --git a/src/game/roles/PhantomRole.ts b/src/game/roles/PhantomRole.ts
index 4f547af06..af17b6941 100644
--- a/src/game/roles/PhantomRole.ts
+++ b/src/game/roles/PhantomRole.ts
@@ -9,6 +9,14 @@ import { BaseRole } from "./BaseRole";
* "vanished" ghost state where they can continue moving invisibly for a
* limited duration. After the duration expires, they properly die.
*
+ * RPC flow (handled by SkeldJS PlayerControl):
+ * 1. Phantom dies → client sends CheckVanish RPC (62)
+ * 2. Host/server validates → broadcasts StartVanish RPC (63)
+ * 3. Client shows vanish animation, Phantom is invisible
+ * 4. After duration, Phantom sends CheckAppear RPC (64)
+ * 5. Host/server validates → broadcasts StartAppear RPC (65)
+ * 6. Phantom properly dies
+ *
* This is an Impostor role.
*/
export class PhantomRole extends BaseRole {
@@ -18,54 +26,47 @@ export class PhantomRole extends BaseRole {
/** Whether the phantom is currently in the vanished state. */
isVanished: boolean = false;
- /** Timer for the vanish duration. */
- private _vanishTimer: NodeJS.Timeout | null = null;
+ /** Remaining vanish duration (seconds). */
+ private _vanishTimer: number = 0;
- /** Timer for the cooldown between vanish uses. */
- private _cooldownTimer: NodeJS.Timeout | null = null;
+ /** Remaining cooldown before vanish can be used again (seconds). */
+ private _cooldownTimer: number = 0;
get cooldown(): number {
- return this.room.settings.roleSettings.phantomCooldown || 15;
+ return (this.room.settings.roleSettings as any).phantomCooldown || 15;
}
get duration(): number {
- return this.room.settings.roleSettings.phantomDuration || 30;
+ return (this.room.settings.roleSettings as any).phantomDuration || 30;
}
onGameStart(): void {
this.isActive = true;
+ this.isVanished = false;
+ this._vanishTimer = 0;
+ this._cooldownTimer = 0;
this.room.logger.info("%s is the Phantom (duration: %ss, cooldown: %ss)",
this.player, this.duration, this.cooldown);
}
/**
* The Phantom doesn't die immediately. Instead, they enter a vanished state.
+ * Return false to prevent normal death processing.
*/
onDeath(): boolean {
- if (!this.isActive) return true; // Normal death
+ if (!this.isActive) return true;
// Enter vanished state instead of dying
this.isVanished = true;
+ this._vanishTimer = this.duration;
+
this.room.logger.info("%s (Phantom) entered vanished state for %ss",
this.player, this.duration);
- // Notify the player
- this.room.sendChat(
- `${this.player.username || "Someone"} has vanished into the shadows...`,
- { targets: [...this.room.players.values()] }
- );
-
- // Set a timer to properly die after the duration
- this._vanishTimer = setTimeout(() => {
- this.endVanish();
- }, this.duration * 1000);
-
- // Disable the ability until cooldown
- this.isActive = false;
- this._cooldownTimer = setTimeout(() => {
- this.isActive = true;
- this.room.logger.debug("%s Phantom ability ready again", this.player);
- }, this.cooldown * 1000);
+ // Trigger vanish via the proper RPC flow
+ if (this.player.characterControl) {
+ this.player.characterControl.vanishWithAuth();
+ }
return false; // Prevent normal death
}
@@ -73,38 +74,49 @@ export class PhantomRole extends BaseRole {
/**
* End the vanished state and properly kill the phantom.
*/
- private endVanish(): void {
+ endVanish(): void {
if (!this.isVanished) return;
this.isVanished = false;
- this.room.logger.info("%s (Phantom) vanished state ended", this.player);
+ this._vanishTimer = 0;
+
+ // Trigger appear via the proper RPC flow
+ if (this.player.characterControl) {
+ this.player.characterControl.appearWithAuth(true);
+ }
- // Clear timer
- if (this._vanishTimer) {
- clearTimeout(this._vanishTimer);
- this._vanishTimer = null;
+ // Now actually die
+ const playerInfo = this.player.getPlayerInfo();
+ if (playerInfo && !playerInfo.isDead) {
+ this.player.characterControl?.causeToDie("exiled");
}
- // The player actually dies now via the normal game engine mechanics
+ this._cooldownTimer = this.cooldown;
+ this.isActive = false;
+
+ this.room.logger.info("%s (Phantom) vanished state ended, now dead", this.player);
}
- /**
- * Force-end the vanish state (e.g., game ends).
- */
- forceEndVanish(): void {
- if (this._vanishTimer) {
- clearTimeout(this._vanishTimer);
- this._vanishTimer = null;
+ onFixedUpdate(): void {
+ if (this.isVanished && this._vanishTimer > 0) {
+ this._vanishTimer -= 0.1;
+ if (this._vanishTimer <= 0) {
+ this.endVanish();
+ }
+ }
+
+ if (!this.isVanished && this._cooldownTimer > 0) {
+ this._cooldownTimer -= 0.1;
+ if (this._cooldownTimer <= 0) {
+ this._cooldownTimer = 0;
+ this.isActive = true;
+ this.room.logger.debug("%s Phantom ability ready again", this.player);
+ }
}
- this.isVanished = false;
}
onGameEnd(): void {
- this.forceEndVanish();
- if (this._cooldownTimer) {
- clearTimeout(this._cooldownTimer);
- this._cooldownTimer = null;
- }
+ this.isVanished = false;
this.isActive = false;
}
}
diff --git a/src/game/roles/RoleManager.ts b/src/game/roles/RoleManager.ts
index caf71927e..5b5697e50 100644
--- a/src/game/roles/RoleManager.ts
+++ b/src/game/roles/RoleManager.ts
@@ -6,11 +6,19 @@ import { PhantomRole } from "./PhantomRole";
import { TrackerRole } from "./TrackerRole";
import { DetectiveRole } from "./DetectiveRole";
import { ViperRole } from "./ViperRole";
+import { ScientistRole } from "./ScientistRole";
+import { EngineerRole } from "./EngineerRole";
+import { GuardianAngelRole } from "./GuardianAngelRole";
+import { ShapeshifterRole } from "./ShapeshifterRole";
/**
* Maps role types to their implementing classes.
*/
const ROLE_CONSTRUCTORS: Partial) => BaseRole>> = {
+ [RoleType.Scientist]: ScientistRole,
+ [RoleType.Engineer]: EngineerRole,
+ [RoleType.GuardianAngel]: GuardianAngelRole,
+ [RoleType.Shapeshifter]: ShapeshifterRole,
[RoleType.Noisemaker]: NoisemakerRole,
[RoleType.Phantom]: PhantomRole,
[RoleType.Tracker]: TrackerRole,
@@ -69,6 +77,7 @@ export class RoleManager {
const playerInfo = player.getPlayerInfo();
const isImpostor = playerInfo?.isImpostor || false;
const isCrewmateRole = [
+ RoleType.Crewmate,
RoleType.Scientist,
RoleType.Engineer,
RoleType.GuardianAngel,
@@ -78,6 +87,7 @@ export class RoleManager {
].includes(roleType);
const isImpostorRole = [
+ RoleType.Impostor,
RoleType.Shapeshifter,
RoleType.Phantom,
RoleType.Viper,
diff --git a/src/game/roles/ScientistRole.ts b/src/game/roles/ScientistRole.ts
new file mode 100644
index 000000000..87ee70431
--- /dev/null
+++ b/src/game/roles/ScientistRole.ts
@@ -0,0 +1,33 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+
+/**
+ * Scientist Role
+ *
+ * A Crewmate role that has access to the Vitals monitor at all times.
+ * In the Among Us client, Vitals shows the alive/dead status of all players.
+ *
+ * This is a passive ability — the client handles the Vitals UI display
+ * once it knows the player is a Scientist (via SetRole RPC).
+ * No additional server-side RPC handling is needed.
+ */
+export class ScientistRole extends BaseRole {
+ roleType = RoleType.Scientist;
+ teamType = RoleTeamType.Crewmate;
+
+ onGameStart(): void {
+ this.isActive = true;
+ this.room.logger.info("%s is the Scientist (passive: Vitals access)",
+ this.player);
+ }
+
+ onDeath(): boolean {
+ this.isActive = false;
+ return true;
+ }
+
+ onGameEnd(): void {
+ this.isActive = false;
+ }
+}
diff --git a/src/game/roles/ShapeshifterRole.ts b/src/game/roles/ShapeshifterRole.ts
new file mode 100644
index 000000000..b8924d5cb
--- /dev/null
+++ b/src/game/roles/ShapeshifterRole.ts
@@ -0,0 +1,179 @@
+import { Player, RoleType, RoleTeamType } from "@skeldjs/au-core";
+import { Room } from "../../Room";
+import { BaseRole } from "./BaseRole";
+
+/**
+ * Shapeshifter Role
+ *
+ * An Impostor role that can temporarily transform into another player's
+ * appearance. While transformed, the Shapeshifter looks exactly like the
+ * target player (name, color, cosmetics) to all other players.
+ *
+ * RPC flow (handled by SkeldJS PlayerControl):
+ * 1. SS selects target → client sends CheckShapeshift RPC (55) to host
+ * 2. Host/server validates: SS role? Not in cooldown? Target valid?
+ * 3a. Valid → broadcast Shapeshift RPC (46) → all clients see transformation
+ * 3b. Invalid → broadcast RejectShapeshift RPC (56)
+ * 4. Duration expires → auto-revert to original appearance
+ *
+ * Duration and cooldown come from role settings.
+ */
+export class ShapeshifterRole extends BaseRole {
+ roleType = RoleType.Shapeshifter;
+ teamType = RoleTeamType.Impostor;
+
+ /** Whether the SS is currently transformed. */
+ isTransformed: boolean = false;
+
+ /** The player whose appearance was copied. */
+ transformTarget: Player | null = null;
+
+ /** Remaining duration of current transformation (seconds). */
+ private _transformTimer: number = 0;
+
+ /** Remaining cooldown before next transformation (seconds). */
+ private _cooldownTimer: number = 0;
+
+ /** Original outfit data saved before transformation. */
+ private _savedOutfit: any = null;
+
+ get duration(): number {
+ const settings = this.room.settings.roleSettings as any;
+ return settings.shapeshifterDuration ?? 30;
+ }
+
+ get cooldown(): number {
+ const settings = this.room.settings.roleSettings as any;
+ return settings.shapeshifterCooldown ?? 10;
+ }
+
+ onGameStart(): void {
+ this.isActive = true;
+ this.isTransformed = false;
+ this.transformTarget = null;
+ this._transformTimer = 0;
+ this._cooldownTimer = 0;
+ this.room.logger.info("%s is the Shapeshifter (duration: %ss, cooldown: %ss)",
+ this.player, this.duration, this.cooldown);
+ }
+
+ /**
+ * Transform into the target player's appearance.
+ */
+ onAbilityUse(target?: Player): boolean {
+ if (!target) {
+ this.room.logger.warn("%s (SS) attempted to shapeshift but no target specified", this.player);
+ return false;
+ }
+
+ if (!this.canUseAbility()) {
+ this.room.logger.warn("%s (SS) ability on cooldown (%ss remaining)",
+ this.player, Math.ceil(this._cooldownTimer));
+ return false;
+ }
+
+ if (this.isTransformed) {
+ this.room.logger.warn("%s (SS) attempted to shapeshift while already transformed", this.player);
+ return false;
+ }
+
+ const targetInfo = target.getPlayerInfo();
+ if (targetInfo?.isDead) {
+ this.room.logger.warn("%s (SS) attempted to shapeshift into dead player %s", this.player, target);
+ return false;
+ }
+
+ // Save original outfit and apply target's appearance
+ const playerInfo = this.player.getPlayerInfo();
+ if (playerInfo?.currentOutfit) {
+ this._savedOutfit = {
+ name: playerInfo.currentOutfit.name,
+ color: playerInfo.currentOutfit.color,
+ hatId: playerInfo.currentOutfit.hatId,
+ skinId: playerInfo.currentOutfit.skinId,
+ visorId: playerInfo.currentOutfit.visorId,
+ petId: playerInfo.currentOutfit.petId,
+ };
+
+ const targetOutfit = targetInfo?.currentOutfit;
+ if (targetOutfit) {
+ playerInfo.currentOutfit.name = targetOutfit.name;
+ playerInfo.currentOutfit.color = targetOutfit.color;
+ playerInfo.currentOutfit.hatId = targetOutfit.hatId;
+ playerInfo.currentOutfit.skinId = targetOutfit.skinId;
+ playerInfo.currentOutfit.visorId = targetOutfit.visorId;
+ playerInfo.currentOutfit.petId = targetOutfit.petId;
+ }
+ }
+
+ this.isTransformed = true;
+ this.transformTarget = target;
+ this._transformTimer = this.duration;
+
+ this.room.logger.info("%s (SS) transformed into %s for %ss",
+ this.player, target, this.duration);
+
+ return true;
+ }
+
+ /**
+ * Revert to original appearance.
+ */
+ revertTransform(): void {
+ if (!this.isTransformed) return;
+
+ const playerInfo = this.player.getPlayerInfo();
+ if (playerInfo?.currentOutfit && this._savedOutfit) {
+ playerInfo.currentOutfit.name = this._savedOutfit.name;
+ playerInfo.currentOutfit.color = this._savedOutfit.color;
+ playerInfo.currentOutfit.hatId = this._savedOutfit.hatId;
+ playerInfo.currentOutfit.skinId = this._savedOutfit.skinId;
+ playerInfo.currentOutfit.visorId = this._savedOutfit.visorId;
+ playerInfo.currentOutfit.petId = this._savedOutfit.petId;
+
+ // Push data update so clients see the reverted appearance
+ playerInfo.pushDataState(1 as any);
+ }
+
+ this.isTransformed = false;
+ this.transformTarget = null;
+ this._savedOutfit = null;
+
+ this.room.logger.info("%s (SS) reverted to original appearance", this.player);
+
+ // Start cooldown
+ this._cooldownTimer = this.cooldown;
+ }
+
+ onFixedUpdate(): void {
+ if (this.isTransformed && this._transformTimer > 0) {
+ this._transformTimer -= 0.1;
+ if (this._transformTimer <= 0) {
+ this._transformTimer = 0;
+ this.revertTransform();
+ }
+ }
+
+ if (!this.isTransformed && this._cooldownTimer > 0) {
+ this._cooldownTimer -= 0.1;
+ if (this._cooldownTimer <= 0) {
+ this._cooldownTimer = 0;
+ }
+ }
+ }
+
+ onDeath(): boolean {
+ if (this.isTransformed) {
+ this.revertTransform();
+ }
+ this.isActive = false;
+ return true;
+ }
+
+ onGameEnd(): void {
+ if (this.isTransformed) {
+ this.revertTransform();
+ }
+ this.isActive = false;
+ }
+}
diff --git a/src/game/roles/TrackerRole.ts b/src/game/roles/TrackerRole.ts
index 79c8af96e..2a0f6eec2 100644
--- a/src/game/roles/TrackerRole.ts
+++ b/src/game/roles/TrackerRole.ts
@@ -6,7 +6,7 @@ import { BaseRole } from "./BaseRole";
* Tracker Role
*
* Can mark a target player and receive periodic position updates
- * (arrow indicators pointing to the target's direction).
+ * (arrow indicators pointing to the target's direction on the map).
*
* This is a Crewmate role.
*/
@@ -17,33 +17,37 @@ export class TrackerRole extends BaseRole {
/** The currently tracked player, if any. */
trackedTarget: Player | null = null;
- /** Timer for periodic position updates. */
- private _trackingTimer: NodeJS.Timeout | null = null;
+ /** Remaining tracking duration (seconds). */
+ private _trackingTimer: number = 0;
- /** How long the tracking has been active. */
- private _trackingElapsed: number = 0;
+ /** Remaining cooldown before next track (seconds). */
+ private _cooldownTimer: number = 0;
+
+ /** Time since last position update (seconds). */
+ private _updateTimer: number = 0;
get cooldown(): number {
- return this.room.settings.roleSettings.trackerCooldown || 15;
+ return (this.room.settings.roleSettings as any).trackerCooldown || 15;
}
get duration(): number {
- return this.room.settings.roleSettings.trackerDuration || 30;
+ return (this.room.settings.roleSettings as any).trackerDuration || 30;
}
get delay(): number {
- return this.room.settings.roleSettings.trackerDelay || 1;
+ return (this.room.settings.roleSettings as any).trackerDelay || 1;
}
onGameStart(): void {
this.isActive = true;
+ this.trackedTarget = null;
+ this._trackingTimer = 0;
+ this._cooldownTimer = 0;
+ this._updateTimer = 0;
this.room.logger.info("%s is the Tracker (duration: %ss, cooldown: %ss, delay: %ss)",
this.player, this.duration, this.cooldown, this.delay);
}
- /**
- * Use the tracking ability on a target player.
- */
onAbilityUse(target?: Player): boolean {
if (!target) {
this.room.logger.warn("%s (Tracker) attempted to track but no target specified", this.player);
@@ -60,9 +64,9 @@ export class TrackerRole extends BaseRole {
return false;
}
- // Start tracking
this.trackedTarget = target;
- this._trackingElapsed = 0;
+ this._trackingTimer = this.duration;
+ this._updateTimer = 0;
this.startCooldown(this.cooldown * 1000);
this.room.logger.info("%s (Tracker) is now tracking %s", this.player, target);
@@ -72,51 +76,67 @@ export class TrackerRole extends BaseRole {
{ targets: [this.player] }
);
- // Start periodic position updates
- this._trackingTimer = setInterval(() => {
- this.sendPositionUpdate();
- this._trackingElapsed += this.delay;
-
- // Stop tracking after duration expires
- if (this._trackingElapsed >= this.duration) {
- this.stopTracking();
- }
- }, this.delay * 1000);
-
return true;
}
/**
* Send a position update about the tracked target to this player.
+ * The Among Us client displays an arrow pointing to the target's location.
*/
private sendPositionUpdate(): void {
- if (!this.trackedTarget || !this.trackedTarget.characterControl) {
+ if (!this.trackedTarget?.characterControl) {
this.stopTracking();
return;
}
- // In the actual game, this would send an arrow/indicator RPC
- // For now, we log the position update
- this.room.logger.debug("%s (Tracker) received position update for %s",
- this.player, this.trackedTarget);
+ // In SaaH mode, the server can directly read the target's position
+ // and send it to the tracker. The client handles arrow display
+ // based on the tracked player's transform data.
+ const transform = this.trackedTarget.characterControl.getComponentSafe(
+ 2, "CustomNetworkTransform" as any
+ ) as any;
+
+ if (transform) {
+ this.room.logger.debug("%s (Tracker) position update for %s: (%.1f, %.1f)",
+ this.player, this.trackedTarget,
+ transform.X ?? 0, transform.Y ?? 0);
+ }
}
/**
* Stop tracking the current target.
*/
private stopTracking(): void {
- if (this._trackingTimer) {
- clearInterval(this._trackingTimer);
- this._trackingTimer = null;
- }
-
if (this.trackedTarget) {
this.room.logger.info("%s (Tracker) stopped tracking %s",
this.player, this.trackedTarget);
}
-
this.trackedTarget = null;
- this._trackingElapsed = 0;
+ this._trackingTimer = 0;
+ this._updateTimer = 0;
+ }
+
+ onFixedUpdate(): void {
+ if (this.trackedTarget && this._trackingTimer > 0) {
+ this._trackingTimer -= 0.1;
+ this._updateTimer -= 0.1;
+
+ if (this._updateTimer <= 0) {
+ this.sendPositionUpdate();
+ this._updateTimer = this.delay;
+ }
+
+ if (this._trackingTimer <= 0) {
+ this.stopTracking();
+ }
+ }
+
+ if (!this.trackedTarget && this._cooldownTimer > 0) {
+ this._cooldownTimer -= 0.1;
+ if (this._cooldownTimer <= 0) {
+ this._cooldownTimer = 0;
+ }
+ }
}
onDeath(): boolean {
diff --git a/src/game/roles/ViperRole.ts b/src/game/roles/ViperRole.ts
index eaf0ddfd5..aa055fe7d 100644
--- a/src/game/roles/ViperRole.ts
+++ b/src/game/roles/ViperRole.ts
@@ -17,53 +17,38 @@ export class ViperRole extends BaseRole {
teamType = RoleTeamType.Impostor;
/** Set of poisoned player IDs awaiting dissolve. */
- private _poisonedPlayers: Set = new Set();
-
- /** Timers for each poisoned player. */
- private _poisonTimers: Map = new Map();
+ private _poisonedPlayers: Map = new Map();
get dissolveTime(): number {
- return this.room.settings.roleSettings.viperDissolveTime || 15;
+ return (this.room.settings.roleSettings as any).viperDissolveTime || 15;
}
onGameStart(): void {
this.isActive = true;
this._poisonedPlayers.clear();
- this._poisonTimers.clear();
-
this.room.logger.info("%s is the Viper (dissolve time: %ss)",
this.player, this.dissolveTime);
}
/**
* When the Viper kills, the target is poisoned instead of dying immediately.
+ * Returns false to prevent normal kill processing — Viper handles it.
*/
onKill(target: Player): boolean {
- if (!this.isActive) return true; // Normal kill if ability inactive
+ if (!this.isActive) return true;
this.room.logger.info("%s (Viper) poisoned %s (dissolve in %ss)",
this.player, target, this.dissolveTime);
- // Mark as poisoned
- this._poisonedPlayers.add(target.clientId);
+ // Track poison with remaining time
+ this._poisonedPlayers.set(target.clientId, this.dissolveTime);
- // Don't kill immediately — the dissolve timer will handle death
- // But we still need to prevent the normal kill from processing
- // The actual kill happens after the dissolve time
-
- // Send a subtle message to the target
+ // Send a message to the target
this.room.sendChat(
`You feel a sharp sting... something is wrong...`,
{ targets: [target] }
);
- // Set the dissolve timer
- const timer = setTimeout(() => {
- this.dissolveTarget(target);
- }, this.dissolveTime * 1000);
-
- this._poisonTimers.set(target.clientId, timer);
-
return false; // Prevent normal kill — Viper handles it
}
@@ -71,12 +56,9 @@ export class ViperRole extends BaseRole {
* Actually kill the poisoned target after the dissolve time.
*/
private dissolveTarget(target: Player): void {
- if (!this._poisonedPlayers.has(target.clientId)) return;
-
this._poisonedPlayers.delete(target.clientId);
- this._poisonTimers.delete(target.clientId);
- // Check if the target is already dead
+ // Check if already dead
const playerInfo = target.getPlayerInfo();
if (playerInfo?.isDead) {
this.room.logger.debug("%s (Viper) target %s was already dead, skipping dissolve",
@@ -86,13 +68,28 @@ export class ViperRole extends BaseRole {
this.room.logger.info("%s (Viper) poison dissolved on %s", this.player, target);
- // Send death message
this.room.sendChat(
`${target.username || "Someone"} succumbed to poison!`
);
- // The target dies via the normal game engine mechanics
- // Poison mechanic is tracked by this role for timing
+ // Kill via normal game mechanics
+ target.characterControl?.causeToDie("exiled");
+ }
+
+ onFixedUpdate(): void {
+ for (const [playerId, remaining] of this._poisonedPlayers) {
+ const newRemaining = remaining - 0.1;
+ if (newRemaining <= 0) {
+ const target = this.room.players.get(playerId);
+ if (target) {
+ this.dissolveTarget(target);
+ } else {
+ this._poisonedPlayers.delete(playerId);
+ }
+ } else {
+ this._poisonedPlayers.set(playerId, newRemaining);
+ }
+ }
}
/**
@@ -103,14 +100,13 @@ export class ViperRole extends BaseRole {
}
/**
- * Get all currently poisoned player IDs.
+ * Clear all active poisons.
*/
- getPoisonedPlayers(): number[] {
- return [...this._poisonedPlayers];
+ clearAllPoisons(): void {
+ this._poisonedPlayers.clear();
}
onDeath(): boolean {
- // Clear all poison when the Viper dies
this.clearAllPoisons();
this.isActive = false;
return true;
@@ -120,15 +116,4 @@ export class ViperRole extends BaseRole {
this.clearAllPoisons();
this.isActive = false;
}
-
- /**
- * Clear all active poisons (e.g., game over, Viper dies).
- */
- private clearAllPoisons(): void {
- for (const [, timer] of this._poisonTimers) {
- clearTimeout(timer);
- }
- this._poisonTimers.clear();
- this._poisonedPlayers.clear();
- }
}
diff --git a/src/game/roles/index.ts b/src/game/roles/index.ts
index bc16b4456..5cf687565 100644
--- a/src/game/roles/index.ts
+++ b/src/game/roles/index.ts
@@ -1,7 +1,11 @@
export * from "./BaseRole";
export * from "./DetectiveRole";
+export * from "./EngineerRole";
+export * from "./GuardianAngelRole";
export * from "./NoisemakerRole";
export * from "./PhantomRole";
export * from "./RoleManager";
+export * from "./ScientistRole";
+export * from "./ShapeshifterRole";
export * from "./TrackerRole";
export * from "./ViperRole";
diff --git a/src/handlers/CmdHandler.ts b/src/handlers/CmdHandler.ts
new file mode 100644
index 000000000..3ee04b573
--- /dev/null
+++ b/src/handlers/CmdHandler.ts
@@ -0,0 +1,284 @@
+import * as util from "util";
+
+import { Player, DisconnectReason } from "@skeldjs/au-core";
+
+import { Room, MessageSide } from "../Room";
+import { Connection } from "../Connection";
+import { RoomPrivacy, logMaps } from "../Room";
+import { GameMode, GameState, GameMap } from "@skeldjs/au-core";
+
+/**
+ * Context for a /cmd command execution.
+ * Provides access to the room, caller, and response methods.
+ */
+export class CmdContext {
+ constructor(
+ public readonly room: Room,
+ public readonly player: Player,
+ public readonly message: string,
+ public readonly args: string[]
+ ) {}
+
+ /**
+ * Reply to the caller privately (only they see the response).
+ */
+ async reply(message: string, ...fmt: any) {
+ await this.room.sendChat(util.format(message, ...fmt), {
+ side: MessageSide.Left,
+ targets: [this.player]
+ });
+ }
+
+ /**
+ * Broadcast a message to all players in the room.
+ */
+ async broadcast(message: string, ...fmt: any) {
+ await this.room.sendChat(util.format(message, ...fmt), {
+ side: MessageSide.Left
+ });
+ }
+}
+
+export type CmdCallback = (ctx: CmdContext) => Promise | void;
+
+interface CmdEntry {
+ name: string;
+ description: string;
+ requiresAuth: boolean;
+ callback: CmdCallback;
+}
+
+/**
+ * Handler for Among Us vanilla /cmd chat commands.
+ *
+ * In Among Us, messages starting with "/cmd" from non-host players are
+ * only sent to the host. In Waterway's SaaH mode, the server IS the host,
+ * so the server receives and processes these commands.
+ *
+ * Supported commands:
+ * /cmd help - Show available commands
+ * /cmd list - List players in the room
+ * /cmd kick - Kick a player from the room
+ * /cmd ban - Ban a player from the room
+ * /cmd room - Set the room name
+ * /cmd public - Make the room public
+ * /cmd private - Make the room private
+ */
+export class CmdHandler {
+ private commands: Map = new Map();
+
+ constructor(public readonly room: Room) {
+ this.registerBuiltinCommands();
+ }
+
+ private registerBuiltinCommands(): void {
+ this.register("help", "Show available commands", false, async (ctx) => {
+ const available = [...this.commands.values()]
+ .filter(c => !c.requiresAuth || ctx.room.getConnection(ctx.player)?.isAuthenticated);
+
+ let out = "Available commands:";
+ for (const cmd of available) {
+ out += `\n /cmd ${cmd.name} — ${cmd.description}`;
+ }
+ await ctx.reply(out);
+ });
+
+ this.register("list", "List players in the room", false, async (ctx) => {
+ const lines: string[] = [];
+ for (const [, player] of ctx.room.players) {
+ const conn = ctx.room.connections.get(player.clientId);
+ const host = ctx.room.authorityId === player.clientId ? " [HOST]" : "";
+ const auth = conn?.isAuthenticated ? ` (${conn.friendCode})` : "";
+ const dead = player.getPlayerInfo()?.isDead ? " [DEAD]" : "";
+ lines.push(` ${player.username}${host}${auth}${dead}`);
+ }
+ const state = GameState[ctx.room.gameState] || "Unknown";
+ const map = logMaps[ctx.room.settings.map] || GameMap[ctx.room.settings.map] || "Unknown";
+ await ctx.reply(
+ `Room ${ctx.room.code} | ${ctx.room.players.size} players | ${state} | ${map}\n${lines.join("\n")}`
+ );
+ });
+
+ this.register("kick", "Kick a player by name or ID. Usage: /cmd kick ", true, async (ctx) => {
+ const targetName = ctx.args.join(" ");
+ if (!targetName) {
+ await ctx.reply("Usage: /cmd kick ");
+ return;
+ }
+
+ const target = this.findPlayer(targetName);
+ if (!target) {
+ await ctx.reply("Player not found: %s", targetName);
+ return;
+ }
+
+ const targetConn = ctx.room.getConnection(target);
+ if (!targetConn) {
+ await ctx.reply("Cannot kick: player has no connection");
+ return;
+ }
+
+ await targetConn.disconnect(DisconnectReason.Kicked);
+ await ctx.broadcast(
+ "%s was kicked by %s",
+ target.username, ctx.player.username
+ );
+ });
+
+ this.register("ban", "Ban a player by name or ID. Usage: /cmd ban ", true, async (ctx) => {
+ const targetName = ctx.args.join(" ");
+ if (!targetName) {
+ await ctx.reply("Usage: /cmd ban ");
+ return;
+ }
+
+ const target = this.findPlayer(targetName);
+ if (!target) {
+ await ctx.reply("Player not found: %s", targetName);
+ return;
+ }
+
+ ctx.room.banPlayer(target);
+ await ctx.broadcast(
+ "%s was banned by %s",
+ target.username, ctx.player.username
+ );
+ });
+
+ this.register("room", "Set room name. Usage: /cmd room ", true, async (ctx) => {
+ const name = ctx.args.join(" ");
+ if (!name) {
+ ctx.room.clearRoomNameOverride();
+ await ctx.reply("Room name override cleared.");
+ return;
+ }
+ ctx.room.setRoomNameOverride(name);
+ await ctx.broadcast(
+ "Room name set to: %s", name
+ );
+ });
+
+ this.register("public", "Make the room public", true, async (ctx) => {
+ ctx.room.privacy = RoomPrivacy.Public;
+ await ctx.broadcast("Room is now PUBLIC");
+ });
+
+ this.register("private", "Make the room private", true, async (ctx) => {
+ ctx.room.privacy = RoomPrivacy.Private;
+ await ctx.broadcast("Room is now PRIVATE");
+ });
+ }
+
+ /**
+ * Register a custom /cmd command.
+ */
+ register(name: string, description: string, requiresAuth: boolean, callback: CmdCallback): void {
+ this.commands.set(name.toLowerCase(), {
+ name: name.toLowerCase(),
+ description,
+ requiresAuth,
+ callback,
+ });
+ }
+
+ /**
+ * Parse and execute a /cmd message.
+ * @param message The full message after "/cmd " prefix
+ */
+ async handleMessage(player: Player, message: string): Promise {
+ const parts = this.splitArgs(message);
+ const commandName = parts.shift()?.toLowerCase();
+
+ if (!commandName) {
+ // Just "/cmd" — show help
+ const cmd = this.commands.get("help");
+ if (cmd) {
+ const ctx = new CmdContext(this.room, player, message, []);
+ await cmd.callback(ctx);
+ }
+ return true;
+ }
+
+ const cmd = this.commands.get(commandName);
+ if (!cmd) {
+ await this.room.sendChat(
+ `Unknown command: /cmd ${commandName}. Use /cmd help for available commands.`,
+ { side: MessageSide.Left, targets: [player] }
+ );
+ return true;
+ }
+
+ if (cmd.requiresAuth) {
+ const conn = this.room.getConnection(player);
+ if (!conn?.isAuthenticated) {
+ await this.room.sendChat(
+ "You must be authenticated to use this command. Log in with your Among Us account first.",
+ { side: MessageSide.Left, targets: [player] }
+ );
+ return true;
+ }
+ }
+
+ const ctx = new CmdContext(this.room, player, message, parts);
+ try {
+ await cmd.callback(ctx);
+ } catch (e) {
+ ctx.room.logger.error("Error executing /cmd %s: %s", commandName, e);
+ await ctx.reply("Error executing command.");
+ }
+
+ return true;
+ }
+
+ /**
+ * Find a player by name (partial match) or client ID.
+ */
+ private findPlayer(query: string): Player | undefined {
+ // Try exact client ID match
+ const id = parseInt(query);
+ if (!isNaN(id)) {
+ return this.room.players.get(id);
+ }
+
+ // Try case-insensitive name match
+ const lower = query.toLowerCase();
+ for (const [, player] of this.room.players) {
+ if (player.username.toLowerCase().includes(lower)) {
+ return player;
+ }
+ }
+
+ return undefined;
+ }
+
+ /**
+ * Split a command string into arguments, respecting quoted strings.
+ */
+ private splitArgs(input: string): string[] {
+ const args: string[] = [];
+ let current = "";
+ let inQuote = false;
+ let quoteChar = "";
+
+ for (let i = 0; i < input.length; i++) {
+ const ch = input[i];
+ if (!inQuote && (ch === '"' || ch === "'")) {
+ inQuote = true;
+ quoteChar = ch;
+ } else if (inQuote && ch === quoteChar) {
+ inQuote = false;
+ quoteChar = "";
+ } else if (!inQuote && ch === " ") {
+ if (current) {
+ args.push(current);
+ current = "";
+ }
+ } else {
+ current += ch;
+ }
+ }
+ if (current) args.push(current);
+
+ return args;
+ }
+}
diff --git a/src/handlers/index.ts b/src/handlers/index.ts
index 2397549fe..4b4d581f1 100644
--- a/src/handlers/index.ts
+++ b/src/handlers/index.ts
@@ -1,3 +1,4 @@
export * from "./ChatCommandHandler";
+export * from "./CmdHandler";
export * from "./Plugin";
export * from "./PluginLoader";
From 11116db7e0e484c227f356561c6415f4e23dcb0a Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Sat, 20 Jun 2026 22:03:07 +0800
Subject: [PATCH 09/17] Fix meeting
---
CHANGELOG.txt | 18 ++++++++++++++++++
src/Room.ts | 39 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 56 insertions(+), 1 deletion(-)
create mode 100644 CHANGELOG.txt
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
new file mode 100644
index 000000000..ff00e09a6
--- /dev/null
+++ b/CHANGELOG.txt
@@ -0,0 +1,18 @@
+
+## Phase 6: 额外修复 (2026-06-20)
+
+### Room.ts — 双重会议防护
+- 新增 meetingInProgress: boolean 字段
+- player.startmeeting 事件: 检测 meetingInProgress 标志,重复触发时抑制并记录警告
+- component.despawn 事件: MeetingHud 销毁时重置 meetingInProgress = false
+- _reset(): 游戏结束时重置 meetingInProgress = false
+- processFixedUpdate(): 调用 roleManager.handleFixedUpdate() 和 gameModeManager 勾子
+
+### Room.ts — 未知RPC容错处理
+- handleRpcMessage(): 当 component.parseRemoteCall 无法解析 RPC 但 tag 已知时
+ - 降级为 warn 日志 (非 error)
+ - 仍按 acceptUnknownGameData 配置转发,不中断游戏流程
+ - 防止 SetScanner (Rpc15) 等已知但因构建/版本不匹配而解析失败的 RPC 导致游戏异常
+
+### Room.ts — /cmd 广播抑制修复
+- handleRpcMessage(): 修复返回逻辑 — /cmd 消息抑制后不广播给非目标玩家
diff --git a/src/Room.ts b/src/Room.ts
index 1b59a737e..f37ffa971 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -341,6 +341,12 @@ export class Room extends StatefulRoom {
*/
gameModeManager: HideAndSeekManager | null;
+ /**
+ * Whether a meeting is currently in progress. Prevents duplicate
+ * meeting starts from PlayerStartMeetingEvent firing multiple times.
+ */
+ meetingInProgress: boolean;
+
/**
* The role manager for this room, handling role assignment and lifecycle.
*/
@@ -393,6 +399,7 @@ export class Room extends StatefulRoom {
this.roomNameOverride = "";
this.eventTargets = [];
this.gameModeManager = null;
+ this.meetingInProgress = false;
this.roleManager = new RoleManager(this);
this.cmdHandler = new CmdHandler(this);
@@ -504,6 +511,13 @@ export class Room extends StatefulRoom {
});
this.on("player.startmeeting", ev => {
+ if (this.meetingInProgress) {
+ this.logger.warn("Duplicate meeting start suppressed (body: %s, caller: %s)",
+ ev.body, ev.player);
+ return;
+ }
+ this.meetingInProgress = true;
+
if (ev.body === "emergency") {
this.logger.info("Meeting started (emergency meeting)");
} else {
@@ -558,6 +572,13 @@ export class Room extends StatefulRoom {
// Route through role manager for role-specific death behavior (e.g., Phantom)
this.roleManager.handleDeath(ev.player);
});
+
+ // Reset meetingInProgress flag when MeetingHud is despawned
+ this.on("component.despawn", (ev: any) => {
+ if (ev.component && ev.component.spawnType === SpawnType.MeetingHud) {
+ this.meetingInProgress = false;
+ }
+ });
}
protected _reset() {
@@ -565,6 +586,7 @@ export class Room extends StatefulRoom {
this.gameModeManager.destroy();
this.gameModeManager = null;
}
+ this.meetingInProgress = false;
this.roleManager.handleGameEnd();
// Don't clear this.players — lobby connections stay
// Clear only game-specific object state (players/connections stay)
@@ -871,8 +893,23 @@ export class Room extends StatefulRoom {
if (message.child instanceof UnknownRpcMessage) {
const parsedRpc = component.parseRemoteCall(message.child.messageTag, message.child.dataReader);
if (!parsedRpc) {
+ // If the component couldn't parse this RPC but we know the tag name,
+ // log a warning and forward it anyway (don't break game flow).
+ const tagName = RpcMessageTag[message.child.messageTag];
+ if (tagName) {
+ this.logger.warn(
+ "Component %s (netId=%s) couldn't parse RPC %s (%s) from %s — forwarding as-is",
+ SpawnType[component.spawnType] || "Unknown",
+ component.netId,
+ tagName,
+ message.child.messageTag,
+ senderPlayer
+ );
+ // Forward the raw message to clients so game flow isn't broken
+ return this.server.config.socket.acceptUnknownGameData;
+ }
this.logger.error("Unknown remote procedure call from player %s for component net id %s, %s: message tag %s",
- senderPlayer, component.netId, SpawnType[component.spawnType] || "Unknown", RpcMessageTag[message.child.messageTag] || message.child.messageTag);
+ senderPlayer, component.netId, SpawnType[component.spawnType] || "Unknown", tagName || message.child.messageTag);
return this.server.config.socket.acceptUnknownGameData;
}
await component.handleRemoteCall(parsedRpc);
From 777dbbd0a89d66ea4d023c2e5a3116a96283516a Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Sat, 20 Jun 2026 22:25:19 +0800
Subject: [PATCH 10/17] Fix
---
CHANGELOG.txt | 23 ++++++++++++++++++++
src/Room.ts | 58 ++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 78 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index ff00e09a6..2a16159f5 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -16,3 +16,26 @@
### Room.ts — /cmd 广播抑制修复
- handleRpcMessage(): 修复返回逻辑 — /cmd 消息抑制后不广播给非目标玩家
+
+## Phase 7: 额外修复 (2026-06-20 #2)
+
+### Room.ts — 游戏结束清理
+- handleEndGame(): 清除没有活跃连接的 stale 玩家 (this.players)
+- handleEndGame(): 清除 stale PlayerInfo (this.playerInfo)
+- handleEndGame(): 清空 waitingForHost
+- 修复 "4个人却显示7个" 的玩家计数错误
+
+### Room.ts — Rejoin 循环修复
+- handleRemoteJoin(): 加入 waitingForHost 前移除同 clientId 的旧条目
+- 防止同一玩家断线重连后在 waitingForHost 中累积重复条目
+
+## Phase 8: 玩家离开/加入修复 (2026-06-20 #3)
+
+### Room.ts — 玩家离开时广播 Despawn
+- handleRemoteLeave(): 在调用 handleLeave 之前收集离开玩家的 PlayerInfo 和 PlayerControl 的 DespawnMessage
+- handleRemoteLeave(): 将 DespawnMessage 与 S2CRemovePlayerMessage 一起广播给所有剩余客户端
+- 修复: 玩家离开后,其角色对象在其他玩家屏幕上残留(幽灵玩家)
+
+### Room.ts — 加入黑屏修复
+- handleSceneChangeMessage(): player-authority 模式下也发送全部现有对象 spawn (getExistingObjectSpawn)
+- 之前只发 server-owned objects,导致加入玩家缺少必要数据卡在黑屏
diff --git a/src/Room.ts b/src/Room.ts
index f37ffa971..872c2242e 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -1060,8 +1060,13 @@ export class Room extends StatefulRoom {
[player]
);
} else {
+ // In player-authority mode, send ALL existing object spawns
+ // (not just server-owned). The joining client needs the full
+ // game state to avoid getting stuck on a black screen.
+ // The host client will also send its spawn data, but sending
+ // everything from the server ensures the client doesn't block.
await this.broadcastImmediate(
- this.getServerOwnedObjectSpawn(),
+ this.getExistingObjectSpawn(),
undefined,
[player],
);
@@ -1575,6 +1580,14 @@ export class Room extends StatefulRoom {
if (this.gameState === GameState.Ended) {
if (!this.isAuthoritative && joiningClient.clientId !== this.authorityId) {
+ // Remove any stale waiting entry for this clientId (from a previous
+ // disconnected connection) before adding the new one.
+ for (const waiting of this.waitingForHost) {
+ if (waiting.clientId === joiningClient.clientId) {
+ this.waitingForHost.delete(waiting);
+ break;
+ }
+ }
this.waitingForHost.add(joiningClient);
await joiningClient.sendPacket(
@@ -1642,6 +1655,21 @@ export class Room extends StatefulRoom {
await this.gameModeManager.handlePlayerDisconnect(leavingPlayer);
}
+ // Collect despawn messages for the leaving player's networked objects
+ // BEFORE calling handleLeave (which removes them from our internal maps).
+ // These must be broadcast to all remaining clients so the ghost
+ // PlayerControl/PlayerInfo don't persist on other players' screens.
+ const despawnMessages: BaseGameDataMessage[] = [];
+ if (leavingPlayer) {
+ const playerInfo = leavingPlayer.getPlayerInfo();
+ if (playerInfo) {
+ despawnMessages.push(new DespawnMessage(playerInfo.netId));
+ }
+ if (leavingPlayer.characterControl) {
+ despawnMessages.push(new DespawnMessage(leavingPlayer.characterControl.netId));
+ }
+ }
+
await this.handleLeave(leavingConnection.clientId);
if (this.connections.size === 0) {
@@ -1673,6 +1701,7 @@ export class Room extends StatefulRoom {
}
}
+ // Broadcast S2CRemovePlayerMessage + object despawns to all remaining clients
const promises = [];
for (const [, otherClient] of this.connections) {
promises.push(otherClient.sendPacket(
@@ -1684,7 +1713,8 @@ export class Room extends StatefulRoom {
leavingConnection.clientId,
reason,
this.getClientAwareAuthorityId(otherClient),
- )
+ ),
+ ...despawnMessages
]
)
));
@@ -1755,12 +1785,34 @@ export class Room extends StatefulRoom {
async handleEndGame(reason: GameOverReason, intent?: EndGameIntent) {
const ev = await this.emit(new RoomGameEndEvent(this, reason, intent));
if (ev.canceled) return;
-
+
await this.flushMessages();
await this.broadcastImmediate([], [new EndGameMessage(this.code.id, reason, false)]);
await super.handleEndGame(reason);
+ // Clean up stale players — players without active connections who
+ // will get new clientIds on rejoin. Prevents "4 players but shows 7" bug.
+ const staleClientIds: number[] = [];
+ for (const [clientId, player] of this.players) {
+ if (!this.connections.has(clientId)) {
+ staleClientIds.push(clientId);
+ }
+ }
+ for (const clientId of staleClientIds) {
+ this.players.delete(clientId);
+ }
+
+ // Also clean up PlayerInfo for stale players
+ for (const [playerId, playerInfo] of this.playerInfo) {
+ if (!this.connections.has(playerInfo.clientId)) {
+ this.playerInfo.delete(playerId);
+ }
+ }
+
+ // Clear waitingForHost — game is over, no host needed until next game
+ this.waitingForHost.clear();
+
this.logger.info("Game ended: %s", GameOverReason[ev.reason]);
await this.flushMessages();
await this.updateAllClientAwareAuthority();
From c6f1cf2b520ba1eb762bad36c8a02200ba705428 Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Sat, 20 Jun 2026 22:59:21 +0800
Subject: [PATCH 11/17] Fix
---
src/Room.ts | 70 ++++++++++++++++++++---------------
src/game/roles/RoleManager.ts | 14 ++++---
2 files changed, 50 insertions(+), 34 deletions(-)
diff --git a/src/Room.ts b/src/Room.ts
index 872c2242e..46a832e48 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -1655,18 +1655,17 @@ export class Room extends StatefulRoom {
await this.gameModeManager.handlePlayerDisconnect(leavingPlayer);
}
- // Collect despawn messages for the leaving player's networked objects
- // BEFORE calling handleLeave (which removes them from our internal maps).
- // These must be broadcast to all remaining clients so the ghost
- // PlayerControl/PlayerInfo don't persist on other players' screens.
- const despawnMessages: BaseGameDataMessage[] = [];
+ // Collect netIds of objects to despawn BEFORE calling handleLeave
+ // (which removes them from internal maps). These must be broadcast
+ // to all remaining clients so ghost objects don't persist.
+ const despawnNetIds: number[] = [];
if (leavingPlayer) {
const playerInfo = leavingPlayer.getPlayerInfo();
if (playerInfo) {
- despawnMessages.push(new DespawnMessage(playerInfo.netId));
+ despawnNetIds.push(playerInfo.netId);
}
if (leavingPlayer.characterControl) {
- despawnMessages.push(new DespawnMessage(leavingPlayer.characterControl.netId));
+ despawnNetIds.push(leavingPlayer.characterControl.netId);
}
}
@@ -1701,21 +1700,31 @@ export class Room extends StatefulRoom {
}
}
- // Broadcast S2CRemovePlayerMessage + object despawns to all remaining clients
+ // Broadcast S2CRemovePlayerMessage to all remaining clients
const promises = [];
for (const [, otherClient] of this.connections) {
+ const rootMessages: BaseRootMessage[] = [
+ new S2CRemovePlayerMessage(
+ this.code.id,
+ leavingConnection.clientId,
+ reason,
+ this.getClientAwareAuthorityId(otherClient),
+ )
+ ];
+
+ // Wrap despawn messages in a GameDataMessage so they reach the
+ // client's game data layer (DespawnMessage is GameData, not Root).
+ if (despawnNetIds.length > 0) {
+ rootMessages.push(new GameDataMessage(
+ this.code.id,
+ despawnNetIds.map(netId => new DespawnMessage(netId))
+ ));
+ }
+
promises.push(otherClient.sendPacket(
new ReliablePacket(
otherClient.getNextNonce(),
- [
- new S2CRemovePlayerMessage(
- this.code.id,
- leavingConnection.clientId,
- reason,
- this.getClientAwareAuthorityId(otherClient),
- ),
- ...despawnMessages
- ]
+ rootMessages
)
));
}
@@ -1769,7 +1778,7 @@ export class Room extends StatefulRoom {
this.logger.info("Game started");
// Assign roles to players
- this.roleManager.assignRoles();
+ await this.roleManager.assignRoles();
// Start mode-specific logic after game is fully initialized
if (this.gameModeManager instanceof HideAndSeekManager) {
@@ -1791,10 +1800,21 @@ export class Room extends StatefulRoom {
await this.broadcastImmediate([], [new EndGameMessage(this.code.id, reason, false)]);
await super.handleEndGame(reason);
- // Clean up stale players — players without active connections who
- // will get new clientIds on rejoin. Prevents "4 players but shows 7" bug.
+ // Fully reset player state on game end.
+ // The previous game's Player/PlayerInfo entries are no longer valid.
+ // Players who stay in the lobby will be re-created with fresh state
+ // when the next game starts or when they rejoin.
+ const stalePlayerIds: number[] = [];
+ for (const [playerId] of this.playerInfo) {
+ stalePlayerIds.push(playerId);
+ }
+ for (const playerId of stalePlayerIds) {
+ this.playerInfo.delete(playerId);
+ }
+ // Keep only players with active connections (they'll rejoin next game).
+ // Remove stale entries whose connections have been closed.
const staleClientIds: number[] = [];
- for (const [clientId, player] of this.players) {
+ for (const [clientId] of this.players) {
if (!this.connections.has(clientId)) {
staleClientIds.push(clientId);
}
@@ -1803,14 +1823,6 @@ export class Room extends StatefulRoom {
this.players.delete(clientId);
}
- // Also clean up PlayerInfo for stale players
- for (const [playerId, playerInfo] of this.playerInfo) {
- if (!this.connections.has(playerInfo.clientId)) {
- this.playerInfo.delete(playerId);
- }
- }
-
- // Clear waitingForHost — game is over, no host needed until next game
this.waitingForHost.clear();
this.logger.info("Game ended: %s", GameOverReason[ev.reason]);
diff --git a/src/game/roles/RoleManager.ts b/src/game/roles/RoleManager.ts
index 5b5697e50..2ca9defdc 100644
--- a/src/game/roles/RoleManager.ts
+++ b/src/game/roles/RoleManager.ts
@@ -43,7 +43,7 @@ export class RoleManager {
* Assign roles to players based on the room's role settings.
* Called when the game starts.
*/
- assignRoles(): void {
+ async assignRoles(): Promise {
const settings = this.room.settings;
const roleChances = settings.roleSettings.roleChances;
@@ -101,7 +101,7 @@ export class RoleManager {
// Roll the dice
const roll = Math.random() * 100;
if (roll <= roleChance.chance) {
- this.assignRoleToPlayer(player, RoleCtor, roleType);
+ await this.assignRoleToPlayer(player, RoleCtor, roleType);
assignedCounts[roleType]!++;
}
}
@@ -121,18 +121,22 @@ export class RoleManager {
/**
* Create and assign a role to a specific player.
*/
- assignRoleToPlayer(
+ async assignRoleToPlayer(
player: Player,
RoleCtor: new (room: Room, player: Player) => BaseRole,
roleType: RoleType
- ): BaseRole {
+ ): Promise {
const role = new RoleCtor(this.room, player);
this.activeRoles.set(player.clientId, role);
// The player's role is tracked by this manager
// The client-facing role is set via PlayerControl.setRole RPC
+ // Must await — setRole is async and broadcasts the SetRole RPC to clients
if (player.characterControl) {
- (player.characterControl as any).setRole(roleType);
+ const roleClass = this.room.registeredRoles.get(roleType);
+ if (roleClass) {
+ await player.characterControl.setRole(roleClass);
+ }
}
// Initialize the role
From c5432f95addfd28a547a1d160e14d3f057136b63 Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Sat, 20 Jun 2026 23:23:34 +0800
Subject: [PATCH 12/17] Fix
---
src/Room.ts | 25 -------------------------
src/WaterwayServer.ts | 2 +-
2 files changed, 1 insertion(+), 26 deletions(-)
diff --git a/src/Room.ts b/src/Room.ts
index 46a832e48..e61eb0daa 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -1800,31 +1800,6 @@ export class Room extends StatefulRoom {
await this.broadcastImmediate([], [new EndGameMessage(this.code.id, reason, false)]);
await super.handleEndGame(reason);
- // Fully reset player state on game end.
- // The previous game's Player/PlayerInfo entries are no longer valid.
- // Players who stay in the lobby will be re-created with fresh state
- // when the next game starts or when they rejoin.
- const stalePlayerIds: number[] = [];
- for (const [playerId] of this.playerInfo) {
- stalePlayerIds.push(playerId);
- }
- for (const playerId of stalePlayerIds) {
- this.playerInfo.delete(playerId);
- }
- // Keep only players with active connections (they'll rejoin next game).
- // Remove stale entries whose connections have been closed.
- const staleClientIds: number[] = [];
- for (const [clientId] of this.players) {
- if (!this.connections.has(clientId)) {
- staleClientIds.push(clientId);
- }
- }
- for (const clientId of staleClientIds) {
- this.players.delete(clientId);
- }
-
- this.waitingForHost.clear();
-
this.logger.info("Game ended: %s", GameOverReason[ev.reason]);
await this.flushMessages();
await this.updateAllClientAwareAuthority();
diff --git a/src/WaterwayServer.ts b/src/WaterwayServer.ts
index f32e94c6c..38d47123b 100644
--- a/src/WaterwayServer.ts
+++ b/src/WaterwayServer.ts
@@ -2100,7 +2100,7 @@ export class WaterwayServer extends EventEmitter {
return connection.disconnect(DisconnectReason.Banned);
}
- if (ev.alteredRoom.connections.size >= ev.alteredRoom.settings.maxPlayers) {
+ if (ev.alteredRoom.players.size >= ev.alteredRoom.settings.maxPlayers) {
this.logger.warn("%s attempted to join %s but it was full",
connection, foundRoom);
return connection.disconnect(DisconnectReason.GameFull);
From adc19662127ea76cc4bc1f5c44abd93cb5893082 Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Sat, 20 Jun 2026 23:43:07 +0800
Subject: [PATCH 13/17] Fix
---
src/Room.ts | 15 +++++++++++++--
src/WaterwayServer.ts | 14 ++++++++++++++
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/src/Room.ts b/src/Room.ts
index e61eb0daa..9dd9858ab 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -588,9 +588,16 @@ export class Room extends StatefulRoom {
}
this.meetingInProgress = false;
this.roleManager.handleGameEnd();
- // Don't clear this.players — lobby connections stay
- // Clear only game-specific object state (players/connections stay)
+
+ // Despawn all objects from the previous game (PlayerInfo, PlayerControl,
+ // ShipStatus, MeetingHud, etc.). This is the safe point to clean up since
+ // the client has already processed the EndGameMessage and built its winner
+ // list from the live PlayerInfo data.
+ for (const [, component] of this.networkedObjects) {
+ this.despawnComponent(component);
+ }
this.networkedObjects.clear();
+ this.playerInfo.clear();
this.objectList.length = 0;
this.messageStream = [];
this.settings = new GameSettings;
@@ -1792,6 +1799,10 @@ export class Room extends StatefulRoom {
}
async handleEndGame(reason: GameOverReason, intent?: EndGameIntent) {
+ // Prevent double-processing: flushEndGameIntents (server-side) and
+ // the host client's EndGameMessage may both trigger handleEndGame.
+ if (this.gameState === GameState.Ended) return;
+
const ev = await this.emit(new RoomGameEndEvent(this, reason, intent));
if (ev.canceled) return;
diff --git a/src/WaterwayServer.ts b/src/WaterwayServer.ts
index 38d47123b..e0611f167 100644
--- a/src/WaterwayServer.ts
+++ b/src/WaterwayServer.ts
@@ -1708,6 +1708,19 @@ export class WaterwayServer extends EventEmitter {
await targetConnection.disconnect(message.banned ? DisconnectReason.Banned : DisconnectReason.Kicked);
}
+ async handleRemovePlayerMessage(message: C2SRemovePlayerMessage, sender: Connection) {
+ // C2SRemovePlayer is sent by the host client to remove a player from the
+ // lobby. Disconnect the target player's connection.
+ if (!sender.room) return;
+
+ const targetConn = sender.room.connections.get(message.clientId);
+ if (!targetConn) return;
+
+ this.logger.info("%s removed %s from the lobby",
+ sender, targetConn);
+ await targetConn.disconnect(message.reason);
+ }
+
async handleGetGameListMessage(message: C2SGetGameListMessage, sender: Connection) {
const listingIp = sender.remoteInfo.address === "127.0.0.1"
? "127.0.0.1"
@@ -1852,6 +1865,7 @@ export class WaterwayServer extends EventEmitter {
if (message instanceof EndGameMessage) return await this.handleEndGameMessage(message, sender);
if (message instanceof KickPlayerMessage) return await this.handleKickPlayerMessage(message, sender);
if (message instanceof C2SGetGameListMessage) return await this.handleGetGameListMessage(message, sender);
+ if (message instanceof C2SRemovePlayerMessage) return await this.handleRemovePlayerMessage(message, sender);
if (message instanceof SetActivePodTypeMessage) {
// not sure what to do with this..
From 350d6d7fca707fc4e068646c29141a895e39f347 Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Sun, 21 Jun 2026 00:01:27 +0800
Subject: [PATCH 14/17] Fix
---
src/Room.ts | 39 ++++++++++-----------------------------
src/WaterwayServer.ts | 8 +++++++-
2 files changed, 17 insertions(+), 30 deletions(-)
diff --git a/src/Room.ts b/src/Room.ts
index 9dd9858ab..60c6f3a13 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -1587,31 +1587,13 @@ export class Room extends StatefulRoom {
if (this.gameState === GameState.Ended) {
if (!this.isAuthoritative && joiningClient.clientId !== this.authorityId) {
- // Remove any stale waiting entry for this clientId (from a previous
- // disconnected connection) before adding the new one.
- for (const waiting of this.waitingForHost) {
- if (waiting.clientId === joiningClient.clientId) {
- this.waitingForHost.delete(waiting);
- break;
- }
- }
- this.waitingForHost.add(joiningClient);
-
- await joiningClient.sendPacket(
- new ReliablePacket(
- joiningClient.getNextNonce(),
- [
- new WaitForHostMessage(
- this.code.id,
- joiningClient.clientId
- )
- ]
- )
- );
-
- this.logger.info("%s joined, waiting for host",
- joiningPlayer);
- return;
+ // Game is over — the previous host may be gone.
+ // Auto-promote this player as the new authority instead of
+ // making them wait indefinitely.
+ this.logger.info("%s joined after game end, becoming new authority", joiningClient);
+ this.authorityId = joiningClient.clientId;
+ await joiningPlayer.emit(new PlayerSetAuthoritativeEvent(this, joiningPlayer));
+ // Fall through to normal join flow below
}
}
@@ -1748,10 +1730,9 @@ export class Room extends StatefulRoom {
}
async handleStartGame(startedBy?: Player) {
- // Reset room state if restarting after a previous game ended
- if (this.gameState === GameState.Ended) {
- this._reset();
- }
+ // Always reset — despawns objects from any previous game regardless
+ // of whether it ended cleanly or was abandoned mid-game.
+ this._reset();
this.gameState = GameState.Started;
diff --git a/src/WaterwayServer.ts b/src/WaterwayServer.ts
index e0611f167..a64555f78 100644
--- a/src/WaterwayServer.ts
+++ b/src/WaterwayServer.ts
@@ -2114,7 +2114,13 @@ export class WaterwayServer extends EventEmitter {
return connection.disconnect(DisconnectReason.Banned);
}
- if (ev.alteredRoom.players.size >= ev.alteredRoom.settings.maxPlayers) {
+ // Only block if room is actually full with DIFFERENT players.
+ // A player reconnecting after game end should not be blocked by
+ // their own previous Player entry still being in the room.
+ const roomPlayerCount = ev.alteredRoom.players.size;
+ const alreadyInRoom = ev.alteredRoom.players.has(connection.clientId);
+ const effectiveCount = alreadyInRoom ? roomPlayerCount - 1 : roomPlayerCount;
+ if (effectiveCount >= ev.alteredRoom.settings.maxPlayers) {
this.logger.warn("%s attempted to join %s but it was full",
connection, foundRoom);
return connection.disconnect(DisconnectReason.GameFull);
From 4afbc7ba91b9f0d9649b2d7bf2237f63534a662b Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Sun, 21 Jun 2026 00:21:01 +0800
Subject: [PATCH 15/17] Fix
---
src/Room.ts | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/src/Room.ts b/src/Room.ts
index 60c6f3a13..5fbc6f4e8 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -589,14 +589,20 @@ export class Room extends StatefulRoom {
this.meetingInProgress = false;
this.roleManager.handleGameEnd();
- // Despawn all objects from the previous game (PlayerInfo, PlayerControl,
- // ShipStatus, MeetingHud, etc.). This is the safe point to clean up since
- // the client has already processed the EndGameMessage and built its winner
- // list from the live PlayerInfo data.
+ // Only despawn server-owned objects (PlayerInfo, netId >= 100000).
+ // Client/host-owned objects (PlayerControl, ShipStatus, etc.) have
+ // netIds < 100000 and are managed by the host client in player-authority
+ // mode. Despawning them causes "non-existent component" errors when
+ // the host continues sending RPCs to now-destroyed netIds.
+ const despawning: NetworkedObject[] = [];
for (const [, component] of this.networkedObjects) {
+ if (component.netId >= 100000) {
+ despawning.push(component);
+ }
+ }
+ for (const component of despawning) {
this.despawnComponent(component);
}
- this.networkedObjects.clear();
this.playerInfo.clear();
this.objectList.length = 0;
this.messageStream = [];
From 12de84dd4ba81f398e2dc9a12520c9fdb7c944ca Mon Sep 17 00:00:00 2001
From: FangkuaiYa <2683748223@qq.com>
Date: Sun, 21 Jun 2026 00:36:46 +0800
Subject: [PATCH 16/17] Fix
---
src/Room.ts | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/src/Room.ts b/src/Room.ts
index 5fbc6f4e8..90e01dbb9 100644
--- a/src/Room.ts
+++ b/src/Room.ts
@@ -606,9 +606,8 @@ export class Room extends StatefulRoom {
this.playerInfo.clear();
this.objectList.length = 0;
this.messageStream = [];
- this.settings = new GameSettings;
+ // Don't reset settings — preserve host-configured map/mode across rounds
this.startGameCounter = -1;
- this.privacy = RoomPrivacy.Private;
this.lastNetId = 100000;
this.ownershipGuards.clear();
}
@@ -1007,6 +1006,11 @@ export class Room extends StatefulRoom {
message.flags,
message.components.map(x => x.netId),
);
+ // Duplicate NetID — the object already exists from a previous spawn.
+ // Silently skip (host may re-send spawns when syncing lobby state).
+ if (!object) {
+ return true;
+ }
for (let i = 0; i < message.components.length; i++) {
const data = message.components[i].data;
const component = object.components[i];
@@ -1736,9 +1740,11 @@ export class Room extends StatefulRoom {
}
async handleStartGame(startedBy?: Player) {
- // Always reset — despawns objects from any previous game regardless
- // of whether it ended cleanly or was abandoned mid-game.
- this._reset();
+ // Only reset if restarting after a previous game.
+ // Don't reset on first start — lobby PlayerInfo objects are needed.
+ if (this.gameState === GameState.Ended) {
+ this._reset();
+ }
this.gameState = GameState.Started;
From a532a2caabcfa175c7eeaf9b2f0f698f4eb07ce4 Mon Sep 17 00:00:00 2001
From: Fang-Kaui <145177672+FangkuaiYa@users.noreply.github.com>
Date: Sun, 21 Jun 2026 00:40:30 +0800
Subject: [PATCH 17/17] Delete CHANGELOG.txt
---
CHANGELOG.txt | 41 -----------------------------------------
1 file changed, 41 deletions(-)
delete mode 100644 CHANGELOG.txt
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
deleted file mode 100644
index 2a16159f5..000000000
--- a/CHANGELOG.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-
-## Phase 6: 额外修复 (2026-06-20)
-
-### Room.ts — 双重会议防护
-- 新增 meetingInProgress: boolean 字段
-- player.startmeeting 事件: 检测 meetingInProgress 标志,重复触发时抑制并记录警告
-- component.despawn 事件: MeetingHud 销毁时重置 meetingInProgress = false
-- _reset(): 游戏结束时重置 meetingInProgress = false
-- processFixedUpdate(): 调用 roleManager.handleFixedUpdate() 和 gameModeManager 勾子
-
-### Room.ts — 未知RPC容错处理
-- handleRpcMessage(): 当 component.parseRemoteCall 无法解析 RPC 但 tag 已知时
- - 降级为 warn 日志 (非 error)
- - 仍按 acceptUnknownGameData 配置转发,不中断游戏流程
- - 防止 SetScanner (Rpc15) 等已知但因构建/版本不匹配而解析失败的 RPC 导致游戏异常
-
-### Room.ts — /cmd 广播抑制修复
-- handleRpcMessage(): 修复返回逻辑 — /cmd 消息抑制后不广播给非目标玩家
-
-## Phase 7: 额外修复 (2026-06-20 #2)
-
-### Room.ts — 游戏结束清理
-- handleEndGame(): 清除没有活跃连接的 stale 玩家 (this.players)
-- handleEndGame(): 清除 stale PlayerInfo (this.playerInfo)
-- handleEndGame(): 清空 waitingForHost
-- 修复 "4个人却显示7个" 的玩家计数错误
-
-### Room.ts — Rejoin 循环修复
-- handleRemoteJoin(): 加入 waitingForHost 前移除同 clientId 的旧条目
-- 防止同一玩家断线重连后在 waitingForHost 中累积重复条目
-
-## Phase 8: 玩家离开/加入修复 (2026-06-20 #3)
-
-### Room.ts — 玩家离开时广播 Despawn
-- handleRemoteLeave(): 在调用 handleLeave 之前收集离开玩家的 PlayerInfo 和 PlayerControl 的 DespawnMessage
-- handleRemoteLeave(): 将 DespawnMessage 与 S2CRemovePlayerMessage 一起广播给所有剩余客户端
-- 修复: 玩家离开后,其角色对象在其他玩家屏幕上残留(幽灵玩家)
-
-### Room.ts — 加入黑屏修复
-- handleSceneChangeMessage(): player-authority 模式下也发送全部现有对象 spawn (getExistingObjectSpawn)
-- 之前只发 server-owned objects,导致加入玩家缺少必要数据卡在黑屏