@alwatr/action 9.26.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.
- package/README.md +86 -211
- package/dist/action-service.d.ts +169 -0
- package/dist/action-service.d.ts.map +1 -0
- package/dist/main.d.ts +87 -69
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +5 -7
- package/package.json +2 -2
- package/src/action-service.ts +388 -0
- package/src/main.ts +111 -69
- package/dist/delegate.d.ts +0 -126
- package/dist/delegate.d.ts.map +0 -1
- package/dist/lib_.d.ts +0 -24
- package/dist/lib_.d.ts.map +0 -1
- package/dist/method.d.ts +0 -169
- package/dist/method.d.ts.map +0 -1
- package/dist/registry_.d.ts +0 -24
- package/dist/registry_.d.ts.map +0 -1
- package/src/delegate.ts +0 -359
- package/src/lib_.ts +0 -27
- package/src/method.ts +0 -219
- package/src/registry_.ts +0 -109
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**Declarative DOM action-dispatch — the Action layer for Unidirectional Data Flow.**
|
|
4
4
|
|
|
5
|
-
`@alwatr/action` bridges HTML `on-<eventType>` attributes to typed signal handlers using **global event delegation**.
|
|
5
|
+
`@alwatr/action` bridges HTML `on-<eventType>` attributes to typed signal handlers using **global event delegation**. It is implemented as a factory service (`ActionService`) with all state and listeners encapsulated. A pre-instantiated singleton `actionService` is exported for immediate use.
|
|
6
|
+
|
|
7
|
+
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
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
@@ -40,7 +42,7 @@ document.body capture listener (1 listener per event type)
|
|
|
40
42
|
parse attribute → 'ui_add_to_cart:42'
|
|
41
43
|
run modifiers → none
|
|
42
44
|
resolve payload → '42'
|
|
43
|
-
|
|
45
|
+
actionService.dispatch({
|
|
44
46
|
type: 'ui_add_to_cart',
|
|
45
47
|
payload: '42',
|
|
46
48
|
context: 'product-list',
|
|
@@ -99,15 +101,18 @@ declare module '@alwatr/action' {
|
|
|
99
101
|
|
|
100
102
|
### 2. Bootstrap delegation
|
|
101
103
|
|
|
104
|
+
Use the pre-instantiated singleton `actionService`:
|
|
105
|
+
|
|
102
106
|
```ts
|
|
103
|
-
import {
|
|
107
|
+
import {actionService} from '@alwatr/action';
|
|
104
108
|
import './action-record.js'; // ensure the declaration is loaded
|
|
105
109
|
|
|
106
|
-
|
|
110
|
+
// Initialize global event delegation
|
|
111
|
+
actionService.setupDelegation();
|
|
107
112
|
|
|
108
113
|
// The handler receives the full Action<K> object — payload, context, and meta in one place.
|
|
109
|
-
|
|
110
|
-
|
|
114
|
+
actionService.on('ui_open_drawer', (action) => openDrawer(action.payload)); // action.payload: string
|
|
115
|
+
actionService.on('ui_add_to_cart', (action) => {
|
|
111
116
|
cartService.add(action.payload.productId, action.payload.qty); // fully typed
|
|
112
117
|
console.log(action.context); // e.g. 'product-list' — from nearest [action-context] ancestor
|
|
113
118
|
});
|
|
@@ -167,7 +172,7 @@ Wrap a group of elements in an `[action-context]` container to scope their actio
|
|
|
167
172
|
```
|
|
168
173
|
|
|
169
174
|
```ts
|
|
170
|
-
|
|
175
|
+
actionService.on('ui_slider_change', (action) => {
|
|
171
176
|
if (action.context === 'volume') audioService.setVolume(Number(action.payload));
|
|
172
177
|
if (action.context === 'brightness') displayService.setBrightness(Number(action.payload));
|
|
173
178
|
});
|
|
@@ -177,20 +182,20 @@ Context is `undefined` when no `[action-context]` ancestor exists — programmat
|
|
|
177
182
|
|
|
178
183
|
### 5. Programmatic dispatch
|
|
179
184
|
|
|
180
|
-
Use `
|
|
185
|
+
Use `actionService.dispatch` for code-originated actions (after async operations, from services, etc.).
|
|
181
186
|
These actions should **not** use the `ui_` prefix — that prefix is reserved for DOM-originated actions.
|
|
182
187
|
|
|
183
188
|
```ts
|
|
184
|
-
import {
|
|
189
|
+
import {actionService} from '@alwatr/action';
|
|
185
190
|
|
|
186
191
|
// Code-originated actions — no 'ui_' prefix
|
|
187
192
|
await uploadFile(file);
|
|
188
|
-
|
|
193
|
+
actionService.dispatch({type: 'upload_complete', payload: fileId});
|
|
189
194
|
|
|
190
|
-
|
|
195
|
+
actionService.dispatch({type: 'navigate', payload: '/dashboard'});
|
|
191
196
|
|
|
192
197
|
// With explicit context and meta
|
|
193
|
-
|
|
198
|
+
actionService.dispatch({
|
|
194
199
|
type: 'slider_change',
|
|
195
200
|
payload: 75,
|
|
196
201
|
context: 'volume',
|
|
@@ -260,10 +265,10 @@ interface Action<K extends keyof ActionRecord> {
|
|
|
260
265
|
Modifiers in the delegation pipeline receive the mutable `action` object and can enrich `meta` before the action reaches subscribers:
|
|
261
266
|
|
|
262
267
|
```ts
|
|
263
|
-
import {
|
|
268
|
+
import {actionService} from '@alwatr/action';
|
|
264
269
|
|
|
265
270
|
// A modifier that stamps a trace ID into meta
|
|
266
|
-
registerModifier('trace', (_event, _element, action) => {
|
|
271
|
+
actionService.registerModifier('trace', (_event, _element, action) => {
|
|
267
272
|
action.meta ??= {};
|
|
268
273
|
action.meta['traceId'] = crypto.randomUUID();
|
|
269
274
|
return true;
|
|
@@ -275,7 +280,7 @@ registerModifier('trace', (_event, _element, action) => {
|
|
|
275
280
|
```
|
|
276
281
|
|
|
277
282
|
```ts
|
|
278
|
-
|
|
283
|
+
actionService.on('ui_submit_order', (action) => {
|
|
279
284
|
console.log(action.meta?.['traceId']); // e.g. 'a1b2-c3d4-…'
|
|
280
285
|
});
|
|
281
286
|
```
|
|
@@ -284,182 +289,87 @@ onAction('ui_submit_order', (action) => {
|
|
|
284
289
|
|
|
285
290
|
## API Reference
|
|
286
291
|
|
|
287
|
-
### `
|
|
292
|
+
### `actionService` (Singleton instance of `ActionService`)
|
|
288
293
|
|
|
289
|
-
The
|
|
294
|
+
The pre-instantiated factory service exported for standard usage.
|
|
290
295
|
|
|
291
|
-
|
|
292
|
-
import type {Action} from '@alwatr/action';
|
|
293
|
-
|
|
294
|
-
// Reading fields in a handler
|
|
295
|
-
onAction('ui_add_to_cart', (action: Action<'ui_add_to_cart'>) => {
|
|
296
|
-
console.log(action.type); // 'ui_add_to_cart'
|
|
297
|
-
console.log(action.payload); // {productId: number; qty: number}
|
|
298
|
-
console.log(action.context); // string | undefined
|
|
299
|
-
console.log(action.meta); // Record<string, unknown> | undefined
|
|
300
|
-
});
|
|
301
|
-
```
|
|
296
|
+
#### `actionService.on(type, handler)`
|
|
302
297
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
### `ActionRecord` (interface)
|
|
306
|
-
|
|
307
|
-
The global action type registry. Extend via declaration merging to register typed actions.
|
|
298
|
+
Subscribes to a single action or an array of actions. O(1) routing via `ChannelSignal`.
|
|
308
299
|
|
|
309
300
|
```ts
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
}
|
|
301
|
+
actionService.on<K extends keyof ActionRecord>(
|
|
302
|
+
type: K | K[],
|
|
303
|
+
handler: (action: Action<K>) => Awaitable<void>
|
|
304
|
+
): SubscribeResult;
|
|
316
305
|
```
|
|
317
306
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
### `setupActionDelegation(eventTypes?)`
|
|
307
|
+
#### `actionService.dispatch(action)`
|
|
321
308
|
|
|
322
|
-
|
|
309
|
+
Dispatches an action to all subscribers.
|
|
323
310
|
|
|
324
311
|
```ts
|
|
325
|
-
|
|
312
|
+
actionService.dispatch<K extends keyof ActionRecord>(action: DispatchParam<K>): void;
|
|
326
313
|
```
|
|
327
314
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
```ts
|
|
331
|
-
import {setupActionDelegation, DEFAULT_DELEGATED_EVENTS} from '@alwatr/action';
|
|
332
|
-
|
|
333
|
-
setupActionDelegation([...DEFAULT_DELEGATED_EVENTS, 'keydown']);
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
---
|
|
315
|
+
#### `actionService.setupDelegation(eventTypes?)`
|
|
337
316
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
Removes all delegation listeners and clears the descriptor cache. Useful in tests or micro-frontend teardown.
|
|
317
|
+
Registers global capture listeners on `document.body`. Defaults to `['click', 'submit', 'input', 'change']`.
|
|
341
318
|
|
|
342
319
|
```ts
|
|
343
|
-
|
|
320
|
+
actionService.setupDelegation(eventTypes?: readonly string[]): void;
|
|
344
321
|
```
|
|
345
322
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
### `onAction(type, handler)`
|
|
323
|
+
#### `actionService.teardownDelegation()`
|
|
349
324
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
```ts
|
|
353
|
-
function onAction<K extends keyof ActionRecord>(
|
|
354
|
-
type: K | K[],
|
|
355
|
-
handler: (action: Action<K>) => Awaitable<void>,
|
|
356
|
-
): SubscribeResult;
|
|
357
|
-
```
|
|
325
|
+
Unregisters all delegation listeners and clears the descriptor cache.
|
|
358
326
|
|
|
359
327
|
```ts
|
|
360
|
-
|
|
361
|
-
const sub = onAction('ui_open_drawer', (action) => {
|
|
362
|
-
openDrawer(action.payload); // payload: string
|
|
363
|
-
console.log(action.context); // e.g. 'sidebar' or undefined
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// Subscribe to multiple actions with a single handler
|
|
367
|
-
const multiSub = onAction(['ui_increment', 'ui_decrement'], (action) => {
|
|
368
|
-
console.log('Action triggered:', action.type);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
sub.unsubscribe(); // prevent memory leaks
|
|
372
|
-
multiSub.unsubscribe();
|
|
328
|
+
actionService.teardownDelegation(): void;
|
|
373
329
|
```
|
|
374
330
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
### `dispatchAction(action)`
|
|
331
|
+
#### `actionService.registerModifier(name, handler)`
|
|
378
332
|
|
|
379
|
-
|
|
333
|
+
Registers custom modifiers.
|
|
380
334
|
|
|
381
335
|
```ts
|
|
382
|
-
|
|
336
|
+
actionService.registerModifier(name: string, handler: ModifierHandler): void;
|
|
383
337
|
```
|
|
384
338
|
|
|
385
|
-
|
|
386
|
-
// With payload (code-originated — no 'ui_' prefix)
|
|
387
|
-
dispatchAction({type: 'navigate', payload: 'settings'});
|
|
339
|
+
#### `actionService.registerPayloadResolver(name, resolver)`
|
|
388
340
|
|
|
389
|
-
|
|
390
|
-
dispatchAction({type: 'auth_expired', payload: undefined});
|
|
341
|
+
Registers custom payload resolvers.
|
|
391
342
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
type: 'navigate',
|
|
395
|
-
payload: 'settings',
|
|
396
|
-
context: 'header',
|
|
397
|
-
meta: {triggeredBy: 'keyboard'},
|
|
398
|
-
});
|
|
343
|
+
```ts
|
|
344
|
+
actionService.registerPayloadResolver(name: string, resolver: PayloadResolver): void;
|
|
399
345
|
```
|
|
400
346
|
|
|
401
347
|
---
|
|
402
348
|
|
|
403
|
-
### `
|
|
349
|
+
### `ActionService` (Class)
|
|
404
350
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
Handler signature: `(event: Event, element: HTMLElement, action: Action) => boolean`
|
|
408
|
-
|
|
409
|
-
The handler receives the mutable `action` object and may write to `action.meta`.
|
|
351
|
+
The class constructor, allowing creation of independent instances of the action bus and delegation pipeline if needed (e.g. in multi-app or test micro-environments).
|
|
410
352
|
|
|
411
353
|
```ts
|
|
412
|
-
import {
|
|
413
|
-
|
|
414
|
-
registerModifier('not_disabled', (_event, element) => {
|
|
415
|
-
return !(element as HTMLButtonElement).disabled;
|
|
416
|
-
});
|
|
354
|
+
import {ActionService} from '@alwatr/action';
|
|
417
355
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
action.meta ??= {};
|
|
421
|
-
action.meta['ts'] = Date.now();
|
|
422
|
-
return true;
|
|
423
|
-
});
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
```html
|
|
427
|
-
<button
|
|
428
|
-
on-click="ui_select_item:$data_id; not_disabled,timestamp"
|
|
429
|
-
data-id="42"
|
|
430
|
-
>
|
|
431
|
-
Select
|
|
432
|
-
</button>
|
|
356
|
+
const myService = new ActionService();
|
|
357
|
+
myService.setupDelegation(['click']);
|
|
433
358
|
```
|
|
434
359
|
|
|
435
360
|
---
|
|
436
361
|
|
|
437
|
-
###
|
|
438
|
-
|
|
439
|
-
Registers a custom payload resolver.
|
|
362
|
+
### Deprecated Global Functions (Backwards Compatibility)
|
|
440
363
|
|
|
441
|
-
|
|
364
|
+
For backwards compatibility with previous versions, the following wrapper functions are exported. They delegate directly to the `actionService` singleton instance and are marked as **deprecated**.
|
|
442
365
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
```html
|
|
452
|
-
<input
|
|
453
|
-
type="checkbox"
|
|
454
|
-
on-change="ui_toggle_feature:$checked"
|
|
455
|
-
/>
|
|
456
|
-
<li
|
|
457
|
-
on-click="ui_select_item:$data_id"
|
|
458
|
-
data-id="42"
|
|
459
|
-
>
|
|
460
|
-
Item
|
|
461
|
-
</li>
|
|
462
|
-
```
|
|
366
|
+
- `onAction(type, handler)` (deprecated wrapper for `actionService.on`)
|
|
367
|
+
- `dispatchAction(action)` (deprecated wrapper for `actionService.dispatch`)
|
|
368
|
+
- `setupActionDelegation(eventTypes?)` (deprecated wrapper for `actionService.setupDelegation`)
|
|
369
|
+
- `teardownActionDelegation()` (deprecated wrapper for `actionService.teardownDelegation`)
|
|
370
|
+
- `registerModifier(name, handler)` (deprecated wrapper for `actionService.registerModifier`)
|
|
371
|
+
- `registerPayloadResolver(name, resolver)` (deprecated wrapper for `actionService.registerPayloadResolver`)
|
|
372
|
+
- `DEFAULT_DELEGATED_EVENTS` (constant mapped to `ActionService.DEFAULT_DELEGATED_EVENTS`)
|
|
463
373
|
|
|
464
374
|
---
|
|
465
375
|
|
|
@@ -472,8 +382,8 @@ registerPayloadResolver('$data_id', (_event, element) => {
|
|
|
472
382
|
│ <button on-click="ui_add_to_cart:42">Add</button> │
|
|
473
383
|
│ </section> │
|
|
474
384
|
└─────────────────────────┬──────────────────────────────────┘
|
|
475
|
-
|
|
476
|
-
|
|
385
|
+
│ DOM event bubbles to body
|
|
386
|
+
▼
|
|
477
387
|
┌────────────────────────────────────────────────────────────┐
|
|
478
388
|
│ Action Layer (@alwatr/action) │
|
|
479
389
|
│ document.body capture listener (1 per event type) │
|
|
@@ -481,26 +391,26 @@ registerPayloadResolver('$data_id', (_event, element) => {
|
|
|
481
391
|
│ → closest('[action-context]') → context = 'cart' │
|
|
482
392
|
│ → run modifiers (may enrich action.meta) │
|
|
483
393
|
│ → resolve payload → '42' │
|
|
484
|
-
│ → dispatch
|
|
394
|
+
│ → actionService.dispatch(Action) [O(1)] │
|
|
485
395
|
└─────────────────────────┬──────────────────────────────────┘
|
|
486
|
-
|
|
487
|
-
|
|
396
|
+
│ O(1) routing via ChannelSignal
|
|
397
|
+
▼
|
|
488
398
|
┌────────────────────────────────────────────────────────────┐
|
|
489
399
|
│ Business Logic Layer │
|
|
490
|
-
│
|
|
400
|
+
│ actionService.on('ui_add_to_cart', (action) => { │
|
|
491
401
|
│ cartService.add(action.payload); │
|
|
492
402
|
│ // action.context === 'cart' │
|
|
493
403
|
│ }) │
|
|
494
404
|
└─────────────────────────┬──────────────────────────────────┘
|
|
495
|
-
|
|
496
|
-
|
|
405
|
+
│ state update
|
|
406
|
+
▼
|
|
497
407
|
┌────────────────────────────────────────────────────────────┐
|
|
498
408
|
│ State Layer (@alwatr/signal) │
|
|
499
409
|
│ cartSignal.set(newCartState) │
|
|
500
410
|
└─────────────────────────┬──────────────────────────────────┘
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
411
|
+
│ state flows down to UI
|
|
412
|
+
▼
|
|
413
|
+
UI re-renders
|
|
504
414
|
```
|
|
505
415
|
|
|
506
416
|
---
|
|
@@ -555,60 +465,25 @@ onAction('ui_add_to_cart', (action) => {
|
|
|
555
465
|
});
|
|
556
466
|
```
|
|
557
467
|
|
|
558
|
-
###
|
|
559
|
-
|
|
560
|
-
Modifier handlers now receive a third `action` argument for `meta` enrichment.
|
|
561
|
-
|
|
562
|
-
**Before:**
|
|
563
|
-
|
|
564
|
-
```ts
|
|
565
|
-
registerModifier('not_disabled', (_event, element) => {
|
|
566
|
-
return !(element as HTMLButtonElement).disabled;
|
|
567
|
-
});
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
**After (backward-compatible — third arg is optional to use):**
|
|
468
|
+
### Automated AI Migration Prompt
|
|
571
469
|
|
|
572
|
-
|
|
573
|
-
registerModifier('not_disabled', (_event, element, _action) => {
|
|
574
|
-
return !(element as HTMLButtonElement).disabled;
|
|
575
|
-
});
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
### Attribute syntax changed
|
|
579
|
-
|
|
580
|
-
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.
|
|
581
|
-
|
|
582
|
-
**Before:**
|
|
583
|
-
|
|
584
|
-
```html
|
|
585
|
-
<button on-action="click->ui_open_drawer:main">Open</button>
|
|
586
|
-
<form
|
|
587
|
-
on-action="submit.prevent.validate->ui_submit_form:$formdata"
|
|
588
|
-
novalidate
|
|
589
|
-
>
|
|
590
|
-
…
|
|
591
|
-
</form>
|
|
592
|
-
<button on-action="click.once->ui_welcome_dismissed">Got it</button>
|
|
593
|
-
```
|
|
470
|
+
If you are using an AI coding assistant (like Cursor, Gemini, Copilot, or Antigravity) to migrate your files to the new `actionService` API, you can use the following prompt to automate the refactoring:
|
|
594
471
|
|
|
595
|
-
|
|
472
|
+
```text
|
|
473
|
+
Refactor this file to migrate from the deprecated global `@alwatr/action` (or `@alwatr/flux`) functions to the new `actionService` singleton API.
|
|
596
474
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
475
|
+
Follow these rules:
|
|
476
|
+
1. Replace imports of `onAction`, `dispatchAction`, `setupActionDelegation`, `teardownActionDelegation`, `registerModifier`, or `registerPayloadResolver` from `@alwatr/action` (or `@alwatr/flux`) with `actionService`.
|
|
477
|
+
2. Convert all calls:
|
|
478
|
+
- `onAction(...)` ➔ `actionService.on(...)`
|
|
479
|
+
- `dispatchAction(...)` ➔ `actionService.dispatch(...)`
|
|
480
|
+
- `setupActionDelegation(...)` ➔ `actionService.setupDelegation(...)`
|
|
481
|
+
- `teardownActionDelegation(...)` ➔ `actionService.teardownDelegation(...)`
|
|
482
|
+
- `registerModifier(...)` ➔ `actionService.registerModifier(...)`
|
|
483
|
+
- `registerPayloadResolver(...)` ➔ `actionService.registerPayloadResolver(...)`
|
|
484
|
+
3. Maintain exact type safety, callback parameter types, and business logic.
|
|
606
485
|
```
|
|
607
486
|
|
|
608
|
-
### `page-ready` moved to `@alwatr/page-ready`
|
|
609
|
-
|
|
610
|
-
`dispatchPageId` / `onPageReady` are no longer part of this package.
|
|
611
|
-
|
|
612
487
|
---
|
|
613
488
|
|
|
614
489
|
## 🌊 Part of Alwatr Flux
|
|
@@ -620,7 +495,7 @@ View (HTML on-<event> attributes + action-context)
|
|
|
620
495
|
↓
|
|
621
496
|
Action Layer (@alwatr/action) — global delegation, O(1) routing, AFSA objects
|
|
622
497
|
↓
|
|
623
|
-
Controller (business logic via
|
|
498
|
+
Controller (business logic via actionService.on — receives full Action object)
|
|
624
499
|
↓
|
|
625
500
|
State Layer (@alwatr/signal) — fine-grained reactivity
|
|
626
501
|
↓
|
|
@@ -633,10 +508,10 @@ View (re-render only affected nodes)
|
|
|
633
508
|
|
|
634
509
|
```typescript
|
|
635
510
|
// Use @alwatr/flux for the complete architecture
|
|
636
|
-
import {
|
|
511
|
+
import {actionService, createStateSignal} from '@alwatr/flux';
|
|
637
512
|
|
|
638
513
|
// Or use @alwatr/action standalone for just the action bus
|
|
639
|
-
import {
|
|
514
|
+
import {actionService} from '@alwatr/action';
|
|
640
515
|
```
|
|
641
516
|
|
|
642
517
|
→ [View the complete Flux documentation](https://github.com/Alwatr/alwatr/tree/next/pkg/flux)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { SubscribeResult } from '@alwatr/signal';
|
|
2
|
+
import type { Awaitable } from '@alwatr/type-helper';
|
|
3
|
+
import type { Action, ActionRecord, DispatchParam, ModifierHandler, PayloadResolver } from './type.js';
|
|
4
|
+
/**
|
|
5
|
+
* Parsed representation of an action attribute descriptor.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
interface ActionDescriptor {
|
|
9
|
+
readonly modifiers: ReadonlySet<string>;
|
|
10
|
+
readonly actionId: string;
|
|
11
|
+
readonly payload: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Service to manage declarative DOM actions, programmatic dispatch,
|
|
15
|
+
* modifiers, payload resolvers, and global event delegation.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import {ActionService} from '@alwatr/action';
|
|
20
|
+
*
|
|
21
|
+
* const customActionService = new ActionService();
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare class ActionService {
|
|
25
|
+
/**
|
|
26
|
+
* Default DOM event types that cover the vast majority of interactive elements.
|
|
27
|
+
*/
|
|
28
|
+
static readonly DEFAULT_DELEGATED_EVENTS: readonly string[];
|
|
29
|
+
protected readonly logger_: import("@alwatr/logger").AlwatrLogger;
|
|
30
|
+
/**
|
|
31
|
+
* Internal ChannelSignal used for routing dispatched actions.
|
|
32
|
+
* @protected
|
|
33
|
+
*/
|
|
34
|
+
protected readonly internalChannel_: import("@alwatr/signal").ChannelSignal<Record<string, Action<never>>>;
|
|
35
|
+
/**
|
|
36
|
+
* Registry mapping custom modifiers to their handlers.
|
|
37
|
+
* @protected
|
|
38
|
+
*/
|
|
39
|
+
protected readonly modifierRegistry_: Map<string, ModifierHandler>;
|
|
40
|
+
/**
|
|
41
|
+
* Registry mapping custom payload resolvers to their functions.
|
|
42
|
+
* @protected
|
|
43
|
+
*/
|
|
44
|
+
protected readonly payloadRegistry_: Map<string, PayloadResolver>;
|
|
45
|
+
/**
|
|
46
|
+
* Cache of parsed action descriptors to prevent redundant regex evaluation.
|
|
47
|
+
* @protected
|
|
48
|
+
*/
|
|
49
|
+
protected readonly descriptorCache_: Map<string, ActionDescriptor | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Tracked event types currently delegated to `document.body`.
|
|
52
|
+
* @protected
|
|
53
|
+
*/
|
|
54
|
+
protected readonly delegatedEventTypes_: Set<string>;
|
|
55
|
+
/**
|
|
56
|
+
* Bound delegation handler for add/removeEventListener.
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
private readonly handleDelegatedEventBound__;
|
|
60
|
+
constructor();
|
|
61
|
+
/**
|
|
62
|
+
* Subscribes to a named action dispatched anywhere in the application.
|
|
63
|
+
*
|
|
64
|
+
* @template K - A key of ActionRecord.
|
|
65
|
+
* @param type - Action type or array of action types to subscribe to.
|
|
66
|
+
* @param handler - Callback invoked with the full Action object.
|
|
67
|
+
* @returns SubscribeResult containing an `unsubscribe` method.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* const sub = actionService.on('ui_open_drawer', (action) => {
|
|
72
|
+
* console.log(action.payload);
|
|
73
|
+
* });
|
|
74
|
+
* sub.unsubscribe();
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
on<K extends keyof ActionRecord>(type: K | K[], handler: (action: Action<K>) => Awaitable<void>): SubscribeResult;
|
|
78
|
+
/**
|
|
79
|
+
* Dispatches an action to all subscribers matching `action.type`.
|
|
80
|
+
*
|
|
81
|
+
* @template K - A key of ActionRecord.
|
|
82
|
+
* @param action - Action object containing `type` and `payload`.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* actionService.dispatch({type: 'upload_complete', payload: 'file-123'});
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
dispatch<K extends keyof ActionRecord>(action: DispatchParam<K>): void;
|
|
90
|
+
/**
|
|
91
|
+
* Registers a custom modifier to enrich or filter actions before dispatch.
|
|
92
|
+
*
|
|
93
|
+
* @param name - Modifier name (lowercase, alphanumeric).
|
|
94
|
+
* @param handler - Function called when modifier is invoked.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* actionService.registerModifier('trace', (_event, _element, action) => {
|
|
99
|
+
* action.meta ??= {};
|
|
100
|
+
* action.meta['time'] = Date.now();
|
|
101
|
+
* return true;
|
|
102
|
+
* });
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
registerModifier(name: string, handler: ModifierHandler): void;
|
|
106
|
+
/**
|
|
107
|
+
* Registers a custom payload resolver to map DOM state to action payload.
|
|
108
|
+
*
|
|
109
|
+
* @param name - Resolver token (by convention starting with `$`).
|
|
110
|
+
* @param resolver - Function yielding payload from the event and element.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* actionService.registerPayloadResolver('$data-id', (_event, element) => {
|
|
115
|
+
* return element.dataset.id;
|
|
116
|
+
* });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
registerPayloadResolver(name: string, resolver: PayloadResolver): void;
|
|
120
|
+
/**
|
|
121
|
+
* Registers global event delegation listeners on `document.body`.
|
|
122
|
+
*
|
|
123
|
+
* @param eventTypes - List of event types to delegate. Defaults to ActionService.DEFAULT_DELEGATED_EVENTS.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* actionService.setupDelegation();
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
setupDelegation(eventTypes?: readonly string[]): void;
|
|
131
|
+
/**
|
|
132
|
+
* Unregisters all global event delegation listeners.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* actionService.teardownDelegation();
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
teardownDelegation(): void;
|
|
140
|
+
/**
|
|
141
|
+
* Parses attribute values into action descriptor, utilizing the internal cache.
|
|
142
|
+
* @protected
|
|
143
|
+
*/
|
|
144
|
+
protected parseDescriptor_(attributeValue: string): ActionDescriptor | null;
|
|
145
|
+
/**
|
|
146
|
+
* Global event delegation handler.
|
|
147
|
+
* @protected
|
|
148
|
+
*/
|
|
149
|
+
protected handleDelegatedEvent_(event: Event): void;
|
|
150
|
+
/**
|
|
151
|
+
* Registers default modifiers and resolvers.
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
private registerDefaultModifiersAndResolvers__;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Singleton instance of the ActionService.
|
|
158
|
+
* Ready for immediate use.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* import {actionService} from '@alwatr/action';
|
|
163
|
+
*
|
|
164
|
+
* actionService.setupDelegation();
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export declare const actionService: ActionService;
|
|
168
|
+
export {};
|
|
169
|
+
//# sourceMappingURL=action-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-service.d.ts","sourceRoot":"","sources":["../src/action-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAC,SAAS,EAAW,MAAM,qBAAqB,CAAC;AAE7D,OAAO,KAAK,EAAC,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAC,MAAM,WAAW,CAAC;AAErG;;;GAGG;AACH,UAAU,gBAAgB;IACxB,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAQD;;;;;;;;;;GAUG;AACH,qBAAa,aAAa;IACxB;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,wBAAwB,EAAE,SAAS,MAAM,EAAE,CAA0C;IAErG,SAAS,CAAC,QAAQ,CAAC,OAAO,wCAAkC;IAE5D;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,gBAAgB,wEAAyE;IAE5G;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,iBAAiB,+BAAsC;IAE1E;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,gBAAgB,+BAAsC;IAEzE;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,gBAAgB,uCAA8C;IAEjF;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,oBAAoB,cAAqB;IAE5D;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAyC;;IAOrF;;;;;;;;;;;;;;;OAeG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,YAAY,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,GAAG,eAAe;IAuBjH;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,CAAC,SAAS,MAAM,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI;IAKtE;;;;;;;;;;;;;;OAcG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAS9D;;;;;;;;;;;;OAYG;IACH,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI;IAStE;;;;;;;;;OASG;IACH,eAAe,CAAC,UAAU,GAAE,SAAS,MAAM,EAA2C,GAAG,IAAI;IAc7F;;;;;;;OAOG;IACH,kBAAkB,IAAI,IAAI;IAY1B;;;OAGG;IACH,SAAS,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAuB3E;;;OAGG;IACH,SAAS,CAAC,qBAAqB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAkFnD;;;OAGG;IACH,OAAO,CAAC,sCAAsC;CA6B/C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,eAAsB,CAAC"}
|