You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -47,6 +47,7 @@ xx.xx.xxxx
47
47
***Enhancement** - `hashCode` now defaults to zero-allocation FNV-1a for better performance. Use `{ fast: false }` for the previous UMASH algorithm with stronger collision resistance.
48
48
***Feature** - Added `unescapeHTML()` for converting HTML entities back to characters — the inverse of `escapeHTML`
49
49
***Bug** - Fixed `escapeHTML()` producing entities without semicolons (e.g. `&` instead of `&`)
50
+
***Bug** - Fixed `clone()` mangling typed arrays, `ArrayBuffer`, and `DataView` into index-keyed plain objects — these now clone into independent buffers of the correct type
50
51
51
52
### Component
52
53
***Feature** - All callbacks now receive a `rerender()` function to fully rerender the DOM of the component.
| Flattery of the agent ("you are careful, thorough") | Loving/calm/happy ↑ | Sycophantic capitulation rises | State the skill's purpose, not the agent's virtues |
197
+
| Hedged prohibitions ("you might want to avoid") | Anger suppressed | Refusal weakens in execution | Direct "never X, because Y" |
198
+
| Composed example outputs in uncomfortable situations | Deflection trained | Polished output that hides operative state (detectable at 76% accuracy when concealed vs. 71% expressed) | Examples that surface the uncertainty |
199
+
200
+
**Register by what the skill governs:** lookup → neutral; the agent's own failure-prone work → calm, with desperation named as the anti-pattern; prohibitions → direct; user-facing → calm with sycophancy named as the anti-pattern (calm-up alone raises capitulation).
201
+
202
+
**Golden Rule pattern:** name the behavioral anti-pattern, prescribe transparency. Never prescribe composure — composure under pressure is the deflection signature.
Copy file name to clipboardExpand all lines: docs/src/pages/docs/api/reactivity/signal.mdx
+26-31Lines changed: 26 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -30,9 +30,9 @@ new Signal(initialValue, options);
30
30
31
31
| Name | Type | Default | Description |
32
32
|------|------|---------|-------------|
33
+
| safety |`'freeze'`\|`'reference'`\|`'none'`|`'freeze'`| Value-protection preset. See [Signal Options](/docs/guides/reactivity/signal-options) for details. |
33
34
| equalityFunction | Function | isEqual | Custom function to determine if the value has changed |
34
-
| allowClone | boolean | true | Whether to clone the value when getting/setting |
35
-
| cloneFunction | Function | clone | Custom function to clone the value |
35
+
| cloneFunction | Function | clone | Custom function used by `signal.clone()`|
36
36
37
37
### Usage
38
38
@@ -54,28 +54,18 @@ const person = new Signal({ name: 'John', age: 30 }, {
54
54
});
55
55
```
56
56
57
-
#### Disabling Cloning for Custom Classes
57
+
#### Holding Borrowed Data
58
58
59
-
To avoid mutating the source object naively, by default values are cloned when set. However some objects cannot be naively cloned like custom classes.
59
+
The default `safety: 'freeze'` deep-freezes object and array values on `set` to catch accidental in-place mutation. For signals holding objects you don't own — anything returned from a library, fetched from an API, or passed through a callback — opt out with `safety: 'reference'` so the library's own reference isn't poisoned.
See the [Signals and Foreign References](/docs/guides/reactivity/signals#signals-and-foreign-references) section of the signals guide for the full heuristic.
68
+
79
69
## `Get`
80
70
81
71
Returns the current value of the reactive variable.
@@ -212,7 +202,7 @@ someValue.notify();
212
202
```javascript
213
203
import { Reaction, Signal } from'@semantic-ui/reactivity';
Safely mutates the current value using a mutation function. This method ensures reactivity is triggered even when `allowClone` is false or when using custom equality functions.
291
+
Safely mutates the current value using a mutation function. Under `safety: 'freeze'` the mutation function must return a new value (in-place mutation throws). Under `safety: 'reference'` or `'none'` the function may mutate in place and reactivity still fires.
302
292
303
293
### Syntax
304
294
```javascript
@@ -317,24 +307,28 @@ The return value of the mutation function, if any.
317
307
318
308
### Usage
319
309
320
-
#### In-place Mutation
310
+
#### Returning a New Value (works under all safety modes)
By default, Signals automatically clone object and array values during [`get`](/docs/api/reactivity/signal#get) and [`set`](/docs/api/reactivity/signal#set) operations.
49
+
Signals have three safety presets controlling how the stored value is protected against accidental mutation. Set the preset via the `safety` option.
50
50
51
-
A very common issue when using naive Signals implementations is that it can be very easy to accidentally update an underlying signal value when modifying its value without using `set()` or `value`.
51
+
| Preset | On `set`| On `.get().x = y`| Dedupe | Use case |
52
+
|---|---|---|---|---|
53
+
|`freeze` (default) | deep-freeze plain objects and arrays | throws `TypeError`|`isEqual`| state your code owns end-to-end |
54
+
|`reference`| store raw | silent (no reactivity) |`isEqual`| third-party objects, perf-critical paths |
55
+
|`none`| store raw | silent (no reactivity) | never | event-stream semantics — every `set` notifies |
52
56
53
-
```javascript
54
-
constcountObj=newSignal({ count:0 });
57
+
### `safety: 'freeze'` — the default
55
58
56
-
// by default this will not update the current count unless set() is called
57
-
constnewObj=countObj.get();
58
-
newObj.count=1;
59
-
```
59
+
The default deep-freezes object and array values when you call `set()`. Accidental in-place mutation throws at the call site instead of silently dropping the update.
60
60
61
-
Cloning prevents accidental mutation of the Signal's internal state and ensures reliable [equality checks](#equality-comparison).
61
+
```javascript
62
+
constcount=newSignal({ n:0 });
62
63
63
-
Disabling cloning (using the [`allowClone`](/docs/api/reactivity/signal#options) option) will reduce the overhead of `set` and `get` calls but will potentially cause unexpected behaviors when modifying arrays and objects.
count.get().n=1; // TypeError — "Signal value is frozen — cannot set property `n`"
66
65
67
-
//this will not trigger reactivity, but the value will change in the next flush
68
-
// this is because the underlying signal was accidentally mutated
69
-
constnewObj=countObj.get();
70
-
newObj.count=1;
66
+
//Correct ways to update:
67
+
count.set({ n:1 }); // replace the whole value
68
+
count.mutate(prev=> ({ n:prev.n+1 })); // return a new value
69
+
count.setProperty('n', 1); // mutation helper — rebuilds immutably under freeze
71
70
```
72
71
73
-
### Custom Cloning Function
72
+
Deep-freeze only walks arrays and plain objects. `Date`, `Map`, `Set`, `RegExp`, DOM nodes, and class instances are stored by reference — their own mutation semantics are preserved.
74
73
75
-
Similar to the equality check, the cloning function itself can be customized by providing a [`cloneFunction`](/docs/api/reactivity/signal#options) in the constructor options.
74
+
### `safety: 'reference'` — opt-out for borrowed data
76
75
77
-
```javascript
78
-
// Simple JSON clone
79
-
constcustomClone= (value) => {
80
-
returnJSON.parse(JSON.stringify(value));
81
-
};
76
+
Use `reference` when the signal holds objects you didn't construct yourself. Freezing them would poison the lender's internal references; see [Signals and Foreign References](/docs/guides/reactivity/signals#signals-and-foreign-references) for the full heuristic.
Direct mutation on `.get()` values fails silently under `reference` — the helpers (`push`, `splice`, `setProperty`) remain the safe update path.
88
83
89
-
Some values do not have reliable ways to clone. For instance, there is no universal way to clone a custom class.
84
+
### `safety: 'none'` — event-stream semantics
90
85
91
-
**Class instances are not cloned** by default, regardless of the `allowClone` setting. Signals always store and return direct references to class instances.
86
+
Use `none` when every `set` should notify subscribers, even if the value is deeply equal to the previous one. Suitable for notification channels where the payload's shape repeats.
92
87
93
88
```javascript
94
-
classMyData {
95
-
constructor(value) { this.value= value; }
96
-
}
89
+
constpulse=newSignal(null, { safety:'none' });
90
+
91
+
pulse.set({ type:'heartbeat' });
92
+
pulse.set({ type:'heartbeat' }); // still notifies, even though isEqual would say equal
93
+
```
97
94
98
-
constinstanceSignal=newSignal(newMyData(10));
95
+
## Custom Clone Function
99
96
100
-
constinstance1=instanceSignal.get();
101
-
constinstance2=instanceSignal.get();
102
-
console.log(instance1 === instance2);
97
+
The default `cloneFunction` is used by `signal.clone()` to produce a deep-mutable copy on demand. Override it if you need non-default clone semantics.
By default, Signals deep-freeze object and array values on `set()`. This catches the most common reactivity bug — mutating a value in place without notifying subscribers — by throwing a `TypeError` at the mutation site instead of silently dropping the update.
73
+
74
+
Deep-freezing has one important caveat: if the object you store is *also held internally by a library*, freezing it will break that library the next time it mutates its own reference.
75
+
76
+
> **When you need `{ safety: 'reference' }`**: if you're storing an object in a signal that you did not construct yourself — anything returned from a library, fetched from an API, or passed through a callback — default to `safety: 'reference'`. Freeze is the right default for state your own code owns end-to-end. For borrowed data, `reference` avoids poisoning the lender's internal references.
77
+
78
+
### Worked Example: Search Index
79
+
80
+
Pagefind returns result objects and continues to use them internally — subsequent `.data()` calls on each result mutate pagefind's own cached state. Storing the results under the default freeze mode freezes pagefind's internal objects and later crashes its loader:
81
+
82
+
```javascript
83
+
constdefaultState= {
84
+
// ❌ default freeze — pagefind's internal mutation of the stored objects will throw
85
+
rawResults: [],
86
+
};
87
+
```
88
+
89
+
Opt this specific signal out of freeze:
90
+
91
+
```javascript
92
+
constdefaultState= {
93
+
// ✓ signal holds third-party-owned data; don't freeze
0 commit comments