@minejs/signals 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +420 -0
- package/dist/main.cjs +2 -0
- package/dist/main.cjs.map +1 -0
- package/dist/main.d.cts +253 -0
- package/dist/main.d.ts +253 -0
- package/dist/main.js +2 -0
- package/dist/main.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Maysara Elshewehy (https://github.com/maysara-elshewehy)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
<!-- ╔══════════════════════════════ BEG ══════════════════════════════╗ -->
|
|
2
|
+
|
|
3
|
+
<br>
|
|
4
|
+
<div align="center">
|
|
5
|
+
<p>
|
|
6
|
+
<img src="./assets/img/logo.png" alt="logo" style="" height="60" />
|
|
7
|
+
</p>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div align="center">
|
|
11
|
+
<img src="https://img.shields.io/badge/v-0.0.1-black"/>
|
|
12
|
+
<img src="https://img.shields.io/badge/🔥-@minjs-black"/>
|
|
13
|
+
<img src="https://img.shields.io/badge/zero-dependencies-black" alt="Test Coverage" />
|
|
14
|
+
<br>
|
|
15
|
+
<img src="https://img.shields.io/badge/coverage-99.14%25-brightgreen" alt="Test Coverage" />
|
|
16
|
+
<img src="https://img.shields.io/github/issues/minejs/signals?style=flat" alt="Github Repo Issues" />
|
|
17
|
+
<img src="https://img.shields.io/github/stars/minejs/signals?style=social" alt="GitHub Repo stars" />
|
|
18
|
+
</div>
|
|
19
|
+
<br>
|
|
20
|
+
|
|
21
|
+
<!-- ╚═════════════════════════════════════════════════════════════════╝ -->
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
<!-- ╔══════════════════════════════ DOC ══════════════════════════════╗ -->
|
|
26
|
+
|
|
27
|
+
- ## Quick Start 🔥
|
|
28
|
+
|
|
29
|
+
> **_A lightweight, zero-dependency signals library for reactive JavaScript applications._**
|
|
30
|
+
|
|
31
|
+
- ### Setup
|
|
32
|
+
|
|
33
|
+
> install [`space`](https://github.com/solution-lib/space) first.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
space i @minejs/signals
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
<div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
|
|
40
|
+
|
|
41
|
+
- ### Usage
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { signal, effect, computed, batch } from '@minejs/signals'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- ### 1. Basic Signal
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// Create a signal
|
|
51
|
+
const count = signal(0)
|
|
52
|
+
|
|
53
|
+
// Read value
|
|
54
|
+
console.log(count()) // 0
|
|
55
|
+
|
|
56
|
+
// Update value
|
|
57
|
+
count.set(5)
|
|
58
|
+
console.log(count()) // 5
|
|
59
|
+
|
|
60
|
+
// Update with function
|
|
61
|
+
count.update(n => n + 1)
|
|
62
|
+
console.log(count()) // 6
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- ### 2. Effects (Auto-run on changes)
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const name = signal('John')
|
|
69
|
+
|
|
70
|
+
// Effect runs automatically when dependencies change
|
|
71
|
+
effect(() => {
|
|
72
|
+
console.log('Hello,', name())
|
|
73
|
+
})
|
|
74
|
+
// Logs: "Hello, John"
|
|
75
|
+
|
|
76
|
+
name.set('Jane')
|
|
77
|
+
// Logs: "Hello, Jane"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- ### 3. Computed Values (Derived state)
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const firstName = signal('John')
|
|
84
|
+
const lastName = signal('Doe')
|
|
85
|
+
|
|
86
|
+
// Computed value updates automatically
|
|
87
|
+
const fullName = computed(() => {
|
|
88
|
+
return `${firstName()} ${lastName()}`
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
console.log(fullName()) // "John Doe"
|
|
92
|
+
|
|
93
|
+
firstName.set('Jane')
|
|
94
|
+
console.log(fullName()) // "Jane Doe"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- ### 4. Batch Updates (Optimize performance)
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const a = signal(0)
|
|
101
|
+
const b = signal(0)
|
|
102
|
+
|
|
103
|
+
effect(() => {
|
|
104
|
+
console.log('Sum:', a() + b())
|
|
105
|
+
})
|
|
106
|
+
// Logs: "Sum: 0"
|
|
107
|
+
|
|
108
|
+
// Without batch: effect runs twice
|
|
109
|
+
a.set(1) // Logs: "Sum: 1"
|
|
110
|
+
b.set(2) // Logs: "Sum: 3"
|
|
111
|
+
|
|
112
|
+
// With batch: effect runs once
|
|
113
|
+
batch(() => {
|
|
114
|
+
a.set(10)
|
|
115
|
+
b.set(20)
|
|
116
|
+
})
|
|
117
|
+
// Logs: "Sum: 30" (only once!)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
<br>
|
|
122
|
+
|
|
123
|
+
- ## API Reference 🔥
|
|
124
|
+
|
|
125
|
+
- #### `signal<T>(value: T): Signal<T>`
|
|
126
|
+
> Create a reactive signal.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const count = signal(0)
|
|
130
|
+
|
|
131
|
+
count() // Read: 0
|
|
132
|
+
count.set(5) // Write: 5
|
|
133
|
+
count.update(n => n + 1) // Update: 6
|
|
134
|
+
count.peek() // Read without tracking: 6
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- #### `effect(fn: () => void | (() => void)): () => void`
|
|
138
|
+
|
|
139
|
+
> Run code automatically when dependencies change.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
const count = signal(0)
|
|
143
|
+
|
|
144
|
+
// Effect with cleanup
|
|
145
|
+
const dispose = effect(() => {
|
|
146
|
+
console.log('Count:', count())
|
|
147
|
+
|
|
148
|
+
// Optional cleanup function
|
|
149
|
+
return () => {
|
|
150
|
+
console.log('Cleaning up...')
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// Stop the effect
|
|
155
|
+
dispose()
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
- #### `computed<T>(fn: () => T): Signal<T>`
|
|
159
|
+
|
|
160
|
+
> Create a derived signal (memoized).
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const count = signal(0)
|
|
164
|
+
const doubled = computed(() => count() * 2)
|
|
165
|
+
|
|
166
|
+
console.log(doubled()) // 0
|
|
167
|
+
count.set(5)
|
|
168
|
+
console.log(doubled()) // 10
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- #### `batch<T>(fn: () => T): T`
|
|
172
|
+
|
|
173
|
+
> Batch multiple updates into one.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const a = signal(0)
|
|
177
|
+
const b = signal(0)
|
|
178
|
+
|
|
179
|
+
batch(() => {
|
|
180
|
+
a.set(1)
|
|
181
|
+
b.set(2)
|
|
182
|
+
// Effects run only once here
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
- #### `untrack<T>(fn: () => T): T`
|
|
187
|
+
|
|
188
|
+
> Read signals without tracking dependencies.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const count = signal(0)
|
|
192
|
+
|
|
193
|
+
effect(() => {
|
|
194
|
+
const value = untrack(() => count())
|
|
195
|
+
// count() is read but NOT tracked
|
|
196
|
+
console.log(value)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
count.set(1) // Effect does NOT run
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
- #### `on<T>(signal: Signal<T>, fn: (value: T, prev: T) => void): () => void`
|
|
203
|
+
|
|
204
|
+
> Run effect only when specific signal changes.
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const count = signal(0)
|
|
208
|
+
const other = signal('hello')
|
|
209
|
+
|
|
210
|
+
on(count, (value, prevValue) => {
|
|
211
|
+
console.log(`Changed from ${prevValue} to ${value}`)
|
|
212
|
+
other() // Can read but won't track
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
other.set('world') // Does NOT trigger
|
|
216
|
+
count.set(1) // DOES trigger
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
- #### `store<T>(obj: T): { [K in keyof T]: Signal<T[K]> }`
|
|
220
|
+
|
|
221
|
+
> Create an object of signals.
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const state = store({
|
|
225
|
+
count : 0,
|
|
226
|
+
name : 'John'
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
state.count() // 0
|
|
230
|
+
state.name() // 'John'
|
|
231
|
+
|
|
232
|
+
state.count.set(5)
|
|
233
|
+
state.name.set('Jane')
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
- #### `root<T>(fn: (dispose: () => void) => T): T`
|
|
237
|
+
|
|
238
|
+
> Create a disposal scope for effects.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
root((dispose) => {
|
|
242
|
+
effect(() => {
|
|
243
|
+
// ... effects ...
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
// Clean up all effects at once
|
|
247
|
+
dispose()
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
- #### `memo<T>(fn: () => T): () => T`
|
|
252
|
+
|
|
253
|
+
> Memoize expensive computations.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
const expensiveComputation = memo(() => {
|
|
257
|
+
// This computation runs only once and returns cached value
|
|
258
|
+
return Math.sqrt(16)
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
const result1 = expensiveComputation() // Computes
|
|
262
|
+
const result2 = expensiveComputation() // Returns cached value
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
- #### `Signal.subscribe(fn: () => void): () => void`
|
|
266
|
+
|
|
267
|
+
> Subscribe to signal changes manually.
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
const count = signal(0)
|
|
271
|
+
|
|
272
|
+
const unsubscribe = count.subscribe(() => {
|
|
273
|
+
console.log('Signal changed!')
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
count.set(1) // Logs: "Signal changed!"
|
|
277
|
+
|
|
278
|
+
unsubscribe() // Stop listening
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
<br>
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
- ## Real-World Examples
|
|
285
|
+
|
|
286
|
+
- #### Counter Component
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { signal, effect } from '@minejs/signals'
|
|
290
|
+
|
|
291
|
+
function Counter() {
|
|
292
|
+
const count = signal(0)
|
|
293
|
+
|
|
294
|
+
const button = document.createElement('button')
|
|
295
|
+
|
|
296
|
+
effect(() => {
|
|
297
|
+
button.textContent = `Count: ${count()}`
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
button.onclick = () => count.update(n => n + 1)
|
|
301
|
+
|
|
302
|
+
return button
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
- #### Todo App
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { signal, computed } from '@minejs/signals'
|
|
310
|
+
|
|
311
|
+
interface Todo {
|
|
312
|
+
id : number
|
|
313
|
+
text : string
|
|
314
|
+
done : boolean
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const todos = signal<Todo[]>([])
|
|
318
|
+
const filter = signal<'all' | 'active' | 'completed'>('all')
|
|
319
|
+
|
|
320
|
+
const filteredTodos = computed(() => {
|
|
321
|
+
const f = filter()
|
|
322
|
+
const t = todos()
|
|
323
|
+
|
|
324
|
+
if (f === 'active') return t.filter(todo => !todo.done)
|
|
325
|
+
if (f === 'completed') return t.filter(todo => todo.done)
|
|
326
|
+
return t
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
const activeTodoCount = computed(() => {
|
|
330
|
+
return todos().filter(t => !t.done).length
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
// Actions
|
|
334
|
+
function addTodo(text: string) {
|
|
335
|
+
todos.update(list => [
|
|
336
|
+
...list,
|
|
337
|
+
{ id: Date.now(), text, done: false }
|
|
338
|
+
])
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function toggleTodo(id: number) {
|
|
342
|
+
todos.update(list =>
|
|
343
|
+
list.map(todo =>
|
|
344
|
+
todo.id === id ? { ...todo, done: !todo.done } : todo
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
- #### Form with Validation
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
import { signal, computed } from '@crux/signals'
|
|
354
|
+
|
|
355
|
+
const email = signal('')
|
|
356
|
+
const password = signal('')
|
|
357
|
+
|
|
358
|
+
const isEmailValid = computed(() => {
|
|
359
|
+
return email().includes('@') && email().length > 3
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
const isPasswordValid = computed(() => {
|
|
363
|
+
return password().length >= 8
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
const canSubmit = computed(() => {
|
|
367
|
+
return isEmailValid() && isPasswordValid()
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
effect(() => {
|
|
371
|
+
const button = document.querySelector('#submit')
|
|
372
|
+
button.disabled = !canSubmit()
|
|
373
|
+
})
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
- #### Data Fetching
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { signal, effect } from '@minejs/signals'
|
|
380
|
+
|
|
381
|
+
const userId = signal<number | null>(null)
|
|
382
|
+
const userData = signal<any>(null)
|
|
383
|
+
const loading = signal(false)
|
|
384
|
+
|
|
385
|
+
effect(async () => {
|
|
386
|
+
const id = userId()
|
|
387
|
+
|
|
388
|
+
if (!id) return
|
|
389
|
+
|
|
390
|
+
loading.set(true)
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
const response = await fetch(`/api/users/${id}`)
|
|
394
|
+
const data = await response.json()
|
|
395
|
+
userData.set(data)
|
|
396
|
+
} finally {
|
|
397
|
+
loading.set(false)
|
|
398
|
+
}
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
// Fetch user when ID changes
|
|
402
|
+
userId.set(123)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
<!-- ╚═════════════════════════════════════════════════════════════════╝ -->
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
<!-- ╔══════════════════════════════ END ══════════════════════════════╗ -->
|
|
411
|
+
|
|
412
|
+
<br>
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
<div align="center">
|
|
417
|
+
<a href="https://github.com/maysara-elshewehy"><img src="https://img.shields.io/badge/by-Maysara-black"/></a>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
<!-- ╚═════════════════════════════════════════════════════════════════╝ -->
|
package/dist/main.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';Object.defineProperty(exports,'__esModule',{value:true});var i=null,s=null,c=0,d=new Set,a=new Set;function v(n){let e=n,t=new Set;function r(){return i&&t.add(i),e}function o(u){Object.is(e,u)||(e=u,c>0?t.forEach(T=>d.add(T)):t.forEach(T=>T()));}function p(u){o(u(e));}function l(){return e}function x(u){return t.add(u),()=>t.delete(u)}let f=r;return f.set=o,f.update=p,f.peek=l,f.subscribe=x,f}function y(n){let e,t=false,r=()=>{if(t)return;e&&(e(),e=void 0);let p=i;i=r;try{let l=n();typeof l=="function"&&(e=l);}finally{i=p;}};r();let o=()=>{t||(t=true,e&&e());};return s&&s.push(o),o}function b(n){let e=v(void 0);y(()=>{e.set(n());});let t=e;return Object.defineProperty(t,"isComputed",{value:true,writable:false}),t}function g(n){c++;try{return n()}finally{if(c--,c===0){c++,a.clear();try{for(;d.size>0;){let e=Array.from(d);d.clear(),e.forEach(t=>{a.has(t)||(a.add(t),t());});}}finally{c--,a.clear();}}}}function h(n){let e=i;i=null;try{return n()}finally{i=e;}}function E(n,e){let t=n.peek();return y(()=>{let r=n(),o=h(()=>e(r,t));return t=r,o})}function C(n){let e={};for(let t in n)e[t]=v(n[t]);return e}function S(n){let e,t=false;return ()=>{if(t)return e;let r=n();return e=r,t=true,r}}function k(n){let e=[],t=s;s=e;try{return n(()=>{e.forEach(o=>o()),e.length=0,s=t;})}finally{s=t;}}var w={getCurrentEffect(){return i},getBatchDepth(){return c},getBatchedEffectsCount(){return d.size}};function m(n){return typeof n=="function"&&"set"in n&&"update"in n&&"peek"in n}function D(n){return m(n)&&"isComputed"in n}var R={signal:v,effect:y,computed:b,batch:g,untrack:h,on:E,store:C,memo:S,root:k,isSignal:m,isComputed:D,dev:w};exports.batch=g;exports.computed=b;exports.default=R;exports.dev=w;exports.effect=y;exports.isComputed=D;exports.isSignal=m;exports.memo=S;exports.on=E;exports.root=k;exports.signal=v;exports.store=C;exports.untrack=h;//# sourceMappingURL=main.cjs.map
|
|
2
|
+
//# sourceMappingURL=main.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/main.ts"],"names":["currentEffect","currentRoot","batchDepth","batchedEffects","flushedEffects","signal","initialValue","value","subscribers","read","write","newValue","fn","update","peek","subscribe","sig","effect","cleanup","isDisposed","execute","prevEffect","result","disposer","computed","batch","effects","untrack","on","prevValue","store","initialState","key","memo","cachedValue","hasCachedValue","root","disposers","prevRoot","d","dev","isSignal","isComputed","main_default"],"mappings":"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","file":"main.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/main.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { Signal, EffectCleanup, Computed } from './types';\r\n export type * from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ INIT ════════════════════════════════════════╗\r\n\r\n let currentEffect : (() => void) | null = null;\r\n let currentRoot : (() => void)[] | null = null;\r\n let batchDepth : number = 0;\r\n const batchedEffects : Set<() => void> = new Set<() => void>();\r\n const flushedEffects : Set<() => void> = new Set<() => void>();\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Creates a reactive signal that can be read, written, and subscribed to.\r\n * @template T - The type of value stored in the signal\r\n * @param {T} initialValue - The initial value of the signal\r\n * @returns {Signal<T>} A signal object with read, set, update, peek, and subscribe methods\r\n * @example\r\n * const count = signal(0);\r\n * console.log(count()); // 0\r\n * count.set(5); // Update value\r\n */\r\n export function signal<T>(initialValue: T): Signal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<() => void>();\r\n\r\n function read(): T {\r\n // Track dependency if inside effect\r\n if (currentEffect) {\r\n subscribers.add(currentEffect);\r\n }\r\n return value;\r\n }\r\n\r\n function write(newValue: T): void {\r\n // Only update if value actually changed\r\n if (Object.is(value, newValue)) return;\r\n\r\n value = newValue;\r\n\r\n // Notify all subscribers\r\n if (batchDepth > 0) {\r\n // Batch mode: collect effects\r\n subscribers.forEach(fn => batchedEffects.add(fn));\r\n } else {\r\n // Immediate mode: run effects now\r\n subscribers.forEach(fn => fn());\r\n }\r\n }\r\n\r\n function update(fn: (prev: T) => T): void {\r\n write(fn(value));\r\n }\r\n\r\n function peek(): T {\r\n // Read without tracking\r\n return value;\r\n }\r\n\r\n function subscribe(fn: () => void): () => void {\r\n subscribers.add(fn);\r\n return () => subscribers.delete(fn);\r\n }\r\n\r\n // Create signal function with methods\r\n const sig = read as Signal<T>;\r\n sig.set = write;\r\n sig.update = update;\r\n sig.peek = peek;\r\n sig.subscribe = subscribe;\r\n\r\n return sig;\r\n }\r\n\r\n /**\r\n * Automatically runs a function when its signal dependencies change.\r\n * @param {() => EffectCleanup} fn - The effect function to run. Can optionally return a cleanup function.\r\n * @returns {() => void} A dispose function to stop the effect and clean up\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * console.log('Count:', count());\r\n * return () => console.log('Cleaning up');\r\n * });\r\n */\r\n export function effect(fn: () => EffectCleanup): () => void {\r\n let cleanup: (() => void) | undefined;\r\n let isDisposed = false;\r\n\r\n const execute = () => {\r\n if (isDisposed) return;\r\n\r\n // Run cleanup from previous execution\r\n if (cleanup) {\r\n cleanup();\r\n cleanup = undefined;\r\n }\r\n\r\n // Set as current effect for dependency tracking\r\n const prevEffect = currentEffect;\r\n currentEffect = execute;\r\n\r\n try {\r\n // Run the effect function\r\n const result = fn();\r\n\r\n // Store cleanup if returned\r\n if (typeof result === 'function') {\r\n cleanup = result;\r\n }\r\n } finally {\r\n // Restore previous effect\r\n currentEffect = prevEffect;\r\n }\r\n };\r\n\r\n // Run immediately\r\n execute();\r\n\r\n // Create dispose function\r\n const disposer = () => {\r\n if (isDisposed) return;\r\n isDisposed = true;\r\n if (cleanup) cleanup();\r\n };\r\n\r\n // Register with current root if one exists\r\n if (currentRoot) {\r\n currentRoot.push(disposer);\r\n }\r\n\r\n // Return dispose function\r\n return disposer;\r\n }\r\n\r\n /**\r\n * Creates a computed signal that automatically updates when its dependencies change.\r\n * The computation result is cached and only recomputed when dependencies change.\r\n * @template T - The type of value computed\r\n * @param {() => T} fn - The computation function\r\n * @returns {Computed<T>} A read-only computed signal\r\n * @example\r\n * const count = signal(5);\r\n * const doubled = computed(() => count() * 2);\r\n * console.log(doubled()); // 10\r\n */\r\n export function computed<T>(fn: () => T): Computed<T> {\r\n const sig = signal<T>(undefined as T);\r\n\r\n // Create effect that updates the signal\r\n effect(() => {\r\n sig.set(fn());\r\n });\r\n\r\n // Mark as computed\r\n const computed = sig as Computed<T>;\r\n Object.defineProperty(computed, 'isComputed', {\r\n value: true,\r\n writable: false\r\n });\r\n\r\n return computed;\r\n }\r\n\r\n /**\r\n * Groups multiple signal updates together, deferring effect execution until all updates complete.\r\n * This improves performance by preventing cascading effect runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that performs multiple signal updates\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const a = signal(1);\r\n * const b = signal(2);\r\n * batch(() => {\r\n * a.set(10);\r\n * b.set(20);\r\n * }); // Effects only run once\r\n */\r\n export function batch<T>(fn: () => T): T {\r\n batchDepth++;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n batchDepth--;\r\n\r\n // If we're back at depth 0, flush batched effects\r\n if (batchDepth === 0) {\r\n // Keep batch mode active while flushing to prevent cascading effects\r\n batchDepth++;\r\n flushedEffects.clear();\r\n try {\r\n // Keep running effects until no more are queued\r\n while (batchedEffects.size > 0) {\r\n const effects = Array.from(batchedEffects);\r\n batchedEffects.clear();\r\n effects.forEach(fn => {\r\n // Only run if we haven't run it in this batch\r\n if (!flushedEffects.has(fn)) {\r\n flushedEffects.add(fn);\r\n fn();\r\n }\r\n });\r\n }\r\n } finally {\r\n batchDepth--;\r\n flushedEffects.clear();\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Reads signals without creating dependencies on them.\r\n * Useful for accessing signal values without triggering effect re-runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that accesses signals\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * const value = untrack(() => count()); // Won't trigger re-run\r\n * console.log(value);\r\n * });\r\n */\r\n export function untrack<T>(fn: () => T): T {\r\n const prevEffect = currentEffect;\r\n currentEffect = null;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n currentEffect = prevEffect;\r\n }\r\n }\r\n\r\n /**\r\n * Runs an effect only when a specific signal changes, providing both new and previous values.\r\n * @template T - The type of the signal\r\n * @param {Signal<T>} sig - The signal to watch\r\n * @param {(value: T, prevValue: T) => EffectCleanup} fn - Effect function called with new and previous values\r\n * @returns {() => void} A dispose function to stop watching\r\n * @example\r\n * const count = signal(0);\r\n * on(count, (newVal, oldVal) => {\r\n * console.log(`Changed from ${oldVal} to ${newVal}`);\r\n * });\r\n */\r\n export function on<T>(\r\n sig: Signal<T>,\r\n fn: (value: T, prevValue: T) => EffectCleanup\r\n ): () => void {\r\n let prevValue = sig.peek();\r\n\r\n return effect(() => {\r\n // Read the signal to create dependency\r\n const value = sig();\r\n\r\n // Run callback without tracking new dependencies\r\n const cleanup = untrack(() => fn(value, prevValue));\r\n prevValue = value;\r\n return cleanup;\r\n });\r\n }\r\n\r\n /**\r\n * Creates a store object where each property is a signal.\r\n * Provides a convenient way to manage multiple related reactive values.\r\n * @template T - The type of the initial state object\r\n * @param {T} initialState - An object with initial values\r\n * @returns {{ [K in keyof T]: Signal<T[K]> }} An object with signals for each property\r\n * @example\r\n * const state = store({ count: 0, name: 'John' });\r\n * console.log(state.count()); // 0\r\n * state.name.set('Jane');\r\n */\r\n export function store<T extends Record<string, any>>(\r\n initialState: T\r\n ): { [K in keyof T]: Signal<T[K]> } {\r\n const store = {} as any;\r\n\r\n for (const key in initialState) {\r\n store[key] = signal(initialState[key]);\r\n }\r\n\r\n return store;\r\n }\r\n\r\n /**\r\n * Memoizes the result of an expensive computation, caching it indefinitely.\r\n * Unlike computed, this doesn't depend on reactive signals.\r\n * @template T - The type of the memoized value\r\n * @param {() => T} fn - A function that performs the computation\r\n * @returns {() => T} A function that returns the cached result\r\n * @example\r\n * const expensiveCalc = memo(() => {\r\n * return Array.from({ length: 1000 }).map(expensiveOp);\r\n * });\r\n * const result = expensiveCalc(); // Computed only once\r\n */\r\n export function memo<T>(fn: () => T): () => T {\r\n let cachedValue: T | undefined;\r\n let hasCachedValue = false;\r\n\r\n return () => {\r\n if (hasCachedValue) {\r\n return cachedValue as T;\r\n }\r\n\r\n // Compute the value\r\n const value = fn();\r\n\r\n // Cache it\r\n cachedValue = value;\r\n hasCachedValue = true;\r\n\r\n return value;\r\n };\r\n }\r\n\r\n /**\r\n * Creates a root scope for managing effect and computed signal lifecycles.\r\n * All effects and disposers created within the function are collected and can be cleaned up together.\r\n * @template T - The return type of the function\r\n * @param {(dispose: () => void) => T} fn - A function that receives a dispose function\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const dispose = root((dispose) => {\r\n * effect(() => console.log('Running'));\r\n * return 42;\r\n * });\r\n * dispose(); // Cleans up all effects created in the root\r\n */\r\n export function root<T>(fn: (dispose: () => void) => T): T {\r\n const disposers: (() => void)[] = [];\r\n const prevRoot = currentRoot;\r\n currentRoot = disposers;\r\n\r\n try {\r\n const dispose = () => {\r\n disposers.forEach(d => d());\r\n disposers.length = 0;\r\n currentRoot = prevRoot;\r\n };\r\n\r\n return fn(dispose);\r\n } finally {\r\n currentRoot = prevRoot;\r\n }\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ HELP ════════════════════════════════════════╗\r\n\r\n /**\r\n * Development utilities for debugging signal reactivity\r\n */\r\n export const dev = {\r\n /**\r\n * Returns the currently executing effect, or null if no effect is running\r\n * @returns {(() => void) | null} The current effect function or null\r\n */\r\n getCurrentEffect(): (() => void) | null {\r\n return currentEffect;\r\n },\r\n\r\n /**\r\n * Returns the current batch depth (for debugging nested batch calls)\r\n * @returns {number} The current batch nesting level\r\n */\r\n getBatchDepth(): number {\r\n return batchDepth;\r\n },\r\n\r\n /**\r\n * Returns the count of effects currently pending in the batch queue\r\n * @returns {number} The number of batched effects waiting to run\r\n */\r\n getBatchedEffectsCount(): number {\r\n return batchedEffects.size;\r\n }\r\n };\r\n\r\n /**\r\n * Type guard to check if a value is a signal\r\n * @template T - The type of value the signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a signal\r\n * @example\r\n * if (isSignal(myValue)) {\r\n * console.log(myValue());\r\n * }\r\n */\r\n export function isSignal<T>(value: any): value is Signal<T> {\r\n return (\r\n typeof value === 'function' &&\r\n 'set' in value &&\r\n 'update' in value &&\r\n 'peek' in value\r\n );\r\n }\r\n\r\n /**\r\n * Type guard to check if a value is a computed signal\r\n * @template T - The type of value the computed signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a computed signal\r\n * @example\r\n * if (isComputed(myValue)) {\r\n * console.log('This is a computed signal');\r\n * }\r\n */\r\n export function isComputed<T>(value: any): value is Computed<T> {\r\n return isSignal(value) && 'isComputed' in value;\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export default {\r\n signal,\r\n effect,\r\n computed,\r\n batch,\r\n untrack,\r\n on,\r\n store,\r\n memo,\r\n root,\r\n isSignal,\r\n isComputed,\r\n dev\r\n };\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
|
package/dist/main.d.cts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// src/types.d.ts
|
|
2
|
+
//
|
|
3
|
+
// Made with ❤️ by Maysara.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
// ╔════════════════════════════════════════ TYPE ════════════════════════════════════════╗
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents a reactive signal that holds a value and notifies dependents when changed.
|
|
11
|
+
* @template T - The type of value stored in the signal
|
|
12
|
+
*/
|
|
13
|
+
interface Signal<T> {
|
|
14
|
+
/**
|
|
15
|
+
* Reads the current signal value and establishes a dependency if inside an effect.
|
|
16
|
+
* @returns {T} The current value
|
|
17
|
+
*/
|
|
18
|
+
(): T;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sets the signal to a new value and notifies all subscribers.
|
|
22
|
+
* @param {T} value - The new value to set
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
set(value: T): void;
|
|
26
|
+
/**
|
|
27
|
+
* Updates the signal by applying a function to its current value.
|
|
28
|
+
* @param {(prev: T) => T} fn - Function that receives current value and returns new value
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
update(fn: (prev: T) => T): void;
|
|
32
|
+
/**
|
|
33
|
+
* Reads the signal value without creating a dependency relationship.
|
|
34
|
+
* @returns {T} The current value
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
peek(): T;
|
|
38
|
+
/**
|
|
39
|
+
* Subscribes to signal changes.
|
|
40
|
+
* @param {() => void} fn - Callback function to execute when signal changes
|
|
41
|
+
* @returns {() => void} Unsubscribe function
|
|
42
|
+
*/
|
|
43
|
+
subscribe(fn: () => void): () => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Represents a computed (memoized) signal derived from other signals.
|
|
48
|
+
* Automatically updates when dependencies change and is read-only.
|
|
49
|
+
* @template T - The type of value computed
|
|
50
|
+
*/
|
|
51
|
+
interface Computed<T> extends Signal<T> {
|
|
52
|
+
/** Marks this signal as computed for type checking purposes */
|
|
53
|
+
readonly isComputed: true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The return type of an effect function.
|
|
58
|
+
* Can be void or a cleanup function that runs when the effect is disposed.
|
|
59
|
+
*/
|
|
60
|
+
type EffectCleanup = void | (() => void);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a reactive signal that can be read, written, and subscribed to.
|
|
64
|
+
* @template T - The type of value stored in the signal
|
|
65
|
+
* @param {T} initialValue - The initial value of the signal
|
|
66
|
+
* @returns {Signal<T>} A signal object with read, set, update, peek, and subscribe methods
|
|
67
|
+
* @example
|
|
68
|
+
* const count = signal(0);
|
|
69
|
+
* console.log(count()); // 0
|
|
70
|
+
* count.set(5); // Update value
|
|
71
|
+
*/
|
|
72
|
+
declare function signal<T>(initialValue: T): Signal<T>;
|
|
73
|
+
/**
|
|
74
|
+
* Automatically runs a function when its signal dependencies change.
|
|
75
|
+
* @param {() => EffectCleanup} fn - The effect function to run. Can optionally return a cleanup function.
|
|
76
|
+
* @returns {() => void} A dispose function to stop the effect and clean up
|
|
77
|
+
* @example
|
|
78
|
+
* const count = signal(0);
|
|
79
|
+
* effect(() => {
|
|
80
|
+
* console.log('Count:', count());
|
|
81
|
+
* return () => console.log('Cleaning up');
|
|
82
|
+
* });
|
|
83
|
+
*/
|
|
84
|
+
declare function effect(fn: () => EffectCleanup): () => void;
|
|
85
|
+
/**
|
|
86
|
+
* Creates a computed signal that automatically updates when its dependencies change.
|
|
87
|
+
* The computation result is cached and only recomputed when dependencies change.
|
|
88
|
+
* @template T - The type of value computed
|
|
89
|
+
* @param {() => T} fn - The computation function
|
|
90
|
+
* @returns {Computed<T>} A read-only computed signal
|
|
91
|
+
* @example
|
|
92
|
+
* const count = signal(5);
|
|
93
|
+
* const doubled = computed(() => count() * 2);
|
|
94
|
+
* console.log(doubled()); // 10
|
|
95
|
+
*/
|
|
96
|
+
declare function computed<T>(fn: () => T): Computed<T>;
|
|
97
|
+
/**
|
|
98
|
+
* Groups multiple signal updates together, deferring effect execution until all updates complete.
|
|
99
|
+
* This improves performance by preventing cascading effect runs.
|
|
100
|
+
* @template T - The return type of the function
|
|
101
|
+
* @param {() => T} fn - A function that performs multiple signal updates
|
|
102
|
+
* @returns {T} The return value of the function
|
|
103
|
+
* @example
|
|
104
|
+
* const a = signal(1);
|
|
105
|
+
* const b = signal(2);
|
|
106
|
+
* batch(() => {
|
|
107
|
+
* a.set(10);
|
|
108
|
+
* b.set(20);
|
|
109
|
+
* }); // Effects only run once
|
|
110
|
+
*/
|
|
111
|
+
declare function batch<T>(fn: () => T): T;
|
|
112
|
+
/**
|
|
113
|
+
* Reads signals without creating dependencies on them.
|
|
114
|
+
* Useful for accessing signal values without triggering effect re-runs.
|
|
115
|
+
* @template T - The return type of the function
|
|
116
|
+
* @param {() => T} fn - A function that accesses signals
|
|
117
|
+
* @returns {T} The return value of the function
|
|
118
|
+
* @example
|
|
119
|
+
* const count = signal(0);
|
|
120
|
+
* effect(() => {
|
|
121
|
+
* const value = untrack(() => count()); // Won't trigger re-run
|
|
122
|
+
* console.log(value);
|
|
123
|
+
* });
|
|
124
|
+
*/
|
|
125
|
+
declare function untrack<T>(fn: () => T): T;
|
|
126
|
+
/**
|
|
127
|
+
* Runs an effect only when a specific signal changes, providing both new and previous values.
|
|
128
|
+
* @template T - The type of the signal
|
|
129
|
+
* @param {Signal<T>} sig - The signal to watch
|
|
130
|
+
* @param {(value: T, prevValue: T) => EffectCleanup} fn - Effect function called with new and previous values
|
|
131
|
+
* @returns {() => void} A dispose function to stop watching
|
|
132
|
+
* @example
|
|
133
|
+
* const count = signal(0);
|
|
134
|
+
* on(count, (newVal, oldVal) => {
|
|
135
|
+
* console.log(`Changed from ${oldVal} to ${newVal}`);
|
|
136
|
+
* });
|
|
137
|
+
*/
|
|
138
|
+
declare function on<T>(sig: Signal<T>, fn: (value: T, prevValue: T) => EffectCleanup): () => void;
|
|
139
|
+
/**
|
|
140
|
+
* Creates a store object where each property is a signal.
|
|
141
|
+
* Provides a convenient way to manage multiple related reactive values.
|
|
142
|
+
* @template T - The type of the initial state object
|
|
143
|
+
* @param {T} initialState - An object with initial values
|
|
144
|
+
* @returns {{ [K in keyof T]: Signal<T[K]> }} An object with signals for each property
|
|
145
|
+
* @example
|
|
146
|
+
* const state = store({ count: 0, name: 'John' });
|
|
147
|
+
* console.log(state.count()); // 0
|
|
148
|
+
* state.name.set('Jane');
|
|
149
|
+
*/
|
|
150
|
+
declare function store<T extends Record<string, any>>(initialState: T): {
|
|
151
|
+
[K in keyof T]: Signal<T[K]>;
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Memoizes the result of an expensive computation, caching it indefinitely.
|
|
155
|
+
* Unlike computed, this doesn't depend on reactive signals.
|
|
156
|
+
* @template T - The type of the memoized value
|
|
157
|
+
* @param {() => T} fn - A function that performs the computation
|
|
158
|
+
* @returns {() => T} A function that returns the cached result
|
|
159
|
+
* @example
|
|
160
|
+
* const expensiveCalc = memo(() => {
|
|
161
|
+
* return Array.from({ length: 1000 }).map(expensiveOp);
|
|
162
|
+
* });
|
|
163
|
+
* const result = expensiveCalc(); // Computed only once
|
|
164
|
+
*/
|
|
165
|
+
declare function memo<T>(fn: () => T): () => T;
|
|
166
|
+
/**
|
|
167
|
+
* Creates a root scope for managing effect and computed signal lifecycles.
|
|
168
|
+
* All effects and disposers created within the function are collected and can be cleaned up together.
|
|
169
|
+
* @template T - The return type of the function
|
|
170
|
+
* @param {(dispose: () => void) => T} fn - A function that receives a dispose function
|
|
171
|
+
* @returns {T} The return value of the function
|
|
172
|
+
* @example
|
|
173
|
+
* const dispose = root((dispose) => {
|
|
174
|
+
* effect(() => console.log('Running'));
|
|
175
|
+
* return 42;
|
|
176
|
+
* });
|
|
177
|
+
* dispose(); // Cleans up all effects created in the root
|
|
178
|
+
*/
|
|
179
|
+
declare function root<T>(fn: (dispose: () => void) => T): T;
|
|
180
|
+
/**
|
|
181
|
+
* Development utilities for debugging signal reactivity
|
|
182
|
+
*/
|
|
183
|
+
declare const dev: {
|
|
184
|
+
/**
|
|
185
|
+
* Returns the currently executing effect, or null if no effect is running
|
|
186
|
+
* @returns {(() => void) | null} The current effect function or null
|
|
187
|
+
*/
|
|
188
|
+
getCurrentEffect(): (() => void) | null;
|
|
189
|
+
/**
|
|
190
|
+
* Returns the current batch depth (for debugging nested batch calls)
|
|
191
|
+
* @returns {number} The current batch nesting level
|
|
192
|
+
*/
|
|
193
|
+
getBatchDepth(): number;
|
|
194
|
+
/**
|
|
195
|
+
* Returns the count of effects currently pending in the batch queue
|
|
196
|
+
* @returns {number} The number of batched effects waiting to run
|
|
197
|
+
*/
|
|
198
|
+
getBatchedEffectsCount(): number;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Type guard to check if a value is a signal
|
|
202
|
+
* @template T - The type of value the signal contains
|
|
203
|
+
* @param {any} value - The value to check
|
|
204
|
+
* @returns {boolean} True if the value is a signal
|
|
205
|
+
* @example
|
|
206
|
+
* if (isSignal(myValue)) {
|
|
207
|
+
* console.log(myValue());
|
|
208
|
+
* }
|
|
209
|
+
*/
|
|
210
|
+
declare function isSignal<T>(value: any): value is Signal<T>;
|
|
211
|
+
/**
|
|
212
|
+
* Type guard to check if a value is a computed signal
|
|
213
|
+
* @template T - The type of value the computed signal contains
|
|
214
|
+
* @param {any} value - The value to check
|
|
215
|
+
* @returns {boolean} True if the value is a computed signal
|
|
216
|
+
* @example
|
|
217
|
+
* if (isComputed(myValue)) {
|
|
218
|
+
* console.log('This is a computed signal');
|
|
219
|
+
* }
|
|
220
|
+
*/
|
|
221
|
+
declare function isComputed<T>(value: any): value is Computed<T>;
|
|
222
|
+
declare const _default: {
|
|
223
|
+
signal: typeof signal;
|
|
224
|
+
effect: typeof effect;
|
|
225
|
+
computed: typeof computed;
|
|
226
|
+
batch: typeof batch;
|
|
227
|
+
untrack: typeof untrack;
|
|
228
|
+
on: typeof on;
|
|
229
|
+
store: typeof store;
|
|
230
|
+
memo: typeof memo;
|
|
231
|
+
root: typeof root;
|
|
232
|
+
isSignal: typeof isSignal;
|
|
233
|
+
isComputed: typeof isComputed;
|
|
234
|
+
dev: {
|
|
235
|
+
/**
|
|
236
|
+
* Returns the currently executing effect, or null if no effect is running
|
|
237
|
+
* @returns {(() => void) | null} The current effect function or null
|
|
238
|
+
*/
|
|
239
|
+
getCurrentEffect(): (() => void) | null;
|
|
240
|
+
/**
|
|
241
|
+
* Returns the current batch depth (for debugging nested batch calls)
|
|
242
|
+
* @returns {number} The current batch nesting level
|
|
243
|
+
*/
|
|
244
|
+
getBatchDepth(): number;
|
|
245
|
+
/**
|
|
246
|
+
* Returns the count of effects currently pending in the batch queue
|
|
247
|
+
* @returns {number} The number of batched effects waiting to run
|
|
248
|
+
*/
|
|
249
|
+
getBatchedEffectsCount(): number;
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export { type Computed, type EffectCleanup, type Signal, batch, computed, _default as default, dev, effect, isComputed, isSignal, memo, on, root, signal, store, untrack };
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// src/types.d.ts
|
|
2
|
+
//
|
|
3
|
+
// Made with ❤️ by Maysara.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
// ╔════════════════════════════════════════ TYPE ════════════════════════════════════════╗
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents a reactive signal that holds a value and notifies dependents when changed.
|
|
11
|
+
* @template T - The type of value stored in the signal
|
|
12
|
+
*/
|
|
13
|
+
interface Signal<T> {
|
|
14
|
+
/**
|
|
15
|
+
* Reads the current signal value and establishes a dependency if inside an effect.
|
|
16
|
+
* @returns {T} The current value
|
|
17
|
+
*/
|
|
18
|
+
(): T;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sets the signal to a new value and notifies all subscribers.
|
|
22
|
+
* @param {T} value - The new value to set
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
set(value: T): void;
|
|
26
|
+
/**
|
|
27
|
+
* Updates the signal by applying a function to its current value.
|
|
28
|
+
* @param {(prev: T) => T} fn - Function that receives current value and returns new value
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
update(fn: (prev: T) => T): void;
|
|
32
|
+
/**
|
|
33
|
+
* Reads the signal value without creating a dependency relationship.
|
|
34
|
+
* @returns {T} The current value
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
peek(): T;
|
|
38
|
+
/**
|
|
39
|
+
* Subscribes to signal changes.
|
|
40
|
+
* @param {() => void} fn - Callback function to execute when signal changes
|
|
41
|
+
* @returns {() => void} Unsubscribe function
|
|
42
|
+
*/
|
|
43
|
+
subscribe(fn: () => void): () => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Represents a computed (memoized) signal derived from other signals.
|
|
48
|
+
* Automatically updates when dependencies change and is read-only.
|
|
49
|
+
* @template T - The type of value computed
|
|
50
|
+
*/
|
|
51
|
+
interface Computed<T> extends Signal<T> {
|
|
52
|
+
/** Marks this signal as computed for type checking purposes */
|
|
53
|
+
readonly isComputed: true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The return type of an effect function.
|
|
58
|
+
* Can be void or a cleanup function that runs when the effect is disposed.
|
|
59
|
+
*/
|
|
60
|
+
type EffectCleanup = void | (() => void);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a reactive signal that can be read, written, and subscribed to.
|
|
64
|
+
* @template T - The type of value stored in the signal
|
|
65
|
+
* @param {T} initialValue - The initial value of the signal
|
|
66
|
+
* @returns {Signal<T>} A signal object with read, set, update, peek, and subscribe methods
|
|
67
|
+
* @example
|
|
68
|
+
* const count = signal(0);
|
|
69
|
+
* console.log(count()); // 0
|
|
70
|
+
* count.set(5); // Update value
|
|
71
|
+
*/
|
|
72
|
+
declare function signal<T>(initialValue: T): Signal<T>;
|
|
73
|
+
/**
|
|
74
|
+
* Automatically runs a function when its signal dependencies change.
|
|
75
|
+
* @param {() => EffectCleanup} fn - The effect function to run. Can optionally return a cleanup function.
|
|
76
|
+
* @returns {() => void} A dispose function to stop the effect and clean up
|
|
77
|
+
* @example
|
|
78
|
+
* const count = signal(0);
|
|
79
|
+
* effect(() => {
|
|
80
|
+
* console.log('Count:', count());
|
|
81
|
+
* return () => console.log('Cleaning up');
|
|
82
|
+
* });
|
|
83
|
+
*/
|
|
84
|
+
declare function effect(fn: () => EffectCleanup): () => void;
|
|
85
|
+
/**
|
|
86
|
+
* Creates a computed signal that automatically updates when its dependencies change.
|
|
87
|
+
* The computation result is cached and only recomputed when dependencies change.
|
|
88
|
+
* @template T - The type of value computed
|
|
89
|
+
* @param {() => T} fn - The computation function
|
|
90
|
+
* @returns {Computed<T>} A read-only computed signal
|
|
91
|
+
* @example
|
|
92
|
+
* const count = signal(5);
|
|
93
|
+
* const doubled = computed(() => count() * 2);
|
|
94
|
+
* console.log(doubled()); // 10
|
|
95
|
+
*/
|
|
96
|
+
declare function computed<T>(fn: () => T): Computed<T>;
|
|
97
|
+
/**
|
|
98
|
+
* Groups multiple signal updates together, deferring effect execution until all updates complete.
|
|
99
|
+
* This improves performance by preventing cascading effect runs.
|
|
100
|
+
* @template T - The return type of the function
|
|
101
|
+
* @param {() => T} fn - A function that performs multiple signal updates
|
|
102
|
+
* @returns {T} The return value of the function
|
|
103
|
+
* @example
|
|
104
|
+
* const a = signal(1);
|
|
105
|
+
* const b = signal(2);
|
|
106
|
+
* batch(() => {
|
|
107
|
+
* a.set(10);
|
|
108
|
+
* b.set(20);
|
|
109
|
+
* }); // Effects only run once
|
|
110
|
+
*/
|
|
111
|
+
declare function batch<T>(fn: () => T): T;
|
|
112
|
+
/**
|
|
113
|
+
* Reads signals without creating dependencies on them.
|
|
114
|
+
* Useful for accessing signal values without triggering effect re-runs.
|
|
115
|
+
* @template T - The return type of the function
|
|
116
|
+
* @param {() => T} fn - A function that accesses signals
|
|
117
|
+
* @returns {T} The return value of the function
|
|
118
|
+
* @example
|
|
119
|
+
* const count = signal(0);
|
|
120
|
+
* effect(() => {
|
|
121
|
+
* const value = untrack(() => count()); // Won't trigger re-run
|
|
122
|
+
* console.log(value);
|
|
123
|
+
* });
|
|
124
|
+
*/
|
|
125
|
+
declare function untrack<T>(fn: () => T): T;
|
|
126
|
+
/**
|
|
127
|
+
* Runs an effect only when a specific signal changes, providing both new and previous values.
|
|
128
|
+
* @template T - The type of the signal
|
|
129
|
+
* @param {Signal<T>} sig - The signal to watch
|
|
130
|
+
* @param {(value: T, prevValue: T) => EffectCleanup} fn - Effect function called with new and previous values
|
|
131
|
+
* @returns {() => void} A dispose function to stop watching
|
|
132
|
+
* @example
|
|
133
|
+
* const count = signal(0);
|
|
134
|
+
* on(count, (newVal, oldVal) => {
|
|
135
|
+
* console.log(`Changed from ${oldVal} to ${newVal}`);
|
|
136
|
+
* });
|
|
137
|
+
*/
|
|
138
|
+
declare function on<T>(sig: Signal<T>, fn: (value: T, prevValue: T) => EffectCleanup): () => void;
|
|
139
|
+
/**
|
|
140
|
+
* Creates a store object where each property is a signal.
|
|
141
|
+
* Provides a convenient way to manage multiple related reactive values.
|
|
142
|
+
* @template T - The type of the initial state object
|
|
143
|
+
* @param {T} initialState - An object with initial values
|
|
144
|
+
* @returns {{ [K in keyof T]: Signal<T[K]> }} An object with signals for each property
|
|
145
|
+
* @example
|
|
146
|
+
* const state = store({ count: 0, name: 'John' });
|
|
147
|
+
* console.log(state.count()); // 0
|
|
148
|
+
* state.name.set('Jane');
|
|
149
|
+
*/
|
|
150
|
+
declare function store<T extends Record<string, any>>(initialState: T): {
|
|
151
|
+
[K in keyof T]: Signal<T[K]>;
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Memoizes the result of an expensive computation, caching it indefinitely.
|
|
155
|
+
* Unlike computed, this doesn't depend on reactive signals.
|
|
156
|
+
* @template T - The type of the memoized value
|
|
157
|
+
* @param {() => T} fn - A function that performs the computation
|
|
158
|
+
* @returns {() => T} A function that returns the cached result
|
|
159
|
+
* @example
|
|
160
|
+
* const expensiveCalc = memo(() => {
|
|
161
|
+
* return Array.from({ length: 1000 }).map(expensiveOp);
|
|
162
|
+
* });
|
|
163
|
+
* const result = expensiveCalc(); // Computed only once
|
|
164
|
+
*/
|
|
165
|
+
declare function memo<T>(fn: () => T): () => T;
|
|
166
|
+
/**
|
|
167
|
+
* Creates a root scope for managing effect and computed signal lifecycles.
|
|
168
|
+
* All effects and disposers created within the function are collected and can be cleaned up together.
|
|
169
|
+
* @template T - The return type of the function
|
|
170
|
+
* @param {(dispose: () => void) => T} fn - A function that receives a dispose function
|
|
171
|
+
* @returns {T} The return value of the function
|
|
172
|
+
* @example
|
|
173
|
+
* const dispose = root((dispose) => {
|
|
174
|
+
* effect(() => console.log('Running'));
|
|
175
|
+
* return 42;
|
|
176
|
+
* });
|
|
177
|
+
* dispose(); // Cleans up all effects created in the root
|
|
178
|
+
*/
|
|
179
|
+
declare function root<T>(fn: (dispose: () => void) => T): T;
|
|
180
|
+
/**
|
|
181
|
+
* Development utilities for debugging signal reactivity
|
|
182
|
+
*/
|
|
183
|
+
declare const dev: {
|
|
184
|
+
/**
|
|
185
|
+
* Returns the currently executing effect, or null if no effect is running
|
|
186
|
+
* @returns {(() => void) | null} The current effect function or null
|
|
187
|
+
*/
|
|
188
|
+
getCurrentEffect(): (() => void) | null;
|
|
189
|
+
/**
|
|
190
|
+
* Returns the current batch depth (for debugging nested batch calls)
|
|
191
|
+
* @returns {number} The current batch nesting level
|
|
192
|
+
*/
|
|
193
|
+
getBatchDepth(): number;
|
|
194
|
+
/**
|
|
195
|
+
* Returns the count of effects currently pending in the batch queue
|
|
196
|
+
* @returns {number} The number of batched effects waiting to run
|
|
197
|
+
*/
|
|
198
|
+
getBatchedEffectsCount(): number;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Type guard to check if a value is a signal
|
|
202
|
+
* @template T - The type of value the signal contains
|
|
203
|
+
* @param {any} value - The value to check
|
|
204
|
+
* @returns {boolean} True if the value is a signal
|
|
205
|
+
* @example
|
|
206
|
+
* if (isSignal(myValue)) {
|
|
207
|
+
* console.log(myValue());
|
|
208
|
+
* }
|
|
209
|
+
*/
|
|
210
|
+
declare function isSignal<T>(value: any): value is Signal<T>;
|
|
211
|
+
/**
|
|
212
|
+
* Type guard to check if a value is a computed signal
|
|
213
|
+
* @template T - The type of value the computed signal contains
|
|
214
|
+
* @param {any} value - The value to check
|
|
215
|
+
* @returns {boolean} True if the value is a computed signal
|
|
216
|
+
* @example
|
|
217
|
+
* if (isComputed(myValue)) {
|
|
218
|
+
* console.log('This is a computed signal');
|
|
219
|
+
* }
|
|
220
|
+
*/
|
|
221
|
+
declare function isComputed<T>(value: any): value is Computed<T>;
|
|
222
|
+
declare const _default: {
|
|
223
|
+
signal: typeof signal;
|
|
224
|
+
effect: typeof effect;
|
|
225
|
+
computed: typeof computed;
|
|
226
|
+
batch: typeof batch;
|
|
227
|
+
untrack: typeof untrack;
|
|
228
|
+
on: typeof on;
|
|
229
|
+
store: typeof store;
|
|
230
|
+
memo: typeof memo;
|
|
231
|
+
root: typeof root;
|
|
232
|
+
isSignal: typeof isSignal;
|
|
233
|
+
isComputed: typeof isComputed;
|
|
234
|
+
dev: {
|
|
235
|
+
/**
|
|
236
|
+
* Returns the currently executing effect, or null if no effect is running
|
|
237
|
+
* @returns {(() => void) | null} The current effect function or null
|
|
238
|
+
*/
|
|
239
|
+
getCurrentEffect(): (() => void) | null;
|
|
240
|
+
/**
|
|
241
|
+
* Returns the current batch depth (for debugging nested batch calls)
|
|
242
|
+
* @returns {number} The current batch nesting level
|
|
243
|
+
*/
|
|
244
|
+
getBatchDepth(): number;
|
|
245
|
+
/**
|
|
246
|
+
* Returns the count of effects currently pending in the batch queue
|
|
247
|
+
* @returns {number} The number of batched effects waiting to run
|
|
248
|
+
*/
|
|
249
|
+
getBatchedEffectsCount(): number;
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export { type Computed, type EffectCleanup, type Signal, batch, computed, _default as default, dev, effect, isComputed, isSignal, memo, on, root, signal, store, untrack };
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var i=null,s=null,c=0,d=new Set,a=new Set;function v(n){let e=n,t=new Set;function r(){return i&&t.add(i),e}function o(u){Object.is(e,u)||(e=u,c>0?t.forEach(T=>d.add(T)):t.forEach(T=>T()));}function p(u){o(u(e));}function l(){return e}function x(u){return t.add(u),()=>t.delete(u)}let f=r;return f.set=o,f.update=p,f.peek=l,f.subscribe=x,f}function y(n){let e,t=false,r=()=>{if(t)return;e&&(e(),e=void 0);let p=i;i=r;try{let l=n();typeof l=="function"&&(e=l);}finally{i=p;}};r();let o=()=>{t||(t=true,e&&e());};return s&&s.push(o),o}function b(n){let e=v(void 0);y(()=>{e.set(n());});let t=e;return Object.defineProperty(t,"isComputed",{value:true,writable:false}),t}function g(n){c++;try{return n()}finally{if(c--,c===0){c++,a.clear();try{for(;d.size>0;){let e=Array.from(d);d.clear(),e.forEach(t=>{a.has(t)||(a.add(t),t());});}}finally{c--,a.clear();}}}}function h(n){let e=i;i=null;try{return n()}finally{i=e;}}function E(n,e){let t=n.peek();return y(()=>{let r=n(),o=h(()=>e(r,t));return t=r,o})}function C(n){let e={};for(let t in n)e[t]=v(n[t]);return e}function S(n){let e,t=false;return ()=>{if(t)return e;let r=n();return e=r,t=true,r}}function k(n){let e=[],t=s;s=e;try{return n(()=>{e.forEach(o=>o()),e.length=0,s=t;})}finally{s=t;}}var w={getCurrentEffect(){return i},getBatchDepth(){return c},getBatchedEffectsCount(){return d.size}};function m(n){return typeof n=="function"&&"set"in n&&"update"in n&&"peek"in n}function D(n){return m(n)&&"isComputed"in n}var R={signal:v,effect:y,computed:b,batch:g,untrack:h,on:E,store:C,memo:S,root:k,isSignal:m,isComputed:D,dev:w};export{g as batch,b as computed,R as default,w as dev,y as effect,D as isComputed,m as isSignal,S as memo,E as on,k as root,v as signal,C as store,h as untrack};//# sourceMappingURL=main.js.map
|
|
2
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/main.ts"],"names":["currentEffect","currentRoot","batchDepth","batchedEffects","flushedEffects","signal","initialValue","value","subscribers","read","write","newValue","fn","update","peek","subscribe","sig","effect","cleanup","isDisposed","execute","prevEffect","result","disposer","computed","batch","effects","untrack","on","prevValue","store","initialState","key","memo","cachedValue","hasCachedValue","root","disposers","prevRoot","d","dev","isSignal","isComputed","main_default"],"mappings":"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","file":"main.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/main.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { Signal, EffectCleanup, Computed } from './types';\r\n export type * from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ INIT ════════════════════════════════════════╗\r\n\r\n let currentEffect : (() => void) | null = null;\r\n let currentRoot : (() => void)[] | null = null;\r\n let batchDepth : number = 0;\r\n const batchedEffects : Set<() => void> = new Set<() => void>();\r\n const flushedEffects : Set<() => void> = new Set<() => void>();\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Creates a reactive signal that can be read, written, and subscribed to.\r\n * @template T - The type of value stored in the signal\r\n * @param {T} initialValue - The initial value of the signal\r\n * @returns {Signal<T>} A signal object with read, set, update, peek, and subscribe methods\r\n * @example\r\n * const count = signal(0);\r\n * console.log(count()); // 0\r\n * count.set(5); // Update value\r\n */\r\n export function signal<T>(initialValue: T): Signal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<() => void>();\r\n\r\n function read(): T {\r\n // Track dependency if inside effect\r\n if (currentEffect) {\r\n subscribers.add(currentEffect);\r\n }\r\n return value;\r\n }\r\n\r\n function write(newValue: T): void {\r\n // Only update if value actually changed\r\n if (Object.is(value, newValue)) return;\r\n\r\n value = newValue;\r\n\r\n // Notify all subscribers\r\n if (batchDepth > 0) {\r\n // Batch mode: collect effects\r\n subscribers.forEach(fn => batchedEffects.add(fn));\r\n } else {\r\n // Immediate mode: run effects now\r\n subscribers.forEach(fn => fn());\r\n }\r\n }\r\n\r\n function update(fn: (prev: T) => T): void {\r\n write(fn(value));\r\n }\r\n\r\n function peek(): T {\r\n // Read without tracking\r\n return value;\r\n }\r\n\r\n function subscribe(fn: () => void): () => void {\r\n subscribers.add(fn);\r\n return () => subscribers.delete(fn);\r\n }\r\n\r\n // Create signal function with methods\r\n const sig = read as Signal<T>;\r\n sig.set = write;\r\n sig.update = update;\r\n sig.peek = peek;\r\n sig.subscribe = subscribe;\r\n\r\n return sig;\r\n }\r\n\r\n /**\r\n * Automatically runs a function when its signal dependencies change.\r\n * @param {() => EffectCleanup} fn - The effect function to run. Can optionally return a cleanup function.\r\n * @returns {() => void} A dispose function to stop the effect and clean up\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * console.log('Count:', count());\r\n * return () => console.log('Cleaning up');\r\n * });\r\n */\r\n export function effect(fn: () => EffectCleanup): () => void {\r\n let cleanup: (() => void) | undefined;\r\n let isDisposed = false;\r\n\r\n const execute = () => {\r\n if (isDisposed) return;\r\n\r\n // Run cleanup from previous execution\r\n if (cleanup) {\r\n cleanup();\r\n cleanup = undefined;\r\n }\r\n\r\n // Set as current effect for dependency tracking\r\n const prevEffect = currentEffect;\r\n currentEffect = execute;\r\n\r\n try {\r\n // Run the effect function\r\n const result = fn();\r\n\r\n // Store cleanup if returned\r\n if (typeof result === 'function') {\r\n cleanup = result;\r\n }\r\n } finally {\r\n // Restore previous effect\r\n currentEffect = prevEffect;\r\n }\r\n };\r\n\r\n // Run immediately\r\n execute();\r\n\r\n // Create dispose function\r\n const disposer = () => {\r\n if (isDisposed) return;\r\n isDisposed = true;\r\n if (cleanup) cleanup();\r\n };\r\n\r\n // Register with current root if one exists\r\n if (currentRoot) {\r\n currentRoot.push(disposer);\r\n }\r\n\r\n // Return dispose function\r\n return disposer;\r\n }\r\n\r\n /**\r\n * Creates a computed signal that automatically updates when its dependencies change.\r\n * The computation result is cached and only recomputed when dependencies change.\r\n * @template T - The type of value computed\r\n * @param {() => T} fn - The computation function\r\n * @returns {Computed<T>} A read-only computed signal\r\n * @example\r\n * const count = signal(5);\r\n * const doubled = computed(() => count() * 2);\r\n * console.log(doubled()); // 10\r\n */\r\n export function computed<T>(fn: () => T): Computed<T> {\r\n const sig = signal<T>(undefined as T);\r\n\r\n // Create effect that updates the signal\r\n effect(() => {\r\n sig.set(fn());\r\n });\r\n\r\n // Mark as computed\r\n const computed = sig as Computed<T>;\r\n Object.defineProperty(computed, 'isComputed', {\r\n value: true,\r\n writable: false\r\n });\r\n\r\n return computed;\r\n }\r\n\r\n /**\r\n * Groups multiple signal updates together, deferring effect execution until all updates complete.\r\n * This improves performance by preventing cascading effect runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that performs multiple signal updates\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const a = signal(1);\r\n * const b = signal(2);\r\n * batch(() => {\r\n * a.set(10);\r\n * b.set(20);\r\n * }); // Effects only run once\r\n */\r\n export function batch<T>(fn: () => T): T {\r\n batchDepth++;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n batchDepth--;\r\n\r\n // If we're back at depth 0, flush batched effects\r\n if (batchDepth === 0) {\r\n // Keep batch mode active while flushing to prevent cascading effects\r\n batchDepth++;\r\n flushedEffects.clear();\r\n try {\r\n // Keep running effects until no more are queued\r\n while (batchedEffects.size > 0) {\r\n const effects = Array.from(batchedEffects);\r\n batchedEffects.clear();\r\n effects.forEach(fn => {\r\n // Only run if we haven't run it in this batch\r\n if (!flushedEffects.has(fn)) {\r\n flushedEffects.add(fn);\r\n fn();\r\n }\r\n });\r\n }\r\n } finally {\r\n batchDepth--;\r\n flushedEffects.clear();\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Reads signals without creating dependencies on them.\r\n * Useful for accessing signal values without triggering effect re-runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that accesses signals\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * const value = untrack(() => count()); // Won't trigger re-run\r\n * console.log(value);\r\n * });\r\n */\r\n export function untrack<T>(fn: () => T): T {\r\n const prevEffect = currentEffect;\r\n currentEffect = null;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n currentEffect = prevEffect;\r\n }\r\n }\r\n\r\n /**\r\n * Runs an effect only when a specific signal changes, providing both new and previous values.\r\n * @template T - The type of the signal\r\n * @param {Signal<T>} sig - The signal to watch\r\n * @param {(value: T, prevValue: T) => EffectCleanup} fn - Effect function called with new and previous values\r\n * @returns {() => void} A dispose function to stop watching\r\n * @example\r\n * const count = signal(0);\r\n * on(count, (newVal, oldVal) => {\r\n * console.log(`Changed from ${oldVal} to ${newVal}`);\r\n * });\r\n */\r\n export function on<T>(\r\n sig: Signal<T>,\r\n fn: (value: T, prevValue: T) => EffectCleanup\r\n ): () => void {\r\n let prevValue = sig.peek();\r\n\r\n return effect(() => {\r\n // Read the signal to create dependency\r\n const value = sig();\r\n\r\n // Run callback without tracking new dependencies\r\n const cleanup = untrack(() => fn(value, prevValue));\r\n prevValue = value;\r\n return cleanup;\r\n });\r\n }\r\n\r\n /**\r\n * Creates a store object where each property is a signal.\r\n * Provides a convenient way to manage multiple related reactive values.\r\n * @template T - The type of the initial state object\r\n * @param {T} initialState - An object with initial values\r\n * @returns {{ [K in keyof T]: Signal<T[K]> }} An object with signals for each property\r\n * @example\r\n * const state = store({ count: 0, name: 'John' });\r\n * console.log(state.count()); // 0\r\n * state.name.set('Jane');\r\n */\r\n export function store<T extends Record<string, any>>(\r\n initialState: T\r\n ): { [K in keyof T]: Signal<T[K]> } {\r\n const store = {} as any;\r\n\r\n for (const key in initialState) {\r\n store[key] = signal(initialState[key]);\r\n }\r\n\r\n return store;\r\n }\r\n\r\n /**\r\n * Memoizes the result of an expensive computation, caching it indefinitely.\r\n * Unlike computed, this doesn't depend on reactive signals.\r\n * @template T - The type of the memoized value\r\n * @param {() => T} fn - A function that performs the computation\r\n * @returns {() => T} A function that returns the cached result\r\n * @example\r\n * const expensiveCalc = memo(() => {\r\n * return Array.from({ length: 1000 }).map(expensiveOp);\r\n * });\r\n * const result = expensiveCalc(); // Computed only once\r\n */\r\n export function memo<T>(fn: () => T): () => T {\r\n let cachedValue: T | undefined;\r\n let hasCachedValue = false;\r\n\r\n return () => {\r\n if (hasCachedValue) {\r\n return cachedValue as T;\r\n }\r\n\r\n // Compute the value\r\n const value = fn();\r\n\r\n // Cache it\r\n cachedValue = value;\r\n hasCachedValue = true;\r\n\r\n return value;\r\n };\r\n }\r\n\r\n /**\r\n * Creates a root scope for managing effect and computed signal lifecycles.\r\n * All effects and disposers created within the function are collected and can be cleaned up together.\r\n * @template T - The return type of the function\r\n * @param {(dispose: () => void) => T} fn - A function that receives a dispose function\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const dispose = root((dispose) => {\r\n * effect(() => console.log('Running'));\r\n * return 42;\r\n * });\r\n * dispose(); // Cleans up all effects created in the root\r\n */\r\n export function root<T>(fn: (dispose: () => void) => T): T {\r\n const disposers: (() => void)[] = [];\r\n const prevRoot = currentRoot;\r\n currentRoot = disposers;\r\n\r\n try {\r\n const dispose = () => {\r\n disposers.forEach(d => d());\r\n disposers.length = 0;\r\n currentRoot = prevRoot;\r\n };\r\n\r\n return fn(dispose);\r\n } finally {\r\n currentRoot = prevRoot;\r\n }\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ HELP ════════════════════════════════════════╗\r\n\r\n /**\r\n * Development utilities for debugging signal reactivity\r\n */\r\n export const dev = {\r\n /**\r\n * Returns the currently executing effect, or null if no effect is running\r\n * @returns {(() => void) | null} The current effect function or null\r\n */\r\n getCurrentEffect(): (() => void) | null {\r\n return currentEffect;\r\n },\r\n\r\n /**\r\n * Returns the current batch depth (for debugging nested batch calls)\r\n * @returns {number} The current batch nesting level\r\n */\r\n getBatchDepth(): number {\r\n return batchDepth;\r\n },\r\n\r\n /**\r\n * Returns the count of effects currently pending in the batch queue\r\n * @returns {number} The number of batched effects waiting to run\r\n */\r\n getBatchedEffectsCount(): number {\r\n return batchedEffects.size;\r\n }\r\n };\r\n\r\n /**\r\n * Type guard to check if a value is a signal\r\n * @template T - The type of value the signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a signal\r\n * @example\r\n * if (isSignal(myValue)) {\r\n * console.log(myValue());\r\n * }\r\n */\r\n export function isSignal<T>(value: any): value is Signal<T> {\r\n return (\r\n typeof value === 'function' &&\r\n 'set' in value &&\r\n 'update' in value &&\r\n 'peek' in value\r\n );\r\n }\r\n\r\n /**\r\n * Type guard to check if a value is a computed signal\r\n * @template T - The type of value the computed signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a computed signal\r\n * @example\r\n * if (isComputed(myValue)) {\r\n * console.log('This is a computed signal');\r\n * }\r\n */\r\n export function isComputed<T>(value: any): value is Computed<T> {\r\n return isSignal(value) && 'isComputed' in value;\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export default {\r\n signal,\r\n effect,\r\n computed,\r\n batch,\r\n untrack,\r\n on,\r\n store,\r\n memo,\r\n root,\r\n isSignal,\r\n isComputed,\r\n dev\r\n };\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@minejs/signals",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A lightweight, zero-dependency signals library for reactive JavaScript applications",
|
|
5
|
+
"keywords": ["minejs", "signals"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/minejs-org/signals#readme",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/minejs-org/signals/issues"
|
|
10
|
+
},
|
|
11
|
+
"author": {
|
|
12
|
+
"name": "Maysara",
|
|
13
|
+
"email": "maysara.elshewehy@gmail.com",
|
|
14
|
+
"url": "https://github.com/maysara-elshewehy"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/minejs-org/signals.git"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "./dist/main.js",
|
|
22
|
+
"types": "./dist/main.d.ts",
|
|
23
|
+
"files": ["dist"],
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/main.d.ts",
|
|
27
|
+
"import": "./dist/main.js",
|
|
28
|
+
"require": "./dist/main.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"lint": "eslint src --ext .ts",
|
|
34
|
+
"test": "bun test"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"bun": ">=1.3.3"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"bun": ">=1.3.3"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@eslint/js": "^9.34.0",
|
|
44
|
+
"@stylistic/eslint-plugin": "^5.3.1",
|
|
45
|
+
"@types/bun": "^1.1.13",
|
|
46
|
+
"@types/node": "^20.12.7",
|
|
47
|
+
"bun-plugin-dts": "^0.3.0",
|
|
48
|
+
"bun-types": "^1.1.38",
|
|
49
|
+
"ts-node": "^10.9.2",
|
|
50
|
+
"tsup": "^8.5.1",
|
|
51
|
+
"typescript": "^5.4.5",
|
|
52
|
+
"typescript-eslint": "^8.42.0"
|
|
53
|
+
}
|
|
54
|
+
}
|