@alwatr/action 9.16.0 → 9.18.1

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 CHANGED
@@ -23,9 +23,11 @@
23
23
 
24
24
  The action bus is powered by a [`ChannelSignal`](../signal/README.md) from `@alwatr/signal`. Dispatching action `'A'` performs a single `Map.get('A')` lookup and invokes only the handlers registered for that specific action — **O(1) per dispatch**, regardless of how many other actions are subscribed.
25
25
 
26
+ Every message on the bus is a full **`Action<K>`** object (Alwatr Flux Standard Action — AFSA) rather than a bare payload. This means every handler receives `type`, `payload`, `context`, and `meta` in one unified structure.
27
+
26
28
  ### Global Event Delegation
27
29
 
28
- A single capture-phase listener on `document.body` handles all `on-<eventType>` elements. When an event fires, the handler walks up from `event.target` using `closest('[on-click]')` (or the matching attribute), parses the attribute value, runs modifiers, resolves the payload, and dispatches the action.
30
+ A single capture-phase listener on `document.body` handles all `on-<eventType>` elements. When an event fires, the handler walks up from `event.target` using `closest('[on-click]')` (or the matching attribute), resolves the nearest `[action-context]` ancestor, parses the attribute value, runs modifiers, resolves the payload, and dispatches the full `Action` object.
29
31
 
30
32
  ```
31
33
  User clicks a button
@@ -34,10 +36,15 @@ User clicks a button
34
36
  document.body capture listener (1 listener per event type)
35
37
 
36
38
  └─ closest('[on-click]') → finds element
39
+ closest('[action-context]') → resolves context (e.g. 'product-list')
37
40
  parse attribute → 'add_to_cart:42'
38
41
  run modifiers → none
39
42
  resolve payload → '42'
40
- internalChannel_.dispatch('add_to_cart', '42')
43
+ internalChannel_.dispatch('add_to_cart', {
44
+ type: 'add_to_cart',
45
+ payload: '42',
46
+ context: 'product-list',
47
+ })
41
48
 
42
49
  └─ Map.get('add_to_cart') → O(1) → invoke only matching handlers
43
50
  ```
@@ -93,10 +100,11 @@ import './action-record.js'; // ensure the declaration is loaded
93
100
 
94
101
  setupActionDelegation();
95
102
 
96
- // Payload types are inferred from ActionRecordno generics needed.
97
- onAction('open_drawer', (panel) => openDrawer(panel)); // panel: string
98
- onAction('add_to_cart', (item) => {
99
- cartService.add(item.productId, item.qty); // fully typed
103
+ // The handler receives the full Action<K> object payload, context, and meta in one place.
104
+ onAction('open_drawer', (action) => openDrawer(action.payload)); // action.payload: string
105
+ onAction('add_to_cart', (action) => {
106
+ cartService.add(action.payload.productId, action.payload.qty); // fully typed
107
+ console.log(action.context); // e.g. 'product-list' — from nearest [action-context] ancestor
100
108
  });
101
109
  ```
102
110
 
@@ -132,15 +140,54 @@ onAction('add_to_cart', (item) => {
132
140
  <button on-click="welcome_dismissed; once">Got it</button>
133
141
  ```
134
142
 
135
- ### 4. Programmatic dispatch
143
+ ### 4. Context scoping with `action-context`
144
+
145
+ Wrap a group of elements in an `[action-context]` container to scope their actions. The delegation handler automatically resolves the nearest ancestor and attaches its value to `action.context`. This lets the same action type serve multiple independent UI regions without creating separate action names.
146
+
147
+ ```html
148
+ <!-- Two sliders on the same page, both dispatching 'slider:change' -->
149
+ <section action-context="volume">
150
+ <input
151
+ type="range"
152
+ on-input="slider:change:$value"
153
+ />
154
+ </section>
155
+
156
+ <section action-context="brightness">
157
+ <input
158
+ type="range"
159
+ on-input="slider:change:$value"
160
+ />
161
+ </section>
162
+ ```
163
+
164
+ ```ts
165
+ onAction('slider:change', (action) => {
166
+ if (action.context === 'volume') audioService.setVolume(Number(action.payload));
167
+ if (action.context === 'brightness') displayService.setBrightness(Number(action.payload));
168
+ });
169
+ ```
170
+
171
+ Context is `undefined` when no `[action-context]` ancestor exists — programmatic dispatches also have no context by default.
172
+
173
+ ### 5. Programmatic dispatch
136
174
 
137
175
  ```ts
138
176
  import {dispatchAction} from '@alwatr/action';
139
177
 
178
+ // dispatchAction takes a full Action object
140
179
  await uploadFile(file);
141
- dispatchAction('upload_complete', fileId);
180
+ dispatchAction({type: 'upload_complete', payload: fileId});
181
+
182
+ dispatchAction({type: 'navigate', payload: '/dashboard'});
142
183
 
143
- dispatchAction('navigate', '/dashboard');
184
+ // With explicit context and meta
185
+ dispatchAction({
186
+ type: 'slider:change',
187
+ payload: 75,
188
+ context: 'volume',
189
+ meta: {source: 'keyboard'},
190
+ });
144
191
  ```
145
192
 
146
193
  ---
@@ -172,11 +219,81 @@ on-<eventType>="actionId[:payload][; modifier1,modifier2,…]"
172
219
  | ------------ | -------------------------------------------------------------- |
173
220
  | `:$value` | `element.value` (for `<input>`, `<select>`, `<textarea>`) |
174
221
  | `:$formdata` | `Object.fromEntries(new FormData(form))` from nearest `<form>` |
222
+ | `:$checked` | `(element as HTMLInputElement).checked` for checkboxes/radios |
223
+
224
+ ---
225
+
226
+ ## The Action Object (AFSA)
227
+
228
+ Every action flowing through the bus — whether triggered from HTML attributes or dispatched programmatically — is a single **`Action<K>`** object:
229
+
230
+ ```ts
231
+ interface Action<K extends keyof ActionRecord> {
232
+ /** Action identifier — must be a key of ActionRecord. */
233
+ type: K;
234
+
235
+ /**
236
+ * DOM context from the nearest [action-context] ancestor.
237
+ * undefined for programmatic dispatches or when no ancestor exists.
238
+ */
239
+ context?: string;
240
+
241
+ /** Business payload — type is inferred from ActionRecord[K]. */
242
+ payload: ActionRecord[K];
243
+
244
+ /**
245
+ * Open-ended metadata bag for cross-cutting concerns.
246
+ * Modifiers may write to this before the action reaches subscribers.
247
+ */
248
+ meta?: Record<string, unknown>;
249
+ }
250
+ ```
251
+
252
+ Modifiers in the delegation pipeline receive the mutable `action` object and can enrich `meta` before the action reaches subscribers:
253
+
254
+ ```ts
255
+ import {registerModifier} from '@alwatr/action';
256
+
257
+ // A modifier that stamps a trace ID into meta
258
+ registerModifier('trace', (_event, _element, action) => {
259
+ action.meta ??= {};
260
+ action.meta['traceId'] = crypto.randomUUID();
261
+ return true;
262
+ });
263
+ ```
264
+
265
+ ```html
266
+ <button on-click="submit_order:42; trace">Place Order</button>
267
+ ```
268
+
269
+ ```ts
270
+ onAction('submit_order', (action) => {
271
+ console.log(action.meta?.['traceId']); // e.g. 'a1b2-c3d4-…'
272
+ });
273
+ ```
175
274
 
176
275
  ---
177
276
 
178
277
  ## API Reference
179
278
 
279
+ ### `Action<K>` (interface)
280
+
281
+ The Alwatr Flux Standard Action object. Every dispatch and every handler callback uses this structure.
282
+
283
+ ```ts
284
+ import type {Action} from '@alwatr/action';
285
+
286
+ // Reading fields in a handler
287
+ onAction('add_to_cart', (action: Action<'add_to_cart'>) => {
288
+ console.log(action.type); // 'add_to_cart'
289
+ console.log(action.payload); // {productId: number; qty: number}
290
+ console.log(action.context); // string | undefined
291
+ console.log(action.meta); // Record<string, unknown> | undefined
292
+ });
293
+ ```
294
+
295
+ ---
296
+
180
297
  ### `ActionRecord` (interface)
181
298
 
182
299
  The global action type registry. Extend via declaration merging to register typed actions.
@@ -220,34 +337,46 @@ function teardownActionDelegation(): void;
220
337
 
221
338
  ---
222
339
 
223
- ### `onAction(actionId, handler)`
340
+ ### `onAction(type, handler)`
224
341
 
225
- Subscribes to a named action. O(1) routing via `ChannelSignal`.
342
+ Subscribes to a named action. O(1) routing via `ChannelSignal`. The handler receives the full `Action<K>` object.
226
343
 
227
344
  ```ts
228
- function onAction<K extends keyof ActionRecord>(
229
- actionId: K,
230
- handler: (payload: ActionRecord[K]) => void,
231
- ): SubscribeResult;
345
+ function onAction<K extends keyof ActionRecord>(type: K, handler: (action: Action<K>) => void): SubscribeResult;
232
346
  ```
233
347
 
234
348
  ```ts
235
- const sub = onAction('open_drawer', (panel) => openDrawer(panel));
349
+ const sub = onAction('open_drawer', (action) => {
350
+ openDrawer(action.payload); // payload: string
351
+ console.log(action.context); // e.g. 'sidebar' or undefined
352
+ });
236
353
  sub.unsubscribe(); // prevent memory leaks
237
354
  ```
238
355
 
239
356
  ---
240
357
 
241
- ### `dispatchAction(actionId, payload?)`
358
+ ### `dispatchAction(action)`
242
359
 
243
360
  Dispatches a named action. Payload type is enforced by `ActionRecord`.
244
361
 
362
+ ```ts
363
+ function dispatchAction<K extends keyof ActionRecord>(action: Action<K>): void;
364
+ ```
365
+
245
366
  ```ts
246
367
  // With payload
247
- dispatchAction('open_drawer', 'settings');
368
+ dispatchAction({type: 'open_drawer', payload: 'settings'});
248
369
 
249
- // Void payload — no second argument
250
- dispatchAction('logout');
370
+ // Void payload
371
+ dispatchAction({type: 'logout', payload: undefined});
372
+
373
+ // With context and meta
374
+ dispatchAction({
375
+ type: 'open_drawer',
376
+ payload: 'settings',
377
+ context: 'header',
378
+ meta: {triggeredBy: 'keyboard'},
379
+ });
251
380
  ```
252
381
 
253
382
  ---
@@ -256,7 +385,9 @@ dispatchAction('logout');
256
385
 
257
386
  Registers a custom modifier. Return `false` to cancel the dispatch.
258
387
 
259
- Handler signature: `(event: Event, element: HTMLElement) => boolean`
388
+ Handler signature: `(event: Event, element: HTMLElement, action: Action) => boolean`
389
+
390
+ The handler receives the mutable `action` object and may write to `action.meta`.
260
391
 
261
392
  ```ts
262
393
  import {registerModifier} from '@alwatr/action';
@@ -264,11 +395,18 @@ import {registerModifier} from '@alwatr/action';
264
395
  registerModifier('not_disabled', (_event, element) => {
265
396
  return !(element as HTMLButtonElement).disabled;
266
397
  });
398
+
399
+ // A modifier that enriches meta before dispatch
400
+ registerModifier('timestamp', (_event, _element, action) => {
401
+ action.meta ??= {};
402
+ action.meta['ts'] = Date.now();
403
+ return true;
404
+ });
267
405
  ```
268
406
 
269
407
  ```html
270
408
  <button
271
- on-click="select_item:$data_id; not_disabled"
409
+ on-click="select_item:$data_id; not_disabled,timestamp"
272
410
  data-id="42"
273
411
  >
274
412
  Select
@@ -286,10 +424,6 @@ Handler signature: `(event: Event, element: HTMLElement) => unknown`
286
424
  ```ts
287
425
  import {registerPayloadResolver} from '@alwatr/action';
288
426
 
289
- registerPayloadResolver('$checked', (_event, element) => {
290
- return (element as HTMLInputElement).checked;
291
- });
292
-
293
427
  registerPayloadResolver('$data_id', (_event, element) => {
294
428
  return (element as HTMLElement).dataset.id ?? null;
295
429
  });
@@ -313,33 +447,41 @@ registerPayloadResolver('$data_id', (_event, element) => {
313
447
  ## Unidirectional Data Flow
314
448
 
315
449
  ```
316
- ┌──────────────────────────────────────────────────────────┐
317
- UI Layer │
318
- │ <button on-click="add_to_cart:42">Add</button>
319
- └─────────────────────────┬────────────────────────────────┘
450
+ ┌────────────────────────────────────────────────────────────┐
451
+ UI Layer │
452
+ │ <section action-context="cart">
453
+ │ <button on-click="add_to_cart:42">Add</button> │
454
+ │ </section> │
455
+ └─────────────────────────┬──────────────────────────────────┘
320
456
  │ DOM event bubbles to body
321
457
 
322
- ┌──────────────────────────────────────────────────────────┐
323
- Action Layer (@alwatr/action) │
324
- │ document.body capture listener (1 per event type)
325
- │ → closest('[on-click]') → parse → modifiers
326
- │ → internalChannel_.dispatch('add_to_cart', '42') [O(1)]
327
- └─────────────────────────┬────────────────────────────────┘
458
+ ┌────────────────────────────────────────────────────────────┐
459
+ Action Layer (@alwatr/action) │
460
+ │ document.body capture listener (1 per event type)
461
+ │ → closest('[on-click]') → parse attribute
462
+ │ → closest('[action-context]') → context = 'cart'
463
+ │ → run modifiers (may enrich action.meta) │
464
+ │ → resolve payload → '42' │
465
+ │ → dispatch Action {type, payload, context, meta} [O(1)] │
466
+ └─────────────────────────┬──────────────────────────────────┘
328
467
  │ O(1) routing via ChannelSignal
329
468
 
330
- ┌──────────────────────────────────────────────────────────┐
331
- Business Logic Layer
332
- │ onAction('add_to_cart', (id) => cartService.add(id))
333
- └─────────────────────────┬────────────────────────────────┘
469
+ ┌────────────────────────────────────────────────────────────┐
470
+ Business Logic Layer
471
+ │ onAction('add_to_cart', (action) => {
472
+ │ cartService.add(action.payload); │
473
+ │ // action.context === 'cart' │
474
+ │ }) │
475
+ └─────────────────────────┬──────────────────────────────────┘
334
476
  │ state update
335
477
 
336
- ┌──────────────────────────────────────────────────────────┐
337
- State Layer (@alwatr/signal)
338
- │ cartSignal.set(newCartState)
339
- └─────────────────────────┬────────────────────────────────┘
478
+ ┌────────────────────────────────────────────────────────────┐
479
+ State Layer (@alwatr/signal)
480
+ │ cartSignal.set(newCartState)
481
+ └─────────────────────────┬──────────────────────────────────┘
340
482
  │ state flows down to UI
341
483
 
342
- UI re-renders
484
+ UI re-renders
343
485
  ```
344
486
 
345
487
  ---
@@ -355,6 +497,65 @@ concern, not a user-interaction action.
355
497
 
356
498
  ## Migration from Previous Versions
357
499
 
500
+ ### `dispatchAction` API changed
501
+
502
+ `dispatchAction` now takes a single `Action` object instead of two positional arguments.
503
+
504
+ **Before:**
505
+
506
+ ```ts
507
+ dispatchAction('open_drawer', 'settings');
508
+ dispatchAction('logout');
509
+ ```
510
+
511
+ **After:**
512
+
513
+ ```ts
514
+ dispatchAction({type: 'open_drawer', payload: 'settings'});
515
+ dispatchAction({type: 'logout', payload: undefined});
516
+ ```
517
+
518
+ ### `onAction` handler signature changed
519
+
520
+ Handlers now receive the full `Action<K>` object instead of just the payload.
521
+
522
+ **Before:**
523
+
524
+ ```ts
525
+ onAction('add_to_cart', (item) => {
526
+ cartService.add(item.productId, item.qty);
527
+ });
528
+ ```
529
+
530
+ **After:**
531
+
532
+ ```ts
533
+ onAction('add_to_cart', (action) => {
534
+ cartService.add(action.payload.productId, action.payload.qty);
535
+ // action.context is now also available
536
+ });
537
+ ```
538
+
539
+ ### `registerModifier` handler signature changed
540
+
541
+ Modifier handlers now receive a third `action` argument for `meta` enrichment.
542
+
543
+ **Before:**
544
+
545
+ ```ts
546
+ registerModifier('not_disabled', (_event, element) => {
547
+ return !(element as HTMLButtonElement).disabled;
548
+ });
549
+ ```
550
+
551
+ **After (backward-compatible — third arg is optional to use):**
552
+
553
+ ```ts
554
+ registerModifier('not_disabled', (_event, element, _action) => {
555
+ return !(element as HTMLButtonElement).disabled;
556
+ });
557
+ ```
558
+
358
559
  ### Attribute syntax changed
359
560
 
360
561
  The event type is now encoded in the **attribute name** instead of the value, and modifiers are listed after a semicolon instead of dot-chained before the arrow.
@@ -385,44 +586,22 @@ The event type is now encoded in the **attribute name** instead of the value, an
385
586
  <button on-click="welcome_dismissed; once">Got it</button>
386
587
  ```
387
588
 
388
- ### `ActionContext` removed
389
-
390
- The `this` context in modifier and resolver handlers changed to explicit parameters:
391
-
392
- **Before:**
393
-
394
- ```ts
395
- registerModifier('not_disabled', function () {
396
- return !(this.element as HTMLButtonElement).disabled;
397
- });
398
- ```
399
-
400
- **After:**
401
-
402
- ```ts
403
- registerModifier('not_disabled', (_event, element) => {
404
- return !(element as HTMLButtonElement).disabled;
405
- });
406
- ```
407
-
408
589
  ### `page-ready` moved to `@alwatr/page-ready`
409
590
 
410
591
  `dispatchPageId` / `onPageReady` are no longer part of this package.
411
592
 
412
593
  ---
413
594
 
414
- ---
415
-
416
595
  ## 🌊 Part of Alwatr Flux
417
596
 
418
597
  `@alwatr/action` is the **Action Layer** of the [Alwatr Flux](https://github.com/Alwatr/alwatr/tree/next/pkg/flux) architecture — a complete Unidirectional Data Flow system for building scalable Progressive Web Applications.
419
598
 
420
599
  ```
421
- View (HTML on-<event> attributes)
600
+ View (HTML on-<event> attributes + action-context)
422
601
 
423
- Action Layer (@alwatr/action) — global delegation, O(1) routing
602
+ Action Layer (@alwatr/action) — global delegation, O(1) routing, AFSA objects
424
603
 
425
- Controller (business logic via onAction)
604
+ Controller (business logic via onAction — receives full Action object)
426
605
 
427
606
  State Layer (@alwatr/signal) — fine-grained reactivity
428
607
 
@@ -16,8 +16,12 @@
16
16
  * - When an event fires, the handler walks up the DOM from `event.target`
17
17
  * using `closest()` to find the nearest element with an `on-<eventType>`
18
18
  * attribute (e.g. `on-click`, `on-submit`).
19
- * - Modifiers and payload resolvers run in the same pipeline as before.
20
- * - `dispatchAction` is called with the resolved payload.
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.
21
25
  *
22
26
  * ## Complexity
23
27
  *
@@ -53,8 +57,9 @@ export declare const DEFAULT_DELEGATED_EVENTS: readonly string[];
53
57
  * Registers global event delegation for `on-<eventType>` attributes.
54
58
  *
55
59
  * Attaches a single `capture`-phase listener on `document.body` for each
56
- * event type in `eventTypes`. All processing — modifier execution, payload
57
- * resolution, and `dispatchAction` — happens inside that one handler.
60
+ * event type in `eventTypes`. All processing — context resolution, modifier
61
+ * execution, payload resolution, and `dispatchAction` — happens inside that
62
+ * one handler.
58
63
  *
59
64
  * **Call this once at application bootstrap**, before any user interaction.
60
65
  * Subsequent calls with the same event types are no-ops (idempotent).
@@ -71,6 +76,24 @@ export declare const DEFAULT_DELEGATED_EVENTS: readonly string[];
71
76
  * after this call — via `innerHTML`, `lit-html`, a framework renderer, or
72
77
  * server-sent HTML — is automatically covered. No re-bootstrap is needed.
73
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="add_to_cart:42">Add</button>
88
+ * </section>
89
+ * ```
90
+ *
91
+ * ```ts
92
+ * onAction('add_to_cart', (action) => {
93
+ * console.log(action.context); // 'product-list'
94
+ * });
95
+ * ```
96
+ *
74
97
  * @param eventTypes - Event types to delegate. Defaults to `DEFAULT_DELEGATED_EVENTS`.
75
98
  *
76
99
  * @example — minimal bootstrap
@@ -80,7 +103,7 @@ export declare const DEFAULT_DELEGATED_EVENTS: readonly string[];
80
103
  * // One call activates the entire page.
81
104
  * setupActionDelegation();
82
105
  *
83
- * onAction('open_drawer', (panel) => openDrawer(panel));
106
+ * onAction('open_drawer', (action) => openDrawer(action.payload));
84
107
  * ```
85
108
  *
86
109
  * @example — with extra event types
@@ -1 +1 @@
1
- {"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AA8LH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAA2C,CAAC;AAElG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,GAAE,SAAS,MAAM,EAA6B,GAAG,IAAI,CASpG;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAO/C"}
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"}
@@ -1,3 +1,4 @@
1
+ import type { Action } from './type.js';
1
2
  /**
2
3
  * Module-scoped logger for `@alwatr/action`.
3
4
  * Scoped to `'alwatr-action'` so log lines are easy to filter in the console.
@@ -6,17 +7,18 @@
6
7
  */
7
8
  export declare const logger_: import("@alwatr/logger").AlwatrLogger;
8
9
  /**
9
- * The action channel — a `ChannelSignal` strictly typed by `ActionRecord`.
10
+ * The internal action channel — a `ChannelSignal` keyed by action `type`.
10
11
  *
11
- * Only action names declared in `ActionRecord` (via declaration merging) are
12
- * accepted at compile time. Passing an unknown action name to `onAction` or
13
- * `dispatchAction` is a **compile error** there is no string fallback.
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.
14
16
  *
15
17
  * Uses `ChannelSignal` for O(1) routing: dispatching action `'A'` performs a
16
18
  * single `Map.get('A')` lookup and invokes only the handlers registered for
17
- * that specific action — never handlers for `'B'`, `'C'`, etc.
19
+ * that specific type — never handlers for `'B'`, `'C'`, etc.
18
20
  *
19
21
  * @internal — not part of the public API; use `onAction` / `dispatchAction` instead.
20
22
  */
21
- export declare const internalChannel_: import("@alwatr/signal").ChannelSignal<Record<string, unknown>>;
22
- //# sourceMappingURL=lib.d.ts.map
23
+ export declare const internalChannel_: import("@alwatr/signal").ChannelSignal<Record<string, Action<never>>>;
24
+ //# sourceMappingURL=lib_.d.ts.map
@@ -0,0 +1 @@
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/main.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  /**
2
2
  * @alwatr/action — Declarative DOM action-dispatch for Unidirectional Data Flow.
3
3
  *
4
+ * Implements the **Alwatr Flux Standard Action (AFSA)** pattern: every action
5
+ * flowing through the bus is a single, typed `Action<K>` object carrying
6
+ * `type`, `payload`, `context`, and optional `meta`. This replaces the previous
7
+ * two-argument `(id, payload)` API with a unified structure that is extensible
8
+ * without breaking existing call sites.
9
+ *
4
10
  * ## Activating `on-<eventType>` attributes
5
11
  *
6
12
  * Call `setupActionDelegation()` once at bootstrap. A single capture-phase
@@ -11,7 +17,7 @@
11
17
  * import {setupActionDelegation, onAction} from '@alwatr/action';
12
18
  *
13
19
  * setupActionDelegation();
14
- * onAction('open_drawer', (panel) => openDrawer(panel));
20
+ * onAction('open_drawer', (action) => openDrawer(action.payload));
15
21
  * ```
16
22
  *
17
23
  * ## Attribute syntax
@@ -26,12 +32,31 @@
26
32
  * <form on-submit="submit_form:$formdata; prevent,validate" novalidate>…</form>
27
33
  * ```
28
34
  *
35
+ * ## Context scoping
36
+ *
37
+ * Wrap elements in an `[action-context]` container to scope their actions.
38
+ * The delegation handler resolves the nearest ancestor and attaches its value
39
+ * to `action.context`:
40
+ *
41
+ * ```html
42
+ * <section action-context="product-list">
43
+ * <button on-click="add_to_cart:42">Add</button>
44
+ * </section>
45
+ * ```
46
+ *
47
+ * ```ts
48
+ * onAction('add_to_cart', (action) => {
49
+ * console.log(action.context); // 'product-list'
50
+ * console.log(action.payload); // '42'
51
+ * });
52
+ * ```
53
+ *
29
54
  * ## Programmatic dispatch
30
55
  *
31
56
  * ```ts
32
57
  * import {dispatchAction} from '@alwatr/action';
33
58
  *
34
- * dispatchAction('navigate', '/dashboard');
59
+ * dispatchAction({type: 'navigate', payload: '/dashboard'});
35
60
  * ```
36
61
  *
37
62
  * ## Registering typed actions
@@ -51,7 +76,8 @@
51
76
  *
52
77
  * ## Public API
53
78
  *
54
- * - `ActionRecord` extend this interface to register typed actions
79
+ * - `Action` the AFSA object interface (`type`, `payload`, `context`, `meta`)
80
+ * - `ActionRecord` — extend this interface to register typed actions
55
81
  * - `setupActionDelegation` / `teardownActionDelegation` — global delegation lifecycle
56
82
  * - `DEFAULT_DELEGATED_EVENTS` — default event types covered by delegation
57
83
  * - `onAction` / `dispatchAction` — subscribe to and dispatch named actions
@@ -61,7 +87,7 @@
61
87
  *
62
88
  * For page-ready signals in SSG/SSR apps, use `@alwatr/page-ready` instead.
63
89
  */
64
- export type { ActionRecord } from './action-record.js';
65
- export * from './method.js';
66
90
  export * from './delegate.js';
91
+ export * from './method.js';
92
+ export type * from './type.js';
67
93
  //# sourceMappingURL=main.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,YAAY,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AACrD,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AACH,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,mBAAmB,WAAW,CAAC"}