@adukiorg/anza 0.2.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/CHANGELOG.md +137 -0
- package/README.md +215 -0
- package/bin/anza.js +63 -0
- package/bin/create.js +150 -0
- package/importmap.json +72 -0
- package/package.json +100 -0
- package/src/core/animations/index.js +55 -0
- package/src/core/animations/play.js +111 -0
- package/src/core/animations/registry.js +54 -0
- package/src/core/animations/scroll.js +50 -0
- package/src/core/animations/tokens.js +58 -0
- package/src/core/animations/usage.md +301 -0
- package/src/core/animations/waapi.js +86 -0
- package/src/core/api/cache.js +120 -0
- package/src/core/api/caches/glob.js +24 -0
- package/src/core/api/caches/index.js +118 -0
- package/src/core/api/events/index.js +75 -0
- package/src/core/api/fetch.js +99 -0
- package/src/core/api/index.js +158 -0
- package/src/core/api/pipeline.js +98 -0
- package/src/core/api/plan.md +209 -0
- package/src/core/api/prefixes/index.js +66 -0
- package/src/core/api/retry.js +69 -0
- package/src/core/api/stream.js +127 -0
- package/src/core/api/upload.js +180 -0
- package/src/core/api/usage.md +206 -0
- package/src/core/events/bus.js +38 -0
- package/src/core/events/delegate.js +79 -0
- package/src/core/events/index.js +26 -0
- package/src/core/events/listen.js +50 -0
- package/src/core/events/missing.md +103 -0
- package/src/core/events/once.js +49 -0
- package/src/core/events/plan.md +177 -0
- package/src/core/events/types/index.js +34 -0
- package/src/core/events/usage.md +107 -0
- package/src/core/offline/bridge.js +51 -0
- package/src/core/offline/clock.js +100 -0
- package/src/core/offline/connectivity.js +116 -0
- package/src/core/offline/index.js +41 -0
- package/src/core/offline/missing.md +89 -0
- package/src/core/offline/plan.md +143 -0
- package/src/core/offline/queue.js +168 -0
- package/src/core/offline/state.js +18 -0
- package/src/core/offline/sync.js +106 -0
- package/src/core/offline/usage.md +273 -0
- package/src/core/platform/guard.js +104 -0
- package/src/core/platform/index.js +42 -0
- package/src/core/platform/missing.md +119 -0
- package/src/core/platform/platform.d.ts +88 -0
- package/src/core/platform/polyfills/anchor.js +79 -0
- package/src/core/platform/polyfills/navigation.js +142 -0
- package/src/core/platform/polyfills/popover.js +142 -0
- package/src/core/platform/polyfills/scheduler.js +194 -0
- package/src/core/platform/polyfills/shadow.js +35 -0
- package/src/core/platform/polyfills/urlpattern.js +119 -0
- package/src/core/platform/supports.js +186 -0
- package/src/core/platform/usage.md +287 -0
- package/src/core/router/cache.js +95 -0
- package/src/core/router/container.js +146 -0
- package/src/core/router/handler.js +52 -0
- package/src/core/router/history.js +120 -0
- package/src/core/router/index.js +158 -0
- package/src/core/router/intercept.js +376 -0
- package/src/core/router/match.js +145 -0
- package/src/core/router/missing.md +716 -0
- package/src/core/router/outlet.js +139 -0
- package/src/core/router/plan.md +370 -0
- package/src/core/router/sync/index.js +16 -0
- package/src/core/router/sync/tab.js +115 -0
- package/src/core/router/sync/transport.js +139 -0
- package/src/core/router/transitions.js +59 -0
- package/src/core/router/usage.md +773 -0
- package/src/core/security/crypto.js +159 -0
- package/src/core/security/index.js +49 -0
- package/src/core/security/missing.md +97 -0
- package/src/core/security/permissions.js +64 -0
- package/src/core/security/sanitize.js +100 -0
- package/src/core/security/usage.md +283 -0
- package/src/core/state/derived.js +117 -0
- package/src/core/state/index.js +23 -0
- package/src/core/state/missing.md +165 -0
- package/src/core/state/persist.js +284 -0
- package/src/core/state/store.js +308 -0
- package/src/core/state/sync.js +46 -0
- package/src/core/state/usage.md +440 -0
- package/src/core/storage/cache.js +83 -0
- package/src/core/storage/idb.js +196 -0
- package/src/core/storage/index.js +373 -0
- package/src/core/storage/lru.js +107 -0
- package/src/core/storage/missing.md +165 -0
- package/src/core/storage/opfs.js +190 -0
- package/src/core/storage/plan.md +69 -0
- package/src/core/storage/quota.js +69 -0
- package/src/core/storage/usage.md +226 -0
- package/src/core/ui/base.js +50 -0
- package/src/core/ui/define/container.js +82 -0
- package/src/core/ui/define/define.js +12 -0
- package/src/core/ui/define/element.js +390 -0
- package/src/core/ui/define/index.js +9 -0
- package/src/core/ui/define/orchestrator.js +105 -0
- package/src/core/ui/define/proxy.js +644 -0
- package/src/core/ui/define/state.js +6 -0
- package/src/core/ui/define/utils.js +134 -0
- package/src/core/ui/implementation.md +170 -0
- package/src/core/ui/index.js +41 -0
- package/src/core/ui/observe.js +117 -0
- package/src/core/ui/plan.md +510 -0
- package/src/core/ui/schedule.js +60 -0
- package/src/core/ui/template.js +37 -0
- package/src/core/ui/transitions.js +37 -0
- package/src/core/ui/ui.types.md +890 -0
- package/src/core/ui/usage.md +1124 -0
- package/src/core/ui/watch.md +346 -0
- package/src/core/workers/broadcast.js +138 -0
- package/src/core/workers/dedicated.js +153 -0
- package/src/core/workers/index.js +169 -0
- package/src/core/workers/locks.js +160 -0
- package/src/core/workers/offscreen.js +166 -0
- package/src/core/workers/plan.md +381 -0
- package/src/core/workers/pool.js +267 -0
- package/src/core/workers/shared.js +137 -0
- package/src/core/workers/usage.md +622 -0
- package/src/elements/base.js +12 -0
- package/src/elements/data/card/index.html +9 -0
- package/src/elements/data/card/index.js +19 -0
- package/src/elements/data/card/index.tags.json +1 -0
- package/src/elements/data/card/style.css +46 -0
- package/src/elements/data/chart/index.html +1 -0
- package/src/elements/data/chart/index.js +143 -0
- package/src/elements/data/chart/index.tags.json +1 -0
- package/src/elements/data/chart/style.css +13 -0
- package/src/elements/data/list/index.html +3 -0
- package/src/elements/data/list/index.js +19 -0
- package/src/elements/data/list/index.tags.json +1 -0
- package/src/elements/data/list/style.css +39 -0
- package/src/elements/data/stat/index.html +9 -0
- package/src/elements/data/stat/index.js +19 -0
- package/src/elements/data/stat/index.tags.json +1 -0
- package/src/elements/data/stat/style.css +50 -0
- package/src/elements/data/table/index.html +1 -0
- package/src/elements/data/table/index.js +16 -0
- package/src/elements/data/table/index.tags.json +1 -0
- package/src/elements/data/table/style.css +50 -0
- package/src/elements/feedback/alert/index.html +11 -0
- package/src/elements/feedback/alert/index.js +28 -0
- package/src/elements/feedback/alert/index.tags.json +1 -0
- package/src/elements/feedback/alert/style.css +75 -0
- package/src/elements/feedback/empty/index.html +13 -0
- package/src/elements/feedback/empty/index.js +34 -0
- package/src/elements/feedback/empty/index.tags.json +1 -0
- package/src/elements/feedback/empty/style.css +45 -0
- package/src/elements/feedback/progress/index.html +7 -0
- package/src/elements/feedback/progress/index.js +46 -0
- package/src/elements/feedback/progress/index.tags.json +1 -0
- package/src/elements/feedback/progress/style.css +36 -0
- package/src/elements/feedback/skeleton/index.html +1 -0
- package/src/elements/feedback/skeleton/index.js +78 -0
- package/src/elements/feedback/skeleton/index.tags.json +1 -0
- package/src/elements/feedback/skeleton/style.css +28 -0
- package/src/elements/feedback/toast/index.html +3 -0
- package/src/elements/feedback/toast/index.js +65 -0
- package/src/elements/feedback/toast/index.tags.json +1 -0
- package/src/elements/feedback/toast/style.css +36 -0
- package/src/elements/forms/checkbox/index.html +7 -0
- package/src/elements/forms/checkbox/index.js +104 -0
- package/src/elements/forms/checkbox/index.tags.json +1 -0
- package/src/elements/forms/checkbox/style.css +86 -0
- package/src/elements/forms/field/index.html +13 -0
- package/src/elements/forms/field/index.js +42 -0
- package/src/elements/forms/field/index.tags.json +1 -0
- package/src/elements/forms/field/style.css +42 -0
- package/src/elements/forms/form/index.html +3 -0
- package/src/elements/forms/form/index.js +122 -0
- package/src/elements/forms/form/index.tags.json +1 -0
- package/src/elements/forms/form/style.css +11 -0
- package/src/elements/forms/input/index.html +4 -0
- package/src/elements/forms/input/index.js +103 -0
- package/src/elements/forms/input/index.tags.json +1 -0
- package/src/elements/forms/input/style.css +39 -0
- package/src/elements/forms/radio/index.html +4 -0
- package/src/elements/forms/radio/index.js +109 -0
- package/src/elements/forms/radio/index.tags.json +1 -0
- package/src/elements/forms/radio/style.css +65 -0
- package/src/elements/forms/select/index.html +9 -0
- package/src/elements/forms/select/index.js +114 -0
- package/src/elements/forms/select/index.tags.json +1 -0
- package/src/elements/forms/select/style.css +95 -0
- package/src/elements/forms/textarea/index.html +4 -0
- package/src/elements/forms/textarea/index.js +115 -0
- package/src/elements/forms/textarea/index.tags.json +1 -0
- package/src/elements/forms/textarea/style.css +46 -0
- package/src/elements/forms/toggle/index.html +4 -0
- package/src/elements/forms/toggle/index.js +89 -0
- package/src/elements/forms/toggle/index.tags.json +1 -0
- package/src/elements/forms/toggle/style.css +63 -0
- package/src/elements/forms/upload/index.html +13 -0
- package/src/elements/forms/upload/index.js +120 -0
- package/src/elements/forms/upload/index.tags.json +1 -0
- package/src/elements/forms/upload/style.css +61 -0
- package/src/elements/index.js +71 -0
- package/src/elements/layout/app/index.html +7 -0
- package/src/elements/layout/app/index.js +16 -0
- package/src/elements/layout/app/index.tags.json +1 -0
- package/src/elements/layout/app/style.css +41 -0
- package/src/elements/layout/grid/index.html +3 -0
- package/src/elements/layout/grid/index.js +41 -0
- package/src/elements/layout/grid/index.tags.json +1 -0
- package/src/elements/layout/grid/style.css +12 -0
- package/src/elements/layout/header/index.html +8 -0
- package/src/elements/layout/header/index.js +16 -0
- package/src/elements/layout/header/index.tags.json +1 -0
- package/src/elements/layout/header/style.css +28 -0
- package/src/elements/layout/scroll/index.html +3 -0
- package/src/elements/layout/scroll/index.js +19 -0
- package/src/elements/layout/scroll/index.tags.json +1 -0
- package/src/elements/layout/scroll/style.css +24 -0
- package/src/elements/layout/sidebar/index.html +3 -0
- package/src/elements/layout/sidebar/index.js +24 -0
- package/src/elements/layout/sidebar/index.tags.json +1 -0
- package/src/elements/layout/sidebar/style.css +30 -0
- package/src/elements/layout/split/index.html +3 -0
- package/src/elements/layout/split/index.js +18 -0
- package/src/elements/layout/split/index.tags.json +1 -0
- package/src/elements/layout/split/style.css +28 -0
- package/src/elements/layout/stack/index.html +3 -0
- package/src/elements/layout/stack/index.js +31 -0
- package/src/elements/layout/stack/index.tags.json +1 -0
- package/src/elements/layout/stack/style.css +15 -0
- package/src/elements/layout/surface/index.html +3 -0
- package/src/elements/layout/surface/index.js +19 -0
- package/src/elements/layout/surface/index.tags.json +1 -0
- package/src/elements/layout/surface/style.css +29 -0
- package/src/elements/navigation/breadcrumb/index.html +5 -0
- package/src/elements/navigation/breadcrumb/index.js +16 -0
- package/src/elements/navigation/breadcrumb/index.tags.json +1 -0
- package/src/elements/navigation/breadcrumb/style.css +36 -0
- package/src/elements/navigation/nav/index.html +3 -0
- package/src/elements/navigation/nav/index.js +24 -0
- package/src/elements/navigation/nav/index.tags.json +1 -0
- package/src/elements/navigation/nav/style.css +38 -0
- package/src/elements/navigation/pagination/index.html +3 -0
- package/src/elements/navigation/pagination/index.js +94 -0
- package/src/elements/navigation/pagination/index.tags.json +1 -0
- package/src/elements/navigation/pagination/style.css +39 -0
- package/src/elements/navigation/steps/index.html +6 -0
- package/src/elements/navigation/steps/index.js +64 -0
- package/src/elements/navigation/steps/index.tags.json +1 -0
- package/src/elements/navigation/steps/style.css +78 -0
- package/src/elements/navigation/tabs/index.html +6 -0
- package/src/elements/navigation/tabs/index.js +132 -0
- package/src/elements/navigation/tabs/index.tags.json +1 -0
- package/src/elements/navigation/tabs/style.css +52 -0
- package/src/elements/overlay/dialog/index.html +5 -0
- package/src/elements/overlay/dialog/index.js +57 -0
- package/src/elements/overlay/dialog/index.tags.json +1 -0
- package/src/elements/overlay/dialog/style.css +31 -0
- package/src/elements/overlay/drawer/index.html +3 -0
- package/src/elements/overlay/drawer/index.js +56 -0
- package/src/elements/overlay/drawer/index.tags.json +1 -0
- package/src/elements/overlay/drawer/style.css +48 -0
- package/src/elements/overlay/menu/index.html +3 -0
- package/src/elements/overlay/menu/index.js +107 -0
- package/src/elements/overlay/menu/index.tags.json +1 -0
- package/src/elements/overlay/menu/style.css +43 -0
- package/src/elements/overlay/popover/index.html +3 -0
- package/src/elements/overlay/popover/index.js +44 -0
- package/src/elements/overlay/popover/index.tags.json +1 -0
- package/src/elements/overlay/popover/style.css +21 -0
- package/src/elements/overlay/sheet/index.html +8 -0
- package/src/elements/overlay/sheet/index.js +105 -0
- package/src/elements/overlay/sheet/index.tags.json +1 -0
- package/src/elements/overlay/sheet/style.css +64 -0
- package/src/elements/overlay/tooltip/index.html +6 -0
- package/src/elements/overlay/tooltip/index.js +16 -0
- package/src/elements/overlay/tooltip/index.tags.json +1 -0
- package/src/elements/overlay/tooltip/style.css +41 -0
- package/src/elements/primitives/avatar/index.html +2 -0
- package/src/elements/primitives/avatar/index.js +79 -0
- package/src/elements/primitives/avatar/index.tags.json +1 -0
- package/src/elements/primitives/avatar/style.css +36 -0
- package/src/elements/primitives/badge/index.html +3 -0
- package/src/elements/primitives/badge/index.js +20 -0
- package/src/elements/primitives/badge/index.tags.json +1 -0
- package/src/elements/primitives/badge/style.css +67 -0
- package/src/elements/primitives/button/index.html +3 -0
- package/src/elements/primitives/button/index.js +61 -0
- package/src/elements/primitives/button/index.tags.json +1 -0
- package/src/elements/primitives/button/style.css +66 -0
- package/src/elements/primitives/divider/index.html +1 -0
- package/src/elements/primitives/divider/index.js +43 -0
- package/src/elements/primitives/divider/index.tags.json +1 -0
- package/src/elements/primitives/divider/style.css +39 -0
- package/src/elements/primitives/icon/index.html +3 -0
- package/src/elements/primitives/icon/index.js +66 -0
- package/src/elements/primitives/icon/index.tags.json +1 -0
- package/src/elements/primitives/icon/style.css +20 -0
- package/src/elements/primitives/link/index.html +3 -0
- package/src/elements/primitives/link/index.js +129 -0
- package/src/elements/primitives/link/index.tags.json +1 -0
- package/src/elements/primitives/link/style.css +40 -0
- package/src/elements/primitives/spinner/index.html +1 -0
- package/src/elements/primitives/spinner/index.js +62 -0
- package/src/elements/primitives/spinner/index.tags.json +1 -0
- package/src/elements/primitives/spinner/style.css +20 -0
- package/src/elements/primitives/text/index.html +1 -0
- package/src/elements/primitives/text/index.js +79 -0
- package/src/elements/primitives/text/index.tags.json +1 -0
- package/src/elements/primitives/text/style.css +25 -0
- package/src/index.js +23 -0
- package/src/styles/base.css +66 -0
- package/src/styles/index.css +10 -0
- package/src/styles/layers.css +9 -0
- package/src/styles/reset.css +66 -0
- package/src/sw/activate.js +51 -0
- package/src/sw/expire.js +47 -0
- package/src/sw/index.js +28 -0
- package/src/sw/install.js +35 -0
- package/src/sw/push.js +58 -0
- package/src/sw/queue.js +60 -0
- package/src/sw/routes.js +71 -0
- package/src/sw/strategies.js +247 -0
- package/src/sw/sync.js +80 -0
- package/src/tokens/index.css +26 -0
- package/src/tokens/primitives/colors.css +54 -0
- package/src/tokens/primitives/motion.css +34 -0
- package/src/tokens/primitives/radius.css +16 -0
- package/src/tokens/primitives/shadow.css +34 -0
- package/src/tokens/primitives/spacing.css +27 -0
- package/src/tokens/primitives/typography.css +46 -0
- package/src/tokens/primitives/zindex.css +18 -0
- package/src/tokens/registered/colors.css +133 -0
- package/src/tokens/registered/dimensions.css +31 -0
- package/src/tokens/semantic/components.css +125 -0
- package/src/tokens/semantic/contrast.css +33 -0
- package/src/tokens/semantic/dark.css +61 -0
- package/src/tokens/semantic/light.css +64 -0
- package/types/core/animations/index.d.ts +52 -0
- package/types/core/api/index.d.ts +68 -0
- package/types/core/events/index.d.ts +50 -0
- package/types/core/offline/index.d.ts +68 -0
- package/types/core/platform/index.d.ts +60 -0
- package/types/core/router/index.d.ts +203 -0
- package/types/core/security/index.d.ts +33 -0
- package/types/core/state/index.d.ts +68 -0
- package/types/core/storage/index.d.ts +40 -0
- package/types/core/ui/index.d.ts +446 -0
- package/types/core/workers/index.d.ts +221 -0
- package/types/elements/index.d.ts +150 -0
- package/types/index.d.ts +18 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# Scoped Mutation Observation API: `watch`
|
|
2
|
+
|
|
3
|
+
`watch` is the component-scoped mutation observation helper injected alongside `tags`, `refs`, and `on`. It wraps `MutationObserver` with lifecycle cleanup, shadow-root scoping, selector/direct-ref targeting, one-shot handlers, and mutation-specific handler signatures.
|
|
4
|
+
|
|
5
|
+
## 1. Injection Shape
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
mount({ el, ctrl, tags, on, refs, watch }) {}
|
|
9
|
+
|
|
10
|
+
update({ el, ctrl, tags, on, refs, watch, name, val, prev }) {}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`watch` is created once per connected lifecycle. If the component disconnects, `ctrl.abort()` disconnects the shared observer and removes every registration.
|
|
14
|
+
|
|
15
|
+
## 2. API Summary
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
watch.attr(target, attrName, handler, signalOrOptions?)
|
|
19
|
+
watch.attr(target, attrNames, handler, signalOrOptions?)
|
|
20
|
+
watch.attr(target, '*', handler, signalOrOptions?)
|
|
21
|
+
|
|
22
|
+
watch.kids(target, handler, signalOrOptions?)
|
|
23
|
+
watch.kids(target, { deep: true }, handler, signalOrOptions?)
|
|
24
|
+
|
|
25
|
+
watch.text(target, handler, signalOrOptions?)
|
|
26
|
+
|
|
27
|
+
watch.tree(target, handler, signalOrOptions?)
|
|
28
|
+
|
|
29
|
+
watch.attr.once(...)
|
|
30
|
+
watch.kids.once(...)
|
|
31
|
+
watch.text.once(...)
|
|
32
|
+
watch.tree.once(...)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`target` can be:
|
|
36
|
+
|
|
37
|
+
- A selector string scoped to the component shadow root.
|
|
38
|
+
- A direct `Element` reference from `refs.*` or `tags.one(...)`.
|
|
39
|
+
|
|
40
|
+
The final argument can be:
|
|
41
|
+
|
|
42
|
+
- An `AbortSignal`.
|
|
43
|
+
- An options object: `{ signal, once }`.
|
|
44
|
+
- Omitted, in which case `ctrl.signal` is used.
|
|
45
|
+
|
|
46
|
+
Every call returns a disposer function.
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const stop = watch.attr(refs.submit, 'disabled', handler);
|
|
50
|
+
stop();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 3. Attribute Watching: `watch.attr`
|
|
54
|
+
|
|
55
|
+
Observe one, many, or all attributes.
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
watch.attr('button', 'disabled', (attr, next, prev, el) => {})
|
|
59
|
+
watch.attr('.card', ['aria-expanded', 'data-state'], handler)
|
|
60
|
+
watch.attr('.card', '*', handler)
|
|
61
|
+
watch.attr(refs.submit, 'disabled', handler)
|
|
62
|
+
watch.attr(tags.one('#toggle'), 'aria-pressed', handler)
|
|
63
|
+
watch.attr.once('details', 'open', handler)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Handler:
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
(attrName, newValue, oldValue, element) => void
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
mount({ refs, watch }) {
|
|
76
|
+
watch.attr(refs.submit, 'disabled', (attr, next, prev, button) => {
|
|
77
|
+
button.dataset.wasDisabled = String(prev !== null);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Notes:
|
|
83
|
+
|
|
84
|
+
- `newValue` is read with `element.getAttribute(attrName)`, so removed attributes produce `null`.
|
|
85
|
+
- `oldValue` comes from the mutation record and is always requested internally.
|
|
86
|
+
- Boolean attributes usually have `''` when present and `null` when absent.
|
|
87
|
+
|
|
88
|
+
## 4. Child Watching: `watch.kids`
|
|
89
|
+
|
|
90
|
+
Observe child additions and removals.
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
watch.kids('ul', ({ added, removed }, list) => {})
|
|
94
|
+
watch.kids(refs.list, handler)
|
|
95
|
+
watch.kids('ul', { deep: true }, handler)
|
|
96
|
+
watch.kids.once('ul', handler)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Handler:
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
({ added, removed }, element) => void
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
mount({ refs, watch }) {
|
|
109
|
+
watch.kids(refs.list, ({ added }) => {
|
|
110
|
+
for (const node of added) {
|
|
111
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
112
|
+
node.classList.add('entry-new');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Rules:
|
|
120
|
+
|
|
121
|
+
- `added` and `removed` are arrays, never `NodeList`.
|
|
122
|
+
- Without `{ deep: true }`, only direct child mutations on the target match.
|
|
123
|
+
- With `{ deep: true }`, descendant child mutations inside the target subtree match.
|
|
124
|
+
|
|
125
|
+
## 5. Text Watching: `watch.text`
|
|
126
|
+
|
|
127
|
+
Observe text-node changes inside a target.
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
watch.text('.label', (next, prev, el) => {})
|
|
131
|
+
watch.text(refs.counter, handler)
|
|
132
|
+
watch.text.once('.status', handler)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Handler:
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
(newText, oldText, element) => void
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
mount({ refs, watch }) {
|
|
145
|
+
watch.text(refs.counter, (next, prev, el) => {
|
|
146
|
+
const direction = Number(next) > Number(prev) ? 'up' : 'down';
|
|
147
|
+
el.dataset.direction = direction;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Notes:
|
|
153
|
+
|
|
154
|
+
- `newText` is the current `textContent` of the watched element.
|
|
155
|
+
- `oldText` is the previous text node value from the mutation record.
|
|
156
|
+
- Multiple text nodes may produce multiple records in the same microtask batch.
|
|
157
|
+
|
|
158
|
+
## 6. Subtree Watching: `watch.tree`
|
|
159
|
+
|
|
160
|
+
Observe all mutation types under a target and receive raw records.
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
watch.tree('.editor', (records) => {})
|
|
164
|
+
watch.tree(refs.canvas, handler)
|
|
165
|
+
watch.tree.once('.editor', handler)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Handler:
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
(records, element) => void
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Example:
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
mount({ refs, watch }) {
|
|
178
|
+
watch.tree(refs.editor, (records) => {
|
|
179
|
+
for (const record of records) {
|
|
180
|
+
if (record.type === 'childList') syncOutline();
|
|
181
|
+
if (record.type === 'attributes') syncToolbar();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`watch.tree` is the escape hatch. Prefer `attr`, `kids`, or `text` when the mutation type is known.
|
|
188
|
+
|
|
189
|
+
## 7. Scoping Rules
|
|
190
|
+
|
|
191
|
+
`watch` never observes outside the component shadow root.
|
|
192
|
+
|
|
193
|
+
- Selector targets are resolved with `shadowRoot.querySelectorAll`.
|
|
194
|
+
- Direct element targets must satisfy `shadowRoot.contains(element)`.
|
|
195
|
+
- A selector with no matches returns a no-op disposer and warns in development.
|
|
196
|
+
- A direct target outside the shadow root throws in development and returns a no-op disposer in production.
|
|
197
|
+
- Cross-component observation must use `CustomEvent`, shared state, or parent-owned refs.
|
|
198
|
+
|
|
199
|
+
## 8. Shared Observer Model
|
|
200
|
+
|
|
201
|
+
Each component instance owns one `MutationObserver`.
|
|
202
|
+
|
|
203
|
+
Registrations are stored internally:
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
{
|
|
207
|
+
id,
|
|
208
|
+
kind,
|
|
209
|
+
target,
|
|
210
|
+
selector,
|
|
211
|
+
attrs,
|
|
212
|
+
deep,
|
|
213
|
+
handler,
|
|
214
|
+
signal,
|
|
215
|
+
once
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
When a registration is added or removed, the observer options are recomputed:
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
{
|
|
223
|
+
attributes: hasAttrWatch || hasTreeWatch,
|
|
224
|
+
attributeOldValue: hasAttrWatch || hasTreeWatch,
|
|
225
|
+
attributeFilter: unionOfSpecificAttrsOrUndefined,
|
|
226
|
+
childList: hasKidsWatch || hasTreeWatch,
|
|
227
|
+
characterData: hasTextWatch || hasTreeWatch,
|
|
228
|
+
characterDataOldValue: hasTextWatch || hasTreeWatch,
|
|
229
|
+
subtree: true
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
If any `watch.attr(..., '*', ...)` or `watch.tree(...)` registration exists, `attributeFilter` is omitted.
|
|
234
|
+
|
|
235
|
+
## 9. Matching Rules
|
|
236
|
+
|
|
237
|
+
Attribute records:
|
|
238
|
+
|
|
239
|
+
- Match `record.type === 'attributes'`.
|
|
240
|
+
- Match when `record.target === registration.target`.
|
|
241
|
+
- For selector targets with multiple elements, match any registered target element.
|
|
242
|
+
- Attribute name must be included in `attrs`, unless `attrs === '*'`.
|
|
243
|
+
|
|
244
|
+
Child records:
|
|
245
|
+
|
|
246
|
+
- Match `record.type === 'childList'`.
|
|
247
|
+
- Without `deep`, match only `record.target === registration.target`.
|
|
248
|
+
- With `deep`, match `registration.target.contains(record.target)`.
|
|
249
|
+
|
|
250
|
+
Text records:
|
|
251
|
+
|
|
252
|
+
- Match `record.type === 'characterData'`.
|
|
253
|
+
- Use `record.target.parentElement` as the element.
|
|
254
|
+
- Match if the parent is the registration target or is contained by it.
|
|
255
|
+
|
|
256
|
+
Tree records:
|
|
257
|
+
|
|
258
|
+
- Match any record where the mutation target is the watched element or inside it.
|
|
259
|
+
- Handler receives the filtered record batch.
|
|
260
|
+
|
|
261
|
+
## 10. Cleanup
|
|
262
|
+
|
|
263
|
+
Cleanup must be deterministic:
|
|
264
|
+
|
|
265
|
+
- Default signal is the component `ctrl.signal`.
|
|
266
|
+
- A custom signal removes only that registration.
|
|
267
|
+
- `.once` removes only that registration after the first matching mutation.
|
|
268
|
+
- Component abort disconnects the shared observer and clears the registry.
|
|
269
|
+
- The mutation callback checks `defaultSignal.aborted` before dispatching because queued observer callbacks can still run after `disconnect()`.
|
|
270
|
+
|
|
271
|
+
## 11. Implementation Pseudocode
|
|
272
|
+
|
|
273
|
+
```javascript
|
|
274
|
+
export function createMutationWatcher(shadowRoot, defaultSignal) {
|
|
275
|
+
const registry = new Map();
|
|
276
|
+
let observer = null;
|
|
277
|
+
let nextId = 0;
|
|
278
|
+
|
|
279
|
+
function add(kind, args, once = false) {
|
|
280
|
+
const reg = normalizeRegistration(kind, args, once, defaultSignal, shadowRoot);
|
|
281
|
+
if (!reg) return () => {};
|
|
282
|
+
|
|
283
|
+
registry.set(reg.id, reg);
|
|
284
|
+
reg.signal?.addEventListener('abort', () => remove(reg.id), { once: true });
|
|
285
|
+
refreshObserver();
|
|
286
|
+
|
|
287
|
+
return () => remove(reg.id);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function remove(id) {
|
|
291
|
+
registry.delete(id);
|
|
292
|
+
refreshObserver();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function refreshObserver() {
|
|
296
|
+
if (observer) observer.disconnect();
|
|
297
|
+
if (!registry.size || defaultSignal.aborted) return;
|
|
298
|
+
observer ||= new MutationObserver(dispatch);
|
|
299
|
+
observer.observe(shadowRoot, computeOptions(registry.values()));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function dispatch(records) {
|
|
303
|
+
if (defaultSignal.aborted) return;
|
|
304
|
+
for (const reg of registry.values()) {
|
|
305
|
+
const matches = filterRecords(records, reg);
|
|
306
|
+
if (!matches.length) continue;
|
|
307
|
+
callHandler(matches, reg);
|
|
308
|
+
if (reg.once) remove(reg.id);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
defaultSignal.addEventListener('abort', () => {
|
|
313
|
+
observer?.disconnect();
|
|
314
|
+
registry.clear();
|
|
315
|
+
}, { once: true });
|
|
316
|
+
|
|
317
|
+
return createWatchProxy(add);
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## 12. Usage with `tags`, `refs`, and `on`
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
mount({ refs, tags, on, watch }) {
|
|
325
|
+
const toggle = refs.toggle ?? tags.one('[data-toggle]');
|
|
326
|
+
const panel = refs.panel;
|
|
327
|
+
|
|
328
|
+
watch.attr(toggle, 'aria-expanded', (attr, next) => {
|
|
329
|
+
panel.hidden = next !== 'true';
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
on.click('[data-toggle]', (event, target) => {
|
|
333
|
+
const open = target.getAttribute('aria-expanded') === 'true';
|
|
334
|
+
target.setAttribute('aria-expanded', String(!open));
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The click mutates an attribute. `watch.attr` reacts without any manual event dispatch.
|
|
340
|
+
|
|
341
|
+
## 13. What `watch` Is Not
|
|
342
|
+
|
|
343
|
+
- It is not cross-component state management.
|
|
344
|
+
- It is not a replacement for `on` for user interactions.
|
|
345
|
+
- It is not a render scheduler. Heavy handlers should schedule work with `ui.schedule` or `ui.scheduleFrame`.
|
|
346
|
+
- It is not for slotted light DOM changes. Use `slotchange` on a `<slot>` element for that.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/core/workers/broadcast.js
|
|
3
|
+
*
|
|
4
|
+
* BroadcastChannel Manager.
|
|
5
|
+
* Reference-counted channels: open on first subscriber, close when the last
|
|
6
|
+
* subscriber leaves. Supports explicit close(name) and clear() for lifecycle
|
|
7
|
+
* control. Cleans up both the message listener and the abort listener on
|
|
8
|
+
* disposal to prevent memory leaks.
|
|
9
|
+
*
|
|
10
|
+
* Source: plan.md Phase 5
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
class BroadcastManager {
|
|
14
|
+
#channels = new Map(); // name → BroadcastChannel
|
|
15
|
+
#listeners = new Map(); // name → Set<{ wrapped, fn, abortCleanup }>
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Dispatches a message to a named channel.
|
|
19
|
+
*
|
|
20
|
+
* BroadcastChannel messages are NOT echoed to the posting instance — only
|
|
21
|
+
* other channel instances with the same name receive them. So we always post
|
|
22
|
+
* from a short-lived peer channel and close it immediately. This correctly
|
|
23
|
+
* delivers to all subscriber channel instances, including the one managed
|
|
24
|
+
* by this BroadcastManager.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} name
|
|
27
|
+
* @param {any} payload
|
|
28
|
+
*/
|
|
29
|
+
broadcast(name, payload) {
|
|
30
|
+
// Always use a peer sender so the subscriber channel receives the message.
|
|
31
|
+
// (A BroadcastChannel instance never fires its own onmessage for messages it posts.)
|
|
32
|
+
const sender = new BroadcastChannel(name);
|
|
33
|
+
sender.postMessage(payload);
|
|
34
|
+
// Close asynchronously to allow same-process delivery before cleanup.
|
|
35
|
+
setTimeout(() => sender.close(), 0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Subscribes to a named channel.
|
|
40
|
+
* Returns a dispose function that removes the listener and cleans up the
|
|
41
|
+
* abort listener.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} name
|
|
44
|
+
* @param {Function} fn
|
|
45
|
+
* @param {AbortSignal} [signal]
|
|
46
|
+
* @returns {() => void} dispose
|
|
47
|
+
*/
|
|
48
|
+
subscribe(name, fn, signal) {
|
|
49
|
+
if (signal?.aborted) return () => {};
|
|
50
|
+
|
|
51
|
+
// Open channel on first subscriber
|
|
52
|
+
if (!this.#channels.has(name)) {
|
|
53
|
+
const ch = new BroadcastChannel(name);
|
|
54
|
+
ch.onmessageerror = (e) => {
|
|
55
|
+
console.error(`BroadcastChannel "${name}" deserialization error:`, e);
|
|
56
|
+
};
|
|
57
|
+
this.#channels.set(name, ch);
|
|
58
|
+
this.#listeners.set(name, new Set());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const channel = this.#channels.get(name);
|
|
62
|
+
const group = this.#listeners.get(name);
|
|
63
|
+
|
|
64
|
+
const wrapped = (event) => {
|
|
65
|
+
try {
|
|
66
|
+
fn(event.data);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error(`Error in BroadcastChannel "${name}" subscriber:`, err);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
channel.addEventListener('message', wrapped);
|
|
73
|
+
|
|
74
|
+
let abortCleanup = null;
|
|
75
|
+
const entry = { wrapped, fn, abortCleanup };
|
|
76
|
+
group.add(entry);
|
|
77
|
+
|
|
78
|
+
const dispose = () => {
|
|
79
|
+
channel.removeEventListener('message', wrapped);
|
|
80
|
+
group.delete(entry);
|
|
81
|
+
|
|
82
|
+
// Remove the abort listener we registered
|
|
83
|
+
if (entry.abortCleanup) {
|
|
84
|
+
signal?.removeEventListener('abort', entry.abortCleanup);
|
|
85
|
+
entry.abortCleanup = null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Auto-close when no listeners remain
|
|
89
|
+
if (group.size === 0) {
|
|
90
|
+
channel.close();
|
|
91
|
+
this.#channels.delete(name);
|
|
92
|
+
this.#listeners.delete(name);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (signal) {
|
|
97
|
+
const onAbort = () => dispose();
|
|
98
|
+
entry.abortCleanup = onAbort;
|
|
99
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return dispose;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Closes one named channel and removes all its listeners.
|
|
107
|
+
*
|
|
108
|
+
* @param {string} name
|
|
109
|
+
*/
|
|
110
|
+
close(name) {
|
|
111
|
+
const channel = this.#channels.get(name);
|
|
112
|
+
if (!channel) return;
|
|
113
|
+
|
|
114
|
+
const group = this.#listeners.get(name) ?? new Set();
|
|
115
|
+
for (const entry of group) {
|
|
116
|
+
channel.removeEventListener('message', entry.wrapped);
|
|
117
|
+
if (entry.abortCleanup) {
|
|
118
|
+
// We don't have the signal reference here, so just null the cleanup
|
|
119
|
+
entry.abortCleanup = null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
channel.close();
|
|
124
|
+
this.#channels.delete(name);
|
|
125
|
+
this.#listeners.delete(name);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Closes all open channels and removes all listeners.
|
|
130
|
+
*/
|
|
131
|
+
clear() {
|
|
132
|
+
for (const name of [...this.#channels.keys()]) {
|
|
133
|
+
this.close(name);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const broadcast = new BroadcastManager();
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/core/workers/dedicated.js
|
|
3
|
+
*
|
|
4
|
+
* Dedicated Worker Wrapper.
|
|
5
|
+
* Manages the lifecycle of standard background dedicated Workers, using
|
|
6
|
+
* native MessageChannel per request for isolated, concurrent execution corridors
|
|
7
|
+
* without message cross-contamination.
|
|
8
|
+
*
|
|
9
|
+
* Message contract:
|
|
10
|
+
* Request → { task, payload, meta }
|
|
11
|
+
* Response → { ok, value, error }
|
|
12
|
+
*
|
|
13
|
+
* Source: plan.md Phase 1, Phase 2
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export class Dedicated {
|
|
17
|
+
#script;
|
|
18
|
+
#worker;
|
|
19
|
+
#pending = new Map(); // port1 → { resolve, reject, timer }
|
|
20
|
+
#closed = false;
|
|
21
|
+
|
|
22
|
+
constructor(script) {
|
|
23
|
+
this.#script = script;
|
|
24
|
+
this.#worker = new Worker(script, { type: 'module' });
|
|
25
|
+
this.#worker.addEventListener('error', (e) => this.#fail(e));
|
|
26
|
+
this.#worker.addEventListener('messageerror', (e) => this.#fail(e));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get closed() { return this.#closed; }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Dispatches a task to the worker.
|
|
33
|
+
*
|
|
34
|
+
* Options:
|
|
35
|
+
* payload – data to send (structured-clone safe)
|
|
36
|
+
* transferables – Transferable[] transferred with the message
|
|
37
|
+
* signal – AbortSignal to cancel the request
|
|
38
|
+
* timeout – milliseconds before automatic abort
|
|
39
|
+
* meta – arbitrary structured data attached to the request
|
|
40
|
+
*/
|
|
41
|
+
run(task, options = {}) {
|
|
42
|
+
if (this.#closed) {
|
|
43
|
+
return Promise.reject(new Error(`Worker "${this.#script}" is closed`));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
payload = null,
|
|
48
|
+
transferables = [],
|
|
49
|
+
signal,
|
|
50
|
+
timeout,
|
|
51
|
+
meta
|
|
52
|
+
} = options;
|
|
53
|
+
|
|
54
|
+
// Abort before anything starts
|
|
55
|
+
if (signal?.aborted) {
|
|
56
|
+
return Promise.reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const channel = new MessageChannel();
|
|
61
|
+
|
|
62
|
+
// Combine caller signal and optional timeout into one controller
|
|
63
|
+
let combined = null;
|
|
64
|
+
let timer = null;
|
|
65
|
+
|
|
66
|
+
if (signal || timeout) {
|
|
67
|
+
combined = new AbortController();
|
|
68
|
+
|
|
69
|
+
if (signal) {
|
|
70
|
+
if (signal.aborted) {
|
|
71
|
+
combined.abort(signal.reason);
|
|
72
|
+
} else {
|
|
73
|
+
signal.addEventListener('abort', () => combined.abort(signal.reason), { once: true });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (timeout) {
|
|
78
|
+
timer = setTimeout(
|
|
79
|
+
() => combined.abort(new Error(`Worker task "${task}" timed out after ${timeout}ms`)),
|
|
80
|
+
timeout
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const abort = combined?.signal;
|
|
86
|
+
|
|
87
|
+
const cleanup = () => {
|
|
88
|
+
if (timer) clearTimeout(timer);
|
|
89
|
+
channel.port1.close();
|
|
90
|
+
this.#pending.delete(channel.port1);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const fail = (reason) => {
|
|
94
|
+
cleanup();
|
|
95
|
+
reject(reason);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
this.#pending.set(channel.port1, { resolve, reject: fail });
|
|
99
|
+
|
|
100
|
+
channel.port1.onmessage = (e) => {
|
|
101
|
+
const { ok, value, error } = e.data;
|
|
102
|
+
cleanup();
|
|
103
|
+
if (ok) {
|
|
104
|
+
resolve(value);
|
|
105
|
+
} else {
|
|
106
|
+
reject(new Error(error ?? 'Worker task failed'));
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
channel.port1.onmessageerror = () => {
|
|
111
|
+
fail(new Error('Deserialization error on message channel'));
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
if (abort) {
|
|
115
|
+
abort.addEventListener('abort', () => {
|
|
116
|
+
fail(abort.reason ?? new DOMException('Aborted', 'AbortError'));
|
|
117
|
+
}, { once: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.#worker.postMessage(
|
|
121
|
+
{ task, payload, meta, port: channel.port2 },
|
|
122
|
+
[channel.port2, ...transferables]
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Route a worker-level error into all pending requests. */
|
|
128
|
+
#fail(event) {
|
|
129
|
+
const reason = event instanceof ErrorEvent
|
|
130
|
+
? new Error(event.message ?? 'Worker error')
|
|
131
|
+
: new Error('Worker message deserialization error');
|
|
132
|
+
|
|
133
|
+
for (const { reject } of this.#pending.values()) {
|
|
134
|
+
reject(reason);
|
|
135
|
+
}
|
|
136
|
+
this.#pending.clear();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Terminate the worker. Subsequent `run` calls will reject immediately. */
|
|
140
|
+
terminate() {
|
|
141
|
+
if (this.#closed) return;
|
|
142
|
+
this.#closed = true;
|
|
143
|
+
this.#worker.terminate();
|
|
144
|
+
const reason = new Error(`Worker "${this.#script}" terminated`);
|
|
145
|
+
for (const { reject } of this.#pending.values()) {
|
|
146
|
+
reject(reason);
|
|
147
|
+
}
|
|
148
|
+
this.#pending.clear();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Compatibility alias
|
|
153
|
+
export { Dedicated as DedicatedWorker };
|