├── .gitignore ├── README.md ├── index.html ├── main.js ├── observable.js ├── package.json ├── pnpm-lock.yaml ├── public └── vite.svg └── signals.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Signals 2 | 3 | A simple JavaScript signals implementation for learning purposes. 4 | 5 | ## Remote Development 6 | 7 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/joysofcode/signals) 8 | 9 | ## Local Development 10 | 11 | ### 🧑‍🤝‍🧑 Clone the project 12 | 13 | ```sh 14 | https://github.com/joysofcode/signals.git 15 | ``` 16 | 17 | ### 📦️ Install dependencies 18 | 19 | ```sh 20 | pnpm i 21 | ``` 22 | 23 | ### 💿️ Run the development server 24 | 25 | ```sh 26 | pnpm run dev 27 | ``` 28 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Signals 8 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import { signal, effect, derived } from './signals.js' 2 | 3 | const btn = document.querySelector('button') 4 | btn.addEventListener('click', () => count.value++) 5 | 6 | const count = signal(0) 7 | const double = derived(() => count.value * 2) 8 | 9 | effect(() => { 10 | btn.innerText = count.value 11 | }) 12 | 13 | effect(() => { 14 | if (count.value > 10) { 15 | console.log('dangerously high') 16 | count.value = 0 17 | } 18 | }) 19 | 20 | effect(() => console.log(double.value)) 21 | 22 | /* 23 | import { observable } from './observable.js' 24 | 25 | const btn = document.querySelector('button') 26 | btn.addEventListener('click', () => counter.increment()) 27 | 28 | const counter = observable(0) 29 | 30 | counter.subscribe((count) => { 31 | btn.innerText = count 32 | }) 33 | 34 | const unsubscribe = counter.subscribe((count) => { 35 | console.log(count) 36 | }) 37 | 38 | unsubscribe() 39 | */ 40 | -------------------------------------------------------------------------------- /observable.js: -------------------------------------------------------------------------------- 1 | export function observable(value) { 2 | const subscribers = new Set() 3 | 4 | return { 5 | increment() { 6 | value++ 7 | this.update(value) 8 | }, 9 | subscribe(fn) { 10 | subscribers.add(fn) 11 | return () => subscribers.delete(fn) 12 | }, 13 | update(value) { 14 | subscribers.forEach((fn) => fn(value)) 15 | }, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "signals", 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "vite": "^5.2.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | devDependencies: 11 | vite: 12 | specifier: ^5.2.0 13 | version: 5.2.11 14 | 15 | packages: 16 | 17 | '@esbuild/aix-ppc64@0.20.2': 18 | resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} 19 | engines: {node: '>=12'} 20 | cpu: [ppc64] 21 | os: [aix] 22 | 23 | '@esbuild/android-arm64@0.20.2': 24 | resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} 25 | engines: {node: '>=12'} 26 | cpu: [arm64] 27 | os: [android] 28 | 29 | '@esbuild/android-arm@0.20.2': 30 | resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} 31 | engines: {node: '>=12'} 32 | cpu: [arm] 33 | os: [android] 34 | 35 | '@esbuild/android-x64@0.20.2': 36 | resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} 37 | engines: {node: '>=12'} 38 | cpu: [x64] 39 | os: [android] 40 | 41 | '@esbuild/darwin-arm64@0.20.2': 42 | resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} 43 | engines: {node: '>=12'} 44 | cpu: [arm64] 45 | os: [darwin] 46 | 47 | '@esbuild/darwin-x64@0.20.2': 48 | resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} 49 | engines: {node: '>=12'} 50 | cpu: [x64] 51 | os: [darwin] 52 | 53 | '@esbuild/freebsd-arm64@0.20.2': 54 | resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} 55 | engines: {node: '>=12'} 56 | cpu: [arm64] 57 | os: [freebsd] 58 | 59 | '@esbuild/freebsd-x64@0.20.2': 60 | resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} 61 | engines: {node: '>=12'} 62 | cpu: [x64] 63 | os: [freebsd] 64 | 65 | '@esbuild/linux-arm64@0.20.2': 66 | resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} 67 | engines: {node: '>=12'} 68 | cpu: [arm64] 69 | os: [linux] 70 | 71 | '@esbuild/linux-arm@0.20.2': 72 | resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} 73 | engines: {node: '>=12'} 74 | cpu: [arm] 75 | os: [linux] 76 | 77 | '@esbuild/linux-ia32@0.20.2': 78 | resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} 79 | engines: {node: '>=12'} 80 | cpu: [ia32] 81 | os: [linux] 82 | 83 | '@esbuild/linux-loong64@0.20.2': 84 | resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} 85 | engines: {node: '>=12'} 86 | cpu: [loong64] 87 | os: [linux] 88 | 89 | '@esbuild/linux-mips64el@0.20.2': 90 | resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} 91 | engines: {node: '>=12'} 92 | cpu: [mips64el] 93 | os: [linux] 94 | 95 | '@esbuild/linux-ppc64@0.20.2': 96 | resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} 97 | engines: {node: '>=12'} 98 | cpu: [ppc64] 99 | os: [linux] 100 | 101 | '@esbuild/linux-riscv64@0.20.2': 102 | resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} 103 | engines: {node: '>=12'} 104 | cpu: [riscv64] 105 | os: [linux] 106 | 107 | '@esbuild/linux-s390x@0.20.2': 108 | resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} 109 | engines: {node: '>=12'} 110 | cpu: [s390x] 111 | os: [linux] 112 | 113 | '@esbuild/linux-x64@0.20.2': 114 | resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} 115 | engines: {node: '>=12'} 116 | cpu: [x64] 117 | os: [linux] 118 | 119 | '@esbuild/netbsd-x64@0.20.2': 120 | resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} 121 | engines: {node: '>=12'} 122 | cpu: [x64] 123 | os: [netbsd] 124 | 125 | '@esbuild/openbsd-x64@0.20.2': 126 | resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} 127 | engines: {node: '>=12'} 128 | cpu: [x64] 129 | os: [openbsd] 130 | 131 | '@esbuild/sunos-x64@0.20.2': 132 | resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} 133 | engines: {node: '>=12'} 134 | cpu: [x64] 135 | os: [sunos] 136 | 137 | '@esbuild/win32-arm64@0.20.2': 138 | resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} 139 | engines: {node: '>=12'} 140 | cpu: [arm64] 141 | os: [win32] 142 | 143 | '@esbuild/win32-ia32@0.20.2': 144 | resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} 145 | engines: {node: '>=12'} 146 | cpu: [ia32] 147 | os: [win32] 148 | 149 | '@esbuild/win32-x64@0.20.2': 150 | resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} 151 | engines: {node: '>=12'} 152 | cpu: [x64] 153 | os: [win32] 154 | 155 | '@rollup/rollup-android-arm-eabi@4.18.0': 156 | resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} 157 | cpu: [arm] 158 | os: [android] 159 | 160 | '@rollup/rollup-android-arm64@4.18.0': 161 | resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} 162 | cpu: [arm64] 163 | os: [android] 164 | 165 | '@rollup/rollup-darwin-arm64@4.18.0': 166 | resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} 167 | cpu: [arm64] 168 | os: [darwin] 169 | 170 | '@rollup/rollup-darwin-x64@4.18.0': 171 | resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} 172 | cpu: [x64] 173 | os: [darwin] 174 | 175 | '@rollup/rollup-linux-arm-gnueabihf@4.18.0': 176 | resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} 177 | cpu: [arm] 178 | os: [linux] 179 | 180 | '@rollup/rollup-linux-arm-musleabihf@4.18.0': 181 | resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} 182 | cpu: [arm] 183 | os: [linux] 184 | 185 | '@rollup/rollup-linux-arm64-gnu@4.18.0': 186 | resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} 187 | cpu: [arm64] 188 | os: [linux] 189 | 190 | '@rollup/rollup-linux-arm64-musl@4.18.0': 191 | resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} 192 | cpu: [arm64] 193 | os: [linux] 194 | 195 | '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': 196 | resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} 197 | cpu: [ppc64] 198 | os: [linux] 199 | 200 | '@rollup/rollup-linux-riscv64-gnu@4.18.0': 201 | resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} 202 | cpu: [riscv64] 203 | os: [linux] 204 | 205 | '@rollup/rollup-linux-s390x-gnu@4.18.0': 206 | resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} 207 | cpu: [s390x] 208 | os: [linux] 209 | 210 | '@rollup/rollup-linux-x64-gnu@4.18.0': 211 | resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} 212 | cpu: [x64] 213 | os: [linux] 214 | 215 | '@rollup/rollup-linux-x64-musl@4.18.0': 216 | resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} 217 | cpu: [x64] 218 | os: [linux] 219 | 220 | '@rollup/rollup-win32-arm64-msvc@4.18.0': 221 | resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} 222 | cpu: [arm64] 223 | os: [win32] 224 | 225 | '@rollup/rollup-win32-ia32-msvc@4.18.0': 226 | resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} 227 | cpu: [ia32] 228 | os: [win32] 229 | 230 | '@rollup/rollup-win32-x64-msvc@4.18.0': 231 | resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} 232 | cpu: [x64] 233 | os: [win32] 234 | 235 | '@types/estree@1.0.5': 236 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} 237 | 238 | esbuild@0.20.2: 239 | resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} 240 | engines: {node: '>=12'} 241 | hasBin: true 242 | 243 | fsevents@2.3.3: 244 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 245 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 246 | os: [darwin] 247 | 248 | nanoid@3.3.7: 249 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 250 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 251 | hasBin: true 252 | 253 | picocolors@1.0.1: 254 | resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} 255 | 256 | postcss@8.4.38: 257 | resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} 258 | engines: {node: ^10 || ^12 || >=14} 259 | 260 | rollup@4.18.0: 261 | resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} 262 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 263 | hasBin: true 264 | 265 | source-map-js@1.2.0: 266 | resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} 267 | engines: {node: '>=0.10.0'} 268 | 269 | vite@5.2.11: 270 | resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==} 271 | engines: {node: ^18.0.0 || >=20.0.0} 272 | hasBin: true 273 | peerDependencies: 274 | '@types/node': ^18.0.0 || >=20.0.0 275 | less: '*' 276 | lightningcss: ^1.21.0 277 | sass: '*' 278 | stylus: '*' 279 | sugarss: '*' 280 | terser: ^5.4.0 281 | peerDependenciesMeta: 282 | '@types/node': 283 | optional: true 284 | less: 285 | optional: true 286 | lightningcss: 287 | optional: true 288 | sass: 289 | optional: true 290 | stylus: 291 | optional: true 292 | sugarss: 293 | optional: true 294 | terser: 295 | optional: true 296 | 297 | snapshots: 298 | 299 | '@esbuild/aix-ppc64@0.20.2': 300 | optional: true 301 | 302 | '@esbuild/android-arm64@0.20.2': 303 | optional: true 304 | 305 | '@esbuild/android-arm@0.20.2': 306 | optional: true 307 | 308 | '@esbuild/android-x64@0.20.2': 309 | optional: true 310 | 311 | '@esbuild/darwin-arm64@0.20.2': 312 | optional: true 313 | 314 | '@esbuild/darwin-x64@0.20.2': 315 | optional: true 316 | 317 | '@esbuild/freebsd-arm64@0.20.2': 318 | optional: true 319 | 320 | '@esbuild/freebsd-x64@0.20.2': 321 | optional: true 322 | 323 | '@esbuild/linux-arm64@0.20.2': 324 | optional: true 325 | 326 | '@esbuild/linux-arm@0.20.2': 327 | optional: true 328 | 329 | '@esbuild/linux-ia32@0.20.2': 330 | optional: true 331 | 332 | '@esbuild/linux-loong64@0.20.2': 333 | optional: true 334 | 335 | '@esbuild/linux-mips64el@0.20.2': 336 | optional: true 337 | 338 | '@esbuild/linux-ppc64@0.20.2': 339 | optional: true 340 | 341 | '@esbuild/linux-riscv64@0.20.2': 342 | optional: true 343 | 344 | '@esbuild/linux-s390x@0.20.2': 345 | optional: true 346 | 347 | '@esbuild/linux-x64@0.20.2': 348 | optional: true 349 | 350 | '@esbuild/netbsd-x64@0.20.2': 351 | optional: true 352 | 353 | '@esbuild/openbsd-x64@0.20.2': 354 | optional: true 355 | 356 | '@esbuild/sunos-x64@0.20.2': 357 | optional: true 358 | 359 | '@esbuild/win32-arm64@0.20.2': 360 | optional: true 361 | 362 | '@esbuild/win32-ia32@0.20.2': 363 | optional: true 364 | 365 | '@esbuild/win32-x64@0.20.2': 366 | optional: true 367 | 368 | '@rollup/rollup-android-arm-eabi@4.18.0': 369 | optional: true 370 | 371 | '@rollup/rollup-android-arm64@4.18.0': 372 | optional: true 373 | 374 | '@rollup/rollup-darwin-arm64@4.18.0': 375 | optional: true 376 | 377 | '@rollup/rollup-darwin-x64@4.18.0': 378 | optional: true 379 | 380 | '@rollup/rollup-linux-arm-gnueabihf@4.18.0': 381 | optional: true 382 | 383 | '@rollup/rollup-linux-arm-musleabihf@4.18.0': 384 | optional: true 385 | 386 | '@rollup/rollup-linux-arm64-gnu@4.18.0': 387 | optional: true 388 | 389 | '@rollup/rollup-linux-arm64-musl@4.18.0': 390 | optional: true 391 | 392 | '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': 393 | optional: true 394 | 395 | '@rollup/rollup-linux-riscv64-gnu@4.18.0': 396 | optional: true 397 | 398 | '@rollup/rollup-linux-s390x-gnu@4.18.0': 399 | optional: true 400 | 401 | '@rollup/rollup-linux-x64-gnu@4.18.0': 402 | optional: true 403 | 404 | '@rollup/rollup-linux-x64-musl@4.18.0': 405 | optional: true 406 | 407 | '@rollup/rollup-win32-arm64-msvc@4.18.0': 408 | optional: true 409 | 410 | '@rollup/rollup-win32-ia32-msvc@4.18.0': 411 | optional: true 412 | 413 | '@rollup/rollup-win32-x64-msvc@4.18.0': 414 | optional: true 415 | 416 | '@types/estree@1.0.5': {} 417 | 418 | esbuild@0.20.2: 419 | optionalDependencies: 420 | '@esbuild/aix-ppc64': 0.20.2 421 | '@esbuild/android-arm': 0.20.2 422 | '@esbuild/android-arm64': 0.20.2 423 | '@esbuild/android-x64': 0.20.2 424 | '@esbuild/darwin-arm64': 0.20.2 425 | '@esbuild/darwin-x64': 0.20.2 426 | '@esbuild/freebsd-arm64': 0.20.2 427 | '@esbuild/freebsd-x64': 0.20.2 428 | '@esbuild/linux-arm': 0.20.2 429 | '@esbuild/linux-arm64': 0.20.2 430 | '@esbuild/linux-ia32': 0.20.2 431 | '@esbuild/linux-loong64': 0.20.2 432 | '@esbuild/linux-mips64el': 0.20.2 433 | '@esbuild/linux-ppc64': 0.20.2 434 | '@esbuild/linux-riscv64': 0.20.2 435 | '@esbuild/linux-s390x': 0.20.2 436 | '@esbuild/linux-x64': 0.20.2 437 | '@esbuild/netbsd-x64': 0.20.2 438 | '@esbuild/openbsd-x64': 0.20.2 439 | '@esbuild/sunos-x64': 0.20.2 440 | '@esbuild/win32-arm64': 0.20.2 441 | '@esbuild/win32-ia32': 0.20.2 442 | '@esbuild/win32-x64': 0.20.2 443 | 444 | fsevents@2.3.3: 445 | optional: true 446 | 447 | nanoid@3.3.7: {} 448 | 449 | picocolors@1.0.1: {} 450 | 451 | postcss@8.4.38: 452 | dependencies: 453 | nanoid: 3.3.7 454 | picocolors: 1.0.1 455 | source-map-js: 1.2.0 456 | 457 | rollup@4.18.0: 458 | dependencies: 459 | '@types/estree': 1.0.5 460 | optionalDependencies: 461 | '@rollup/rollup-android-arm-eabi': 4.18.0 462 | '@rollup/rollup-android-arm64': 4.18.0 463 | '@rollup/rollup-darwin-arm64': 4.18.0 464 | '@rollup/rollup-darwin-x64': 4.18.0 465 | '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 466 | '@rollup/rollup-linux-arm-musleabihf': 4.18.0 467 | '@rollup/rollup-linux-arm64-gnu': 4.18.0 468 | '@rollup/rollup-linux-arm64-musl': 4.18.0 469 | '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 470 | '@rollup/rollup-linux-riscv64-gnu': 4.18.0 471 | '@rollup/rollup-linux-s390x-gnu': 4.18.0 472 | '@rollup/rollup-linux-x64-gnu': 4.18.0 473 | '@rollup/rollup-linux-x64-musl': 4.18.0 474 | '@rollup/rollup-win32-arm64-msvc': 4.18.0 475 | '@rollup/rollup-win32-ia32-msvc': 4.18.0 476 | '@rollup/rollup-win32-x64-msvc': 4.18.0 477 | fsevents: 2.3.3 478 | 479 | source-map-js@1.2.0: {} 480 | 481 | vite@5.2.11: 482 | dependencies: 483 | esbuild: 0.20.2 484 | postcss: 8.4.38 485 | rollup: 4.18.0 486 | optionalDependencies: 487 | fsevents: 2.3.3 488 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /signals.js: -------------------------------------------------------------------------------- 1 | let subscriber = null 2 | 3 | export function signal(value) { 4 | const subscriptions = new Set() 5 | 6 | return { 7 | get value() { 8 | if (subscriber) { 9 | subscriptions.add(subscriber) 10 | } 11 | return value 12 | }, 13 | set value(updated) { 14 | value = updated 15 | subscriptions.forEach((fn) => fn()) 16 | }, 17 | } 18 | } 19 | 20 | export function effect(fn) { 21 | subscriber = fn 22 | fn() 23 | subscriber = null 24 | } 25 | 26 | export function derived(fn) { 27 | const derived = signal() 28 | effect(() => { 29 | derived.value = fn() 30 | }) 31 | return derived 32 | } 33 | --------------------------------------------------------------------------------