@alwatr/action 9.25.0 → 9.27.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.
@@ -1,126 +0,0 @@
1
- /**
2
- * @file delegate.ts
3
- *
4
- * Global Event Delegation engine for `@alwatr/action`.
5
- *
6
- * ## Why delegation instead of per-element listeners?
7
- *
8
- * The classic directive approach attaches one `addEventListener` per element.
9
- * With 100 buttons on a page, that means 100 listener registrations at boot
10
- * time — O(N) initialization cost, O(N) memory for listener references, and
11
- * zero support for elements added after bootstrap.
12
- *
13
- * This module implements the global delegation pattern:
14
- * - A single listener per event type is attached to `document.body` with
15
- * `capture: true` (so it fires even for non-bubbling events).
16
- * - When an event fires, the handler walks up the DOM from `event.target`
17
- * using `closest()` to find the nearest element with an `on-<eventType>`
18
- * attribute (e.g. `on-click`, `on-submit`).
19
- * - The nearest `[action-context]` ancestor is also resolved and attached to
20
- * the `Action` object as `context` — enabling the same action type to be
21
- * scoped to different UI regions.
22
- * - Modifiers run with access to the mutable `Action` object so they can
23
- * enrich `meta` before the action reaches subscribers.
24
- * - `dispatchAction` is called with the fully assembled `Action` object.
25
- *
26
- * ## Complexity
27
- *
28
- * | Metric | Per-element listeners | Global delegation |
29
- * | --------------- | --------------------- | ----------------- |
30
- * | Boot time | O(N elements) | O(1) — 1 loop |
31
- * | Memory | O(N listeners) | O(1) — 1 handler |
32
- * | Dynamic content | Requires re-bootstrap | Works out-of-box |
33
- * | `once` modifier | Native option | Manual tracking |
34
- *
35
- * ## Trade-offs vs. the directive approach
36
- *
37
- * - `passive` is not supported as a per-element option (all delegated listeners
38
- * are non-passive so that `prevent` can call `preventDefault()`).
39
- * - `stop` stops further bubbling but the delegation handler has already
40
- * captured the event at `body` level — it does not prevent other delegation
41
- * handlers from running on the same element.
42
- * - `once` is emulated by removing the attribute after first fire.
43
- */
44
- /**
45
- * Default DOM event types that cover the vast majority of interactive elements.
46
- *
47
- * - `click` — buttons, links, checkboxes, custom interactive elements
48
- * - `submit` — form submission
49
- * - `input` — live text input, range sliders
50
- * - `change` — select boxes, checkboxes, radio buttons (fires on commit)
51
- *
52
- * Pass additional types to `setupActionDelegation` when your app uses other
53
- * events (e.g. `'keydown'`, `'pointerup'`).
54
- */
55
- export declare const DEFAULT_DELEGATED_EVENTS: readonly string[];
56
- /**
57
- * Registers global event delegation for `on-<eventType>` attributes.
58
- *
59
- * Attaches a single `capture`-phase listener on `document.body` for each
60
- * event type in `eventTypes`. All processing — context resolution, modifier
61
- * execution, payload resolution, and `dispatchAction` — happens inside that
62
- * one handler.
63
- *
64
- * **Call this once at application bootstrap**, before any user interaction.
65
- * Subsequent calls with the same event types are no-ops (idempotent).
66
- *
67
- * ### Why `capture: true`?
68
- *
69
- * Capture-phase listeners fire before bubble-phase listeners and also catch
70
- * events that do not bubble (e.g. `focus`, `blur`). This ensures the delegation
71
- * handler always runs, even when a child element calls `stopPropagation()`.
72
- *
73
- * ### Dynamic content
74
- *
75
- * Because the listener lives on `document.body`, any element added to the DOM
76
- * after this call — via `innerHTML`, `lit-html`, a framework renderer, or
77
- * server-sent HTML — is automatically covered. No re-bootstrap is needed.
78
- *
79
- * ### Context scoping
80
- *
81
- * Wrap a group of elements in a `[action-context]` container to scope their
82
- * actions. The delegation handler automatically resolves the nearest ancestor
83
- * and attaches its value to `action.context`:
84
- *
85
- * ```html
86
- * <section action-context="product-list">
87
- * <button on-click="ui_add_to_cart:42">Add</button>
88
- * </section>
89
- * ```
90
- *
91
- * ```ts
92
- * onAction('ui_add_to_cart', (action) => {
93
- * console.log(action.context); // 'product-list'
94
- * });
95
- * ```
96
- *
97
- * @param eventTypes - Event types to delegate. Defaults to `DEFAULT_DELEGATED_EVENTS`.
98
- *
99
- * @example — minimal bootstrap
100
- * ```ts
101
- * import {setupActionDelegation, onAction} from '@alwatr/action';
102
- *
103
- * // One call activates the entire page.
104
- * setupActionDelegation();
105
- *
106
- * onAction('ui_open_drawer', (action) => openDrawer(action.payload));
107
- * ```
108
- *
109
- * @example — with extra event types
110
- * ```ts
111
- * import {setupActionDelegation, DEFAULT_DELEGATED_EVENTS} from '@alwatr/action';
112
- *
113
- * setupActionDelegation([...DEFAULT_DELEGATED_EVENTS, 'keydown', 'pointerup']);
114
- * ```
115
- */
116
- export declare function setupActionDelegation(eventTypes?: readonly string[]): void;
117
- /**
118
- * Removes all global delegation listeners registered by `setupActionDelegation`.
119
- *
120
- * Useful in test environments where each test needs a clean slate, or in
121
- * micro-frontend setups where a sub-app is unmounted.
122
- *
123
- * After calling this, `setupActionDelegation` can be called again to re-register.
124
- */
125
- export declare function teardownActionDelegation(): void;
126
- //# sourceMappingURL=delegate.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAyNH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAA2C,CAAC;AAElG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,GAAE,SAAS,MAAM,EAA6B,GAAG,IAAI,CASpG;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAO/C"}
package/dist/lib_.d.ts DELETED
@@ -1,24 +0,0 @@
1
- import type { Action } from './type.js';
2
- /**
3
- * Module-scoped logger for `@alwatr/action`.
4
- * Scoped to `'alwatr-action'` so log lines are easy to filter in the console.
5
- *
6
- * @internal
7
- */
8
- export declare const logger_: import("@alwatr/logger").AlwatrLogger;
9
- /**
10
- * The internal action channel — a `ChannelSignal` keyed by action `type`.
11
- *
12
- * Each message on this channel is a full `Action` object (AFSA), not just a
13
- * raw payload. Subscribers registered via `onAction('foo', handler)` receive
14
- * the entire `Action<'foo'>` so they have access to `context`, `meta`, and
15
- * `payload` in one place.
16
- *
17
- * Uses `ChannelSignal` for O(1) routing: dispatching action `'A'` performs a
18
- * single `Map.get('A')` lookup and invokes only the handlers registered for
19
- * that specific type — never handlers for `'B'`, `'C'`, etc.
20
- *
21
- * @internal — not part of the public API; use `onAction` / `dispatchAction` instead.
22
- */
23
- export declare const internalChannel_: import("@alwatr/signal").ChannelSignal<Record<string, Action<never>>>;
24
- //# sourceMappingURL=lib_.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lib_.d.ts","sourceRoot":"","sources":["../src/lib_.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,WAAW,CAAC;AAEtC;;;;;GAKG;AACH,eAAO,MAAM,OAAO,uCAAgC,CAAC;AAErD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,uEAAuE,CAAC"}
package/dist/method.d.ts DELETED
@@ -1,169 +0,0 @@
1
- import type { Awaitable } from '@alwatr/type-helper';
2
- import type { SubscribeResult } from '@alwatr/signal';
3
- import type { Action, ActionRecord, DispatchParam, ModifierHandler, PayloadResolver } from './type.js';
4
- /**
5
- * Subscribes to a named action dispatched anywhere in the application.
6
- *
7
- * `type` must be a key of `ActionRecord`. The handler receives the full
8
- * `Action<K>` object — giving access to `payload`, `context`, and `meta`
9
- * in one place. No manual generic annotation is needed; the compiler infers
10
- * the correct `payload` type from `ActionRecord`:
11
- *
12
- * ```ts
13
- * // ActionRecord declares: 'ui_add_to_cart': {productId: number; qty: number}
14
- * onAction('ui_add_to_cart', (action) => {
15
- * cartService.add(action.payload.productId, action.payload.qty); // fully typed
16
- * console.log(action.context); // e.g. 'product-list' (from DOM) or undefined
17
- * });
18
- * ```
19
- *
20
- * Passing an action name not declared in `ActionRecord` is a **compile error**.
21
- * Register new actions by extending `ActionRecord` via declaration merging:
22
- *
23
- * ```ts
24
- * // src/action-record.ts
25
- * declare module '@alwatr/action' {
26
- * interface ActionRecord {
27
- * 'ui_open_drawer': string;
28
- * }
29
- * }
30
- * ```
31
- *
32
- * Internally delegates to `ChannelSignal.on()` for **O(1) routing** — dispatching
33
- * action `'A'` never invokes handlers registered for action `'B'`.
34
- *
35
- * @param type - A key of `ActionRecord`.
36
- * @param handler - Callback invoked with the full `Action<K>` on each dispatch.
37
- * @returns A `SubscribeResult` with an `unsubscribe()` method for cleanup.
38
- *
39
- * @example
40
- * ```ts
41
- * import {onAction} from '@alwatr/action';
42
- *
43
- * const sub = onAction('ui_page_ready', (action) => {
44
- * router.setPage(action.payload); // payload: string — inferred from ActionRecord
45
- * });
46
- *
47
- * sub.unsubscribe(); // stop listening when no longer needed
48
- * ```
49
- */
50
- export declare function onAction<K extends keyof ActionRecord>(type: K | K[], handler: (action: Action<K>) => Awaitable<void>): SubscribeResult;
51
- /**
52
- * Dispatches an action to all `onAction` subscribers with a matching `type`.
53
- *
54
- * Accepts a full `Action<K>` object. The `payload` field is automatically
55
- * typed from `ActionRecord[K]` — passing the wrong shape is a **compile error**:
56
- *
57
- * ```ts
58
- * // ActionRecord declares: 'ui_add_to_cart': {productId: number; qty: number}
59
- * dispatchAction({type: 'ui_add_to_cart', payload: {productId: 42, qty: 1}}); // ✅
60
- * dispatchAction({type: 'ui_add_to_cart', payload: 'wrong'}); // ❌ compile error
61
- * dispatchAction({type: 'unknown_action', payload: 'x'}); // ❌ compile error
62
- * ```
63
- *
64
- * The `context` and `meta` fields are optional. When dispatching from code
65
- * (not from the DOM), omit `context` — it is only meaningful for DOM-originated
66
- * actions where an `[action-context]` ancestor exists.
67
- *
68
- * Use `dispatchAction` when triggering an action from code — e.g. after an
69
- * async operation, from a service layer, or in tests. For DOM-driven actions,
70
- * use the `on-<eventType>` HTML attribute with `setupActionDelegation`.
71
- *
72
- * @param action - A full `Action<K>` object with at minimum `type` and `payload`.
73
- *
74
- * @example — with payload (code-originated action — no 'ui_' prefix)
75
- * ```ts
76
- * import {dispatchAction} from '@alwatr/action';
77
- *
78
- * dispatchAction({type: 'navigate', payload: '/dashboard'});
79
- * dispatchAction({type: 'upload_complete', payload: fileId});
80
- * ```
81
- *
82
- * @example — void payload (payload field is optional and can be omitted entirely)
83
- * ```ts
84
- * dispatchAction({type: 'auth_expired'});
85
- * // or explicitly:
86
- * dispatchAction({type: 'auth_expired', payload: undefined});
87
- * ```
88
- *
89
- * @example — with context and meta
90
- * ```ts
91
- * dispatchAction({
92
- * type: 'slider_change',
93
- * payload: 75,
94
- * context: 'volume_slider',
95
- * meta: {traceId: 'abc-123'},
96
- * });
97
- * ```
98
- */
99
- export declare function dispatchAction<K extends keyof ActionRecord>(action: DispatchParam<K>): void;
100
- /**
101
- * Registers a custom modifier that can be used in `on-<eventType>` attribute syntax.
102
- *
103
- * A modifier is a comma-separated token placed after the `;` separator
104
- * (e.g. `on-click="action-id; mymod"`). Its handler runs before the payload is
105
- * resolved and the action is dispatched. Returning `false` cancels the dispatch.
106
- *
107
- * The handler also receives the **mutable** `action` object being built, so it
108
- * can attach data to `action.meta` before the action reaches subscribers.
109
- *
110
- * Built-in modifiers (`prevent`, `validate`, `once`) are always available.
111
- * This function lets you add domain-specific ones.
112
- *
113
- * Registering the same name twice logs an accident and overwrites the previous
114
- * handler — avoid duplicate registrations in production code.
115
- *
116
- * @param name - The modifier token (lowercase, no special characters).
117
- * @param handler - A `ModifierHandler` receiving `(event, element, action)`.
118
- *
119
- * @example — a `confirm` modifier that shows a browser dialog
120
- * ```ts
121
- * import {registerModifier} from '@alwatr/action';
122
- *
123
- * registerModifier('confirm', () => window.confirm('Are you sure?'));
124
- * ```
125
- * ```html
126
- * <button on-click="ui_delete_item:42; confirm">Delete</button>
127
- * ```
128
- *
129
- * @example — a `trace` modifier that stamps a trace ID into meta
130
- * ```ts
131
- * registerModifier('trace', (_event, _element, action) => {
132
- * action.meta ??= {};
133
- * action.meta['traceId'] = crypto.randomUUID();
134
- * return true;
135
- * });
136
- * ```
137
- */
138
- export declare function registerModifier(name: string, handler: ModifierHandler): void;
139
- /**
140
- * Registers a custom payload resolver that can be used in `on-<eventType>` attribute syntax.
141
- *
142
- * A payload resolver is a colon-prefixed token in the attribute value
143
- * (e.g. `on-click="action-id:$mytoken"`). Its function is called at dispatch time
144
- * with the DOM event and the element. The return value becomes the `payload`
145
- * field of the `Action` object passed to `onAction` subscribers.
146
- *
147
- * Built-in resolvers (`$value`, `$formdata`, `$checked`) are always available.
148
- * This function lets you add domain-specific ones.
149
- *
150
- * Registering the same name twice logs an accident and overwrites the previous
151
- * resolver — avoid duplicate registrations in production code.
152
- *
153
- * @param name - The resolver token (should start with `$` by convention).
154
- * @param resolver - A `PayloadResolver` receiving `(event, element)`.
155
- *
156
- * @example — a `$data-id` resolver that reads a data attribute
157
- * ```ts
158
- * import {registerPayloadResolver} from '@alwatr/action';
159
- *
160
- * registerPayloadResolver('$data-id', (_event, element) => {
161
- * return (element as HTMLElement).dataset.id ?? null;
162
- * });
163
- * ```
164
- * ```html
165
- * <button on-click="ui_select_item:$data-id" data-id="42">Select</button>
166
- * ```
167
- */
168
- export declare function registerPayloadResolver(name: string, resolver: PayloadResolver): void;
169
- //# sourceMappingURL=method.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"method.d.ts","sourceRoot":"","sources":["../src/method.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAW,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAIpD,OAAO,KAAK,EAAC,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAC,MAAM,WAAW,CAAC;AAIrG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,YAAY,EACnD,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,EACb,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,GAC9C,eAAe,CAsBjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAG3F;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAM7E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAMrF"}
@@ -1,24 +0,0 @@
1
- import type { ModifierHandler, PayloadResolver } from './type.js';
2
- /**
3
- * Registry of all named modifier handlers.
4
- *
5
- * Keys are modifier names used in the `on-<eventType>` attribute syntax
6
- * (e.g. `on-click="action-id; prevent"`). Values are `ModifierHandler` functions.
7
- * Populated at module load with built-in modifiers; extended at runtime via
8
- * `registerModifier`.
9
- *
10
- * @internal
11
- */
12
- export declare const modifierRegistry: Map<string, ModifierHandler>;
13
- /**
14
- * Registry of all named payload resolvers.
15
- *
16
- * Keys are resolver tokens used in the `on-<eventType>` attribute syntax
17
- * (e.g. `on-input="ui_search_query:$value"`). Values are `PayloadResolver` functions.
18
- * Populated at module load with built-in resolvers; extended at runtime via
19
- * `registerPayloadResolver`.
20
- *
21
- * @internal
22
- */
23
- export declare const payloadRegistry: Map<string, PayloadResolver>;
24
- //# sourceMappingURL=registry_.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry_.d.ts","sourceRoot":"","sources":["../src/registry_.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAE,eAAe,EAAC,MAAM,WAAW,CAAC;AAEhE;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,8BAAqC,CAAC;AAEnE;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,8BAAqC,CAAC"}