From 1af4aeb0109f1dec4b1b59a76a07ce9ce0de38bc Mon Sep 17 00:00:00 2001 From: Jonathan Raphaelson Date: Tue, 14 Oct 2025 00:14:44 -0600 Subject: [PATCH] devenv on the new machine --- .devenv/devenv.json | 1 - .devenv/flake.json | 1 - .devenv/gc/shell | 1 - .devenv/gc/shell-5-link | 1 - .devenv/imports.txt | 0 .devenv/input-paths.txt | 7 -- .devenv/load-exports | 1 - .devenv/nix-eval-cache.db | Bin 147456 -> 0 bytes .devenv/nix-eval-cache.db-shm | Bin 32768 -> 0 bytes .devenv/profile | 1 - .devenv/run | 1 - .devenv/tasks.db | Bin 36864 -> 0 bytes .devenv/tasks.db-shm | Bin 32768 -> 0 bytes .devenv/tasks.db-wal | Bin 53592 -> 0 bytes .envrc | 8 ++ .gitignore | 12 +++ CLAUDE.md | 155 ++++++++++++++++++++++++++++++++++ devenv.lock | 103 ++++++++++++++++++++++ devenv.nix | 8 ++ devenv.yaml | 15 ++++ package-lock.json | 20 ++++- package.json | 1 + 22 files changed, 319 insertions(+), 17 deletions(-) delete mode 100644 .devenv/devenv.json delete mode 100644 .devenv/flake.json delete mode 120000 .devenv/gc/shell delete mode 120000 .devenv/gc/shell-5-link delete mode 100644 .devenv/imports.txt delete mode 100644 .devenv/input-paths.txt delete mode 100755 .devenv/load-exports delete mode 100644 .devenv/nix-eval-cache.db delete mode 100644 .devenv/nix-eval-cache.db-shm delete mode 120000 .devenv/profile delete mode 120000 .devenv/run delete mode 100644 .devenv/tasks.db delete mode 100644 .devenv/tasks.db-shm delete mode 100644 .devenv/tasks.db-wal create mode 100644 .envrc create mode 100644 CLAUDE.md create mode 100644 devenv.lock create mode 100644 devenv.nix create mode 100644 devenv.yaml diff --git a/.devenv/devenv.json b/.devenv/devenv.json deleted file mode 100644 index 383c521..0000000 --- a/.devenv/devenv.json +++ /dev/null @@ -1 +0,0 @@ -{"inputs":{"nixpkgs":{"url":"github:cachix/devenv-nixpkgs/rolling"}}} \ No newline at end of file diff --git a/.devenv/flake.json b/.devenv/flake.json deleted file mode 100644 index 3b139e1..0000000 --- a/.devenv/flake.json +++ /dev/null @@ -1 +0,0 @@ -{"nixpkgs":{"url":"github:cachix/devenv-nixpkgs/rolling"}} \ No newline at end of file diff --git a/.devenv/gc/shell b/.devenv/gc/shell deleted file mode 120000 index 2417b47..0000000 --- a/.devenv/gc/shell +++ /dev/null @@ -1 +0,0 @@ -shell-5-link \ No newline at end of file diff --git a/.devenv/gc/shell-5-link b/.devenv/gc/shell-5-link deleted file mode 120000 index 94ea2a7..0000000 --- a/.devenv/gc/shell-5-link +++ /dev/null @@ -1 +0,0 @@ -/nix/store/nsxv5ns3yk1sfbzvdgr75z2m0ylg8njb-devenv-shell-env \ No newline at end of file diff --git a/.devenv/imports.txt b/.devenv/imports.txt deleted file mode 100644 index e69de29..0000000 diff --git a/.devenv/input-paths.txt b/.devenv/input-paths.txt deleted file mode 100644 index feaec13..0000000 --- a/.devenv/input-paths.txt +++ /dev/null @@ -1,7 +0,0 @@ -/home/jonathan/code/skypod-node/.devenv/flake.json -/home/jonathan/code/skypod-node/.devenv.flake.nix -/home/jonathan/code/skypod-node/.env -/home/jonathan/code/skypod-node/devenv.local.nix -/home/jonathan/code/skypod-node/devenv.lock -/home/jonathan/code/skypod-node/devenv.nix -/home/jonathan/code/skypod-node/devenv.yaml \ No newline at end of file diff --git a/.devenv/load-exports b/.devenv/load-exports deleted file mode 100755 index 8b13789..0000000 --- a/.devenv/load-exports +++ /dev/null @@ -1 +0,0 @@ - diff --git a/.devenv/nix-eval-cache.db b/.devenv/nix-eval-cache.db deleted file mode 100644 index c776c3c7d01987c8daf03b1198841be81a9a67ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147456 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±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 C>Bf8j diff --git a/.devenv/profile b/.devenv/profile deleted file mode 120000 index f930ff6..0000000 --- a/.devenv/profile +++ /dev/null @@ -1 +0,0 @@ -/nix/store/wmh3hwran6m1ln7n4ci6794xyrrgcs6w-devenv-profile \ No newline at end of file diff --git a/.devenv/run b/.devenv/run deleted file mode 120000 index 52a6201..0000000 --- a/.devenv/run +++ /dev/null @@ -1 +0,0 @@ -/run/user/1000/devenv-d48b567 \ No newline at end of file diff --git a/.devenv/tasks.db b/.devenv/tasks.db deleted file mode 100644 index d6091455ea363a6eb2fa9e34b83e978fed7f1e39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36864 zcmWFz^vNtqRY=P(%1ta$FlG>7U}9o$P*7lCU|?ckVBlmx09FPD1{MUDff0#~i^;^G zS5?5v|AT>pJDY*8n}0j+EWRk7P~JrD7u?ynG>wXnhQMeDjE2By2#kinXb6mkz-S1J zhQKfgfocwRaYt3gM%m1~l+=pMl#2NB#FFHU)Rg$N%$(HtlEmWd_=3ce3}jJG=O9AZR{QVS=^=W`j%S+5n)lmSMshQ2rE^e;M*vO9Ca%BA+gzd#`rDhW+ zE4#R=Dr0kKNn%n?Dzd@IY&NJRA&yQyt|-n|(BM)~$V^f2^b2uycMVbq2=erG42o3n zc8%2GQc!?Vg0hWdF1hPq+| zJC~+88=JVdDkB3p+=~lyGD}k96H80-!F<#dV2B(z?2Wd}?BcSrj7`4aKm_}_s5B2o zv!DbXOq9HUQ-HW0SL$3hH_JF((lf}V~P6jD-)lZ!G7Ko)>w9Ui+N?D9F_>Bp$8=qP@5@IXShs7$fOGWRo*e zle3FUa}}I?{GH%tLgZ2_Qj<$Tu7adksPkb7mP^x~iA_9M7gyk-2S&Uhx(G7^0|N)B zOyy-@U|{55&cMGM#2dwwlXkY9G@38snzxFuc)7e zK948mExX@&n4{N1y{MX5?)K$^`)OMX6Y87!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GZ|X2#n7E(!3HyGN>enAsq6 z|04lpaS)r4fq{XEfq{XUfq?;;&BDOIz{{-g(GVC7 zfzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c z4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5E#lK zz{mhPUt%cxVASoSAwX^jFfp()fX-zhSO2KK(GVEAA;1hiQ)uYAf7IopAwYfzfX>n) zU-zia(GVEQApkl>ZYaBd)a|1oKz;~-&UYkV_o&X%5E#lKz%`T|KkD|;5TGaoxGB;- zs(Ca7hH?n-3}wfUx_vYRMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0q zLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1W-Z% E04jGY^#A|> diff --git a/.devenv/tasks.db-wal b/.devenv/tasks.db-wal deleted file mode 100644 index 717db94317a535996f829cea2fe58419fd40a80c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53592 zcmXr7XKP~6eI&uaAiw|u78W7j*p}V?aoXD2yZYabZww3!jL71fUyJiGneM00R>fgM)$s0|NsS0|NsyNDmZ)^s_+OAPU3>(fBYEgI-kuFaHk) zCcbV4zHa{Q_!N)IjfTKz2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mm5NOh5WEa=f zWo%(BNleN~jV~_Dsff?bOfO0-$;{6yM&~m*2e~?ixGID=I{CONpvx*~a49G#D3qlZ z6=&w>DL8q$d-{ba1O$2dItE25c)LdGfaOzCi<65o3qTeqgt$h8DERq@DENi?_&}sH z^NLFnb8=Es;`8$qLOgw4gF_sB1K^4jTwL88Lw!ONoI`_xT>V1g;YuLdi%XM}Q;Ulg zoc#TLTpj)3`XI8&8L7$H#ih9lPCoulXmY6)smY}vSH+iP=BC1&4|5!sracpzc(5)b zLuOt|YDIBjPG(7Jd}3)yKA0bm9vJb4=pvwgJqI*Zf%^Fju@tX8vE~B#b#z<+6bhqwGz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nhI0sj z*ZsrB1;QJ}dPa)qS=q%^RT-N@!7IX%4Mt|OK`nu-1V@$uuL93ZQSkH&admeMLSO$4 z_Z;ThZ;)p(MRW60GSf0sQy{CdVWz^CWrLP=C+Fvtq~?{xXCxM9fc1j}GmGO>GK*4^ zOY(~*KH`+3@i_6L~Hu-`B z5$xxp(mWW=f)aQzQSt�pfa)KXMX_OX7=4^WZ@b3naLAHHu4$(m<=*HPls$)pZoq z^YY8pH8nw+^Giz#N=p>Hg8lutH1%27#GPevc^u~8ctaSAb#xyAVgti~?IS4B_A`FH z{Qhy!J_1Jml??nVhg&f`>YvdN7!85Z5Eu=C(GVC7fzc2c4S~@R7!85Z5Eu=C(GVDd zArQ*Q&MIomnZqcQ^}%h&fy}(j5+eg6Q(Z#?T|+YkBLgdAV=DvW#qoJjlUiNx{)+lp z=<|3|-m?3hhdFvJ)QhT_vM%aBp=(GVC7fzc2c4S~@R7!85Z z5Eu=C(GVC7fzc2c4S~@R7!84;9|EBB0pRx$gc*Cfh5tRe0<>OWbX;KQNA9Q(MnhmU z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONfItYaf#hN91eH8*g-yojSC!k+~!iO;TJf% zUH}v-qj)p~MnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz5ly2ylRW2pbo8HHkGr zZ_$IzqvHagP#MLeAut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?>_dQ)fq{V& zHZHKiOyB0?jHR-p;{wAz-bXz+8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*O zpb%g}9v3*$f5k2N>2H6~`2vjm%Nh8WgOrTo(GVC7fzc2c4S~@R7!85Z5Eu=C(GVC7 zfzc2c4S~@R7!82|41sV)c2-ei&KzdQ?Sh$knI%RBMy9%k2D*l33PuK2#>Q3##*5?g zq9(Pv-u)Hzv(V@9q`YPKI}da8TBsLQGt1q+TyQ^aYetf>+!mqkMNen3?NwOD)X50| DZal)R diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..aefcad4 --- /dev/null +++ b/.envrc @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +export DIRENV_WARN_TIMEOUT=20s +eval "$(devenv direnvrc)" + +# To silence all output, use `--quiet`. +# Example usage: use devenv --quiet --impure --option services.postgres.enable:bool true +use devenv --quiet diff --git a/.gitignore b/.gitignore index 907bac4..cbf5007 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,15 @@ tsconfig.tsbuildinfo coverage/ .jest-cache/ /.jestcache/ + +# Devenv +.devenv* +.devenv/ +devenv.local.nix +devenv.local.yaml + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..21076c6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,155 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**Skypod** is an offline-first, peer-to-peer RSS and podcast Progressive Web App (PWA). The application enables users to manage subscriptions and listening history across multiple devices with direct peer-to-peer synchronization (no central server for data). + +**Key Technologies:** TypeScript, Preact, Vite, Dexie.js (IndexedDB), Express, WebRTC (simple-peer), WebSockets, Zod + +## Development Commands + +### Starting Development +```bash +npm run dev +``` +Starts all development services concurrently using wireit: +- Vite dev server at `http://127.0.0.1:4000` (frontend) +- Backend server at `http://127.0.0.1:4001` (WebSocket + API) +- Live type-checking and linting in watch mode + +### Running Tests +```bash +npm run test # Run all tests once +npm run start:tests # Run tests in watch mode +``` +Tests use Jest with ts-jest. Test files are named `*.spec.ts` or `*.spec.tsx`. + +To run a single test file: +```bash +npx jest src/path/to/file.spec.ts +``` + +### Linting and Type-Checking +```bash +npm run lint # Run ESLint +npm run types # Run TypeScript type-checking +npm run build # Build production frontend +``` + +### Production +```bash +npm run start:prod # Build and run production server +``` + +### Git Hooks +Pre-commit hook runs type-checking and linting automatically. Enable with: +```bash +git config core.hooksPath .githooks +``` + +## Architecture + +### Module Structure + +The codebase is organized into four main directories with path aliases: + +- **`#client/*`** (`src/client/`) - Preact frontend application +- **`#server/*`** (`src/server/`) - Node.js Express backend +- **`#common/*`** (`src/common/`) - Shared code (protocol, crypto, utilities) +- **`#skypod/*`** (`src/skypod/`) - Domain-specific schemas and actions + +These path aliases are configured in both `package.json` (imports) and `tsconfig.json` (paths). Always use these aliases for absolute imports. + +### Frontend Client (`src/client/`) + +Built with Preact and Vite. Key subdirectories: + +- **`realm/`** - Core P2P connection management + - `service-connection.ts` - Main WebSocket connection to signaling server + - `service-connection-peer.ts` - WebRTC peer connection management + - `service-connection-sync.ts` - Sync protocol coordinator + - `context-connection.tsx` - React context for connection state + - `context-identity.tsx` - React context for user identity +- **`root/`** - Database setup (Dexie/IndexedDB) +- **`skypod/`** - Application-specific components and workers +- **`components/`** - Reusable UI components + +### Backend Server (`src/server/`) + +Lightweight Express server with two primary functions: + +1. **WebSocket Signaling Server** (`routes-socket/`) - Enables WebRTC connection establishment between peers +2. **API Proxy** (`routes-api/`) - Fetches and parses external RSS feeds + +The server maintains realm state (connected peers) but does NOT store user data - all data lives on clients. + +### Common Library (`src/common/`) + +Shared isomorphic code: + +- **`protocol/`** - P2P protocol definitions + - `logical-clock.ts` - Hybrid Logical Clock (HLC) implementation for causal ordering + - `messages.ts` - Zod schemas for all WebSocket/WebRTC messages + - `brands.ts` - Branded types for identity IDs +- **`crypto/`** - JWT and JWK utilities for authentication +- **`async/`** - Utilities (semaphore, blocking queue, sleep, etc.) +- **`schema/`** - Common Zod schemas +- `socket.ts` - Shared WebSocket message handling +- `errors.ts` - Custom error types (ProtocolError) + +### Domain Layer (`src/skypod/`) + +Application-specific schemas and action definitions for the podcast/RSS domain. + +## Synchronization Protocol + +The P2P sync protocol is the core architectural component. It uses a **Hybrid Logical Clock (HLC)** for causal ordering of events and follows a specific pattern: + +### PULL for Catch-up +When a new client connects or comes online after being offline, it PULLS the complete action history from a single deterministically chosen `syncPartner`. This efficiently brings the client up to date. + +### PUSH for Updates +All clients PUSH their own new or offline-generated changes to all connected peers. This is NOT a simple broadcast - each client sends a *tailored* set of missing actions to each peer based on a handshake where "knowledge vectors" (last known timestamps) are exchanged. + +### Key Components +- **Hybrid Logical Clock** (`src/common/protocol/logical-clock.ts`) - Provides total ordering across distributed events even with clock skew +- **Message Schemas** (`src/common/protocol/messages.ts`) - Defines all protocol messages using Zod +- **Connection Services** (`src/client/realm/service-connection-*.ts`) - Implements the sync protocol + +When working on sync-related code, understand that ordering is critical - the HLC timestamp format is `lc:seconds:counter:identid` where each component is used for deterministic ordering. + +## Build System + +Uses **wireit** for dependency-aware task orchestration. All build tasks are defined in `package.json` under the `wireit` key. + +- Tasks automatically track file dependencies and outputs +- Service tasks (prefixed with `run:`) run continuously in watch mode +- Compound tasks like `start:dev` orchestrate multiple services + +## Testing Conventions + +- Test files use the pattern `*.spec.ts` or `*.spec.tsx` +- Tests use `@jest/globals` imports for Jest functions +- Use `#common/*`, `#client/*`, `#server/*` aliases in tests (configured in `jest.config.js`) +- Tests support ESM and handle Preact/JSX via ts-jest + +## Code Style + +- **ESLint + Prettier** enforce consistent style +- **Strict TypeScript** with `strict: true` and `noImplicitAny: true` +- **Zod** for runtime validation and schema definitions +- **JSX** configured for Preact (`jsxImportSource: "preact"`) + +## Important Context for AI Assistance + +1. **Path Aliases Are Critical** - Always use `#common/*`, `#client/*`, `#server/*`, `#skypod/*` for imports, never relative paths across module boundaries + +2. **Sync Protocol Is Complex** - The HLC-based synchronization is the most intricate part of the codebase. Changes to sync logic require careful consideration of causal ordering and distributed consistency + +3. **Client Is Offline-First** - All user data lives in IndexedDB via Dexie. The server is stateless regarding user data (only maintains ephemeral realm/peer state) + +4. **WebRTC for P2P** - Peers communicate directly via WebRTC data channels (using simple-peer). The WebSocket connection to the server is only used for signaling and as a fallback broadcast mechanism + +5. **Zod Schemas Define Protocol** - All message formats are defined as Zod schemas in `src/common/protocol/`. These schemas are the single source of truth for protocol messages diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..d8d5d30 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,103 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1760560333, + "owner": "cachix", + "repo": "devenv", + "rev": "0a4043938f540027e562c5a0feebbe6be872c3ea", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1760392170, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "46d55f0aeb1d567a78223e69729734f3dca25a85", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1758532697, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "git-hooks": "git-hooks", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": [ + "git-hooks" + ] + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..674486f --- /dev/null +++ b/devenv.nix @@ -0,0 +1,8 @@ +{ pkgs, lib, config, inputs, ... }: + +{ + languages.javascript = { + enable = true; + npm.enable = true; + }; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..116a2ad --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,15 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling + +# If you're using non-OSS software, you can set allowUnfree to true. +# allowUnfree: true + +# If you're willing to use a package that's vulnerable +# permittedInsecurePackages: +# - "openssl-1.1.1w" + +# If you have more than one devenv you can merge them +#imports: +# - ./backend diff --git a/package-lock.json b/package-lock.json index e6a6f7f..fa87078 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,6 +59,7 @@ "typescript": "^5.8.3", "typescript-eslint": "^8.34.1", "typescript-eslint-language-service": "^5.0.5", + "typescript-language-server": "^5.0.1", "vite": "^6.3.5", "vite-plugin-checker": "^0.9.3", "vite-plugin-node-polyfills": "^0.23.0", @@ -3611,9 +3612,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -16457,6 +16458,19 @@ "typescript": ">= 4.0.0" } }, + "node_modules/typescript-language-server": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/typescript-language-server/-/typescript-language-server-5.0.1.tgz", + "integrity": "sha512-+qwfddk3LvEbMRlcdDJya58A6pn0tO9nuBp6hRHfBwwGBm8KTkkf3V9iQLM2lgIEUc3vnuMmfzFG/dCFF/whvg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "typescript-language-server": "lib/cli.mjs" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/typeson": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/typeson/-/typeson-9.0.3.tgz", diff --git a/package.json b/package.json index e153f2c..8c4600d 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "typescript": "^5.8.3", "typescript-eslint": "^8.34.1", "typescript-eslint-language-service": "^5.0.5", + "typescript-language-server": "^5.0.1", "vite": "^6.3.5", "vite-plugin-checker": "^0.9.3", "vite-plugin-node-polyfills": "^0.23.0", -- 2.51.2