├── .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 | [](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 |
--------------------------------------------------------------------------------