@alwatr/action 9.13.0 → 9.16.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Declarative DOM action-dispatch — the Action layer for Unidirectional Data Flow.**
4
4
 
5
- `@alwatr/action` bridges HTML `on-action` attributes to typed signal handlers using **global event delegation**. One listener on `document.body` covers every element on the page — including elements added dynamically after bootstrap — with O(1) initialization cost regardless of how many elements exist.
5
+ `@alwatr/action` bridges HTML `on-<eventType>` attributes to typed signal handlers using **global event delegation**. One listener on `document.body` covers every element on the page — including elements added dynamically after bootstrap — with O(1) initialization cost regardless of how many elements exist.
6
6
 
7
7
  ---
8
8
 
@@ -19,26 +19,27 @@
19
19
 
20
20
  ## How It Works
21
21
 
22
+ ### Action Bus
23
+
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
+
22
26
  ### Global Event Delegation
23
27
 
24
- Instead of attaching one listener per element, a single capture-phase listener is registered on `document.body` for each event type. When an event fires anywhere on the page, the handler walks up from `event.target` using `closest('[on-action]')` to find the nearest element with an `on-action` attribute, parses the attribute, runs modifiers, resolves the payload, and calls `dispatchAction`.
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.
25
29
 
26
30
  ```
27
31
  User clicks a button
28
32
 
29
33
 
30
- document.body capture listener (1 listener total)
34
+ document.body capture listener (1 listener per event type)
31
35
 
32
- └─ closest('[on-action]') → finds element
33
- parse attribute → 'click->add-to-cart:42'
36
+ └─ closest('[on-click]') → finds element
37
+ parse attribute → 'add_to_cart:42'
34
38
  run modifiers → none
35
39
  resolve payload → '42'
36
- dispatchAction('add-to-cart', '42')
37
-
38
-
39
- ChannelSignal.dispatch('add-to-cart', '42') [O(1)]
40
+ internalChannel_.dispatch('add_to_cart', '42')
40
41
 
41
- └─ Map.get('add-to-cart') → invoke only matching handlers
42
+ └─ Map.get('add_to_cart') → O(1) → invoke only matching handlers
42
43
  ```
43
44
 
44
45
  ### Complexity
@@ -48,10 +49,11 @@ document.body capture listener (1 listener total)
48
49
  | Boot time | O(N elements) | **O(1)** |
49
50
  | Memory | O(N listeners) | **O(1)** |
50
51
  | Dynamic content | Requires re-bootstrap | **Works out-of-box** |
52
+ | `once` modifier | Native option | Remove attribute |
51
53
 
52
- ### Action Bus
54
+ ### `once` modifier
53
55
 
54
- 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.
56
+ In delegation mode, `once` is implemented by removing the `on-<eventType>` attribute from the element after the first fire. This is simpler than a `WeakSet` cache and naturally handles element reuseif the element is re-rendered with the attribute, it fires again.
55
57
 
56
58
  ---
57
59
 
@@ -69,55 +71,54 @@ npm i @alwatr/action
69
71
 
70
72
  ### 1. Register your action types
71
73
 
72
- Create a declaration file in your package to extend `ActionRecord`. This gives you full type safety and IDE autocomplete across the entire app:
74
+ Extend `ActionRecord` via declaration merging. This gives you full type safety and IDE autocomplete passing an undeclared action name is a **compile error**.
73
75
 
74
76
  ```ts
75
77
  // src/action-record.ts
76
78
  declare module '@alwatr/action' {
77
79
  interface ActionRecord {
78
- 'open-drawer': string;
79
- 'search-query': string;
80
- 'add-to-cart': {productId: number; qty: number};
81
- 'logout': void;
80
+ open_drawer: string;
81
+ search_query: string;
82
+ add_to_cart: {productId: number; qty: number};
83
+ logout: void;
82
84
  }
83
85
  }
84
86
  ```
85
87
 
86
- Passing an action name not declared in `ActionRecord` is a **compile error** — there is no string fallback.
87
-
88
88
  ### 2. Bootstrap delegation
89
89
 
90
90
  ```ts
91
91
  import {setupActionDelegation, onAction} from '@alwatr/action';
92
92
  import './action-record.js'; // ensure the declaration is loaded
93
93
 
94
- // One call — the entire page is covered, including future dynamic content.
95
94
  setupActionDelegation();
96
95
 
97
- // Payload types are inferred automatically from ActionRecord — no generics needed.
98
- onAction('open-drawer', (panel) => openDrawer(panel)); // panel: string
99
- onAction('search-query', (query) => performSearch(query)); // query: string
100
- onAction('add-to-cart', (item) => {
101
- cartService.add(item.productId, item.qty); // fully typed, no `!`
96
+ // Payload types are inferred from ActionRecord — no 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
102
100
  });
103
101
  ```
104
102
 
105
103
  ### 3. Add attributes to HTML
106
104
 
107
105
  ```html
108
- <!-- Dispatches 'open-drawer' with payload 'main' on click -->
109
- <button on-action="click->open-drawer:main">Open Drawer</button>
106
+ <!-- Dispatches 'close_drawer' on click no payload -->
107
+ <button on-click="close_drawer">Close</button>
108
+
109
+ <!-- Dispatches 'open_drawer' with payload 'main' on click -->
110
+ <button on-click="open_drawer:main">Open Drawer</button>
110
111
 
111
- <!-- Dispatches 'search-query' with the input's live value -->
112
+ <!-- Dispatches 'search_query' with the input's live value -->
112
113
  <input
113
114
  type="search"
114
- on-action="input->search-query:$value"
115
+ on-input="search_query:$value"
115
116
  placeholder="Search…"
116
117
  />
117
118
 
118
119
  <!-- Prevents default, validates, then dispatches all field values -->
119
120
  <form
120
- on-action="submit.prevent.validate->submit-form:$formdata"
121
+ on-submit="submit_form:$formdata; prevent,validate"
121
122
  novalidate
122
123
  >
123
124
  <input
@@ -126,19 +127,20 @@ onAction('add-to-cart', (item) => {
126
127
  />
127
128
  <button type="submit">Save</button>
128
129
  </form>
130
+
131
+ <!-- Fires only once — attribute is removed after first click -->
132
+ <button on-click="welcome_dismissed; once">Got it</button>
129
133
  ```
130
134
 
131
- ### 3. Programmatic dispatch
135
+ ### 4. Programmatic dispatch
132
136
 
133
137
  ```ts
134
138
  import {dispatchAction} from '@alwatr/action';
135
139
 
136
- // Trigger actions from code — after async ops, from service layers, etc.
137
140
  await uploadFile(file);
138
- dispatchAction('upload-complete', fileId);
141
+ dispatchAction('upload_complete', fileId);
139
142
 
140
143
  dispatchAction('navigate', '/dashboard');
141
- dispatchAction<{code: number}>('show-error', {code: 404});
142
144
  ```
143
145
 
144
146
  ---
@@ -146,27 +148,23 @@ dispatchAction<{code: number}>('show-error', {code: 404});
146
148
  ## Attribute Syntax
147
149
 
148
150
  ```
149
- on-action="eventType[.modifier…]->actionId[:payload]"
151
+ on-<eventType>="actionId[:payload][; modifier1,modifier2,…]"
150
152
  ```
151
153
 
152
- | Segment | Description | Example |
153
- | ----------- | --------------------------------------------------------- | ----------------------------- |
154
- | `eventType` | Any standard DOM event name | `click`, `input`, `submit` |
155
- | `modifier` | Optional dot-chained tokens processed before dispatch | `.prevent`, `.validate` |
156
- | `actionId` | Identifier your handler subscribes to | `open-drawer`, `search-query` |
157
- | `:payload` | Optional literal string, or a `$`-prefixed resolver token | `:main`, `:$value` |
154
+ | Segment | Description | Example |
155
+ | ------------- | ----------------------------------------------------------- | ----------------------------- |
156
+ | `eventType` | Any standard DOM event name — encoded in the attribute name | `on-click`, `on-submit` |
157
+ | `actionId` | Identifier your handler subscribes to | `open_drawer`, `search_query` |
158
+ | `:payload` | Optional literal string, or a `$`-prefixed resolver token | `:main`, `:$value` |
159
+ | `; modifiers` | Optional comma-separated modifier list after a semicolon | `; prevent,validate` |
158
160
 
159
161
  ### Built-in modifiers
160
162
 
161
- | Modifier | Behavior |
162
- | ----------- | -------------------------------------------------------------------- |
163
- | `.prevent` | Calls `event.preventDefault()` |
164
- | `.stop` | Calls `event.stopPropagation()` |
165
- | `.once` | Dispatches the action only once per element (emulated via `WeakSet`) |
166
- | `.validate` | Cancels dispatch if the nearest `<form>` fails `checkValidity()` |
167
-
168
- > **Note:** `.passive` is not supported in delegation mode because all delegated
169
- > listeners must be non-passive to allow `.prevent` to work.
163
+ | Modifier | Behavior |
164
+ | ---------- | ------------------------------------------------------------------------------------- |
165
+ | `prevent` | Calls `event.preventDefault()` |
166
+ | `once` | Removes the `on-<eventType>` attribute after first fire — action dispatches only once |
167
+ | `validate` | Cancels dispatch if the nearest `<form>` fails `checkValidity()` |
170
168
 
171
169
  ### Built-in payload resolvers
172
170
 
@@ -181,31 +179,22 @@ on-action="eventType[.modifier…]->actionId[:payload]"
181
179
 
182
180
  ### `ActionRecord` (interface)
183
181
 
184
- The global action type registry. Extend it via declaration merging to register your application's actions and unlock full type safety in `onAction` and `dispatchAction`.
182
+ The global action type registry. Extend via declaration merging to register typed actions.
185
183
 
186
184
  ```ts
187
- // src/action-record.ts
188
185
  declare module '@alwatr/action' {
189
186
  interface ActionRecord {
190
- 'open-drawer': string;
191
- 'add-to-cart': {productId: number; qty: number};
192
- 'logout': void;
187
+ open_drawer: string;
188
+ logout: void;
193
189
  }
194
190
  }
195
191
  ```
196
192
 
197
- Once declared:
198
-
199
- - `onAction('open-drawer', (panel) => …)` — `panel` is inferred as `string`
200
- - `dispatchAction('add-to-cart', {productId: 42, qty: 1})` — payload type enforced
201
- - `dispatchAction('unknown-action', …)` — **compile error**
202
-
203
193
  ---
204
194
 
205
195
  ### `setupActionDelegation(eventTypes?)`
206
196
 
207
- Registers global event delegation on `document.body`. Call once at bootstrap.
208
- Subsequent calls with the same event types are no-ops (idempotent).
197
+ Registers global event delegation on `document.body`. Call once at bootstrap. Idempotent.
209
198
 
210
199
  ```ts
211
200
  function setupActionDelegation(eventTypes?: readonly string[]): void;
@@ -216,18 +205,14 @@ Defaults to `DEFAULT_DELEGATED_EVENTS`: `['click', 'submit', 'input', 'change']`
216
205
  ```ts
217
206
  import {setupActionDelegation, DEFAULT_DELEGATED_EVENTS} from '@alwatr/action';
218
207
 
219
- // Default events
220
- setupActionDelegation();
221
-
222
- // Add extra event types
223
- setupActionDelegation([...DEFAULT_DELEGATED_EVENTS, 'keydown', 'pointerup']);
208
+ setupActionDelegation([...DEFAULT_DELEGATED_EVENTS, 'keydown']);
224
209
  ```
225
210
 
226
211
  ---
227
212
 
228
213
  ### `teardownActionDelegation()`
229
214
 
230
- Removes all delegation listeners. Useful in tests or micro-frontend teardown.
215
+ Removes all delegation listeners and clears the descriptor cache. Useful in tests or micro-frontend teardown.
231
216
 
232
217
  ```ts
233
218
  function teardownActionDelegation(): void;
@@ -237,54 +222,32 @@ function teardownActionDelegation(): void;
237
222
 
238
223
  ### `onAction(actionId, handler)`
239
224
 
240
- Subscribes to a named action. Uses `ChannelSignal.on()` for O(1) routing.
225
+ Subscribes to a named action. O(1) routing via `ChannelSignal`.
241
226
 
242
227
  ```ts
243
- function onAction<T = string>(actionId: string, handler: (payload?: T) => void): SubscribeResult;
228
+ function onAction<K extends keyof ActionRecord>(
229
+ actionId: K,
230
+ handler: (payload: ActionRecord[K]) => void,
231
+ ): SubscribeResult;
244
232
  ```
245
233
 
246
234
  ```ts
247
- const sub = onAction('open-drawer', (panel) => openDrawer(panel));
248
-
249
- // Unsubscribe when no longer needed (prevents memory leaks)
250
- sub.unsubscribe();
235
+ const sub = onAction('open_drawer', (panel) => openDrawer(panel));
236
+ sub.unsubscribe(); // prevent memory leaks
251
237
  ```
252
238
 
253
239
  ---
254
240
 
255
241
  ### `dispatchAction(actionId, payload?)`
256
242
 
257
- Dispatches a named action to all matching `onAction` subscribers.
243
+ Dispatches a named action. Payload type is enforced by `ActionRecord`.
258
244
 
259
245
  ```ts
260
- function dispatchAction<T = string>(actionId: string, actionPayload?: T): void;
261
- ```
262
-
263
- ---
264
-
265
- ### `dispatchPageId(element?)`
246
+ // With payload
247
+ dispatchAction('open_drawer', 'settings');
266
248
 
267
- Reads the `page-id` attribute from `element` (defaults to `document.body`) and
268
- dispatches a `'page-ready'` action with the page identifier as payload.
269
-
270
- ```ts
271
- function dispatchPageId(element?: HTMLElement): void;
272
- ```
273
-
274
- ```html
275
- <body page-id="home">
276
-
277
- </body>
278
- ```
279
-
280
- ```ts
281
- import {dispatchPageId, onAction} from '@alwatr/action';
282
-
283
- dispatchPageId(); // → dispatchAction('page-ready', 'home')
284
-
285
- onAction('page-ready', (pageId) => {
286
- console.log('navigated to:', pageId); // 'home'
287
- });
249
+ // Void payload no second argument
250
+ dispatchAction('logout');
288
251
  ```
289
252
 
290
253
  ---
@@ -292,48 +255,57 @@ onAction('page-ready', (pageId) => {
292
255
  ### `registerModifier(name, handler)`
293
256
 
294
257
  Registers a custom modifier. Return `false` to cancel the dispatch.
295
- Works with both delegation and programmatic dispatch.
258
+
259
+ Handler signature: `(event: Event, element: HTMLElement) => boolean`
296
260
 
297
261
  ```ts
298
262
  import {registerModifier} from '@alwatr/action';
299
263
 
300
- registerModifier('confirm', function () {
301
- return window.confirm('Are you sure?');
264
+ registerModifier('not_disabled', (_event, element) => {
265
+ return !(element as HTMLButtonElement).disabled;
302
266
  });
303
267
  ```
304
268
 
305
269
  ```html
306
- <button on-action="click.confirm->delete-item:42">Delete</button>
307
- ```
308
-
309
- The handler receives an `ActionContext` as `this`:
310
-
311
- ```ts
312
- interface ActionContext {
313
- readonly element: HTMLElement; // the element with the on-action attribute
314
- }
270
+ <button
271
+ on-click="select_item:$data_id; not_disabled"
272
+ data-id="42"
273
+ >
274
+ Select
275
+ </button>
315
276
  ```
316
277
 
317
278
  ---
318
279
 
319
280
  ### `registerPayloadResolver(name, resolver)`
320
281
 
321
- Registers a custom payload resolver. The return value becomes the action payload.
322
- Works with both delegation and programmatic dispatch.
282
+ Registers a custom payload resolver.
283
+
284
+ Handler signature: `(event: Event, element: HTMLElement) => unknown`
323
285
 
324
286
  ```ts
325
287
  import {registerPayloadResolver} from '@alwatr/action';
326
288
 
327
- registerPayloadResolver('$checked', function () {
328
- return (this.element as HTMLInputElement).checked;
289
+ registerPayloadResolver('$checked', (_event, element) => {
290
+ return (element as HTMLInputElement).checked;
291
+ });
292
+
293
+ registerPayloadResolver('$data_id', (_event, element) => {
294
+ return (element as HTMLElement).dataset.id ?? null;
329
295
  });
330
296
  ```
331
297
 
332
298
  ```html
333
299
  <input
334
300
  type="checkbox"
335
- on-action="change->toggle-feature:$checked"
301
+ on-change="toggle_feature:$checked"
336
302
  />
303
+ <li
304
+ on-click="select_item:$data_id"
305
+ data-id="42"
306
+ >
307
+ Item
308
+ </li>
337
309
  ```
338
310
 
339
311
  ---
@@ -341,93 +313,135 @@ registerPayloadResolver('$checked', function () {
341
313
  ## Unidirectional Data Flow
342
314
 
343
315
  ```
344
- ┌─────────────────────────────────────────────────────────┐
345
- UI Layer │
346
- │ <button on-action="click->add-to-cart:42">Add</button>
347
- └────────────────────────┬────────────────────────────────┘
348
- │ DOM event bubbles to body
349
-
350
- ┌─────────────────────────────────────────────────────────┐
351
- Action Layer (@alwatr/action)
352
- │ document.body capture listener (1 listener total)
353
- │ → closest('[on-action]') → parse → run modifiers
354
- │ → dispatchAction('add-to-cart', '42') [O(1)] │
355
- └────────────────────────┬────────────────────────────────┘
356
- action signal (O(1) routing)
357
-
358
- ┌─────────────────────────────────────────────────────────┐
359
- Business Logic Layer │
360
- │ onAction('add-to-cart', (id) => cartService.add(id))
361
- └────────────────────────┬────────────────────────────────┘
362
- │ state update
363
-
364
- ┌─────────────────────────────────────────────────────────┐
365
- State Layer (@alwatr/signal)
366
- │ cartSignal.set(newCartState)
367
- └────────────────────────┬────────────────────────────────┘
368
- │ state flows down to UI
369
-
370
- UI re-renders
316
+ ┌──────────────────────────────────────────────────────────┐
317
+ UI Layer │
318
+ │ <button on-click="add_to_cart:42">Add</button>
319
+ └─────────────────────────┬────────────────────────────────┘
320
+ │ DOM event bubbles to body
321
+
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
+ └─────────────────────────┬────────────────────────────────┘
328
+ │ O(1) routing via ChannelSignal
329
+
330
+ ┌──────────────────────────────────────────────────────────┐
331
+ Business Logic Layer │
332
+ │ onAction('add_to_cart', (id) => cartService.add(id))
333
+ └─────────────────────────┬────────────────────────────────┘
334
+ │ state update
335
+
336
+ ┌──────────────────────────────────────────────────────────┐
337
+ State Layer (@alwatr/signal)
338
+ │ cartSignal.set(newCartState)
339
+ └─────────────────────────┬────────────────────────────────┘
340
+ │ state flows down to UI
341
+
342
+ UI re-renders
371
343
  ```
372
344
 
373
345
  ---
374
346
 
347
+ ## Page Identity
348
+
349
+ For page-ready signals in SSG/SSR apps (reading `page-id` attribute and notifying
350
+ page-specific handlers), use [`@alwatr/page-ready`](../page-ready/README.md) instead.
351
+ It is intentionally separate from the action bus — page identity is a routing/lifecycle
352
+ concern, not a user-interaction action.
353
+
354
+ ---
355
+
375
356
  ## Migration from Previous Versions
376
357
 
377
- ### `registerActionDirective` / `registerPageIdDirective` removed
358
+ ### Attribute syntax changed
378
359
 
379
- The directive-based approach has been replaced by global delegation.
360
+ 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.
380
361
 
381
362
  **Before:**
382
363
 
383
- ```ts
384
- import {registerActionDirective, registerPageIdDirective} from '@alwatr/action';
385
- import {bootstrapDirectives} from '@alwatr/directive';
386
-
387
- registerActionDirective();
388
- registerPageIdDirective();
389
- bootstrapDirectives();
364
+ ```html
365
+ <button on-action="click->open_drawer:main">Open</button>
366
+ <form
367
+ on-action="submit.prevent.validate->submit_form:$formdata"
368
+ novalidate
369
+ >
370
+
371
+ </form>
372
+ <button on-action="click.once->welcome_dismissed">Got it</button>
390
373
  ```
391
374
 
392
375
  **After:**
393
376
 
394
- ```ts
395
- import {setupActionDelegation, dispatchPageId} from '@alwatr/action';
396
-
397
- setupActionDelegation();
398
- dispatchPageId();
377
+ ```html
378
+ <button on-click="open_drawer:main">Open</button>
379
+ <form
380
+ on-submit="submit_form:$formdata; prevent,validate"
381
+ novalidate
382
+ >
383
+
384
+ </form>
385
+ <button on-click="welcome_dismissed; once">Got it</button>
399
386
  ```
400
387
 
401
- ### `ActionDirective` / `PageIdDirective` removed
402
-
403
- These classes are no longer exported. Use `setupActionDelegation()` and
404
- `dispatchPageId()` instead.
405
-
406
- ### `ModifierHandler` / `PayloadResolver` context changed
388
+ ### `ActionContext` removed
407
389
 
408
- The `this` context in custom modifier and resolver functions changed from
409
- `ActionDirective` to `ActionContext`:
390
+ The `this` context in modifier and resolver handlers changed to explicit parameters:
410
391
 
411
392
  **Before:**
412
393
 
413
394
  ```ts
414
- registerModifier('not-disabled', function () {
415
- return !(this.element_ as HTMLButtonElement).disabled; // this.element_
395
+ registerModifier('not_disabled', function () {
396
+ return !(this.element as HTMLButtonElement).disabled;
416
397
  });
417
398
  ```
418
399
 
419
400
  **After:**
420
401
 
421
402
  ```ts
422
- registerModifier('not-disabled', function () {
423
- return !(this.element as HTMLButtonElement).disabled; // this.element (no underscore)
403
+ registerModifier('not_disabled', (_event, element) => {
404
+ return !(element as HTMLButtonElement).disabled;
424
405
  });
425
406
  ```
426
407
 
427
- ### `ActionSignalPayload` removed
408
+ ### `page-ready` moved to `@alwatr/page-ready`
409
+
410
+ `dispatchPageId` / `onPageReady` are no longer part of this package.
411
+
412
+ ---
413
+
414
+ ---
415
+
416
+ ## 🌊 Part of Alwatr Flux
417
+
418
+ `@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
+
420
+ ```
421
+ View (HTML on-<event> attributes)
422
+
423
+ Action Layer (@alwatr/action) — global delegation, O(1) routing
424
+
425
+ Controller (business logic via onAction)
426
+
427
+ State Layer (@alwatr/signal) — fine-grained reactivity
428
+
429
+ View (re-render only affected nodes)
430
+ ```
431
+
432
+ `@alwatr/action` is the bridge between the **View** and **Controller** layers. It captures user intent from HTML attributes and routes it to the right handler — without any coupling between the UI and business logic.
433
+
434
+ **The full Flux bundle** (`@alwatr/flux`) includes actions, signals, directives, page-ready, and storage — everything you need to build a complete reactive application from a single import.
435
+
436
+ ```typescript
437
+ // Use @alwatr/flux for the complete architecture
438
+ import {setupActionDelegation, onAction, createStateSignal} from '@alwatr/flux';
439
+
440
+ // Or use @alwatr/action standalone for just the action bus
441
+ import {setupActionDelegation, onAction, dispatchAction} from '@alwatr/action';
442
+ ```
428
443
 
429
- This type was an implementation detail of the old `EventSignal`-based bus and
430
- is no longer needed. Use `onAction` and `dispatchAction` directly.
444
+ [View the complete Flux documentation](https://github.com/Alwatr/alwatr/tree/next/pkg/flux)
431
445
 
432
446
  ---
433
447
 
@@ -13,8 +13,8 @@
13
13
  * // In your package: src/action-record.ts
14
14
  * declare module '@alwatr/action' {
15
15
  * interface ActionRecord {
16
- * 'open-drawer': string;
17
- * 'add-to-cart': {productId: number; qty: number};
16
+ * 'open_drawer': string;
17
+ * 'add_to_cart': {productId: number; qty: number};
18
18
  * 'logout': void;
19
19
  * }
20
20
  * }
@@ -33,34 +33,22 @@
33
33
  * Extend this interface via declaration merging to register your application's
34
34
  * actions and gain full type safety in `onAction` and `dispatchAction`.
35
35
  *
36
- * Built-in system actions are declared here. Application-level actions should
37
- * be declared in a dedicated `action-record.ts` file within each feature package.
36
+ * This interface is intentionally empty in the base package all actions are
37
+ * application-specific and should be declared in a dedicated `action-record.ts`
38
+ * file within each feature package.
38
39
  *
39
40
  * @example — registering actions in a feature package
40
41
  * ```ts
41
42
  * // pkg/my-feature/src/action-record.ts
42
43
  * declare module '@alwatr/action' {
43
44
  * interface ActionRecord {
44
- * 'open-drawer': string;
45
- * 'add-to-cart': {productId: number; qty: number};
45
+ * 'open_drawer': string;
46
+ * 'add_to_cart': {productId: number; qty: number};
46
47
  * 'logout': void;
47
48
  * }
48
49
  * }
49
50
  * ```
50
51
  */
51
52
  export interface ActionRecord {
52
- /**
53
- * Dispatched by `dispatchPageId()` when the page identity is read from the
54
- * `page-id` HTML attribute. Payload is the page identifier string.
55
- *
56
- * @example
57
- * ```html
58
- * <body page-id="home">…</body>
59
- * ```
60
- * ```ts
61
- * onAction('page-ready', (pageId) => router.setPage(pageId));
62
- * ```
63
- */
64
- 'page-ready': string;
65
53
  }
66
54
  //# sourceMappingURL=action-record.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action-record.d.ts","sourceRoot":"","sources":["../src/action-record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;;;OAWG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB"}
1
+ {"version":3,"file":"action-record.d.ts","sourceRoot":"","sources":["../src/action-record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,YAAY;CAAG"}