fusee-framework 1.4.1 → 1.5.0
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/README.md +22 -4
- package/app/routes/TestFor.js +2 -6
- package/auto-imports.d.ts +3 -0
- package/framework/auto-import-preset.js +1 -1
- package/framework/core/component.js +5 -0
- package/framework/core/signal.js +186 -22
- package/framework/core/store.js +81 -0
- package/framework/index.js +2 -1
- package/framework/types/index.d.ts +2 -0
- package/framework/types/signal.d.ts +15 -0
- package/framework/types/store.d.ts +8 -0
- package/package.json +6 -4
- package/vite.config.js +5 -0
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Fusée Framework 🚀
|
|
2
|
-
**v1.
|
|
2
|
+
**v1.5.0 — Signals-First JS Framework | Atomic Reactivity | Peak Performance**
|
|
3
3
|
|
|
4
4
|
Fusée is a custom, high-performance fine-grained reactive JavaScript framework built for speed and simplicity. It features a recursive, non-greedy compiler, a signals-based reactivity engine, Dependency Injection for components and a comprehensive CLI for instant application scaffolding.
|
|
5
5
|
|
|
@@ -76,10 +76,28 @@ export const Welcome = defineComponent({
|
|
|
76
76
|
|
|
77
77
|
---
|
|
78
78
|
|
|
79
|
+
## ✅ Quality Assurance & Testing
|
|
80
|
+
|
|
81
|
+
Fusée is built with a test-driven mindset to ensure the reliability of its reactivity engine and component lifecycle.
|
|
82
|
+
|
|
83
|
+
- **Total Tests:** `200`
|
|
84
|
+
- **Status:** `All Passed`
|
|
85
|
+
- **Framework:** [Vitest](https://vitest.dev/)
|
|
86
|
+
- **Environment:** JSDOM (Browser simulation)
|
|
87
|
+
|
|
88
|
+
### Test Coverage includes:
|
|
89
|
+
* **Reactivity Engine:** 100% (Signals, Batching, Computed, Watchers).
|
|
90
|
+
* **Component System:** Props, Slots, Async Components, and Lifecycle Hooks.
|
|
91
|
+
* **Dependency Injection:** Nested Provide/Inject and Shadowing.
|
|
92
|
+
* **Directives:** `f-if`, `f-for`, `f-model`, and Event Modifiers.
|
|
93
|
+
* **Memory Safety:** Explicit checks for memory leaks and automatic effect disposal.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
79
97
|
## 🚀 Future Roadmap
|
|
80
98
|
|
|
81
|
-
- [ ]
|
|
82
|
-
- [ ]
|
|
83
|
-
- [ ] Integrated
|
|
99
|
+
- [ ] Advanced file-based routing support and state management
|
|
100
|
+
- [ ] Lightweight compiler which transforms declarative HTML into optimized runtime instructions
|
|
101
|
+
- [ ] Integrated backend development support
|
|
84
102
|
|
|
85
103
|
Built with ❤️ by me, Sebi Somu, a forward-thinking JavaScript Architect.
|
package/app/routes/TestFor.js
CHANGED
|
@@ -6,10 +6,8 @@ export const TestFor = defineComponent({
|
|
|
6
6
|
{ id: 3, name: 'Item 3' }
|
|
7
7
|
])
|
|
8
8
|
const search = signal('')
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// This returns a Computed signal that updates automatically.
|
|
12
|
-
const filteredItems = items.filter(item =>
|
|
9
|
+
|
|
10
|
+
const filteredItems = items.filter(item =>
|
|
13
11
|
item.name.toLowerCase().includes(search().toLowerCase())
|
|
14
12
|
)
|
|
15
13
|
|
|
@@ -19,11 +17,9 @@ export const TestFor = defineComponent({
|
|
|
19
17
|
items,
|
|
20
18
|
search,
|
|
21
19
|
filteredItems,
|
|
22
|
-
// 🔗 Chaining test: filter -> map -> join
|
|
23
20
|
namesSummary: filteredItems.map(i => i.name).join(', '),
|
|
24
21
|
|
|
25
22
|
updateItem: (id, newName) => {
|
|
26
|
-
// We update the SOURCE signal so the .filter() can see the new values!
|
|
27
23
|
items(items().map(i => i.id === id ? { ...i, name: newName } : i))
|
|
28
24
|
},
|
|
29
25
|
addItem: () => {
|
package/auto-imports.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ declare global {
|
|
|
9
9
|
const batch: typeof import('fusee-framework').batch
|
|
10
10
|
const computed: typeof import('fusee-framework').computed
|
|
11
11
|
const defineComponent: typeof import('fusee-framework').defineComponent
|
|
12
|
+
const defineStore: typeof import('fusee-framework').defineStore
|
|
12
13
|
const directive: typeof import('fusee-framework').directive
|
|
13
14
|
const effect: typeof import('fusee-framework').effect
|
|
14
15
|
const emit: typeof import('fusee-framework').emit
|
|
@@ -21,4 +22,6 @@ declare global {
|
|
|
21
22
|
const provide: typeof import('fusee-framework').provide
|
|
22
23
|
const signal: typeof import('fusee-framework').signal
|
|
23
24
|
const untrack: typeof import('fusee-framework').untrack
|
|
25
|
+
const watch: typeof import('fusee-framework').watch
|
|
26
|
+
const watchEffect: typeof import('fusee-framework').watchEffect
|
|
24
27
|
}
|
|
@@ -11,6 +11,10 @@ export function getCurrentInstance() {
|
|
|
11
11
|
return currentInstance
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export function setCurrentInstance(instance) {
|
|
15
|
+
currentInstance = instance
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
function resolveProps(schema, received) {
|
|
15
19
|
const isArray = Array.isArray(schema)
|
|
16
20
|
const resolved = {}
|
|
@@ -139,6 +143,7 @@ export function defineComponent(options) {
|
|
|
139
143
|
|
|
140
144
|
currentInstance = instance
|
|
141
145
|
const result = options.setup(instance.props, { emit, slots })
|
|
146
|
+
instance.state = result
|
|
142
147
|
result._instance = instance
|
|
143
148
|
currentInstance = null
|
|
144
149
|
|
package/framework/core/signal.js
CHANGED
|
@@ -59,17 +59,33 @@ export function signal(initialValue) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export function effect(fn) {
|
|
62
|
+
let active = true
|
|
63
|
+
let running = false
|
|
64
|
+
|
|
62
65
|
const run = () => {
|
|
66
|
+
if (!active || running) {
|
|
67
|
+
if (running) console.warn('[framework] Recursive effect detected')
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
running = true
|
|
63
72
|
for (const dep of run.deps) dep.delete(run)
|
|
64
73
|
run.deps.clear()
|
|
65
74
|
|
|
75
|
+
const prevEffect = currentEffect
|
|
66
76
|
currentEffect = run
|
|
67
77
|
try { fn() }
|
|
68
|
-
finally {
|
|
78
|
+
finally {
|
|
79
|
+
currentEffect = prevEffect
|
|
80
|
+
running = false
|
|
81
|
+
}
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
run.deps = new Set()
|
|
85
|
+
|
|
72
86
|
const cleanup = () => {
|
|
87
|
+
active = false
|
|
88
|
+
pendingEffects.delete(run)
|
|
73
89
|
for (const dep of run.deps) dep.delete(run)
|
|
74
90
|
run.deps.clear()
|
|
75
91
|
}
|
|
@@ -83,53 +99,80 @@ export function effect(fn) {
|
|
|
83
99
|
export function computed(fn) {
|
|
84
100
|
let value
|
|
85
101
|
let dirty = true
|
|
102
|
+
let active = true
|
|
86
103
|
const subscribers = new Set()
|
|
104
|
+
const deps = new Set()
|
|
87
105
|
|
|
88
106
|
const computedNode = () => {
|
|
89
|
-
if (!dirty)
|
|
107
|
+
if (!active || dirty) return
|
|
108
|
+
|
|
109
|
+
if (subscribers.size > 0) {
|
|
110
|
+
if (!runRecompute()) return
|
|
111
|
+
} else {
|
|
90
112
|
dirty = true
|
|
91
|
-
for (const
|
|
92
|
-
|
|
93
|
-
|
|
113
|
+
for (const d of deps) d.delete(computedNode)
|
|
114
|
+
deps.clear()
|
|
115
|
+
return
|
|
94
116
|
}
|
|
117
|
+
|
|
118
|
+
for (const sub of [...subscribers]) scheduleEffect(sub)
|
|
95
119
|
}
|
|
96
120
|
|
|
97
|
-
computedNode.deps =
|
|
121
|
+
computedNode.deps = deps
|
|
122
|
+
|
|
123
|
+
let computing = false
|
|
124
|
+
|
|
125
|
+
function runRecompute() {
|
|
126
|
+
if (computing) {
|
|
127
|
+
console.warn('[framework] Circular dependency detected in computed()')
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
computing = true
|
|
132
|
+
for (const d of deps) d.delete(computedNode)
|
|
133
|
+
deps.clear()
|
|
134
|
+
|
|
135
|
+
const prevEffect = currentEffect
|
|
136
|
+
currentEffect = active ? computedNode : null
|
|
137
|
+
try {
|
|
138
|
+
const newValue = fn()
|
|
139
|
+
const changed = !Object.is(newValue, value)
|
|
140
|
+
value = newValue
|
|
141
|
+
dirty = false
|
|
142
|
+
return changed
|
|
143
|
+
} finally {
|
|
144
|
+
currentEffect = prevEffect
|
|
145
|
+
computing = false
|
|
146
|
+
}
|
|
147
|
+
}
|
|
98
148
|
|
|
99
149
|
function accessor() {
|
|
100
150
|
if (arguments.length === 0) {
|
|
101
|
-
if (currentEffect) {
|
|
151
|
+
if (active && currentEffect) {
|
|
102
152
|
subscribers.add(currentEffect)
|
|
103
153
|
currentEffect.deps.add(subscribers)
|
|
104
154
|
}
|
|
155
|
+
|
|
105
156
|
if (dirty) {
|
|
106
|
-
|
|
107
|
-
computedNode.deps.clear()
|
|
108
|
-
|
|
109
|
-
const prevEffect = currentEffect
|
|
110
|
-
currentEffect = computedNode
|
|
111
|
-
try {
|
|
112
|
-
const newValue = fn()
|
|
113
|
-
if (newValue !== value) value = newValue
|
|
114
|
-
} finally {
|
|
115
|
-
currentEffect = prevEffect
|
|
116
|
-
dirty = false
|
|
117
|
-
}
|
|
157
|
+
runRecompute()
|
|
118
158
|
}
|
|
159
|
+
|
|
119
160
|
return value
|
|
120
161
|
}
|
|
121
162
|
console.warn('[framework] computed() is read-only')
|
|
122
163
|
}
|
|
123
164
|
|
|
124
165
|
accessor.dispose = () => {
|
|
125
|
-
|
|
126
|
-
|
|
166
|
+
active = false
|
|
167
|
+
dirty = true
|
|
168
|
+
pendingEffects.delete(computedNode)
|
|
169
|
+
for (const d of deps) d.delete(computedNode)
|
|
170
|
+
deps.clear()
|
|
127
171
|
subscribers.clear()
|
|
128
172
|
}
|
|
129
173
|
|
|
130
174
|
accessor.isSignal = true
|
|
131
175
|
accessor.readonly = true
|
|
132
|
-
|
|
133
176
|
addReactiveArrayMethods(accessor)
|
|
134
177
|
|
|
135
178
|
return accessor
|
|
@@ -145,6 +188,82 @@ export function untrack(fn) {
|
|
|
145
188
|
}
|
|
146
189
|
}
|
|
147
190
|
|
|
191
|
+
export function watch(source, callback, options = {}) {
|
|
192
|
+
if (typeof callback !== 'function') {
|
|
193
|
+
console.warn('[framework] watch() callback must be a function')
|
|
194
|
+
return () => { }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const { immediate = false, equals = Object.is } = options
|
|
198
|
+
const { getter, isMultiSource } = normalizeWatchSource(source)
|
|
199
|
+
|
|
200
|
+
let initialized = false
|
|
201
|
+
let oldValue
|
|
202
|
+
const cleanupRef = { fn: null }
|
|
203
|
+
|
|
204
|
+
const onCleanup = (fn) => {
|
|
205
|
+
if (typeof fn !== 'function') {
|
|
206
|
+
console.warn('[framework] onCleanup() expects a function')
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
cleanupRef.fn = fn
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const stopEffect = effect(() => {
|
|
213
|
+
const newValue = getter()
|
|
214
|
+
|
|
215
|
+
if (!initialized) {
|
|
216
|
+
initialized = true
|
|
217
|
+
oldValue = cloneWatchValue(newValue, isMultiSource)
|
|
218
|
+
if (immediate) {
|
|
219
|
+
untrack(() => callback(newValue, undefined, onCleanup))
|
|
220
|
+
}
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!hasWatchChanged(newValue, oldValue, isMultiSource, equals)) {
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const previousValue = oldValue
|
|
229
|
+
oldValue = cloneWatchValue(newValue, isMultiSource)
|
|
230
|
+
|
|
231
|
+
runWatchCleanup(cleanupRef)
|
|
232
|
+
untrack(() => callback(newValue, previousValue, onCleanup))
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
return () => {
|
|
236
|
+
stopEffect()
|
|
237
|
+
runWatchCleanup(cleanupRef)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function watchEffect(fn) {
|
|
242
|
+
if (typeof fn !== 'function') {
|
|
243
|
+
console.warn('[framework] watchEffect() expects a function')
|
|
244
|
+
return () => { }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const cleanupRef = { fn: null }
|
|
248
|
+
const onCleanup = (cleanup) => {
|
|
249
|
+
if (typeof cleanup !== 'function') {
|
|
250
|
+
console.warn('[framework] onCleanup() expects a function')
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
cleanupRef.fn = cleanup
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const stopEffect = effect(() => {
|
|
257
|
+
runWatchCleanup(cleanupRef)
|
|
258
|
+
fn(onCleanup)
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
return () => {
|
|
262
|
+
stopEffect()
|
|
263
|
+
runWatchCleanup(cleanupRef)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
148
267
|
export function inspect(...args) {
|
|
149
268
|
const isDev = typeof import.meta.env !== 'undefined' ? !!import.meta.env.DEV : true
|
|
150
269
|
if (!isDev) return
|
|
@@ -244,3 +363,48 @@ function addMutatingArrayMethods(accessor) {
|
|
|
244
363
|
return accessor
|
|
245
364
|
}
|
|
246
365
|
}
|
|
366
|
+
|
|
367
|
+
// Helpers for watch/watchEffect
|
|
368
|
+
function normalizeWatchSource(source) {
|
|
369
|
+
if (Array.isArray(source)) {
|
|
370
|
+
const getters = source.map(normalizeSingleWatchSource)
|
|
371
|
+
return {
|
|
372
|
+
getter: () => getters.map(get => get()),
|
|
373
|
+
isMultiSource: true
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
getter: normalizeSingleWatchSource(source),
|
|
378
|
+
isMultiSource: false
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function normalizeSingleWatchSource(source) {
|
|
383
|
+
if (typeof source === 'function') return source
|
|
384
|
+
console.warn('[framework] watch() source should be a signal, getter, or an array of those')
|
|
385
|
+
return () => source
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function cloneWatchValue(value, isMultiSource) {
|
|
389
|
+
if (isMultiSource && Array.isArray(value)) return value.slice()
|
|
390
|
+
return value
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function hasWatchChanged(newValue, oldValue, isMultiSource, equals) {
|
|
394
|
+
if (isMultiSource) {
|
|
395
|
+
if (!Array.isArray(oldValue) || newValue.length !== oldValue.length) return true
|
|
396
|
+
for (let i = 0; i < newValue.length; i++) {
|
|
397
|
+
if (!equals(newValue[i], oldValue[i])) return true
|
|
398
|
+
}
|
|
399
|
+
return false
|
|
400
|
+
}
|
|
401
|
+
return !equals(newValue, oldValue)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function runWatchCleanup(cleanupRef) {
|
|
405
|
+
if (typeof cleanupRef.fn === 'function') {
|
|
406
|
+
const fn = cleanupRef.fn
|
|
407
|
+
cleanupRef.fn = null
|
|
408
|
+
untrack(() => fn())
|
|
409
|
+
}
|
|
410
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getCurrentInstance, setCurrentInstance } from './component.js'
|
|
2
|
+
|
|
3
|
+
const storesRegistry = new Map()
|
|
4
|
+
const storePlugins = []
|
|
5
|
+
|
|
6
|
+
export function registerStorePlugin(plugin) {
|
|
7
|
+
if (typeof plugin === 'function') {
|
|
8
|
+
storePlugins.push(plugin)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const isDev = typeof import.meta.env !== 'undefined' ? !!import.meta.env.DEV : true
|
|
13
|
+
if (isDev && typeof window !== 'undefined') {
|
|
14
|
+
window.__FRAMEWORK_STORES__ = storesRegistry
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function defineStore(id, setup) {
|
|
18
|
+
if (typeof id !== 'string' || !id.trim()) {
|
|
19
|
+
console.error('[framework] defineStore requires a valid string ID.')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function useStore() {
|
|
23
|
+
let store = storesRegistry.get(id)
|
|
24
|
+
|
|
25
|
+
if (!store) {
|
|
26
|
+
const prevInstance = getCurrentInstance()
|
|
27
|
+
|
|
28
|
+
setCurrentInstance(null)
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
store = setup()
|
|
32
|
+
|
|
33
|
+
if (store instanceof Promise) {
|
|
34
|
+
console.error(`[framework] Store "${id}" cannot be async.`)
|
|
35
|
+
throw new Error(`Async store "${id}" is not supported.`)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.error(`[framework] Error initializing store "${id}":`, err)
|
|
40
|
+
throw err
|
|
41
|
+
} finally {
|
|
42
|
+
setCurrentInstance(prevInstance)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Object.defineProperties(store, {
|
|
46
|
+
$id: {
|
|
47
|
+
value: id,
|
|
48
|
+
enumerable: false
|
|
49
|
+
},
|
|
50
|
+
$type: {
|
|
51
|
+
value: 'store',
|
|
52
|
+
enumerable: false
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
for (const plugin of storePlugins) {
|
|
57
|
+
try {
|
|
58
|
+
plugin(store, id)
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.warn(`[framework] Store plugin error for "${id}":`, e)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
storesRegistry.set(id, store)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return store
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
useStore.$id = id
|
|
71
|
+
|
|
72
|
+
return useStore
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function resetStore(id) {
|
|
76
|
+
storesRegistry.delete(id)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function clearStores() {
|
|
80
|
+
storesRegistry.clear()
|
|
81
|
+
}
|
package/framework/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// ─── Framework Public API ─────────────────────────────────────────────────────
|
|
2
|
-
export { signal, computed, effect, batch, untrack, inspect } from './core/signal.js'
|
|
2
|
+
export { signal, computed, effect, batch, untrack, inspect, watch, watchEffect } from './core/signal.js'
|
|
3
3
|
export { defineComponent, defineAsyncComponent, onMount, onUnmount, parseSlots, provide, inject, getCurrentInstance } from './core/component.js'
|
|
4
4
|
export { createRouter, navigate, mountOutlet } from './router/router.js'
|
|
5
5
|
export { mountTemplate } from './core/compiler.js'
|
|
6
6
|
|
|
7
7
|
export { defineComposable, assertSetupContext } from './core/composable.js'
|
|
8
|
+
export { defineStore, resetStore, clearStores, registerStorePlugin } from './core/store.js'
|
|
8
9
|
export { directive } from './core/directives.js'
|
|
9
10
|
|
|
10
11
|
export const emit = () => console.warn('[framework] emit() can only be used inside component setup()')
|
|
@@ -4,6 +4,7 @@ export * from './composable'
|
|
|
4
4
|
export * from './router'
|
|
5
5
|
export * from './compiler'
|
|
6
6
|
export * from './directives'
|
|
7
|
+
export * from './store'
|
|
7
8
|
|
|
8
9
|
declare global {
|
|
9
10
|
type Signal<T = any> = import('./signal').Signal<T>
|
|
@@ -12,6 +13,7 @@ declare global {
|
|
|
12
13
|
type Composable<T extends (...args: any[]) => any> = import('./composable').Composable<T>
|
|
13
14
|
type DirectiveBinding<V = any> = import('./directives').DirectiveBinding<V>
|
|
14
15
|
type DirectiveHooks<V = any> = import('./directives').DirectiveHooks<V>
|
|
16
|
+
const defineStore: typeof import('./store').defineStore
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export as namespace Fusee
|
|
@@ -54,12 +54,27 @@ type EffectRunner = {
|
|
|
54
54
|
deps: Set<Set<EffectRunner>>;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
export type WatchSource<T = any> = (() => T) | Signal<T> | Computed<T>
|
|
58
|
+
export type WatchCallback<T = any> = (newValue: T, oldValue: T | undefined, onCleanup: (fn: () => void) => void) => void
|
|
59
|
+
|
|
60
|
+
export interface WatchOptions {
|
|
61
|
+
immediate?: boolean;
|
|
62
|
+
equals?: (a: any, b: any) => boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
export declare function signal<T extends any[]>(initialValue: T): ArraySignalAccessor<T>
|
|
58
66
|
export declare function signal<T>(initialValue: T): SignalAccessor<T>
|
|
59
67
|
export declare function effect(fn: () => void): () => void
|
|
60
68
|
export declare function computed<T>(fn: () => T): Computed<T>
|
|
61
69
|
export declare function batch<T>(fn: () => T): T
|
|
62
70
|
export declare function untrack<T>(fn: () => T): T
|
|
71
|
+
export declare function watch<T>(source: WatchSource<T>, callback: WatchCallback<T>, options?: WatchOptions): () => void
|
|
72
|
+
export declare function watch<T extends any[]>(
|
|
73
|
+
sources: [...{ [K in keyof T]: WatchSource<T[K]> }],
|
|
74
|
+
callback: (newValue: T, oldValue: T | undefined, onCleanup: (fn: () => void) => void) => void,
|
|
75
|
+
options?: WatchOptions
|
|
76
|
+
): () => void
|
|
77
|
+
export declare function watchEffect(fn: (onCleanup: (fn: () => void) => void) => void): () => void
|
|
63
78
|
export declare function inspect(...args: any[]): (() => void) | void
|
|
64
79
|
|
|
65
80
|
declare function setEffectHook(fn: (eff: any) => void): void
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function defineStore<T>(id: string, setup: () => T): () => T;
|
|
2
|
+
export function resetStore(id: string): void;
|
|
3
|
+
export function clearStores(): void;
|
|
4
|
+
export function registerStorePlugin(plugin: (store: any, id: string) => void): void;
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
function defineStore<T>(id: string, setup: () => T): () => T;
|
|
8
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fusee-framework",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Fusée - Signals-first JS Framework | Peak Performance",
|
|
5
5
|
"main": "framework/index.js",
|
|
6
6
|
"types": "framework/types/index.d.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"dev": "vite",
|
|
13
13
|
"build": "vite build",
|
|
14
14
|
"preview": "vite preview",
|
|
15
|
-
"test": "
|
|
15
|
+
"test": "vitest run"
|
|
16
16
|
},
|
|
17
17
|
"keywords": [],
|
|
18
18
|
"author": "",
|
|
@@ -25,8 +25,10 @@
|
|
|
25
25
|
"registry": "https://registry.npmjs.org/"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
+
"jsdom": "^29.0.2",
|
|
28
29
|
"typescript": "^6.0.2",
|
|
29
30
|
"unplugin-auto-import": "^21.0.0",
|
|
30
|
-
"vite": "^5.0.0"
|
|
31
|
+
"vite": "^5.0.0",
|
|
32
|
+
"vitest": "^4.1.4"
|
|
31
33
|
}
|
|
32
|
-
}
|
|
34
|
+
}
|