@fest-lib/object 0.1.19 → 0.1.20
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 +77 -372
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,420 +1,125 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
- [Reactivity](#reactivity)
|
|
20
|
-
- [DOM Utilities](#dom-utilities)
|
|
21
|
-
- [Related Projects](#related-projects)
|
|
22
|
-
- [License](#license)
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Features
|
|
27
|
-
|
|
28
|
-
- **Non-intrusive subscriptions:** Subscribers do not retain references to target objects.
|
|
29
|
-
- **Efficient reactivity:** Triggers only on actual value changes.
|
|
30
|
-
- **Compatibility:** Works with recent versions of popular reactive libraries.
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Usage & Examples
|
|
35
|
-
|
|
36
|
-
### Reactivity (basics)
|
|
37
|
-
|
|
38
|
-
- **`observe(initial)`**
|
|
39
|
-
Creates a reactive primitive from an existing object, array, Set, or Map.
|
|
40
|
-
|
|
41
|
-
- **`affected(obj, callback)`**
|
|
42
|
-
Subscribes to changes in an observable object, `Set`, or `Map`.
|
|
43
|
-
- Supports `[obj, key]` to subscribe to a specific property.
|
|
44
|
-
- Returns an unsubscribe function.
|
|
45
|
-
|
|
46
|
-
- **`ref(initial)`**
|
|
47
|
-
Creates an observable reference with a `value` property.
|
|
48
|
-
|
|
49
|
-
- **`computed(src, cb, behavior, prop)`**
|
|
50
|
-
Creates a computed reactive value from a source.
|
|
51
|
-
- Computes its value using a callback function.
|
|
52
|
-
- Automatically updates when dependencies change.
|
|
53
|
-
|
|
54
|
-
- **`conditional(cond, ifTrue, ifFalse)`**
|
|
55
|
-
Reactive value that switches between `ifTrue` and `ifFalse` based on `cond.value`.
|
|
1
|
+
<p align="center">
|
|
2
|
+
<strong>@fest-lib/object</strong><br>
|
|
3
|
+
Level 1 — observe / affected / refs. LUR.E and FL.UI subscribe through <code>affected</code>.
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://www.npmjs.com/package/@fest-lib/object"><img src="https://img.shields.io/npm/v/@fest-lib/object?style=flat-square" alt="npm"></a>
|
|
8
|
+
<a href="LICENSE"><img src="https://img.shields.io/npm/l/@fest-lib/object?style=flat-square" alt="MIT"></a>
|
|
9
|
+
<a href="https://github.com/fest-live/object.ts"><img src="https://img.shields.io/github/stars/fest-live/object.ts?style=flat-square" alt="stars"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
Reactive proxies for objects, arrays, `Set`, and `Map`. Subscriptions do **not** retain the target. Updates fire only when the value actually changes.
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
core · uniform
|
|
16
|
+
└── fest/object ← you are here
|
|
17
|
+
└── lure · veela · icon · image · fl-ui
|
|
18
|
+
```
|
|
56
19
|
|
|
57
|
-
|
|
20
|
+
## Install
|
|
58
21
|
|
|
59
22
|
```bash
|
|
60
|
-
npm
|
|
23
|
+
npm install @fest-lib/core @fest-lib/uniform @fest-lib/object
|
|
61
24
|
```
|
|
62
25
|
|
|
63
|
-
|
|
26
|
+
Peers: `@fest-lib/core`, `@fest-lib/uniform` (`>=0.1.0`). ESM, Node **20+**.
|
|
64
27
|
|
|
65
28
|
```ts
|
|
66
|
-
import {
|
|
67
|
-
// Core reactivity
|
|
68
|
-
observe,
|
|
69
|
-
affected,
|
|
70
|
-
makeArrayObservable,
|
|
71
|
-
isObservable,
|
|
72
|
-
recoverReactive,
|
|
29
|
+
import { observe, affected, ref, computed } from "@fest-lib/object";
|
|
73
30
|
|
|
74
|
-
// Primitive refs
|
|
75
|
-
ref,
|
|
76
|
-
numberRef,
|
|
77
|
-
stringRef,
|
|
78
|
-
booleanRef,
|
|
79
|
-
autoRef,
|
|
80
|
-
promised,
|
|
81
|
-
|
|
82
|
-
// Computed and derived values
|
|
83
|
-
computed,
|
|
84
|
-
derivate,
|
|
85
|
-
conditional,
|
|
86
|
-
remap,
|
|
87
|
-
unified,
|
|
88
|
-
|
|
89
|
-
// Binding and assignment
|
|
90
|
-
bindBy,
|
|
91
|
-
assign,
|
|
92
|
-
link,
|
|
93
|
-
|
|
94
|
-
// Collection utilities
|
|
95
|
-
observableBySet,
|
|
96
|
-
observableByMap,
|
|
97
|
-
iterated,
|
|
98
|
-
|
|
99
|
-
// Subscription management
|
|
100
|
-
unaffected,
|
|
101
|
-
triggerWithDelay,
|
|
102
|
-
delayedBehavior,
|
|
103
|
-
delayedOrInstantBehavior,
|
|
104
|
-
|
|
105
|
-
// Utilities
|
|
106
|
-
safe,
|
|
107
|
-
deref,
|
|
108
|
-
unwrap,
|
|
109
|
-
propRef,
|
|
110
|
-
|
|
111
|
-
// Legacy (deprecated)
|
|
112
|
-
createReactive,
|
|
113
|
-
createReactiveMap,
|
|
114
|
-
createReactiveSet
|
|
115
|
-
} from "@fest-lib/object";
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Quick start
|
|
119
|
-
|
|
120
|
-
```ts
|
|
121
31
|
const state = observe({ count: 0, user: { name: "Ada" } });
|
|
122
|
-
|
|
123
32
|
const stop = affected(state, (value, prop) => {
|
|
124
|
-
|
|
33
|
+
console.log("changed:", prop, value[prop as keyof typeof value]);
|
|
125
34
|
});
|
|
126
|
-
|
|
127
35
|
state.count = 1;
|
|
128
36
|
stop?.();
|
|
129
37
|
```
|
|
130
38
|
|
|
131
|
-
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- Non-intrusive subscriptions (disposer from `affected`).
|
|
42
|
+
- `observe` adapts `Set` / `Map` so iteration changes notify.
|
|
43
|
+
- Primitive refs with coercion (`numberRef`, `stringRef`, `booleanRef`).
|
|
44
|
+
- `computed` / `derivate` / `conditional` / `propRef`.
|
|
45
|
+
- `bindBy` / `assign` / `link` for shape sync.
|
|
46
|
+
- `safe()` for JSON (cycles, WeakRef).
|
|
47
|
+
|
|
48
|
+
## Primitive refs
|
|
132
49
|
|
|
133
50
|
```ts
|
|
51
|
+
import { numberRef, stringRef, booleanRef, autoRef, promised } from "@fest-lib/object";
|
|
52
|
+
|
|
134
53
|
const n = numberRef(0);
|
|
135
54
|
const s = stringRef("hello");
|
|
136
55
|
const b = booleanRef(false);
|
|
56
|
+
n.value++;
|
|
57
|
+
s.value = `${s}!`;
|
|
58
|
+
b.value = 1; // truthy → true
|
|
137
59
|
|
|
138
|
-
|
|
139
|
-
s.value = s + "!"; // "hello!"
|
|
140
|
-
b.value = 1; // true (truthy coercion)
|
|
60
|
+
const later = promised(fetch("/api").then((r) => r.status));
|
|
141
61
|
```
|
|
142
62
|
|
|
143
|
-
|
|
63
|
+
`autoRef(true)` / `autoRef(42)` / `autoRef("hi")` pick the matching primitive ref.
|
|
144
64
|
|
|
145
|
-
|
|
146
|
-
const r1 = autoRef(true); // booleanRef
|
|
147
|
-
const r2 = autoRef(42); // numberRef
|
|
148
|
-
const r3 = autoRef("hi"); // stringRef
|
|
149
|
-
const r4 = ref<any>({ a: 1 });
|
|
150
|
-
|
|
151
|
-
const later = promised(fetch("/api").then(r => r.status));
|
|
152
|
-
subscribe(later, () => console.log("ready:", later.value));
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Observing collections and properties
|
|
65
|
+
## Collections
|
|
156
66
|
|
|
157
67
|
```ts
|
|
158
68
|
const list = observe([1, 2, 3]);
|
|
159
|
-
const bag
|
|
160
|
-
const map
|
|
161
|
-
|
|
162
|
-
const unAll = affected(list, (v, prop) => console.log("list changed", prop));
|
|
163
|
-
const unBag = affected(bag, (v, prop) => console.log("set changed", prop));
|
|
164
|
-
const unMap = affected(map, (v, prop) => console.log("map changed", prop));
|
|
165
|
-
|
|
166
|
-
const person = observe({ name: "Ada", age: 36 });
|
|
167
|
-
const unName = affected([person, "name"], (v) => console.log("name:", v));
|
|
168
|
-
|
|
169
|
-
person.name = "Grace";
|
|
170
|
-
unAll?.(); unBag?.(); unMap?.(); unName?.();
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Deriving and binding
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
const source = observe({ x: 1, y: 2 });
|
|
177
|
-
|
|
178
|
-
// Read-only derivative
|
|
179
|
-
const sum = derivate(source, s => ({ sum: s.x + s.y }));
|
|
180
|
-
affected(sum, () => console.log("sum:", sum.sum));
|
|
181
|
-
|
|
182
|
-
// Two-way bind by shape
|
|
183
|
-
const target: any = { x: 0, y: 0 };
|
|
184
|
-
bindBy(target, source);
|
|
69
|
+
const bag = observe(new Set(["a", "b"]));
|
|
70
|
+
const map = observe(new Map([["x", 1]]));
|
|
185
71
|
|
|
186
|
-
|
|
72
|
+
affected(list, (_v, prop) => console.log("list", prop));
|
|
73
|
+
affected([observe({ name: "Ada" }), "name"], (v) => console.log("name:", v));
|
|
187
74
|
```
|
|
188
75
|
|
|
189
|
-
|
|
76
|
+
`observableBySet` / `observableByMap` expose a reactive array view. `iterated` subscribes to iteration.
|
|
190
77
|
|
|
191
|
-
|
|
192
|
-
const complex = observe({ d: new Date(), w: new WeakRef({ a: 1 }) });
|
|
193
|
-
JSON.stringify(safe(complex));
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### Computed values
|
|
78
|
+
## Derived & bind
|
|
197
79
|
|
|
198
80
|
```ts
|
|
199
|
-
const
|
|
200
|
-
const
|
|
81
|
+
const source = observe({ x: 1, y: 2 });
|
|
82
|
+
const total = computed(source, (s) => s.x + s.y);
|
|
83
|
+
const view = derivate(source, (s) => ({ sum: s.x + s.y }));
|
|
201
84
|
|
|
202
|
-
|
|
203
|
-
|
|
85
|
+
const target = { x: 0, y: 0 };
|
|
86
|
+
bindBy(target, source); // two-way by shape
|
|
87
|
+
source.x = 3; // target.x === 3
|
|
204
88
|
```
|
|
205
89
|
|
|
206
|
-
### Conditional reactivity
|
|
207
|
-
|
|
208
90
|
```ts
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
affected(result, () => console.log("result:", result.value));
|
|
213
|
-
condition.value = false; // triggers: result: no
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Property references
|
|
91
|
+
const cond = booleanRef(true);
|
|
92
|
+
const pick = conditional(cond, "yes", "no");
|
|
93
|
+
cond.value = false; // pick.value === "no"
|
|
217
94
|
|
|
218
|
-
```ts
|
|
219
95
|
const obj = observe({ nested: { value: 42 } });
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
affected(propRef, () => console.log("nested value:", propRef.value));
|
|
223
|
-
obj.nested.value = 100; // triggers: nested value: 100
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### Collection observables
|
|
227
|
-
|
|
228
|
-
```ts
|
|
229
|
-
const set = observe(new Set([1, 2, 3]));
|
|
230
|
-
const arrayFromSet = observableBySet(set);
|
|
231
|
-
|
|
232
|
-
affected(arrayFromSet, () => console.log("set as array:", arrayFromSet));
|
|
233
|
-
set.add(4); // triggers: set as array: [1, 2, 3, 4]
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Notes
|
|
237
|
-
|
|
238
|
-
- Subscriptions fire only on actual changes.
|
|
239
|
-
- `observe` adapts `Set`/`Map` to emit iteration changes.
|
|
240
|
-
- To stop listening, keep the disposer returned by `affected` and call it.
|
|
241
|
-
|
|
242
|
-
## API Reference
|
|
243
|
-
|
|
244
|
-
### Core Reactivity
|
|
245
|
-
|
|
246
|
-
#### `observe<T>(target: T): observeValid<T>`
|
|
247
|
-
|
|
248
|
-
Creates a reactive proxy from an object, array, Set, or Map. Changes to the reactive object will trigger subscriptions.
|
|
249
|
-
|
|
250
|
-
```ts
|
|
251
|
-
const obj = observe({ count: 0 });
|
|
252
|
-
const arr = observe([1, 2, 3]);
|
|
253
|
-
const set = observe(new Set([1, 2]));
|
|
96
|
+
const deep = propRef(obj, "nested.value");
|
|
254
97
|
```
|
|
255
98
|
|
|
256
|
-
|
|
99
|
+
## API map
|
|
257
100
|
|
|
258
|
-
|
|
101
|
+
| Group | Exports |
|
|
102
|
+
| --- | --- |
|
|
103
|
+
| Reactivity | `observe`, `affected`, `isObservable`, `recoverReactive`, `makeArrayObservable` |
|
|
104
|
+
| Refs | `ref`, `numberRef`, `stringRef`, `booleanRef`, `autoRef`, `promised`, `propRef` |
|
|
105
|
+
| Derived | `computed`, `derivate`, `conditional`, `remap`, `unified` |
|
|
106
|
+
| Bind | `bindBy`, `assign`, `link` |
|
|
107
|
+
| Collections | `observableBySet`, `observableByMap`, `iterated` |
|
|
108
|
+
| Timing | `triggerWithDelay`, `delayedBehavior`, `delayedOrInstantBehavior` |
|
|
109
|
+
| Utils | `safe`, `deref`, `unwrap`, `unaffected` |
|
|
110
|
+
| Legacy | `createReactive`, `createReactiveMap`, `createReactiveSet` (deprecated) |
|
|
259
111
|
|
|
260
|
-
|
|
261
|
-
const state = observe({ count: 0 });
|
|
262
|
-
const unsubscribe = affected(state, (value, prop, old) => {
|
|
263
|
-
console.log(`${prop} changed from ${old} to ${value[prop]}`);
|
|
264
|
-
});
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
#### `isObservable(obj): boolean`
|
|
268
|
-
|
|
269
|
-
Checks if an object is already reactive.
|
|
270
|
-
|
|
271
|
-
#### `recoverReactive(obj): observeValid | null`
|
|
272
|
-
|
|
273
|
-
Attempts to recover the reactive version of an object.
|
|
274
|
-
|
|
275
|
-
### Reactive References
|
|
276
|
-
|
|
277
|
-
#### `ref<T>(initial: T): Ref<T>`
|
|
278
|
-
|
|
279
|
-
Creates a reactive reference with a `.value` property.
|
|
280
|
-
|
|
281
|
-
#### `numberRef(initial?: number): NumberRef`
|
|
282
|
-
|
|
283
|
-
Creates a reactive number reference with type coercion.
|
|
284
|
-
|
|
285
|
-
#### `stringRef(initial?: string): StringRef`
|
|
286
|
-
|
|
287
|
-
Creates a reactive string reference with type coercion.
|
|
288
|
-
|
|
289
|
-
#### `booleanRef(initial?: boolean): BooleanRef`
|
|
290
|
-
|
|
291
|
-
Creates a reactive boolean reference with truthy coercion.
|
|
292
|
-
|
|
293
|
-
#### `autoRef(initial: any): Ref`
|
|
294
|
-
|
|
295
|
-
Automatically creates the appropriate ref type based on the initial value.
|
|
296
|
-
|
|
297
|
-
#### `promised<T>(promise: Promise<T>): PromisedRef<T>`
|
|
112
|
+
`affected(obj, cb)` or `affected([obj, key], cb)` → unsubscribe function. Keep it and call it.
|
|
298
113
|
|
|
299
|
-
|
|
114
|
+
Sources: `src/core/Mainline.ts` (subscribe), `Primitives.ts`, `Assigned.ts`, `Subscript.ts`, `src/wrap/*`.
|
|
300
115
|
|
|
301
|
-
|
|
116
|
+
## Workspace
|
|
302
117
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const state = observe({ a: 1, b: 2 });
|
|
309
|
-
const sum = computed(state, s => s.a + s.b);
|
|
118
|
+
```bash
|
|
119
|
+
cd modules/projects/object.ts
|
|
120
|
+
npm test # node + deno + browser
|
|
121
|
+
npm run build
|
|
122
|
+
npm run publish
|
|
310
123
|
```
|
|
311
124
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
Creates a derived reactive object from a source.
|
|
315
|
-
|
|
316
|
-
#### `conditional<T>(cond, ifTrue, ifFalse): ConditionalRef<T>`
|
|
317
|
-
|
|
318
|
-
Creates a reactive value that switches between two values based on a condition.
|
|
319
|
-
|
|
320
|
-
### Binding & Assignment
|
|
321
|
-
|
|
322
|
-
#### `bindBy(target, reactive, watch?)`
|
|
323
|
-
|
|
324
|
-
Two-way binds a target object to a reactive source.
|
|
325
|
-
|
|
326
|
-
#### `assign<T>(a, b, prop?): UnsubscribeFn`
|
|
327
|
-
|
|
328
|
-
Assigns reactive values between objects with automatic synchronization.
|
|
329
|
-
|
|
330
|
-
#### `link<T>(a, b, prop?): UnsubscribeFn`
|
|
331
|
-
|
|
332
|
-
Creates a bidirectional link between two reactive values.
|
|
333
|
-
|
|
334
|
-
### Collection Utilities
|
|
335
|
-
|
|
336
|
-
#### `observableBySet<T>(set: Set<T>): T[]`
|
|
337
|
-
|
|
338
|
-
Converts a reactive Set to a reactive array.
|
|
339
|
-
|
|
340
|
-
#### `observableByMap<K, V>(map: Map<K, V>): [K, V][]`
|
|
341
|
-
|
|
342
|
-
Converts a reactive Map to a reactive array of key-value pairs.
|
|
343
|
-
|
|
344
|
-
#### `iterated<T>(target, cb, ctx?): UnsubscribeFn`
|
|
345
|
-
|
|
346
|
-
Subscribes to iteration changes on collections.
|
|
347
|
-
|
|
348
|
-
#### `makeArrayObservable(target): observeValid`
|
|
349
|
-
|
|
350
|
-
Makes arrays observable.
|
|
351
|
-
|
|
352
|
-
### Utilities
|
|
353
|
-
|
|
354
|
-
#### `safe(obj): any`
|
|
355
|
-
|
|
356
|
-
Prepares an object for JSON serialization by handling circular references and WeakRefs.
|
|
357
|
-
|
|
358
|
-
#### `deref(obj): any`
|
|
359
|
-
|
|
360
|
-
Dereferences WeakRefs and unwraps reactive objects.
|
|
361
|
-
|
|
362
|
-
#### `unwrap(obj): any`
|
|
363
|
-
|
|
364
|
-
Unwraps reactive objects to their original form.
|
|
365
|
-
|
|
366
|
-
#### `propRef<T>(src, prop, initial?, behavior?): Ref<T>`
|
|
367
|
-
|
|
368
|
-
Creates a reactive reference to a specific property.
|
|
369
|
-
|
|
370
|
-
### Subscription Management
|
|
371
|
-
|
|
372
|
-
#### `unaffected<T>(target, cb?, ctx?): Promise<T>`
|
|
373
|
-
|
|
374
|
-
Removes subscriptions from an object.
|
|
375
|
-
|
|
376
|
-
#### `triggerWithDelay(ref, cb, delay?): Timeout`
|
|
377
|
-
|
|
378
|
-
Triggers a callback after a delay if the ref value is truthy.
|
|
379
|
-
|
|
380
|
-
#### `delayedBehavior(delay?): Function`
|
|
381
|
-
|
|
382
|
-
Creates a behavior function that delays execution.
|
|
383
|
-
|
|
384
|
-
#### `delayedOrInstantBehavior(delay?): Function`
|
|
385
|
-
|
|
386
|
-
Creates a behavior that executes immediately or after delay.
|
|
387
|
-
|
|
388
|
-
### Legacy (Deprecated)
|
|
389
|
-
|
|
390
|
-
#### `createReactive(target, stateName?)`
|
|
391
|
-
|
|
392
|
-
Legacy function for creating reactive objects.
|
|
393
|
-
|
|
394
|
-
#### `createReactiveMap<K, V>(map?): Map<K, V>`
|
|
395
|
-
|
|
396
|
-
Legacy function for creating reactive Maps.
|
|
397
|
-
|
|
398
|
-
#### `createReactiveSet<V>(set?): Set<V>`
|
|
399
|
-
|
|
400
|
-
Legacy function for creating reactive Sets.
|
|
401
|
-
|
|
402
|
-
***
|
|
403
|
-
|
|
404
|
-
## Modules
|
|
405
|
-
|
|
406
|
-
- [core/Assigned](src/core/Assigned.ts) - Assignment and binding utilities
|
|
407
|
-
- [core/Mainline](src/core/Mainline.ts) - Core subscription and reactivity system
|
|
408
|
-
- [core/Primitives](src/core/Primitives.ts) - Primitive reactive references
|
|
409
|
-
- [core/Specific](src/core/Specific.ts) - Specific object type handlers
|
|
410
|
-
- [core/Subscript](src/core/Subscript.ts) - Subscription registry system
|
|
411
|
-
- [index](src/index.ts) - Main exports
|
|
412
|
-
- [wrap/AssignObject](src/wrap/AssignObject.ts) - Object assignment proxy
|
|
413
|
-
- [wrap/Symbol](src/wrap/Symbol.ts) - Internal symbols and triggers
|
|
414
|
-
- [wrap/Utils](src/wrap/Utils.ts) - Utility functions and types
|
|
415
|
-
|
|
416
|
-
---
|
|
417
|
-
|
|
418
|
-
## License
|
|
419
|
-
|
|
420
|
-
This project is licensed under the [MIT License](LICENSE).
|
|
125
|
+
License: [MIT](LICENSE).
|