memorio 5.0.0 → 5.1.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 +80 -449
- package/SECURITY.md +152 -42
- package/SUMMARY.md +1 -1
- package/adr/001-state-proxy-model.md +95 -96
- package/adr/002-observer-semantics.md +179 -180
- package/adr/003-deep-mutation-semantics.md +7 -8
- package/adr/004-array-mutation-semantics.md +127 -128
- package/adr/005-scheduler-contract.md +148 -149
- package/adr/006-context-isolation.md +91 -92
- package/adr/007-mutation-records.md +5 -6
- package/adr/008-transactions.md +6 -7
- package/adr/009-history-model.md +6 -7
- package/adr/README.md +46 -46
- package/adr/template.md +48 -49
- package/bin/cli.js +68 -0
- package/global.cjs +1462 -323
- package/global.js +1459 -324
- package/index.cjs +1462 -323
- package/index.d.ts +1 -0
- package/index.js +1459 -324
- package/llms.txt +42 -5
- package/markdown/AUDIT-REPORT.md +7 -8
- package/markdown/CACHE.md +190 -99
- package/markdown/DEVTOOLS.md +0 -1
- package/markdown/DISPATCH.md +0 -1
- package/markdown/HISTORY.md +0 -1
- package/markdown/IDB.md +0 -1
- package/markdown/IMPORT.md +0 -1
- package/markdown/INSPECT.md +0 -1
- package/markdown/LOGGER.md +0 -1
- package/markdown/MEMORY-ATTACHMENT.md +0 -1
- package/markdown/MEMORY.md +0 -1
- package/markdown/OBSERVER.md +0 -1
- package/markdown/PLATFORM.md +277 -271
- package/markdown/REDUX.md +54 -0
- package/markdown/SCHEMA.md +0 -1
- package/markdown/SESSION.md +0 -1
- package/markdown/SQLITE.md +0 -1
- package/markdown/STATE.md +0 -1
- package/markdown/STORE.md +0 -1
- package/markdown/SYNC.md +0 -1
- package/markdown/TYPED.md +0 -1
- package/markdown/USEOBSERVER.md +0 -1
- package/modules/redux.cjs +381 -10
- package/modules/redux.cjs.map +1 -1
- package/modules/redux.js +381 -10
- package/modules/redux.js.map +1 -1
- package/package.json +14 -2
- package/types/broadcast.d.ts +61 -0
- package/types/computed.d.ts +96 -0
- package/types/encryption.d.ts +129 -0
- package/types/exports.d.ts +9 -0
- package/types/memorio.d.ts +19 -12
- package/types/security.d.ts +67 -0
- package/types/session.d.ts +23 -5
- package/types/store.d.ts +19 -3
- package/vsix/memorio.vsix +0 -0
- package/markdown/CHANGELOG.md +0 -243
- package/markdown/PROJECT.md +0 -311
- package/markdown/SECURITY.md +0 -330
|
@@ -1,128 +1,127 @@
|
|
|
1
|
-
# ADR-004: Array Mutation Semantics
|
|
2
|
-
|
|
3
|
-
> **Status:** Proposed
|
|
4
|
-
> **Date:** 2026-09-12
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- The
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
| `state.arr
|
|
44
|
-
| `state.arr.
|
|
45
|
-
| `state.arr.
|
|
46
|
-
| `state.arr.
|
|
47
|
-
| `state.arr.
|
|
48
|
-
| `state.arr.
|
|
49
|
-
| `state.arr.
|
|
50
|
-
| `state.arr.
|
|
51
|
-
| `state.arr.
|
|
52
|
-
| `state.arr.length = n` (
|
|
53
|
-
| `state.arr
|
|
54
|
-
| `state.arr =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
`
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
`
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- `tests/vitest/tests/contracts/adr-004-arrays.test.ts`
|
|
1
|
+
# ADR-004: Array Mutation Semantics
|
|
2
|
+
|
|
3
|
+
> **Status:** Proposed
|
|
4
|
+
> **Date:** 2026-09-12
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Arrays are the most error-prone data structure in proxy-based state
|
|
9
|
+
libraries. The 4.7.3 baseline handled `push` and `pop` but had no
|
|
10
|
+
explicit contract for `sort`, `splice`, `shift`, `unshift`, `fill`,
|
|
11
|
+
`reverse`, `length` changes, or whole-array replacement. Section 9 of
|
|
12
|
+
the Memorio 5.x plan demands explicit, regression-tested behavior for
|
|
13
|
+
every array operation.
|
|
14
|
+
|
|
15
|
+
A mutation that persists but does not notify, or notifies but does not
|
|
16
|
+
persist, is considered a **semantic defect** unless explicitly
|
|
17
|
+
documented.
|
|
18
|
+
|
|
19
|
+
## Assumptions
|
|
20
|
+
|
|
21
|
+
- Arrays are plain JS arrays stored on the Proxy raw target.
|
|
22
|
+
- Array methods (`push`, `pop`, `splice`, etc.) mutate the array in
|
|
23
|
+
place and trigger the set trap on the Proxy wrapping the array.
|
|
24
|
+
- The set trap's `key` for array mutations is the array index or
|
|
25
|
+
`length`.
|
|
26
|
+
- The callback receives `{ action, path, newValue, previousValue }`.
|
|
27
|
+
- The state Proxy dispatches via `dispatch.set("state." + path)`.
|
|
28
|
+
|
|
29
|
+
## Decision
|
|
30
|
+
|
|
31
|
+
### Mutation → Persistence mapping
|
|
32
|
+
|
|
33
|
+
Every array operation below **must** persist to the underlying raw
|
|
34
|
+
array and **must** dispatch an event on the **array path**
|
|
35
|
+
(`state.<arrayPath>`), not on the individual index path. The event
|
|
36
|
+
path is computed from the Proxy `tree` (e.g. for `state.items`, the
|
|
37
|
+
tree is `["items"]`, so the path is `items` and the event is
|
|
38
|
+
`state.items`).
|
|
39
|
+
|
|
40
|
+
| Operation | Persists? | Dispatches on | Patch op |
|
|
41
|
+
|-----------|-----------|---------------|----------|
|
|
42
|
+
| `state.arr[i] = val` | Yes | `state.arr` | `replace` (index i) |
|
|
43
|
+
| `state.arr.push(val)` | Yes | `state.arr` | `add` (at index `arr.length`) |
|
|
44
|
+
| `state.arr.pop()` | Yes | `state.arr` | `remove` (last index) |
|
|
45
|
+
| `state.arr.shift()` | Yes | `state.arr` | `remove` (index 0) |
|
|
46
|
+
| `state.arr.unshift(val)` | Yes | `state.arr` | `add` (at index 0) |
|
|
47
|
+
| `state.arr.splice(i, n, ...items)` | Yes | `state.arr` | `remove` + `add` |
|
|
48
|
+
| `state.arr.sort()` | Yes | `state.arr` | `replace` (whole array) |
|
|
49
|
+
| `state.arr.reverse()` | Yes | `state.arr` | `replace` (whole array) |
|
|
50
|
+
| `state.arr.fill(val, start, end)` | Yes | `state.arr` | `replace` (whole array) |
|
|
51
|
+
| `state.arr.length = n` (truncate) | Yes | `state.arr` | `remove` (removed indices) |
|
|
52
|
+
| `state.arr.length = n` (extend) | Yes | `state.arr` | `add` (new indices) |
|
|
53
|
+
| `state.arr = newArray` | Yes | `state.arr` | `replace` (whole array) |
|
|
54
|
+
| `state.arr[i].prop = val` | Yes | `state.arr.i.prop` | `replace` (leaf) |
|
|
55
|
+
|
|
56
|
+
### sort() persistence
|
|
57
|
+
|
|
58
|
+
`sort()` mutates the array in place and returns a reference to the same
|
|
59
|
+
(sorted) array. The Proxy set trap fires because `length` changes and
|
|
60
|
+
indices are reassigned. The event dispatched is `state.arr`. The
|
|
61
|
+
mutation is **persisted** to the raw target.
|
|
62
|
+
|
|
63
|
+
**Known constraint:** `sort()` with a custom comparator must also
|
|
64
|
+
persist correctly. The comparator function is applied by V8's native
|
|
65
|
+
`Array.prototype.sort`; the Proxy only sees the resulting index
|
|
66
|
+
reassignments.
|
|
67
|
+
|
|
68
|
+
### splice() semantics
|
|
69
|
+
|
|
70
|
+
`splice()` can add, remove, or replace elements at an index. The Proxy
|
|
71
|
+
sees multiple set/delete traps. The event dispatched is `state.arr`
|
|
72
|
+
(the first trap fires, and subsequent traps on the same array path
|
|
73
|
+
are de-duplicated by the dispatch layer's microtask scheduling).
|
|
74
|
+
|
|
75
|
+
### length changes
|
|
76
|
+
|
|
77
|
+
Setting `arr.length = n` where `n < arr.length` truncates the array.
|
|
78
|
+
The deleteProperty trap fires for each removed index. Setting
|
|
79
|
+
`arr.length = n` where `n > arr.length` extends the array with
|
|
80
|
+
`undefined` holes; the set trap fires for `length`.
|
|
81
|
+
|
|
82
|
+
### Whole-array replacement
|
|
83
|
+
|
|
84
|
+
`state.arr = [1, 2, 3]` replaces the entire array. The set trap fires
|
|
85
|
+
once with `path = "arr"`, `newValue = [1, 2, 3]` (deepRaw'd). The
|
|
86
|
+
event dispatched is `state.arr`. Observers on `state.arr` fire.
|
|
87
|
+
Previous array observers (on the old array instance) are orphaned -
|
|
88
|
+
the consumer must re-register if they observe specific indices.
|
|
89
|
+
|
|
90
|
+
### Array element object mutation
|
|
91
|
+
|
|
92
|
+
`state.arr[0].name = "X"` mutates an object inside the array. This
|
|
93
|
+
fires the set trap on the Proxy wrapping `arr[0]` with
|
|
94
|
+
`path = "arr.0.name"`. The event dispatched is `state.arr.0.name`.
|
|
95
|
+
|
|
96
|
+
Per ADR-003, this does **not** notify observers on `state.arr`. This
|
|
97
|
+
is a known limitation. Consumers who need to observe changes to
|
|
98
|
+
objects within arrays must observe the specific element path
|
|
99
|
+
(`state.arr.0.name`) or use `useObserver` auto-discovery.
|
|
100
|
+
|
|
101
|
+
### Event deduplication
|
|
102
|
+
|
|
103
|
+
Because a single array operation (e.g. `splice`) can produce multiple
|
|
104
|
+
set/delete traps, the dispatch layer may fire `state.arr` multiple
|
|
105
|
+
times within the same synchronous block. Each dispatch schedules a
|
|
106
|
+
separate microtask. Observers on `state.arr` will fire **multiple
|
|
107
|
+
times** - once per trap. A future scheduler enhancement may coalesce
|
|
108
|
+
these. This ADR documents the current behavior.
|
|
109
|
+
|
|
110
|
+
## Consequences
|
|
111
|
+
|
|
112
|
+
- **Positive:** Every array operation persists and notifies - no
|
|
113
|
+
silent failures.
|
|
114
|
+
- **Positive:** Array observers fire on the array path, not individual
|
|
115
|
+
indices - this matches React list-reconciliation mental model.
|
|
116
|
+
- **Positive:** `sort()`, `splice()`, and other complex operations are
|
|
117
|
+
explicitly tested for persistence + notification.
|
|
118
|
+
- **Negative:** Multiple traps from a single array operation may
|
|
119
|
+
produce multiple observer notifications - consumers must
|
|
120
|
+
de-duplicate if needed.
|
|
121
|
+
- **Negative:** Array element object mutations do not notify the
|
|
122
|
+
array-path observer - this is a fundamental limitation of the
|
|
123
|
+
exact-path observer model.
|
|
124
|
+
|
|
125
|
+
## Compliance tests
|
|
126
|
+
|
|
127
|
+
- `tests/vitest/tests/contracts/adr-004-arrays.test.ts`
|
|
@@ -1,149 +1,148 @@
|
|
|
1
|
-
# ADR-005: Scheduler Contract
|
|
2
|
-
|
|
3
|
-
> **Status:** Proposed
|
|
4
|
-
> **Date:** 2026-09-12
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
The
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
- **
|
|
141
|
-
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
- `tests/vitest/tests/contracts/adr-005-scheduler.test.ts`
|
|
1
|
+
# ADR-005: Scheduler Contract
|
|
2
|
+
|
|
3
|
+
> **Status:** Proposed
|
|
4
|
+
> **Date:** 2026-09-12
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The Memorio 5.x plan (Section 10) requires an explicit, configurable
|
|
9
|
+
scheduler. The 4.7.3 baseline dispatches observer callbacks via
|
|
10
|
+
`queueMicrotask` (in `core/dispatch.ts`). There is no configuration
|
|
11
|
+
point, no batching, and no flush control. This must be formalized into
|
|
12
|
+
a contract that supports future UI-framework integration (React
|
|
13
|
+
batched mode, requestAnimationFrame, manual flush for tests).
|
|
14
|
+
|
|
15
|
+
The scheduler contract must define: ordering, batching, reentrancy,
|
|
16
|
+
error isolation, nested mutations, transaction interaction, and flush
|
|
17
|
+
behavior.
|
|
18
|
+
|
|
19
|
+
## Assumptions
|
|
20
|
+
|
|
21
|
+
- The core engine dispatches events via `dispatch.set(name, value)`.
|
|
22
|
+
- `dispatch.listen(name, cb)` wraps `cb` in a microtask boundary.
|
|
23
|
+
- The scheduler is orthogonal to the Mutation Engine (ADR-007) and
|
|
24
|
+
Transactions (ADR-008) - it only affects *when* observer callbacks
|
|
25
|
+
fire, not *what* is recorded.
|
|
26
|
+
- `__DEV__` is a build-time constant.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
### Current default: microtask
|
|
31
|
+
|
|
32
|
+
The current dispatch layer uses `queueMicrotask` (or
|
|
33
|
+
`Promise.resolve().then(cb)` fallback). This means:
|
|
34
|
+
|
|
35
|
+
1. **Ordering:** Observer callbacks fire in registration order (FIFO),
|
|
36
|
+
within the microtask queue, after the current synchronous block
|
|
37
|
+
completes.
|
|
38
|
+
2. **Asynchronous:** Callbacks fire after the mutation is committed
|
|
39
|
+
to the raw Proxy target.
|
|
40
|
+
3. **No batching:** Each `dispatch.set` schedules its own microtask.
|
|
41
|
+
|
|
42
|
+
### Proposed scheduler configuration
|
|
43
|
+
|
|
44
|
+
A future `createMemorio({ scheduler: ... })` or
|
|
45
|
+
`memorio.configureScheduler({ mode, batching })` API would allow:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
type SchedulerMode = "sync" | "microtask" | "macrotask" | "raf" | "manual"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
| Mode | Behavior |
|
|
52
|
+
|------|----------|
|
|
53
|
+
| `sync` | Callbacks fire synchronously during `dispatch.set`. |
|
|
54
|
+
| `microtask` | Callbacks fire in the microtask queue (default). |
|
|
55
|
+
| `macrotask` | Callbacks fire via `setTimeout(cb, 0)` (macro task). |
|
|
56
|
+
| `raf` | Callbacks fire via `requestAnimationFrame`. |
|
|
57
|
+
| `manual` | Callbacks only fire when `flush()` is called. |
|
|
58
|
+
|
|
59
|
+
### Batching semantics (proposed)
|
|
60
|
+
|
|
61
|
+
When `batching: true` (only meaningful with `microtask` or `macrotask`
|
|
62
|
+
mode):
|
|
63
|
+
|
|
64
|
+
1. All `dispatch.set` calls within a single synchronous block are
|
|
65
|
+
collected.
|
|
66
|
+
2. Each unique event name is invoked once, after the synchronous
|
|
67
|
+
block completes, in the scheduled task.
|
|
68
|
+
3. The order of batched callbacks follows the order of first
|
|
69
|
+
`dispatch.set` for each unique event name.
|
|
70
|
+
|
|
71
|
+
When `batching: false` (default):
|
|
72
|
+
|
|
73
|
+
1. Each `dispatch.set` schedules its own callback independently.
|
|
74
|
+
|
|
75
|
+
### Reentrancy
|
|
76
|
+
|
|
77
|
+
- Reentrant `dispatch.set` calls (observer callbacks that dispatch new
|
|
78
|
+
events) are queued after the current callback completes.
|
|
79
|
+
- `sync` mode has no reentrancy boundary - recursive
|
|
80
|
+
`dispatch.set` calls fire immediately, risking stack overflow.
|
|
81
|
+
- No built-in guard against infinite dispatch loops. Consumers must
|
|
82
|
+
break the cycle.
|
|
83
|
+
|
|
84
|
+
### Error isolation
|
|
85
|
+
|
|
86
|
+
- A throwing callback in `microtask`/`macrotask`/`raf` mode produces
|
|
87
|
+
an unhandled rejection / error. Other callbacks scheduled in the same
|
|
88
|
+
tick are unaffected (each runs in its own closure).
|
|
89
|
+
- In `sync` mode, a throwing callback propagates to the caller of
|
|
90
|
+
`dispatch.set`.
|
|
91
|
+
- A future enhancement may wrap each callback in `try/catch` with an
|
|
92
|
+
optional error reporter. This ADR does **not** implement that.
|
|
93
|
+
|
|
94
|
+
### Nested mutations
|
|
95
|
+
|
|
96
|
+
Mutations inside observer callbacks create a new dispatch cycle. They
|
|
97
|
+
are **not** coalesced with the current cycle. This means:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
state.counter = 1
|
|
101
|
+
// microtask fires:
|
|
102
|
+
observer('state.counter', () => {
|
|
103
|
+
state.counter = 2 // → schedules a NEW microtask
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Transaction interaction
|
|
108
|
+
|
|
109
|
+
- During a transaction, each mutation dispatches normally (no
|
|
110
|
+
suppression).
|
|
111
|
+
- `undo()` / `redo()` set `internal.historyEnabled = false` to prevent
|
|
112
|
+
re-recording, but the Proxy set trap still dispatches events to
|
|
113
|
+
observers.
|
|
114
|
+
- A future "transaction-level notification" may suppress individual
|
|
115
|
+
mutation dispatches during a transaction and fire a single
|
|
116
|
+
"transaction-end" event. This ADR does **not** define that.
|
|
117
|
+
|
|
118
|
+
### Flush behavior
|
|
119
|
+
|
|
120
|
+
- In `manual` mode, `memorio.flush()` (or equivalent) processes all
|
|
121
|
+
queued callbacks. This is primarily for testing.
|
|
122
|
+
- In other modes, "flush" is implicit (microtask queue drains
|
|
123
|
+
naturally).
|
|
124
|
+
|
|
125
|
+
## Current status
|
|
126
|
+
|
|
127
|
+
The scheduler is currently **not configurable**. The default
|
|
128
|
+
`microtask` behavior is in effect. This ADR is **Proposed** - the
|
|
129
|
+
configuration API is a Phase 2/3 enhancement. The compliance tests
|
|
130
|
+
for the default behavior lock in the current semantics so that future
|
|
131
|
+
scheduler changes do not break existing code.
|
|
132
|
+
|
|
133
|
+
## Consequences
|
|
134
|
+
|
|
135
|
+
- **Positive:** Documenting the microtask default gives consumers a
|
|
136
|
+
deterministic mental model.
|
|
137
|
+
- **Positive:** The proposed API is framework-agnostic - all modes are
|
|
138
|
+
expressible as platform primitives.
|
|
139
|
+
- **Positive:** `manual` mode enables deterministic testing.
|
|
140
|
+
- **Negative:** No batching means multiple synchronous mutations each
|
|
141
|
+
produce a microtask - this is a known performance limitation for
|
|
142
|
+
bulk updates.
|
|
143
|
+
- **Negative:** No error isolation means a buggy observer can crash
|
|
144
|
+
the app via unhandled rejection.
|
|
145
|
+
|
|
146
|
+
## Compliance tests
|
|
147
|
+
|
|
148
|
+
- `tests/vitest/tests/contracts/adr-005-scheduler.test.ts`
|