sia-reactor 0.0.6 → 0.0.7
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 +260 -4
- package/dist/index.cjs +9 -2
- package/dist/index.global.js +9 -2
- package/dist/index.js +120 -15
- package/dist/utils.cjs +10 -0
- package/dist/utils.d.cts +3 -1
- package/dist/utils.d.ts +3 -1
- package/dist/utils.global.js +9 -0
- package/dist/utils.js +136 -18
- package/package.json +12 -5
- package/dist/chunk-GN5OE7KB.js +0 -146
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 OLUWATOBILOBA OKETADE
|
|
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
CHANGED
|
@@ -1,8 +1,264 @@
|
|
|
1
|
-
# sia-
|
|
1
|
+
# sia-reacor
|
|
2
2
|
|
|
3
|
-
The Programmable Data DOM. A high-performance State Intent Architecture (S.I.A.) Engine
|
|
3
|
+
> The Programmable Data DOM. A high-performance State & Intent Architecture (S.I.A.) Engine featuring zero-allocation loops, DOM-style event propagation, microtask batching, and structural sharing.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.npmjs.com/package/sia-reactor)
|
|
7
|
+
|
|
8
|
+
[Live Demo & Benchmarks](https://tobi007-del.github.io/t007-tools/packages/sia-reactor/src/index.html) | [Report Bug](https://github.com/Tobi007-del/t007-tools/issues)
|
|
9
|
+
|
|
10
|
+
[CHRONICLES](https://github.com/Tobi007-del/tmg-media-player/blob/main/CHRONICLES.md) | [FOLKLORE](https://github.com/Tobi007-del/tmg-media-player/blob/main/FOLKLORE.md)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table of contents
|
|
15
|
+
|
|
16
|
+
- [sia-reacor](#sia-reacor)
|
|
17
|
+
- [Table of contents](#table-of-contents)
|
|
18
|
+
- [Overview: The Paradigm Shift](#overview-the-paradigm-shift)
|
|
19
|
+
- [The Philosophy: Collecting Like Terms](#the-philosophy-collecting-like-terms)
|
|
20
|
+
- [State vs. Intent](#state-vs-intent)
|
|
21
|
+
- [The Art of Resolution: The Power Line](#the-art-of-resolution-the-power-line)
|
|
22
|
+
- [The Triad of Notifications](#the-triad-of-notifications)
|
|
23
|
+
- [Tech Stack](#tech-stack)
|
|
24
|
+
- [Getting Started](#getting-started)
|
|
25
|
+
- [Usage](#usage)
|
|
26
|
+
- [API Reference](#api-reference)
|
|
27
|
+
- [Benchmarks](#benchmarks)
|
|
28
|
+
- [Author](#author)
|
|
29
|
+
- [Acknowledgments](#acknowledgments)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Overview: The Paradigm Shift
|
|
34
|
+
|
|
35
|
+
**sia-reactor** is not just another state management library. It is an architectural paradigm shift.
|
|
36
|
+
|
|
37
|
+
Instead of treating your application's data as a flat object, the S.I.A. Engine converts your data into a **Programmable Data DOM**. Deeply nested object properties now possess the exact same lifecycle as HTML elements: they support strict event phases (Capture, Target, Bubble), they can intercept intents before memory is written, and they batch mutations synchronously to guarantee 60FPS UI renders.
|
|
38
|
+
|
|
39
|
+
Built for engineer surgeons, this engine allows you to wire up massive, deeply nested UIs with zero-allocation performance.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## The Philosophy: Collecting Like Terms
|
|
44
|
+
|
|
45
|
+
Most developers build systems that are entirely too rigid. They create Adapters, Contexts, and Request Managers just to make two different entities behave the same way.
|
|
46
|
+
|
|
47
|
+
> Picture a room with air conditioners. When the entity inside gets hot, the AC should turn on.
|
|
48
|
+
> A standard developer sees a human in the room and writes: *"When human sweats, AC on."*
|
|
49
|
+
> Then, the human is replaced by a pig. Pigs don't sweat. Now the developer has to build an entire adapter layer just to simulate fake sweat so the AC will trigger.
|
|
50
|
+
|
|
51
|
+
**The S.I.A. approach is declarative state pluralism.** We do not care *why* or *how* the entity gets hot. We establish a stable core and collect like terms: **"If entity is hot, turn AC on."** The adapter simply gives the entity a state (`hot = false`). When the entity gets hot, it flips the state to `true`. We just listen.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## State vs. Intent
|
|
56
|
+
|
|
57
|
+
We divide the world into two concepts to completely eliminate the need for bloated class methods (`makeNervous()`, `straightenNose()`).
|
|
58
|
+
|
|
59
|
+
1. **State (Fact):** The current reality of the system. It can only be determined *after* something happens. The UI is a mirror reflecting this state.
|
|
60
|
+
2. **Intent (Request/Wish):** A request to change reality.
|
|
61
|
+
|
|
62
|
+
Instead of learning complex APIs to trigger actions, you simply declare your intent:
|
|
63
|
+
`pig.intent.hot = true` (The Request)
|
|
64
|
+
↳ *The system internally evaluates* ↳ `pig.state.hot = true` (The Fact).
|
|
65
|
+
|
|
66
|
+
This brings the "appeal" directly into the state tree.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### Semantic Structuring: Plain State vs. Intentful State
|
|
71
|
+
|
|
72
|
+
You are not forced to use Intents. If you are building a simple application where data updates are immediate and undisputed, the S.I.A. engine functions perfectly as a hyper-fast, standard state manager. You can strictly use plain state and ignore the complexities of the rejection event loop.
|
|
73
|
+
|
|
74
|
+
However, the moment you introduce Intents into your architecture, **semantics matter**.
|
|
75
|
+
|
|
76
|
+
> If you use an `intent` object to capture user requests, your `state` object must act strictly as the factual mirror to that `intent`. Because `state` is now semantically locked to your intents, you must separate your other data.
|
|
77
|
+
|
|
78
|
+
Do not pollute your `state` object with data that doesn't require an intent. Instead, categorize them semantically:
|
|
79
|
+
- **`intent`**: For asynchronous requests or delayed validations (e.g., `intent.playing = true`).
|
|
80
|
+
- **`state`**: The factual mirror of granted intents (e.g., `state.playing = true`).
|
|
81
|
+
- **`settings` / `config`**: For immediate, undisputed user preferences (e.g., `settings.playbackRate = 2`).
|
|
82
|
+
- **`status`**: For read-only system facts (e.g., `status.network = "offline"`).
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
import { reactive, intent } from 'sia-reactor';
|
|
86
|
+
|
|
87
|
+
// A perfectly structured S.I.A. Data DOM
|
|
88
|
+
const player = reactive({
|
|
89
|
+
intent: intent({ playing: false, fullscreen: false }), // Can be rejected
|
|
90
|
+
state: { playing: false, fullscreen: false }, // The factual mirror
|
|
91
|
+
settings: { volume: 50, theme: "dark" }, // Immediate, plain state
|
|
92
|
+
status: { buffering: false, duration: 120 } // System facts
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## The Art of Resolution: The Power Line
|
|
99
|
+
|
|
100
|
+
Because an **Intent** is just a wish, the system must be able to reject it. This introduces a political hierarchy; a Chain of Responsibility driven by the Event Loop.
|
|
101
|
+
|
|
102
|
+
### The Parable of the King
|
|
103
|
+
Imagine a King wishes to fly: `man.intent.flying = true`.
|
|
104
|
+
You cannot stop him from wishing it. But the system can determine if it will grant the wish.
|
|
105
|
+
|
|
106
|
+
Everything crucial happens in the **Capture Phase**:
|
|
107
|
+
1. **The Higher Power:** A plugin that registers first. It intercepts the wish and can choose to `resolve(message)` or `reject(reason)`.
|
|
108
|
+
2. **The Adviser (The Tech):** Listens further down the capture line. If it sees `e.resolved`, it stands down. If it sees `e.rejected`, it knows the Higher Power failed and can attempt to save the situation or allow the failure.
|
|
109
|
+
3. **The Observers (The UI):** Listens on the **Bubble Phase**. They don't get involved in politics; they just watch the aftermath.
|
|
110
|
+
|
|
111
|
+
#### The Observer Types
|
|
112
|
+
- **The Smart Optimist (Court Man):** Checks if the intent was rejected before updating the UI.
|
|
113
|
+
- **The Reckless Optimist (Artist):** Doesn't care about rejections and paints the wish immediately, trusting the system will snap back later if needed.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## The Triad of Notifications
|
|
118
|
+
|
|
119
|
+
This architecture replaces the chaos of traditional state management with three distinct layers of surgical precision.
|
|
120
|
+
|
|
121
|
+
### 1. The Gatekeepers (Mediators) - `get`, `set`, `delete`
|
|
122
|
+
Synchronous operations that occur *before* or *during* a state change.
|
|
123
|
+
- Use `.set()` for data integrity and sanitization. You can intercept a value, modify it, or completely block the memory write by returning the `TERMINATOR` symbol.
|
|
124
|
+
- Use `.get()` to format or derive output on the fly without altering the underlying data.
|
|
125
|
+
|
|
126
|
+
### 2. The Rule of Survival (Watchers) - `watch`
|
|
127
|
+
Synchronous operations that occur *immediately after* a state change.
|
|
128
|
+
- Use `.watch()` when the very next line of code will crash if a system isn't updated instantly (e.g., `video.src`, internal engine states). This bypasses the async event loop for immediate execution.
|
|
129
|
+
|
|
130
|
+
### 3. The Rule of the Cloud (Listeners) - `on`
|
|
131
|
+
Asynchronous operations that run in the next microtask.
|
|
132
|
+
- Use `.on()` for **all UI updates** (e.g., `volume`, `brightness`). The reactor will automatically smash 1,000 synchronous mutations into a single `queueMicrotask` UI render tick.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Tech Stack
|
|
137
|
+
|
|
138
|
+
### Built with
|
|
139
|
+
- ECMAScript 2015+ Proxy API
|
|
140
|
+
- `queueMicrotask` Async Batching
|
|
141
|
+
- Bitwise Operations & Zero-Allocation Caching
|
|
142
|
+
- Bundled via `tsup` (ESM, CJS, IIFE outputs)
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Getting Started
|
|
147
|
+
|
|
148
|
+
### Installation
|
|
149
|
+
|
|
150
|
+
Install via your preferred package manager:
|
|
6
151
|
|
|
7
152
|
```bash
|
|
8
|
-
npm install sia-reactor
|
|
153
|
+
npm install sia-reactor
|
|
154
|
+
# or
|
|
155
|
+
yarn add sia-reactor
|
|
156
|
+
# or
|
|
157
|
+
pnpm add sia-reactor
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
// 1. Core Engine
|
|
162
|
+
import { reactive, Reactor, TERMINATOR } from 'sia-reactor';
|
|
163
|
+
|
|
164
|
+
// 2. Deep Object Utilities
|
|
165
|
+
import { setAny, getAny, mergeObjs } from 'sia-reactor/utils';
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Usage
|
|
171
|
+
|
|
172
|
+
### Modern Bundlers (ESM)
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
import { reactive, Reactor } from 'sia-reactor'; // also attached to window.sia in non-module scripts
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### CDN / Browser (Global)
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
const { reactive, Reactor } = window.sia;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## API Reference
|
|
185
|
+
|
|
186
|
+
### Initialization (`reactive` & `Reactor`)
|
|
187
|
+
|
|
188
|
+
The primary way to use the engine is to wrap an object using `reactive()`, which directly mixes the reactor methods into your target object for a pristine, flat API.
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
const state = reactive({ player: { volume: 50 } });
|
|
192
|
+
|
|
193
|
+
// Methods are attached directly to the object!
|
|
194
|
+
state.set("player.volume", (val) => Math.min(val, 100));
|
|
195
|
+
state.on("player.volume", (e) => console.log(e.value));
|
|
196
|
+
|
|
197
|
+
state.player.volume = 150; // Triggers mediation, clamps to 100, fires listener.
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Alternatively, you can instantiate the `Reactor` class directly to keep the API separate from your data:
|
|
201
|
+
```javascript
|
|
202
|
+
const engine = new Reactor({ player: { volume: 50 } }, { debug: true, referenceTracking: true });
|
|
203
|
+
engine.core.player.volume = 100;
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Memory & Granular Control Flags
|
|
207
|
+
|
|
208
|
+
You can wrap properties in special flags *before* initializing the reactor to dictate exactly how the Proxy treats them.
|
|
209
|
+
|
|
210
|
+
- **`inert(obj)` / `live(obj)`**: Tells the proxy to completely ignore an object. It will not be deeply tracked.
|
|
211
|
+
- **`intent(obj)` / `state(obj)`**: Marks an object as rejectable. Allows listeners to call `e.reject()` during the Capture phase.
|
|
212
|
+
- **`volatile(obj)` / `stable(obj)`**: Forces the engine to fire event waves even if the new value is identical to the old value (bypassing the Proxy's unchanged performance check).
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
import { reactive, intent, volatile, inert } from 'sia-reactor';
|
|
216
|
+
|
|
217
|
+
const data = reactive({
|
|
218
|
+
apiResponse: inert({ heavy: "data" }), // Proxy won't traverse this
|
|
219
|
+
userWish: intent({ flying: false }), // Can be rejected by a Higher Power
|
|
220
|
+
trigger: volatile({ clickCount: 0 }) // Fires events even if set to 0 again
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Core Methods
|
|
225
|
+
|
|
226
|
+
All methods are available on `Reactor` instances or objects wrapped in `reactive()`.
|
|
227
|
+
|
|
228
|
+
#### **Mediators (Synchronous Gatekeepers)**
|
|
229
|
+
- **`set(path, callback, options)`**: Intercept memory writes. Return a value to modify it, or return `TERMINATOR` to block the write entirely.
|
|
230
|
+
- **`get(path, callback, options)`**: Intercept and format data during retrieval.
|
|
231
|
+
- **`delete(path, callback, options)`**: Intercept property deletion.
|
|
232
|
+
|
|
233
|
+
#### **Watchers (Synchronous Observers)**
|
|
234
|
+
- **`watch(path, callback, options)`**: Fires instantly after a mutation. Use strictly for critical internal engine syncing.
|
|
235
|
+
|
|
236
|
+
#### **Listeners (Asynchronous/Batched UI Observers)**
|
|
237
|
+
- **`on(path, callback, options)`**: Attach DOM-style event listeners. Supports `{ capture: true, depth: 1, once: true, immediate: true }`.
|
|
238
|
+
- **`once(path, callback, options)`**: Fires once and self-destructs.
|
|
239
|
+
- **`off(path, callback, options)`**: Removes a listener.
|
|
240
|
+
|
|
241
|
+
#### **Lifecycle & Utilities**
|
|
242
|
+
- **`tick(path)`**: Forces a synchronous flush of the batch queue for a specific path.
|
|
243
|
+
- **`stall(task)` / `nostall(task)`**: Manually stall the queue to wait for calculations before rendering.
|
|
244
|
+
- **`cascade(payload)`**: Manually trigger deep-object event waves, bypassing strict unchanged-proxy traps. Perfect for dumping massive API payloads into the tree.
|
|
245
|
+
- **`snapshot(raw)`**: Generates a strict, structurally-shared, un-proxied clone of the current state tree.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Benchmarks
|
|
250
|
+
|
|
251
|
+
No fancy screenshots here. True engineers look at performance metrics.
|
|
252
|
+
|
|
253
|
+
To see the S.I.A Engine handle deep DAG mutations, DOM-style event routing, and microtask batching in real-time, visit the **[Live Demo](https://tobi007-del.github.io/t007-tools/packages/sia-reactor/src/index.html)**, open your DevTools console, and run the built-in Grand Master Stress Suite directly on your own CPU.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Author
|
|
258
|
+
|
|
259
|
+
- Architect & Developer - [Oketade Oluwatobiloba (Tobi007-del)](https://github.com/Tobi007-del)
|
|
260
|
+
- Project - [t007-tools](https://github.com/Tobi007-del/t007-tools)
|
|
261
|
+
|
|
262
|
+
## Acknowledgments
|
|
263
|
+
|
|
264
|
+
Designed to bring absolute architectural dominance and rendering efficiency to complex front-end systems. The foundational data layer of the `@t007` ecosystem.
|
package/dist/index.cjs
CHANGED
|
@@ -255,10 +255,15 @@ var Reactor = class {
|
|
|
255
255
|
isLogging = false;
|
|
256
256
|
// keeping track so API getter doesn't slow down internal iterations in any way
|
|
257
257
|
isTracing = false;
|
|
258
|
+
// Lineage Tracing
|
|
258
259
|
isTracking = false;
|
|
260
|
+
// Reference Tracking
|
|
259
261
|
isSCloning = false;
|
|
260
262
|
// Smart Cloning
|
|
261
263
|
isBatching = false;
|
|
264
|
+
// Async Batching
|
|
265
|
+
isCascading = false;
|
|
266
|
+
// Setter Cascading
|
|
262
267
|
constructor(obj = {}, options) {
|
|
263
268
|
this[INERTIA] = true;
|
|
264
269
|
this.config = { crossRealms: false, eventBubbling: true, batchingFunction: R_BATCH, ...options };
|
|
@@ -307,7 +312,7 @@ var Reactor = class {
|
|
|
307
312
|
safeValue = value?.[RAW] || value;
|
|
308
313
|
unchanged = Object.is(safeValue, safeOldValue);
|
|
309
314
|
}
|
|
310
|
-
if (!indiffable && unchanged) return true;
|
|
315
|
+
if (!indiffable && unchanged && !this.isCascading) return true;
|
|
311
316
|
this.log(`\u270F\uFE0F [SET Trap] Initiated for "${safeKey}" on "${paths}"`);
|
|
312
317
|
if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
|
|
313
318
|
if (this.setters) {
|
|
@@ -605,7 +610,9 @@ var Reactor = class {
|
|
|
605
610
|
cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objSafe = true) {
|
|
606
611
|
if (type !== "set" && type !== "delete" || !(isStrictObj(news, this.config.crossRealms) || isArr(news)) || (objSafe ? !(isStrictObj(olds, this.config.crossRealms) || isArr(olds)) : false)) return;
|
|
607
612
|
const obj = objSafe ? mergeObjs(olds, news) : news, keys = Object.keys(obj);
|
|
613
|
+
this.isCascading = true;
|
|
608
614
|
for (let i = 0, len = keys.length; i < len; i++) setAny(this.core, path + "." + keys[i], obj[keys[i]]);
|
|
615
|
+
this.isCascading = false;
|
|
609
616
|
}
|
|
610
617
|
reset() {
|
|
611
618
|
this.getters?.clear(), this.setters?.clear(), this.deleters?.clear(), this.watchers?.clear(), this.listeners?.clear(), this.queue?.clear(), this.batch?.clear();
|
|
@@ -615,7 +622,7 @@ var Reactor = class {
|
|
|
615
622
|
this.reset(), nuke(this);
|
|
616
623
|
}
|
|
617
624
|
get canLog() {
|
|
618
|
-
return this.
|
|
625
|
+
return this.isLogging;
|
|
619
626
|
}
|
|
620
627
|
set canLog(value) {
|
|
621
628
|
this.log = (this.isLogging = value) ? R_LOG : NOOP;
|
package/dist/index.global.js
CHANGED
|
@@ -255,10 +255,15 @@ var sia = (() => {
|
|
|
255
255
|
isLogging = false;
|
|
256
256
|
// keeping track so API getter doesn't slow down internal iterations in any way
|
|
257
257
|
isTracing = false;
|
|
258
|
+
// Lineage Tracing
|
|
258
259
|
isTracking = false;
|
|
260
|
+
// Reference Tracking
|
|
259
261
|
isSCloning = false;
|
|
260
262
|
// Smart Cloning
|
|
261
263
|
isBatching = false;
|
|
264
|
+
// Async Batching
|
|
265
|
+
isCascading = false;
|
|
266
|
+
// Setter Cascading
|
|
262
267
|
constructor(obj = {}, options) {
|
|
263
268
|
this[INERTIA] = true;
|
|
264
269
|
this.config = { crossRealms: false, eventBubbling: true, batchingFunction: R_BATCH, ...options };
|
|
@@ -307,7 +312,7 @@ var sia = (() => {
|
|
|
307
312
|
safeValue = value?.[RAW] || value;
|
|
308
313
|
unchanged = Object.is(safeValue, safeOldValue);
|
|
309
314
|
}
|
|
310
|
-
if (!indiffable && unchanged) return true;
|
|
315
|
+
if (!indiffable && unchanged && !this.isCascading) return true;
|
|
311
316
|
this.log(`\u270F\uFE0F [SET Trap] Initiated for "${safeKey}" on "${paths}"`);
|
|
312
317
|
if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
|
|
313
318
|
if (this.setters) {
|
|
@@ -605,7 +610,9 @@ var sia = (() => {
|
|
|
605
610
|
cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objSafe = true) {
|
|
606
611
|
if (type !== "set" && type !== "delete" || !(isStrictObj(news, this.config.crossRealms) || isArr(news)) || (objSafe ? !(isStrictObj(olds, this.config.crossRealms) || isArr(olds)) : false)) return;
|
|
607
612
|
const obj = objSafe ? mergeObjs(olds, news) : news, keys = Object.keys(obj);
|
|
613
|
+
this.isCascading = true;
|
|
608
614
|
for (let i = 0, len = keys.length; i < len; i++) setAny(this.core, path + "." + keys[i], obj[keys[i]]);
|
|
615
|
+
this.isCascading = false;
|
|
609
616
|
}
|
|
610
617
|
reset() {
|
|
611
618
|
this.getters?.clear(), this.setters?.clear(), this.deleters?.clear(), this.watchers?.clear(), this.listeners?.clear(), this.queue?.clear(), this.batch?.clear();
|
|
@@ -615,7 +622,7 @@ var sia = (() => {
|
|
|
615
622
|
this.reset(), nuke(this);
|
|
616
623
|
}
|
|
617
624
|
get canLog() {
|
|
618
|
-
return this.
|
|
625
|
+
return this.isLogging;
|
|
619
626
|
}
|
|
620
627
|
set canLog(value) {
|
|
621
628
|
this.log = (this.isLogging = value) ? R_LOG : NOOP;
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,114 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
// src/utils/obj.ts
|
|
2
|
+
var arrRx = /^([^\[\]]+)\[(\d+)\]$/;
|
|
3
|
+
function isArr(obj) {
|
|
4
|
+
return Array.isArray(obj);
|
|
5
|
+
}
|
|
6
|
+
function isObj(obj, checkArr = true) {
|
|
7
|
+
return "object" === typeof obj && obj !== null && (checkArr ? !Array.isArray(obj) : true);
|
|
8
|
+
}
|
|
9
|
+
function isStrictObj(obj, crossRealms = false, typecheck = true) {
|
|
10
|
+
return (typecheck ? isObj(obj, false) : true) && (crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
|
|
11
|
+
}
|
|
12
|
+
function setAny(target, key, value, separator = ".", keyFunc) {
|
|
13
|
+
if (!key.includes(separator)) return void (target[keyFunc ? keyFunc(key) : key] = value);
|
|
14
|
+
const keys = key.split(separator);
|
|
15
|
+
for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
|
|
16
|
+
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
17
|
+
if (match) {
|
|
18
|
+
const [, key3, iStr] = match;
|
|
19
|
+
if (!isArr(currObj[key3])) currObj[key3] = [];
|
|
20
|
+
if (i === len - 1) currObj[key3][Number(iStr)] = value;
|
|
21
|
+
else currObj[key3][Number(iStr)] ||= {}, currObj = currObj[key3][Number(iStr)];
|
|
22
|
+
} else {
|
|
23
|
+
if (i === len - 1) currObj[key2] = value;
|
|
24
|
+
else currObj[key2] ||= {}, currObj = currObj[key2];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function getAny(source, key, separator = ".", keyFunc) {
|
|
29
|
+
if (!key.includes(separator)) return source[keyFunc ? keyFunc(key) : key];
|
|
30
|
+
const keys = key.split(separator);
|
|
31
|
+
let currObj = source;
|
|
32
|
+
for (let i = 0, len = keys.length; i < len; i++) {
|
|
33
|
+
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
34
|
+
if (match) {
|
|
35
|
+
const [, key3, iStr] = match;
|
|
36
|
+
if (!isArr(currObj[key3]) || !(key3 in currObj)) return void 0;
|
|
37
|
+
currObj = currObj[key3][Number(iStr)];
|
|
38
|
+
} else {
|
|
39
|
+
if (!isObj(currObj) || !(key2 in currObj)) return void 0;
|
|
40
|
+
currObj = currObj[key2];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return currObj;
|
|
44
|
+
}
|
|
45
|
+
function deleteAny(target, key, separator = ".", keyFunc) {
|
|
46
|
+
if (!key.includes(separator)) return void delete target[keyFunc ? keyFunc(key) : key];
|
|
47
|
+
const keys = key.split(separator);
|
|
48
|
+
for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
|
|
49
|
+
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
50
|
+
if (match) {
|
|
51
|
+
const [, key3, iStr] = match;
|
|
52
|
+
if (!isArr(currObj[key3]) || !(key3 in currObj)) return;
|
|
53
|
+
if (i === len - 1) delete currObj[key3][Number(iStr)];
|
|
54
|
+
else currObj = currObj[key3][Number(iStr)];
|
|
55
|
+
} else {
|
|
56
|
+
if (!isObj(currObj) || !(key2 in currObj)) return;
|
|
57
|
+
if (i === len - 1) delete currObj[key2];
|
|
58
|
+
else currObj = currObj[key2];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function inAny(source, key, separator = ".", keyFunc) {
|
|
63
|
+
if (!key.includes(separator)) return key in source;
|
|
64
|
+
const keys = key.split(separator);
|
|
65
|
+
for (let currObj = source, i = 0, len = keys.length; i < len; i++) {
|
|
66
|
+
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
67
|
+
if (match) {
|
|
68
|
+
const [, key3, iStr] = match;
|
|
69
|
+
if (!isArr(currObj[key3]) || !(key3 in currObj)) return false;
|
|
70
|
+
if (i === len - 1) return true;
|
|
71
|
+
currObj = currObj[key3][Number(iStr)];
|
|
72
|
+
} else {
|
|
73
|
+
if (!isObj(currObj) || !(key2 in currObj)) return false;
|
|
74
|
+
if (i === len - 1) return true;
|
|
75
|
+
currObj = currObj[key2];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
function parseEvOpts(options, opts, boolOpt = opts[0], result = {}) {
|
|
81
|
+
return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
|
|
82
|
+
}
|
|
83
|
+
function mergeObjs(o1 = {}, o2 = {}) {
|
|
84
|
+
const merged = { ...o1 || {}, ...o2 || {} };
|
|
85
|
+
return Object.keys(merged).forEach((k) => isObj(o1?.[k]) && isObj(o2?.[k]) && (merged[k] = mergeObjs(o1[k], o2[k]))), merged;
|
|
86
|
+
}
|
|
87
|
+
function getTrailPaths(path, reverse = true) {
|
|
88
|
+
const parts = path.split("."), chain = ["*"];
|
|
89
|
+
let acc = "";
|
|
90
|
+
for (let i = 0, len = parts.length; i < len; i++) chain.push(acc += (i === 0 ? "" : ".") + parts[i]);
|
|
91
|
+
return reverse ? chain.reverse() : chain;
|
|
92
|
+
}
|
|
93
|
+
function getTrailRecords(obj, path) {
|
|
94
|
+
const parts = path.split("."), record = [["*", obj, obj]];
|
|
95
|
+
let acc = "", currObj = obj;
|
|
96
|
+
for (let i = 0, len = parts.length; i < len; i++) record.push([acc += (i ? "." : "") + parts[i], currObj, currObj = currObj?.[parts[i]]]);
|
|
97
|
+
return record;
|
|
98
|
+
}
|
|
99
|
+
function nuke(target) {
|
|
100
|
+
let proto = target;
|
|
101
|
+
while (proto && proto !== Object.prototype) {
|
|
102
|
+
const keys = Object.getOwnPropertyNames(proto);
|
|
103
|
+
for (let i = 0, len = keys.length; i < len; i++) {
|
|
104
|
+
if (keys[i] === "constructor") continue;
|
|
105
|
+
const desc = Object.getOwnPropertyDescriptor(proto, keys[i]);
|
|
106
|
+
if (desc && ("function" === typeof desc.value || desc.get || desc.set)) continue;
|
|
107
|
+
proto[keys[i]] = null;
|
|
108
|
+
}
|
|
109
|
+
proto = Object.getPrototypeOf(proto);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
14
112
|
|
|
15
113
|
// src/core/reactor.ts
|
|
16
114
|
var RAW = /* @__PURE__ */ Symbol.for("S.I.A_RAW");
|
|
@@ -116,10 +214,15 @@ var Reactor = class {
|
|
|
116
214
|
isLogging = false;
|
|
117
215
|
// keeping track so API getter doesn't slow down internal iterations in any way
|
|
118
216
|
isTracing = false;
|
|
217
|
+
// Lineage Tracing
|
|
119
218
|
isTracking = false;
|
|
219
|
+
// Reference Tracking
|
|
120
220
|
isSCloning = false;
|
|
121
221
|
// Smart Cloning
|
|
122
222
|
isBatching = false;
|
|
223
|
+
// Async Batching
|
|
224
|
+
isCascading = false;
|
|
225
|
+
// Setter Cascading
|
|
123
226
|
constructor(obj = {}, options) {
|
|
124
227
|
this[INERTIA] = true;
|
|
125
228
|
this.config = { crossRealms: false, eventBubbling: true, batchingFunction: R_BATCH, ...options };
|
|
@@ -168,7 +271,7 @@ var Reactor = class {
|
|
|
168
271
|
safeValue = value?.[RAW] || value;
|
|
169
272
|
unchanged = Object.is(safeValue, safeOldValue);
|
|
170
273
|
}
|
|
171
|
-
if (!indiffable && unchanged) return true;
|
|
274
|
+
if (!indiffable && unchanged && !this.isCascading) return true;
|
|
172
275
|
this.log(`\u270F\uFE0F [SET Trap] Initiated for "${safeKey}" on "${paths}"`);
|
|
173
276
|
if (this.config.set) terminated = (value = this.config.set(object, key2, value, oldValue, receiver, paths)) === TERMINATOR;
|
|
174
277
|
if (this.setters) {
|
|
@@ -466,7 +569,9 @@ var Reactor = class {
|
|
|
466
569
|
cascade({ type, currentTarget: { path, value: news, oldValue: olds } }, objSafe = true) {
|
|
467
570
|
if (type !== "set" && type !== "delete" || !(isStrictObj(news, this.config.crossRealms) || isArr(news)) || (objSafe ? !(isStrictObj(olds, this.config.crossRealms) || isArr(olds)) : false)) return;
|
|
468
571
|
const obj = objSafe ? mergeObjs(olds, news) : news, keys = Object.keys(obj);
|
|
572
|
+
this.isCascading = true;
|
|
469
573
|
for (let i = 0, len = keys.length; i < len; i++) setAny(this.core, path + "." + keys[i], obj[keys[i]]);
|
|
574
|
+
this.isCascading = false;
|
|
470
575
|
}
|
|
471
576
|
reset() {
|
|
472
577
|
this.getters?.clear(), this.setters?.clear(), this.deleters?.clear(), this.watchers?.clear(), this.listeners?.clear(), this.queue?.clear(), this.batch?.clear();
|
|
@@ -476,7 +581,7 @@ var Reactor = class {
|
|
|
476
581
|
this.reset(), nuke(this);
|
|
477
582
|
}
|
|
478
583
|
get canLog() {
|
|
479
|
-
return this.
|
|
584
|
+
return this.isLogging;
|
|
480
585
|
}
|
|
481
586
|
set canLog(value) {
|
|
482
587
|
this.log = (this.isLogging = value) ? R_LOG : NOOP;
|
package/dist/utils.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/utils.ts
|
|
21
21
|
var utils_exports = {};
|
|
22
22
|
__export(utils_exports, {
|
|
23
|
+
deepClone: () => deepClone,
|
|
23
24
|
deleteAny: () => deleteAny,
|
|
24
25
|
getAny: () => getAny,
|
|
25
26
|
getTrailPaths: () => getTrailPaths,
|
|
@@ -153,6 +154,14 @@ function getTrailRecords(obj, path) {
|
|
|
153
154
|
for (let i = 0, len = parts.length; i < len; i++) record.push([acc += (i ? "." : "") + parts[i], currObj, currObj = currObj?.[parts[i]]]);
|
|
154
155
|
return record;
|
|
155
156
|
}
|
|
157
|
+
function deepClone(obj, crossRealms, visited = /* @__PURE__ */ new WeakMap()) {
|
|
158
|
+
if (!(isStrictObj(obj, crossRealms) || isArr(obj)) || visited.has(obj)) return obj;
|
|
159
|
+
const clone = isArr(obj) ? [] : {};
|
|
160
|
+
visited.set(obj, clone);
|
|
161
|
+
const keys = Object.keys(obj);
|
|
162
|
+
for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = deepClone(obj[keys[i]], crossRealms, visited);
|
|
163
|
+
return clone;
|
|
164
|
+
}
|
|
156
165
|
function nuke(target) {
|
|
157
166
|
let proto = target;
|
|
158
167
|
while (proto && proto !== Object.prototype) {
|
|
@@ -168,6 +177,7 @@ function nuke(target) {
|
|
|
168
177
|
}
|
|
169
178
|
// Annotate the CommonJS export names for ESM import in node:
|
|
170
179
|
0 && (module.exports = {
|
|
180
|
+
deepClone,
|
|
171
181
|
deleteAny,
|
|
172
182
|
getAny,
|
|
173
183
|
getTrailPaths,
|
package/dist/utils.d.cts
CHANGED
|
@@ -386,6 +386,7 @@ declare function mergeObjs<T1 extends object>(o1: T1): T1;
|
|
|
386
386
|
declare function mergeObjs<T2 extends object>(o1: undefined | null, o2: T2): T2;
|
|
387
387
|
declare function getTrailPaths<T>(path: WildPaths<T>, reverse?: boolean): WildPaths<T>[];
|
|
388
388
|
declare function getTrailRecords<T extends object>(obj: T, path: WildPaths<T>): [WildPaths<T>, PathValue<T, WildPaths<T>>, PathValue<T, WildPaths<T>>][];
|
|
389
|
+
declare function deepClone<T>(obj: T, crossRealms?: boolean, visited?: WeakMap<WeakKey, any>): T;
|
|
389
390
|
declare function nuke(target: any): void;
|
|
390
391
|
|
|
391
392
|
declare const INERTIA: unique symbol;
|
|
@@ -444,6 +445,7 @@ declare class Reactor<T extends object> {
|
|
|
444
445
|
protected isTracking: boolean;
|
|
445
446
|
protected isSCloning: boolean;
|
|
446
447
|
protected isBatching: boolean;
|
|
448
|
+
protected isCascading: boolean;
|
|
447
449
|
constructor(obj?: T, options?: ReactorOptions<T>);
|
|
448
450
|
protected proxied<O extends object>(obj: O, rejectable?: boolean, indiffable?: boolean, parent?: object, key?: string, path?: string): O;
|
|
449
451
|
protected trace(target: object, path: string, paths?: string[], visited?: WeakSet<object>): Paths<T>[];
|
|
@@ -494,4 +496,4 @@ declare class Reactor<T extends object> {
|
|
|
494
496
|
get canSmartClone(): boolean;
|
|
495
497
|
}
|
|
496
498
|
|
|
497
|
-
export { live as $, type StrictPathKey as A, type SyncOptions as B, type ChildPaths as C, type DeepKeys as D, type SyncOptionsTuple as E, type Target as F, type Getter as G, type UnionToIntersection as H, type Inert as I, type UpdatePayload as J, type WatcherRecord as K, type Listener as L, type WildPaths as M, getSnapshotVersion as N, getVersion as O, type PathBranch as P, inert as Q, type REvent as R, type Setter as S, TERMINATOR as T, type Unflatten as U, type Volatile as V, type Watcher as W, intent as X, isInert as Y, isIntent as Z, isVolatile as _, type DeepMerge as a, methods as a0, reactive as a1, stable as a2, state as a3, volatile as a4, type DeepPartial as b, type DeepRequired as c, type Deleter as d, deleteAny, type DeleterRecord as e, type DirectPayload as f, type GetterRecord as g, getAny, getTrailPaths, getTrailRecords, type Intent as h, type ListenerOptions as i, inAny, inBoolArrOpt, isArr, isDef, isIter, isObj, isStrictObj, type ListenerOptionsTuple as j, type ListenerRecord as k, type Live as l, type PathBranchValue as m, mergeObjs, type PathKey as n, nuke, type PathLeaf as o, type PathValue as p, parseAnyObj, parseEvOpts, type Paths as q, type Payload as r, type Reactive as s, setAny, type ReactivePrefs as t, Reactor as u, ReactorEvent as v, type ReactorOptions as w, type SetterRecord as x, type Stable as y, type State as z };
|
|
499
|
+
export { live as $, type StrictPathKey as A, type SyncOptions as B, type ChildPaths as C, type DeepKeys as D, type SyncOptionsTuple as E, type Target as F, type Getter as G, type UnionToIntersection as H, type Inert as I, type UpdatePayload as J, type WatcherRecord as K, type Listener as L, type WildPaths as M, getSnapshotVersion as N, getVersion as O, type PathBranch as P, inert as Q, type REvent as R, type Setter as S, TERMINATOR as T, type Unflatten as U, type Volatile as V, type Watcher as W, intent as X, isInert as Y, isIntent as Z, isVolatile as _, type DeepMerge as a, methods as a0, reactive as a1, stable as a2, state as a3, volatile as a4, type DeepPartial as b, type DeepRequired as c, type Deleter as d, deepClone, deleteAny, type DeleterRecord as e, type DirectPayload as f, type GetterRecord as g, getAny, getTrailPaths, getTrailRecords, type Intent as h, type ListenerOptions as i, inAny, inBoolArrOpt, isArr, isDef, isIter, isObj, isStrictObj, type ListenerOptionsTuple as j, type ListenerRecord as k, type Live as l, type PathBranchValue as m, mergeObjs, type PathKey as n, nuke, type PathLeaf as o, type PathValue as p, parseAnyObj, parseEvOpts, type Paths as q, type Payload as r, type Reactive as s, setAny, type ReactivePrefs as t, Reactor as u, ReactorEvent as v, type ReactorOptions as w, type SetterRecord as x, type Stable as y, type State as z };
|
package/dist/utils.d.ts
CHANGED
|
@@ -386,6 +386,7 @@ declare function mergeObjs<T1 extends object>(o1: T1): T1;
|
|
|
386
386
|
declare function mergeObjs<T2 extends object>(o1: undefined | null, o2: T2): T2;
|
|
387
387
|
declare function getTrailPaths<T>(path: WildPaths<T>, reverse?: boolean): WildPaths<T>[];
|
|
388
388
|
declare function getTrailRecords<T extends object>(obj: T, path: WildPaths<T>): [WildPaths<T>, PathValue<T, WildPaths<T>>, PathValue<T, WildPaths<T>>][];
|
|
389
|
+
declare function deepClone<T>(obj: T, crossRealms?: boolean, visited?: WeakMap<WeakKey, any>): T;
|
|
389
390
|
declare function nuke(target: any): void;
|
|
390
391
|
|
|
391
392
|
declare const INERTIA: unique symbol;
|
|
@@ -444,6 +445,7 @@ declare class Reactor<T extends object> {
|
|
|
444
445
|
protected isTracking: boolean;
|
|
445
446
|
protected isSCloning: boolean;
|
|
446
447
|
protected isBatching: boolean;
|
|
448
|
+
protected isCascading: boolean;
|
|
447
449
|
constructor(obj?: T, options?: ReactorOptions<T>);
|
|
448
450
|
protected proxied<O extends object>(obj: O, rejectable?: boolean, indiffable?: boolean, parent?: object, key?: string, path?: string): O;
|
|
449
451
|
protected trace(target: object, path: string, paths?: string[], visited?: WeakSet<object>): Paths<T>[];
|
|
@@ -494,4 +496,4 @@ declare class Reactor<T extends object> {
|
|
|
494
496
|
get canSmartClone(): boolean;
|
|
495
497
|
}
|
|
496
498
|
|
|
497
|
-
export { live as $, type StrictPathKey as A, type SyncOptions as B, type ChildPaths as C, type DeepKeys as D, type SyncOptionsTuple as E, type Target as F, type Getter as G, type UnionToIntersection as H, type Inert as I, type UpdatePayload as J, type WatcherRecord as K, type Listener as L, type WildPaths as M, getSnapshotVersion as N, getVersion as O, type PathBranch as P, inert as Q, type REvent as R, type Setter as S, TERMINATOR as T, type Unflatten as U, type Volatile as V, type Watcher as W, intent as X, isInert as Y, isIntent as Z, isVolatile as _, type DeepMerge as a, methods as a0, reactive as a1, stable as a2, state as a3, volatile as a4, type DeepPartial as b, type DeepRequired as c, type Deleter as d, deleteAny, type DeleterRecord as e, type DirectPayload as f, type GetterRecord as g, getAny, getTrailPaths, getTrailRecords, type Intent as h, type ListenerOptions as i, inAny, inBoolArrOpt, isArr, isDef, isIter, isObj, isStrictObj, type ListenerOptionsTuple as j, type ListenerRecord as k, type Live as l, type PathBranchValue as m, mergeObjs, type PathKey as n, nuke, type PathLeaf as o, type PathValue as p, parseAnyObj, parseEvOpts, type Paths as q, type Payload as r, type Reactive as s, setAny, type ReactivePrefs as t, Reactor as u, ReactorEvent as v, type ReactorOptions as w, type SetterRecord as x, type Stable as y, type State as z };
|
|
499
|
+
export { live as $, type StrictPathKey as A, type SyncOptions as B, type ChildPaths as C, type DeepKeys as D, type SyncOptionsTuple as E, type Target as F, type Getter as G, type UnionToIntersection as H, type Inert as I, type UpdatePayload as J, type WatcherRecord as K, type Listener as L, type WildPaths as M, getSnapshotVersion as N, getVersion as O, type PathBranch as P, inert as Q, type REvent as R, type Setter as S, TERMINATOR as T, type Unflatten as U, type Volatile as V, type Watcher as W, intent as X, isInert as Y, isIntent as Z, isVolatile as _, type DeepMerge as a, methods as a0, reactive as a1, stable as a2, state as a3, volatile as a4, type DeepPartial as b, type DeepRequired as c, type Deleter as d, deepClone, deleteAny, type DeleterRecord as e, type DirectPayload as f, type GetterRecord as g, getAny, getTrailPaths, getTrailRecords, type Intent as h, type ListenerOptions as i, inAny, inBoolArrOpt, isArr, isDef, isIter, isObj, isStrictObj, type ListenerOptionsTuple as j, type ListenerRecord as k, type Live as l, type PathBranchValue as m, mergeObjs, type PathKey as n, nuke, type PathLeaf as o, type PathValue as p, parseAnyObj, parseEvOpts, type Paths as q, type Payload as r, type Reactive as s, setAny, type ReactivePrefs as t, Reactor as u, ReactorEvent as v, type ReactorOptions as w, type SetterRecord as x, type Stable as y, type State as z };
|
package/dist/utils.global.js
CHANGED
|
@@ -21,6 +21,7 @@ var sia = (() => {
|
|
|
21
21
|
// src/utils.ts
|
|
22
22
|
var utils_exports = {};
|
|
23
23
|
__export(utils_exports, {
|
|
24
|
+
deepClone: () => deepClone,
|
|
24
25
|
deleteAny: () => deleteAny,
|
|
25
26
|
getAny: () => getAny,
|
|
26
27
|
getTrailPaths: () => getTrailPaths,
|
|
@@ -153,6 +154,14 @@ var sia = (() => {
|
|
|
153
154
|
for (let i = 0, len = parts.length; i < len; i++) record.push([acc += (i ? "." : "") + parts[i], currObj, currObj = currObj?.[parts[i]]]);
|
|
154
155
|
return record;
|
|
155
156
|
}
|
|
157
|
+
function deepClone(obj, crossRealms, visited = /* @__PURE__ */ new WeakMap()) {
|
|
158
|
+
if (!(isStrictObj(obj, crossRealms) || isArr(obj)) || visited.has(obj)) return obj;
|
|
159
|
+
const clone = isArr(obj) ? [] : {};
|
|
160
|
+
visited.set(obj, clone);
|
|
161
|
+
const keys = Object.keys(obj);
|
|
162
|
+
for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = deepClone(obj[keys[i]], crossRealms, visited);
|
|
163
|
+
return clone;
|
|
164
|
+
}
|
|
156
165
|
function nuke(target) {
|
|
157
166
|
let proto = target;
|
|
158
167
|
while (proto && proto !== Object.prototype) {
|
package/dist/utils.js
CHANGED
|
@@ -1,22 +1,140 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
// src/utils/obj.ts
|
|
2
|
+
var arrRx = /^([^\[\]]+)\[(\d+)\]$/;
|
|
3
|
+
function isDef(val) {
|
|
4
|
+
return val !== void 0;
|
|
5
|
+
}
|
|
6
|
+
function isArr(obj) {
|
|
7
|
+
return Array.isArray(obj);
|
|
8
|
+
}
|
|
9
|
+
function isObj(obj, checkArr = true) {
|
|
10
|
+
return "object" === typeof obj && obj !== null && (checkArr ? !Array.isArray(obj) : true);
|
|
11
|
+
}
|
|
12
|
+
function isStrictObj(obj, crossRealms = false, typecheck = true) {
|
|
13
|
+
return (typecheck ? isObj(obj, false) : true) && (crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
|
|
14
|
+
}
|
|
15
|
+
function isIter(obj) {
|
|
16
|
+
return obj != null && "function" === typeof obj[Symbol.iterator];
|
|
17
|
+
}
|
|
18
|
+
function inBoolArrOpt(opt, str) {
|
|
19
|
+
return opt?.includes?.(str) ?? opt;
|
|
20
|
+
}
|
|
21
|
+
function setAny(target, key, value, separator = ".", keyFunc) {
|
|
22
|
+
if (!key.includes(separator)) return void (target[keyFunc ? keyFunc(key) : key] = value);
|
|
23
|
+
const keys = key.split(separator);
|
|
24
|
+
for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
|
|
25
|
+
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
26
|
+
if (match) {
|
|
27
|
+
const [, key3, iStr] = match;
|
|
28
|
+
if (!isArr(currObj[key3])) currObj[key3] = [];
|
|
29
|
+
if (i === len - 1) currObj[key3][Number(iStr)] = value;
|
|
30
|
+
else currObj[key3][Number(iStr)] ||= {}, currObj = currObj[key3][Number(iStr)];
|
|
31
|
+
} else {
|
|
32
|
+
if (i === len - 1) currObj[key2] = value;
|
|
33
|
+
else currObj[key2] ||= {}, currObj = currObj[key2];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function getAny(source, key, separator = ".", keyFunc) {
|
|
38
|
+
if (!key.includes(separator)) return source[keyFunc ? keyFunc(key) : key];
|
|
39
|
+
const keys = key.split(separator);
|
|
40
|
+
let currObj = source;
|
|
41
|
+
for (let i = 0, len = keys.length; i < len; i++) {
|
|
42
|
+
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
43
|
+
if (match) {
|
|
44
|
+
const [, key3, iStr] = match;
|
|
45
|
+
if (!isArr(currObj[key3]) || !(key3 in currObj)) return void 0;
|
|
46
|
+
currObj = currObj[key3][Number(iStr)];
|
|
47
|
+
} else {
|
|
48
|
+
if (!isObj(currObj) || !(key2 in currObj)) return void 0;
|
|
49
|
+
currObj = currObj[key2];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return currObj;
|
|
53
|
+
}
|
|
54
|
+
function deleteAny(target, key, separator = ".", keyFunc) {
|
|
55
|
+
if (!key.includes(separator)) return void delete target[keyFunc ? keyFunc(key) : key];
|
|
56
|
+
const keys = key.split(separator);
|
|
57
|
+
for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
|
|
58
|
+
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
59
|
+
if (match) {
|
|
60
|
+
const [, key3, iStr] = match;
|
|
61
|
+
if (!isArr(currObj[key3]) || !(key3 in currObj)) return;
|
|
62
|
+
if (i === len - 1) delete currObj[key3][Number(iStr)];
|
|
63
|
+
else currObj = currObj[key3][Number(iStr)];
|
|
64
|
+
} else {
|
|
65
|
+
if (!isObj(currObj) || !(key2 in currObj)) return;
|
|
66
|
+
if (i === len - 1) delete currObj[key2];
|
|
67
|
+
else currObj = currObj[key2];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function inAny(source, key, separator = ".", keyFunc) {
|
|
72
|
+
if (!key.includes(separator)) return key in source;
|
|
73
|
+
const keys = key.split(separator);
|
|
74
|
+
for (let currObj = source, i = 0, len = keys.length; i < len; i++) {
|
|
75
|
+
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
76
|
+
if (match) {
|
|
77
|
+
const [, key3, iStr] = match;
|
|
78
|
+
if (!isArr(currObj[key3]) || !(key3 in currObj)) return false;
|
|
79
|
+
if (i === len - 1) return true;
|
|
80
|
+
currObj = currObj[key3][Number(iStr)];
|
|
81
|
+
} else {
|
|
82
|
+
if (!isObj(currObj) || !(key2 in currObj)) return false;
|
|
83
|
+
if (i === len - 1) return true;
|
|
84
|
+
currObj = currObj[key2];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
function parseAnyObj(obj, separator = ".", keyFunc = (p) => p, visited = /* @__PURE__ */ new WeakSet()) {
|
|
90
|
+
if (!isObj(obj) || visited.has(obj)) return obj;
|
|
91
|
+
visited.add(obj);
|
|
92
|
+
const result = {};
|
|
93
|
+
Object.keys(obj).forEach((k) => k.includes(separator) ? setAny(result, k, parseAnyObj(obj[k], separator, keyFunc, visited), separator, keyFunc) : result[k] = isObj(obj[k]) ? parseAnyObj(obj[k], separator, keyFunc, visited) : obj[k]);
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
function parseEvOpts(options, opts, boolOpt = opts[0], result = {}) {
|
|
97
|
+
return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
|
|
98
|
+
}
|
|
99
|
+
function mergeObjs(o1 = {}, o2 = {}) {
|
|
100
|
+
const merged = { ...o1 || {}, ...o2 || {} };
|
|
101
|
+
return Object.keys(merged).forEach((k) => isObj(o1?.[k]) && isObj(o2?.[k]) && (merged[k] = mergeObjs(o1[k], o2[k]))), merged;
|
|
102
|
+
}
|
|
103
|
+
function getTrailPaths(path, reverse = true) {
|
|
104
|
+
const parts = path.split("."), chain = ["*"];
|
|
105
|
+
let acc = "";
|
|
106
|
+
for (let i = 0, len = parts.length; i < len; i++) chain.push(acc += (i === 0 ? "" : ".") + parts[i]);
|
|
107
|
+
return reverse ? chain.reverse() : chain;
|
|
108
|
+
}
|
|
109
|
+
function getTrailRecords(obj, path) {
|
|
110
|
+
const parts = path.split("."), record = [["*", obj, obj]];
|
|
111
|
+
let acc = "", currObj = obj;
|
|
112
|
+
for (let i = 0, len = parts.length; i < len; i++) record.push([acc += (i ? "." : "") + parts[i], currObj, currObj = currObj?.[parts[i]]]);
|
|
113
|
+
return record;
|
|
114
|
+
}
|
|
115
|
+
function deepClone(obj, crossRealms, visited = /* @__PURE__ */ new WeakMap()) {
|
|
116
|
+
if (!(isStrictObj(obj, crossRealms) || isArr(obj)) || visited.has(obj)) return obj;
|
|
117
|
+
const clone = isArr(obj) ? [] : {};
|
|
118
|
+
visited.set(obj, clone);
|
|
119
|
+
const keys = Object.keys(obj);
|
|
120
|
+
for (let i = 0, len = keys.length; i < len; i++) clone[keys[i]] = deepClone(obj[keys[i]], crossRealms, visited);
|
|
121
|
+
return clone;
|
|
122
|
+
}
|
|
123
|
+
function nuke(target) {
|
|
124
|
+
let proto = target;
|
|
125
|
+
while (proto && proto !== Object.prototype) {
|
|
126
|
+
const keys = Object.getOwnPropertyNames(proto);
|
|
127
|
+
for (let i = 0, len = keys.length; i < len; i++) {
|
|
128
|
+
if (keys[i] === "constructor") continue;
|
|
129
|
+
const desc = Object.getOwnPropertyDescriptor(proto, keys[i]);
|
|
130
|
+
if (desc && ("function" === typeof desc.value || desc.get || desc.set)) continue;
|
|
131
|
+
proto[keys[i]] = null;
|
|
132
|
+
}
|
|
133
|
+
proto = Object.getPrototypeOf(proto);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
19
136
|
export {
|
|
137
|
+
deepClone,
|
|
20
138
|
deleteAny,
|
|
21
139
|
getAny,
|
|
22
140
|
getTrailPaths,
|
package/package.json
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sia-reactor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "The Programmable Data DOM. A high-performance State Intent Architecture (S.I.A.) Engine with zero-allocation loops, event propagation, and structural sharing.",
|
|
5
5
|
"author": "Oketade Oluwatobiloba <tobioketade007@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/
|
|
9
|
+
"url": "git+https://github.com/Tobi007-del/t007-tools.git",
|
|
10
10
|
"directory": "packages/sia-reactor"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://github.com/
|
|
12
|
+
"homepage": "https://github.com/Tobi007-del/tree/main/packages/sia-reactor#readme",
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/
|
|
14
|
+
"url": "https://github.com/Tobi007-del/sia-reactor/issues"
|
|
15
15
|
},
|
|
16
16
|
"type": "module",
|
|
17
17
|
"main": "./dist/index.cjs",
|
|
18
18
|
"module": "./dist/index.js",
|
|
19
19
|
"browser": "./dist/index.global.js",
|
|
20
20
|
"unpkg": "./dist/index.global.js",
|
|
21
|
+
"jsdelivr": "./dist/index.global.js",
|
|
21
22
|
"types": "./dist/index.d.ts",
|
|
22
23
|
"sideEffects": false,
|
|
23
24
|
"exports": {
|
|
@@ -34,13 +35,19 @@
|
|
|
34
35
|
"default": "./dist/utils.js"
|
|
35
36
|
}
|
|
36
37
|
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
37
41
|
"scripts": {
|
|
38
|
-
"build": "tsup src/index.ts src/utils.ts src/types.ts --format cjs,esm,iife --global-name sia --dts --clean"
|
|
42
|
+
"build": "tsup src/index.ts src/utils.ts src/types.ts --format cjs,esm,iife --global-name sia --dts --clean --no-splitting",
|
|
43
|
+
"prepublishOnly": "shx cp ../../LICENSE ."
|
|
39
44
|
},
|
|
40
45
|
"files": [
|
|
41
46
|
"dist"
|
|
42
47
|
],
|
|
43
48
|
"keywords": [
|
|
49
|
+
"t007",
|
|
50
|
+
"ecosystem",
|
|
44
51
|
"reactor",
|
|
45
52
|
"sia",
|
|
46
53
|
"state",
|
package/dist/chunk-GN5OE7KB.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
// src/utils/obj.ts
|
|
2
|
-
var arrRx = /^([^\[\]]+)\[(\d+)\]$/;
|
|
3
|
-
function isDef(val) {
|
|
4
|
-
return val !== void 0;
|
|
5
|
-
}
|
|
6
|
-
function isArr(obj) {
|
|
7
|
-
return Array.isArray(obj);
|
|
8
|
-
}
|
|
9
|
-
function isObj(obj, checkArr = true) {
|
|
10
|
-
return "object" === typeof obj && obj !== null && (checkArr ? !Array.isArray(obj) : true);
|
|
11
|
-
}
|
|
12
|
-
function isStrictObj(obj, crossRealms = false, typecheck = true) {
|
|
13
|
-
return (typecheck ? isObj(obj, false) : true) && (crossRealms ? Object.prototype.toString.call(obj) === "[object Object]" : obj.constructor === Object);
|
|
14
|
-
}
|
|
15
|
-
function isIter(obj) {
|
|
16
|
-
return obj != null && "function" === typeof obj[Symbol.iterator];
|
|
17
|
-
}
|
|
18
|
-
function inBoolArrOpt(opt, str) {
|
|
19
|
-
return opt?.includes?.(str) ?? opt;
|
|
20
|
-
}
|
|
21
|
-
function setAny(target, key, value, separator = ".", keyFunc) {
|
|
22
|
-
if (!key.includes(separator)) return void (target[keyFunc ? keyFunc(key) : key] = value);
|
|
23
|
-
const keys = key.split(separator);
|
|
24
|
-
for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
|
|
25
|
-
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
26
|
-
if (match) {
|
|
27
|
-
const [, key3, iStr] = match;
|
|
28
|
-
if (!isArr(currObj[key3])) currObj[key3] = [];
|
|
29
|
-
if (i === len - 1) currObj[key3][Number(iStr)] = value;
|
|
30
|
-
else currObj[key3][Number(iStr)] ||= {}, currObj = currObj[key3][Number(iStr)];
|
|
31
|
-
} else {
|
|
32
|
-
if (i === len - 1) currObj[key2] = value;
|
|
33
|
-
else currObj[key2] ||= {}, currObj = currObj[key2];
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function getAny(source, key, separator = ".", keyFunc) {
|
|
38
|
-
if (!key.includes(separator)) return source[keyFunc ? keyFunc(key) : key];
|
|
39
|
-
const keys = key.split(separator);
|
|
40
|
-
let currObj = source;
|
|
41
|
-
for (let i = 0, len = keys.length; i < len; i++) {
|
|
42
|
-
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
43
|
-
if (match) {
|
|
44
|
-
const [, key3, iStr] = match;
|
|
45
|
-
if (!isArr(currObj[key3]) || !(key3 in currObj)) return void 0;
|
|
46
|
-
currObj = currObj[key3][Number(iStr)];
|
|
47
|
-
} else {
|
|
48
|
-
if (!isObj(currObj) || !(key2 in currObj)) return void 0;
|
|
49
|
-
currObj = currObj[key2];
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return currObj;
|
|
53
|
-
}
|
|
54
|
-
function deleteAny(target, key, separator = ".", keyFunc) {
|
|
55
|
-
if (!key.includes(separator)) return void delete target[keyFunc ? keyFunc(key) : key];
|
|
56
|
-
const keys = key.split(separator);
|
|
57
|
-
for (let currObj = target, i = 0, len = keys.length; i < len; i++) {
|
|
58
|
-
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
59
|
-
if (match) {
|
|
60
|
-
const [, key3, iStr] = match;
|
|
61
|
-
if (!isArr(currObj[key3]) || !(key3 in currObj)) return;
|
|
62
|
-
if (i === len - 1) delete currObj[key3][Number(iStr)];
|
|
63
|
-
else currObj = currObj[key3][Number(iStr)];
|
|
64
|
-
} else {
|
|
65
|
-
if (!isObj(currObj) || !(key2 in currObj)) return;
|
|
66
|
-
if (i === len - 1) delete currObj[key2];
|
|
67
|
-
else currObj = currObj[key2];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function inAny(source, key, separator = ".", keyFunc) {
|
|
72
|
-
if (!key.includes(separator)) return key in source;
|
|
73
|
-
const keys = key.split(separator);
|
|
74
|
-
for (let currObj = source, i = 0, len = keys.length; i < len; i++) {
|
|
75
|
-
const key2 = keyFunc ? keyFunc(keys[i]) : keys[i], match = key2.includes("[") && key2.match(arrRx);
|
|
76
|
-
if (match) {
|
|
77
|
-
const [, key3, iStr] = match;
|
|
78
|
-
if (!isArr(currObj[key3]) || !(key3 in currObj)) return false;
|
|
79
|
-
if (i === len - 1) return true;
|
|
80
|
-
currObj = currObj[key3][Number(iStr)];
|
|
81
|
-
} else {
|
|
82
|
-
if (!isObj(currObj) || !(key2 in currObj)) return false;
|
|
83
|
-
if (i === len - 1) return true;
|
|
84
|
-
currObj = currObj[key2];
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
function parseAnyObj(obj, separator = ".", keyFunc = (p) => p, visited = /* @__PURE__ */ new WeakSet()) {
|
|
90
|
-
if (!isObj(obj) || visited.has(obj)) return obj;
|
|
91
|
-
visited.add(obj);
|
|
92
|
-
const result = {};
|
|
93
|
-
Object.keys(obj).forEach((k) => k.includes(separator) ? setAny(result, k, parseAnyObj(obj[k], separator, keyFunc, visited), separator, keyFunc) : result[k] = isObj(obj[k]) ? parseAnyObj(obj[k], separator, keyFunc, visited) : obj[k]);
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
function parseEvOpts(options, opts, boolOpt = opts[0], result = {}) {
|
|
97
|
-
return Object.assign(result, "boolean" === typeof options ? { [boolOpt]: options } : options), result;
|
|
98
|
-
}
|
|
99
|
-
function mergeObjs(o1 = {}, o2 = {}) {
|
|
100
|
-
const merged = { ...o1 || {}, ...o2 || {} };
|
|
101
|
-
return Object.keys(merged).forEach((k) => isObj(o1?.[k]) && isObj(o2?.[k]) && (merged[k] = mergeObjs(o1[k], o2[k]))), merged;
|
|
102
|
-
}
|
|
103
|
-
function getTrailPaths(path, reverse = true) {
|
|
104
|
-
const parts = path.split("."), chain = ["*"];
|
|
105
|
-
let acc = "";
|
|
106
|
-
for (let i = 0, len = parts.length; i < len; i++) chain.push(acc += (i === 0 ? "" : ".") + parts[i]);
|
|
107
|
-
return reverse ? chain.reverse() : chain;
|
|
108
|
-
}
|
|
109
|
-
function getTrailRecords(obj, path) {
|
|
110
|
-
const parts = path.split("."), record = [["*", obj, obj]];
|
|
111
|
-
let acc = "", currObj = obj;
|
|
112
|
-
for (let i = 0, len = parts.length; i < len; i++) record.push([acc += (i ? "." : "") + parts[i], currObj, currObj = currObj?.[parts[i]]]);
|
|
113
|
-
return record;
|
|
114
|
-
}
|
|
115
|
-
function nuke(target) {
|
|
116
|
-
let proto = target;
|
|
117
|
-
while (proto && proto !== Object.prototype) {
|
|
118
|
-
const keys = Object.getOwnPropertyNames(proto);
|
|
119
|
-
for (let i = 0, len = keys.length; i < len; i++) {
|
|
120
|
-
if (keys[i] === "constructor") continue;
|
|
121
|
-
const desc = Object.getOwnPropertyDescriptor(proto, keys[i]);
|
|
122
|
-
if (desc && ("function" === typeof desc.value || desc.get || desc.set)) continue;
|
|
123
|
-
proto[keys[i]] = null;
|
|
124
|
-
}
|
|
125
|
-
proto = Object.getPrototypeOf(proto);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export {
|
|
130
|
-
isDef,
|
|
131
|
-
isArr,
|
|
132
|
-
isObj,
|
|
133
|
-
isStrictObj,
|
|
134
|
-
isIter,
|
|
135
|
-
inBoolArrOpt,
|
|
136
|
-
setAny,
|
|
137
|
-
getAny,
|
|
138
|
-
deleteAny,
|
|
139
|
-
inAny,
|
|
140
|
-
parseAnyObj,
|
|
141
|
-
parseEvOpts,
|
|
142
|
-
mergeObjs,
|
|
143
|
-
getTrailPaths,
|
|
144
|
-
getTrailRecords,
|
|
145
|
-
nuke
|
|
146
|
-
};
|