@llui/dom 0.0.39 → 0.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 +22 -21
- package/dist/binding.d.ts +5 -0
- package/dist/binding.d.ts.map +1 -1
- package/dist/binding.js +1 -0
- package/dist/binding.js.map +1 -1
- package/dist/combine.d.ts +67 -0
- package/dist/combine.d.ts.map +1 -0
- package/dist/combine.js +76 -0
- package/dist/combine.js.map +1 -0
- package/dist/devtools.d.ts.map +1 -1
- package/dist/devtools.js +15 -5
- package/dist/devtools.js.map +1 -1
- package/dist/el-split.d.ts +1 -1
- package/dist/el-split.d.ts.map +1 -1
- package/dist/el-split.js +20 -2
- package/dist/el-split.js.map +1 -1
- package/dist/el-template.d.ts +8 -2
- package/dist/el-template.d.ts.map +1 -1
- package/dist/el-template.js +2 -1
- package/dist/el-template.js.map +1 -1
- package/dist/escape-hatch.d.ts +47 -0
- package/dist/escape-hatch.d.ts.map +1 -0
- package/dist/escape-hatch.js +63 -0
- package/dist/escape-hatch.js.map +1 -0
- package/dist/hmr.d.ts +2 -1
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hmr.js +3 -2
- package/dist/hmr.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/primitives/branch.d.ts.map +1 -1
- package/dist/primitives/branch.js +1 -0
- package/dist/primitives/branch.js.map +1 -1
- package/dist/primitives/each.d.ts.map +1 -1
- package/dist/primitives/each.js +14 -4
- package/dist/primitives/each.js.map +1 -1
- package/dist/primitives/memo.d.ts +2 -2
- package/dist/primitives/memo.d.ts.map +1 -1
- package/dist/primitives/memo.js +13 -5
- package/dist/primitives/memo.js.map +1 -1
- package/dist/primitives/unsafe-html.d.ts.map +1 -1
- package/dist/primitives/unsafe-html.js +1 -0
- package/dist/primitives/unsafe-html.js.map +1 -1
- package/dist/primitives/virtual-each.d.ts.map +1 -1
- package/dist/primitives/virtual-each.js +10 -0
- package/dist/primitives/virtual-each.js.map +1 -1
- package/dist/structural.d.ts +8 -4
- package/dist/structural.d.ts.map +1 -1
- package/dist/structural.js.map +1 -1
- package/dist/types.d.ts +21 -12
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/update-loop.d.ts +14 -6
- package/dist/update-loop.d.ts.map +1 -1
- package/dist/update-loop.js +130 -34
- package/dist/update-loop.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -70,30 +70,31 @@ Element helpers (`div`, `button`, `span`, etc.) stay as imports — they're stat
|
|
|
70
70
|
|
|
71
71
|
### View Primitives
|
|
72
72
|
|
|
73
|
-
| Primitive | Purpose
|
|
74
|
-
| ---------------------------------- |
|
|
75
|
-
| `text(accessor)` | Reactive text node
|
|
76
|
-
| `show({ when, render })` | Conditional rendering
|
|
77
|
-
| `branch({ on, cases, default? })` | Multi-case switching with optional default
|
|
78
|
-
| `scope({ on, render })` | Keyed subtree rebuild on key change
|
|
79
|
-
| `each({ items, key, render })` | Keyed list rendering
|
|
80
|
-
| `portal({ target, render })` | Render into a different DOM location
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `slice(h, selector)` | View over a sub-slice of state |
|
|
73
|
+
| Primitive | Purpose |
|
|
74
|
+
| ---------------------------------- | ------------------------------------------- |
|
|
75
|
+
| `text(accessor)` | Reactive text node |
|
|
76
|
+
| `show({ when, render })` | Conditional rendering |
|
|
77
|
+
| `branch({ on, cases, default? })` | Multi-case switching with optional default |
|
|
78
|
+
| `scope({ on, render })` | Keyed subtree rebuild on key change |
|
|
79
|
+
| `each({ items, key, render })` | Keyed list rendering |
|
|
80
|
+
| `portal({ target, render })` | Render into a different DOM location |
|
|
81
|
+
| `memo(accessor)` | Memoized derived value |
|
|
82
|
+
| `sample(selector)` | One-shot imperative state read (no binding) |
|
|
83
|
+
| `selector(field)` | O(1) one-of-N selection binding |
|
|
84
|
+
| `onMount(callback)` | Lifecycle hook (runs once after mount) |
|
|
85
|
+
| `errorBoundary(opts)` | Catch render errors |
|
|
86
|
+
| `foreign({ create, update })` | Integrate non-LLui libraries |
|
|
87
|
+
| `clientOnly({ render, fallback })` | Browser-only subtree (skipped during SSR) |
|
|
88
|
+
| `slice(h, selector)` | View over a sub-slice of state |
|
|
90
89
|
|
|
91
90
|
### Composition
|
|
92
91
|
|
|
93
|
-
| Export
|
|
94
|
-
|
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
92
|
+
| Export | Purpose |
|
|
93
|
+
| ------------------------------------------- | ----------------------------------------------------------------------- |
|
|
94
|
+
| `combine({ slice: reducer, ... }, top?)` | Compose slice reducers by `${slice}/${action}` message-prefix routing |
|
|
95
|
+
| `mergeHandlers(...handlers)` | Combine multiple update handlers |
|
|
96
|
+
| `sliceHandler({ get, set, narrow, sub })` | Route messages to a state slice |
|
|
97
|
+
| `subApp({ reason, def, data?, onHandle? })` | Embed an isolated TEA loop (escape hatch — requires non-empty `reason`) |
|
|
97
98
|
|
|
98
99
|
### Context
|
|
99
100
|
|
package/dist/binding.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Lifetime, Binding, BindingKind } from './types.js';
|
|
2
2
|
export interface CreateBindingOpts {
|
|
3
3
|
mask: number;
|
|
4
|
+
/**
|
|
5
|
+
* High-word bits 31..61. Optional; defaults to 0. Compiler-emitted
|
|
6
|
+
* only when the accessor reads a prefix past bit 30.
|
|
7
|
+
*/
|
|
8
|
+
maskHi?: number;
|
|
4
9
|
accessor: (state: never) => unknown;
|
|
5
10
|
kind: BindingKind;
|
|
6
11
|
node: Node;
|
package/dist/binding.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGhE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IACnC,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,IAAI,CAAA;IACV,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB;AAID,wBAAgB,eAAe,IAAI,OAAO,EAAE,GAAG,IAAI,CAElD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,IAAI,CAE3D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGhE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IACnC,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,IAAI,CAAA;IACV,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB;AAID,wBAAgB,eAAe,IAAI,OAAO,EAAE,GAAG,IAAI,CAElD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,IAAI,CAE3D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAkB/E;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACvD,KAAK,EAAE,OAAO,GACb,IAAI,CA+DN"}
|
package/dist/binding.js
CHANGED
package/dist/binding.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Lifetime, Binding, BindingKind } from './types.js'\nimport { addBinding } from './lifetime.js'\n\nexport interface CreateBindingOpts {\n mask: number\n /**\n * High-word bits 31..61. Optional; defaults to 0. Compiler-emitted\n * only when the accessor reads a prefix past bit 30.\n */\n maskHi?: number\n accessor: (state: never) => unknown\n kind: BindingKind\n node: Node\n key?: string\n perItem: boolean\n}\n\nlet flatBindings: Binding[] | null = null\n\nexport function getFlatBindings(): Binding[] | null {\n return flatBindings\n}\n\nexport function setFlatBindings(arr: Binding[] | null): void {\n flatBindings = arr\n}\n\nexport function createBinding(scope: Lifetime, opts: CreateBindingOpts): Binding {\n const binding: Binding = {\n mask: opts.mask,\n maskHi: opts.maskHi ?? 0,\n accessor: opts.accessor as (state: unknown) => unknown,\n lastValue: undefined,\n kind: opts.kind,\n node: opts.node,\n key: opts.key,\n ownerLifetime: scope,\n perItem: opts.perItem,\n dead: false,\n }\n\n addBinding(scope, binding)\n if (flatBindings) flatBindings.push(binding)\n\n return binding\n}\n\nexport function applyBinding(\n target: { kind: BindingKind; node: Node; key?: string },\n value: unknown,\n): void {\n // Defensive guard: if a reactive accessor leaks through as a raw\n // function value, emitting its `.toString()` into the DOM (e.g. as\n // an attribute) would be a silent correctness bug that only surfaces\n // on server-rendered pages. Throw loudly so the callsite is obvious.\n // Event handlers (onXxx → 'prop' kind) are NOT handled here; events\n // are registered via addEventListener in the element helpers, not\n // via applyBinding.\n if (typeof value === 'function') {\n throw new TypeError(\n `[LLui] applyBinding(${target.kind}${target.key ? `, '${target.key}'` : ''}) received ` +\n `a function as its value. This means an accessor wasn't invoked before ` +\n `reaching the binding layer — usually a bug in a compiled binding tuple or ` +\n `in a helper that forwards props without calling them. The arrow's source ` +\n `would have been serialized into the DOM otherwise. Offender: ${value.toString().slice(0, 120)}`,\n )\n }\n\n switch (target.kind) {\n case 'effect':\n // Side-effect-only binding — the accessor already ran for its\n // side effects in Phase 2. We keep `applyBinding` callable for\n // type-uniform call sites, but there is no DOM write to perform.\n break\n\n case 'text':\n target.node.nodeValue = String(value)\n break\n\n case 'prop': {\n const el = target.node as HTMLElement\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ;(el as any)[target.key!] = value\n break\n }\n\n case 'attr':\n if (value == null) {\n ;(target.node as Element).removeAttribute(target.key!)\n } else if (value === false) {\n // ARIA attributes need explicit \"false\"; others are removed\n if (target.key!.startsWith('aria-')) {\n ;(target.node as Element).setAttribute(target.key!, 'false')\n } else {\n ;(target.node as Element).removeAttribute(target.key!)\n }\n } else {\n ;(target.node as Element).setAttribute(target.key!, String(value))\n }\n break\n\n case 'class':\n ;(target.node as Element).setAttribute('class', value as string)\n break\n\n case 'style':\n if (value == null) {\n ;(target.node as HTMLElement).style.removeProperty(target.key!)\n } else {\n ;(target.node as HTMLElement).style.setProperty(target.key!, value as string)\n }\n break\n }\n}\n"]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A slice reducer — operates on a sub-tree of host state plus its own
|
|
3
|
+
* message variants, returns the next sub-tree and effects to dispatch.
|
|
4
|
+
* Effects from slice reducers bubble up to the top-level effect handler
|
|
5
|
+
* unchanged.
|
|
6
|
+
*/
|
|
7
|
+
export type SliceReducer<Slice, Msg, Effect> = (state: Slice, msg: Msg) => [Slice, Effect[]];
|
|
8
|
+
/**
|
|
9
|
+
* Map of slice name → its reducer. The slice name is BOTH:
|
|
10
|
+
* - The top-level state key the slice lives under (`s[name]`)
|
|
11
|
+
* - The message-namespace prefix that routes to this slice
|
|
12
|
+
* (`{type: 'name/action'}` → routes to slice `name`)
|
|
13
|
+
*
|
|
14
|
+
* The matching state shape's top-level keys must therefore be the same
|
|
15
|
+
* set as the routing prefixes — TypeScript enforces this via the `S`
|
|
16
|
+
* generic. Messages whose `type` doesn't start with `${name}/` (or
|
|
17
|
+
* doesn't contain `/` at all) fall through to the optional `_top`
|
|
18
|
+
* reducer below.
|
|
19
|
+
*/
|
|
20
|
+
export type SliceMap<S, _M, E> = {
|
|
21
|
+
[K in keyof S]?: SliceReducer<S[K], any, E>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Optional top-level reducer for messages that don't match any slice
|
|
25
|
+
* prefix. Use for cross-cutting messages (e.g. `{type: 'reset'}`) or
|
|
26
|
+
* messages that operate on multiple slices at once.
|
|
27
|
+
*/
|
|
28
|
+
export type TopReducer<S, M, E> = (state: S, msg: M) => [S, E[]];
|
|
29
|
+
/**
|
|
30
|
+
* Compose slice reducers into a single top-level reducer.
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* type AppState = { matrix: MatrixState; ui: UiState }
|
|
34
|
+
* type AppMsg =
|
|
35
|
+
* | { type: 'matrix/setName'; v: string }
|
|
36
|
+
* | { type: 'ui/toggleSidebar' }
|
|
37
|
+
* | { type: 'reset' }
|
|
38
|
+
*
|
|
39
|
+
* const update = combine<AppState, AppMsg, AppEffect>({
|
|
40
|
+
* matrix: matrixUpdate,
|
|
41
|
+
* ui: uiUpdate,
|
|
42
|
+
* }, (s, msg) => {
|
|
43
|
+
* if (msg.type === 'reset') return [initialState(), []]
|
|
44
|
+
* return [s, []]
|
|
45
|
+
* })
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* Routing:
|
|
49
|
+
* - `{type: 'matrix/setName'}` → `matrixUpdate(s.matrix, msg)`. The slice
|
|
50
|
+
* reducer sees the FULL message (including the `matrix/` prefix); the
|
|
51
|
+
* slice's own update body matches on `.type`. This is intentional: it
|
|
52
|
+
* keeps slice reducers usable standalone in unit tests.
|
|
53
|
+
* - `{type: 'reset'}` → routes to the optional top reducer (the second
|
|
54
|
+
* argument). If `_top` is absent, the message is a no-op (state
|
|
55
|
+
* unchanged, no effects).
|
|
56
|
+
*
|
|
57
|
+
* Effects bubble: slice reducers and the top reducer can both return
|
|
58
|
+
* effects; combine concatenates them into the returned array in
|
|
59
|
+
* source order.
|
|
60
|
+
*
|
|
61
|
+
* @param slices Map of slice name → reducer
|
|
62
|
+
* @param _top Optional cross-cutting reducer for unprefixed messages
|
|
63
|
+
*/
|
|
64
|
+
export declare function combine<S, M extends {
|
|
65
|
+
type: string;
|
|
66
|
+
}, E>(slices: SliceMap<S, M, E>, _top?: TopReducer<S, M, E>): (state: S, msg: M) => [S, E[]];
|
|
67
|
+
//# sourceMappingURL=combine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combine.d.ts","sourceRoot":"","sources":["../src/combine.ts"],"names":[],"mappings":"AAiBA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;AAE5F;;;;;;;;;;;GAWG;AAIH,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI;KAE9B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;CAC5C,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EACtD,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EACzB,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GACzB,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAsBhC"}
|
package/dist/combine.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Reducer composition for slice-based decomposition.
|
|
2
|
+
//
|
|
3
|
+
// `combine()` is the canonical way to compose multiple sub-reducers into
|
|
4
|
+
// a single top-level reducer. Each slice owns a sub-tree of state and an
|
|
5
|
+
// independent `(slice, msg) → [slice, effects]` reducer; the host's
|
|
6
|
+
// state shape nests these slices as top-level keys; messages are routed
|
|
7
|
+
// to slices by namespace (`{type: 'slice/action', ...}` → `slice`'s
|
|
8
|
+
// reducer).
|
|
9
|
+
//
|
|
10
|
+
// This is the load-bearing piece of the unified-composition-model: it
|
|
11
|
+
// replaces the per-component `update()` machinery that `child()` /
|
|
12
|
+
// `component()` previously gave you. A slice is just a module exporting
|
|
13
|
+
// `update` + (optionally) `view` functions. Hosts wire them together
|
|
14
|
+
// with `combine()`.
|
|
15
|
+
//
|
|
16
|
+
// See `docs/proposals/unified-composition-model.md`.
|
|
17
|
+
/**
|
|
18
|
+
* Compose slice reducers into a single top-level reducer.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* type AppState = { matrix: MatrixState; ui: UiState }
|
|
22
|
+
* type AppMsg =
|
|
23
|
+
* | { type: 'matrix/setName'; v: string }
|
|
24
|
+
* | { type: 'ui/toggleSidebar' }
|
|
25
|
+
* | { type: 'reset' }
|
|
26
|
+
*
|
|
27
|
+
* const update = combine<AppState, AppMsg, AppEffect>({
|
|
28
|
+
* matrix: matrixUpdate,
|
|
29
|
+
* ui: uiUpdate,
|
|
30
|
+
* }, (s, msg) => {
|
|
31
|
+
* if (msg.type === 'reset') return [initialState(), []]
|
|
32
|
+
* return [s, []]
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Routing:
|
|
37
|
+
* - `{type: 'matrix/setName'}` → `matrixUpdate(s.matrix, msg)`. The slice
|
|
38
|
+
* reducer sees the FULL message (including the `matrix/` prefix); the
|
|
39
|
+
* slice's own update body matches on `.type`. This is intentional: it
|
|
40
|
+
* keeps slice reducers usable standalone in unit tests.
|
|
41
|
+
* - `{type: 'reset'}` → routes to the optional top reducer (the second
|
|
42
|
+
* argument). If `_top` is absent, the message is a no-op (state
|
|
43
|
+
* unchanged, no effects).
|
|
44
|
+
*
|
|
45
|
+
* Effects bubble: slice reducers and the top reducer can both return
|
|
46
|
+
* effects; combine concatenates them into the returned array in
|
|
47
|
+
* source order.
|
|
48
|
+
*
|
|
49
|
+
* @param slices Map of slice name → reducer
|
|
50
|
+
* @param _top Optional cross-cutting reducer for unprefixed messages
|
|
51
|
+
*/
|
|
52
|
+
export function combine(slices, _top) {
|
|
53
|
+
return (state, msg) => {
|
|
54
|
+
// Find the slice this message routes to. Convention: msg.type is
|
|
55
|
+
// `${slice}/${action}`. Anything else falls through to _top.
|
|
56
|
+
const slashIdx = msg.type.indexOf('/');
|
|
57
|
+
if (slashIdx > 0) {
|
|
58
|
+
const sliceName = msg.type.slice(0, slashIdx);
|
|
59
|
+
const reducer = slices[sliceName];
|
|
60
|
+
if (reducer !== undefined) {
|
|
61
|
+
const slice = state[sliceName];
|
|
62
|
+
const [nextSlice, effects] = reducer(slice, msg);
|
|
63
|
+
if (Object.is(nextSlice, slice)) {
|
|
64
|
+
// No state change — preserve top-level reference too.
|
|
65
|
+
return [state, effects];
|
|
66
|
+
}
|
|
67
|
+
return [{ ...state, [sliceName]: nextSlice }, effects];
|
|
68
|
+
}
|
|
69
|
+
// Slice name didn't match — fall through to top.
|
|
70
|
+
}
|
|
71
|
+
if (_top)
|
|
72
|
+
return _top(state, msg);
|
|
73
|
+
return [state, []];
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=combine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combine.js","sourceRoot":"","sources":["../src/combine.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,oEAAoE;AACpE,wEAAwE;AACxE,oEAAoE;AACpE,YAAY;AACZ,EAAE;AACF,sEAAsE;AACtE,mEAAmE;AACnE,wEAAwE;AACxE,qEAAqE;AACrE,oBAAoB;AACpB,EAAE;AACF,qDAAqD;AAqCrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,OAAO,CACrB,MAAyB,EACzB,IAA0B;IAE1B,OAAO,CAAC,KAAQ,EAAE,GAAM,EAAY,EAAE;QACpC,iEAAiE;QACjE,6DAA6D;QAC7D,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACtC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAY,CAAA;YACxD,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;YACjC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAA;gBAC9B,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;gBAChD,IAAI,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;oBAChC,sDAAsD;oBACtD,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;gBACzB,CAAC;gBACD,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,CAAA;YACxD,CAAC;YACD,iDAAiD;QACnD,CAAC;QACD,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QACjC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACpB,CAAC,CAAA;AACH,CAAC","sourcesContent":["// Reducer composition for slice-based decomposition.\n//\n// `combine()` is the canonical way to compose multiple sub-reducers into\n// a single top-level reducer. Each slice owns a sub-tree of state and an\n// independent `(slice, msg) → [slice, effects]` reducer; the host's\n// state shape nests these slices as top-level keys; messages are routed\n// to slices by namespace (`{type: 'slice/action', ...}` → `slice`'s\n// reducer).\n//\n// This is the load-bearing piece of the unified-composition-model: it\n// replaces the per-component `update()` machinery that `child()` /\n// `component()` previously gave you. A slice is just a module exporting\n// `update` + (optionally) `view` functions. Hosts wire them together\n// with `combine()`.\n//\n// See `docs/proposals/unified-composition-model.md`.\n\n/**\n * A slice reducer — operates on a sub-tree of host state plus its own\n * message variants, returns the next sub-tree and effects to dispatch.\n * Effects from slice reducers bubble up to the top-level effect handler\n * unchanged.\n */\nexport type SliceReducer<Slice, Msg, Effect> = (state: Slice, msg: Msg) => [Slice, Effect[]]\n\n/**\n * Map of slice name → its reducer. The slice name is BOTH:\n * - The top-level state key the slice lives under (`s[name]`)\n * - The message-namespace prefix that routes to this slice\n * (`{type: 'name/action'}` → routes to slice `name`)\n *\n * The matching state shape's top-level keys must therefore be the same\n * set as the routing prefixes — TypeScript enforces this via the `S`\n * generic. Messages whose `type` doesn't start with `${name}/` (or\n * doesn't contain `/` at all) fall through to the optional `_top`\n * reducer below.\n */\n// The Msg generic isn't used directly here — slice reducers carry their\n// own Msg variants. The host's `M` union enforces routing correctness\n// at the `combine()` call site (the returned reducer is typed `(S, M)`).\nexport type SliceMap<S, _M, E> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof S]?: SliceReducer<S[K], any, E>\n}\n\n/**\n * Optional top-level reducer for messages that don't match any slice\n * prefix. Use for cross-cutting messages (e.g. `{type: 'reset'}`) or\n * messages that operate on multiple slices at once.\n */\nexport type TopReducer<S, M, E> = (state: S, msg: M) => [S, E[]]\n\n/**\n * Compose slice reducers into a single top-level reducer.\n *\n * ```ts\n * type AppState = { matrix: MatrixState; ui: UiState }\n * type AppMsg =\n * | { type: 'matrix/setName'; v: string }\n * | { type: 'ui/toggleSidebar' }\n * | { type: 'reset' }\n *\n * const update = combine<AppState, AppMsg, AppEffect>({\n * matrix: matrixUpdate,\n * ui: uiUpdate,\n * }, (s, msg) => {\n * if (msg.type === 'reset') return [initialState(), []]\n * return [s, []]\n * })\n * ```\n *\n * Routing:\n * - `{type: 'matrix/setName'}` → `matrixUpdate(s.matrix, msg)`. The slice\n * reducer sees the FULL message (including the `matrix/` prefix); the\n * slice's own update body matches on `.type`. This is intentional: it\n * keeps slice reducers usable standalone in unit tests.\n * - `{type: 'reset'}` → routes to the optional top reducer (the second\n * argument). If `_top` is absent, the message is a no-op (state\n * unchanged, no effects).\n *\n * Effects bubble: slice reducers and the top reducer can both return\n * effects; combine concatenates them into the returned array in\n * source order.\n *\n * @param slices Map of slice name → reducer\n * @param _top Optional cross-cutting reducer for unprefixed messages\n */\nexport function combine<S, M extends { type: string }, E>(\n slices: SliceMap<S, M, E>,\n _top?: TopReducer<S, M, E>,\n): (state: S, msg: M) => [S, E[]] {\n return (state: S, msg: M): [S, E[]] => {\n // Find the slice this message routes to. Convention: msg.type is\n // `${slice}/${action}`. Anything else falls through to _top.\n const slashIdx = msg.type.indexOf('/')\n if (slashIdx > 0) {\n const sliceName = msg.type.slice(0, slashIdx) as keyof S\n const reducer = slices[sliceName]\n if (reducer !== undefined) {\n const slice = state[sliceName]\n const [nextSlice, effects] = reducer(slice, msg)\n if (Object.is(nextSlice, slice)) {\n // No state change — preserve top-level reference too.\n return [state, effects]\n }\n return [{ ...state, [sliceName]: nextSlice }, effects]\n }\n // Slice name didn't match — fall through to top.\n }\n if (_top) return _top(state, msg)\n return [state, []]\n }\n}\n"]}
|
package/dist/devtools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAqB,YAAY,EAAE,MAAM,YAAY,CAAA;AAEjE,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACzE,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACrF,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAA;AAEtC,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CACxD;AAyBD;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAiGD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,UAAU,CAAC,IAAI,SAAO,GAAG,IAAI,CA+B5C;AAmCD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,OAAO,CAAA;IACZ,WAAW,EAAE,OAAO,CAAA;IACpB,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,OAAO,EAAE,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,OAAO,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IACvB,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,OAAO,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,IAAI,OAAO,CAAA;IACnB,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,KAAK,IAAI,IAAI,CAAA;IACb,iBAAiB,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa,EAAE,CAAA;IAC7E,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,EAAE,CAAA;KAAE,CAAA;IAChE,WAAW,IAAI;QACb,SAAS,EAAE,CAAC,CAAA;QACZ,SAAS,EAAE,MAAM,CAAA;QACjB,WAAW,EAAE,MAAM,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,OAAO,CAAC;YAAC,aAAa,EAAE,OAAO,CAAC;YAAC,eAAe,EAAE,OAAO,EAAE,CAAA;SAAE,CAAC,CAAA;KACrF,CAAA;IACD,QAAQ,IAAI,IAAI,CAAA;IAChB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,EAAE,GAAG,IAAI,CAAA;IACvD,WAAW,IAAI,gBAAgB,EAAE,CAAA;IACjC,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,iBAAiB,CAAA;IACrD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IACnC,4EAA4E;IAC5E,gBAAgB,IAAI,iBAAiB,GAAG,IAAI,CAAA;IAC5C,+FAA+F;IAC/F,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;IAC9C,6EAA6E;IAC7E,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAClC,oFAAoF;IACpF,gBAAgB,IAAI,aAAa,CAAA;IACjC,oFAAoF;IACpF,cAAc,IAAI,MAAM,GAAG,IAAI,CAAA;IAC/B,wFAAwF;IACxF,eAAe,IAAI,MAAM,GAAG,IAAI,CAAA;IAChC,oGAAoG;IACpG,aAAa,IAAI,OAAO,CAAA;IACxB,kGAAkG;IAClG,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IACjC,iFAAiF;IACjF,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,EAAE,CAAA;IACnD,gKAAgK;IAChK,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAAA;IACtD,6KAA6K;IAC7K,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9D,8LAA8L;IAC9L,gBAAgB,CACd,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,SAAS,GACf;QACD,UAAU,EAAE,OAAO,CAAA;QACnB,uBAAuB,EAAE,MAAM,EAAE,CAAA;QACjC,cAAc,EAAE,OAAO,GAAG,IAAI,CAAA;KAC/B,CAAA;IACD,kLAAkL;IAClL,QAAQ,IAAI;QACV,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;QACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;QACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;QAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAC5B,CAAA;IACD,yJAAyJ;IACzJ,aAAa,IAAI;QAAE,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IAC9C,oLAAoL;IACpL,WAAW,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAA;IAC5C,8KAA8K;IAC9K,YAAY,CAAC,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY,CAAA;IACvE,4JAA4J;IAC5J,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE,CAAA;IAC/C,oMAAoM;IACpM,eAAe,IAAI,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;IACzE,mKAAmK;IACnK,iBAAiB,IAAI,aAAa,EAAE,CAAA;IACpC,kMAAkM;IAClM,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAAA;IACxD,kPAAkP;IAClP,UAAU,CACR,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,OAAO,EACjB,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAC3B;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACrB,sLAAsL;IACtL,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IACzE,kLAAkL;IAClL,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;IACnF,8GAA8G;IAC9G,WAAW,IAAI,gBAAgB,CAAA;IAC/B,mRAAmR;IACnR,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;QACxB,MAAM,EAAE,OAAO,GAAG;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;QACnC,WAAW,EAAE;YACX,YAAY,EAAE,SAAS,GAAG,IAAI,CAAA;YAC9B,iBAAiB,EAAE,MAAM,CAAA;YACzB,iBAAiB,EAAE,aAAa,EAAE,CAAA;YAClC,mBAAmB,EAAE,MAAM,EAAE,CAAA;SAC9B,CAAA;KACF,CAAA;IACD,kPAAkP;IAClP,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACzE,kLAAkL;IAClL,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;IAC9C,iMAAiM;IACjM,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC7F,qLAAqL;IACrL,kBAAkB,IAAI,mBAAmB,EAAE,CAAA;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,CAAA;QACf,UAAU,EAAE,MAAM,CAAA;QAClB,QAAQ,EAAE,MAAM,CAAA;QAChB,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,WAAW,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACpE,QAAQ,EAAE,KAAK,CAAC;QACd,YAAY,EAAE,MAAM,CAAA;QACpB,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,SAAS,EAAE,OAAO,CAAA;QAClB,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAAA;KAClD,CAAC,CAAA;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,OAAO,CAAA;IAClB,wIAAwI;IACxI,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAAA;CAClD;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAClD;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,YAAY,CAAA;IACzC,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;CAChB;AAuED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA6tBlD"}
|
package/dist/devtools.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { flushInstance, _forceState } from './update-loop.js';
|
|
1
|
+
import { flushInstance, _forceState, computeDirtyFromPrefixes, } from './update-loop.js';
|
|
2
2
|
import { _setDevToolsInstall } from './mount.js';
|
|
3
3
|
import { _markDisposerLogInstalled } from './lifetime.js';
|
|
4
4
|
import { applyBinding } from './binding.js';
|
|
@@ -898,10 +898,20 @@ export function installDevTools(inst) {
|
|
|
898
898
|
const originalUpdate = ci.def.update;
|
|
899
899
|
ci.def.update = ((state, msg) => {
|
|
900
900
|
const [newState, effects] = originalUpdate(state, msg);
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
901
|
+
// Compute the dirty mask from `__prefixes` (the supported reactivity
|
|
902
|
+
// path). Devtools recording only surfaces the LOW word here for the
|
|
903
|
+
// existing MessageRecord shape; high-word changes contribute to the
|
|
904
|
+
// mask via the runtime's full two-word computation but are folded
|
|
905
|
+
// into the `dirtyMask` field's display via FULL_MASK fallback when
|
|
906
|
+
// they alone fire. A future devtools schema upgrade can surface both
|
|
907
|
+
// words separately if needed.
|
|
908
|
+
const prefixes = ci.def.__prefixes;
|
|
909
|
+
let dirty = -1;
|
|
910
|
+
if (prefixes) {
|
|
911
|
+
const computed = computeDirtyFromPrefixes(prefixes, state, newState);
|
|
912
|
+
dirty = typeof computed === 'number' ? computed : computed[0] | computed[1];
|
|
913
|
+
}
|
|
914
|
+
lastDirtyMask = dirty;
|
|
905
915
|
const record = {
|
|
906
916
|
index: idx,
|
|
907
917
|
timestamp: Date.now(),
|