@alwatr/flux 9.26.0 → 9.28.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 +192 -50
- package/dist/main.d.ts +1 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +3 -3
- package/package.json +6 -5
- package/src/main.ts +1 -0
package/README.md
CHANGED
|
@@ -65,15 +65,15 @@ declare module '@alwatr/flux' {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// Get compile-time safety everywhere — handler receives the full Action object
|
|
68
|
-
|
|
68
|
+
actionService.on('ui_add_to_cart', (action) => {
|
|
69
69
|
// action.payload is typed as {productId: number; qty: number}
|
|
70
70
|
cartService.add(action.payload.productId, action.payload.qty);
|
|
71
71
|
// action.context is the nearest [action-context] ancestor value (or undefined)
|
|
72
72
|
console.log(action.context); // e.g. 'product-list'
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
actionService.dispatch({type: 'ui_add_to_cart', payload: {productId: 42, qty: 1}}); // ✅
|
|
76
|
+
actionService.dispatch({type: 'ui_add_to_cart', payload: 'wrong'}); // ❌ Compile error
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
---
|
|
@@ -173,7 +173,7 @@ Connect DOM events to typed actions without writing JavaScript. Wrap elements in
|
|
|
173
173
|
|
|
174
174
|
```typescript
|
|
175
175
|
// Handler receives the full Action object — payload, context, and meta together
|
|
176
|
-
|
|
176
|
+
actionService.on('ui_slider_change', (action) => {
|
|
177
177
|
if (action.context === 'volume') audioService.setVolume(Number(action.payload));
|
|
178
178
|
if (action.context === 'brightness') displayService.setBrightness(Number(action.payload));
|
|
179
179
|
});
|
|
@@ -377,12 +377,71 @@ createEffect({
|
|
|
377
377
|
});
|
|
378
378
|
```
|
|
379
379
|
|
|
380
|
+
### 🤖 **Finite State Machine (FSM) & Actor Model**
|
|
381
|
+
|
|
382
|
+
Flux natively aggregates `@alwatr/fsm` to eliminate ad-hoc state variables, boolean flags, and race conditions. Instead of writing unpredictable, disjointed spaghetti code, you model your application's lifecycle as a **declarative, type-safe statechart**.
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
import {createFsmService} from '@alwatr/flux';
|
|
386
|
+
import type {StateMachineConfig} from '@alwatr/flux';
|
|
387
|
+
|
|
388
|
+
// 1. Define strict Union Types for compile-time safety
|
|
389
|
+
type FetchState = 'idle' | 'loading' | 'success' | 'failed';
|
|
390
|
+
type FetchEvent = {type: 'FETCH'} | {type: 'SUCCESS'} | {type: 'ERROR'};
|
|
391
|
+
interface FetchContext { retries: number }
|
|
392
|
+
|
|
393
|
+
// 2. Configure the machine declaratively
|
|
394
|
+
const fetchConfig: StateMachineConfig<FetchState, FetchEvent, FetchContext> = {
|
|
395
|
+
name: 'api-fetch-lifecycle',
|
|
396
|
+
initial: 'idle',
|
|
397
|
+
context: {retries: 0},
|
|
398
|
+
states: {
|
|
399
|
+
idle: {
|
|
400
|
+
on: { FETCH: { target: 'loading' } },
|
|
401
|
+
},
|
|
402
|
+
loading: {
|
|
403
|
+
on: {
|
|
404
|
+
SUCCESS: { target: 'success', assigners: [({context}) => ({...context, retries: 0})] },
|
|
405
|
+
ERROR: [
|
|
406
|
+
// Guard evaluates condition; first matching transition branch is chosen
|
|
407
|
+
{ target: 'loading', guard: ({context}) => context.retries < 3, assigners: [({context}) => ({...context, retries: context.retries + 1})] },
|
|
408
|
+
{ target: 'failed' }
|
|
409
|
+
],
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
success: {},
|
|
413
|
+
failed: {
|
|
414
|
+
on: { FETCH: { target: 'loading', assigners: [({context}) => ({...context, retries: 0})] } },
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// 3. Create the reactive service (FSM Service instance)
|
|
420
|
+
export const fetchService = createFsmService(fetchConfig);
|
|
421
|
+
|
|
422
|
+
// 4. Surgical Reactivity: Subscribe to state changes in the view layer
|
|
423
|
+
fetchService.stateSignal.subscribe((state) => {
|
|
424
|
+
console.log(`Current State: ${state.name}, Retries: ${state.context.retries}`);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// 5. Dispatch events to trigger atomic transitions
|
|
428
|
+
fetchService.dispatch({type: 'FETCH'});
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
#### 🛡️ Why is Flux FSM a Game-Changer?
|
|
432
|
+
|
|
433
|
+
- **Eliminates Race Conditions (Run-to-Completion)**: FSM events are queued and processed sequentially. Async callbacks from concurrent network fetches never overwrite or corrupt the state.
|
|
434
|
+
- **Prevents State Explosion**: Ad-hoc booleans (like `isLoading`, `isError`, `hasFetched`) scale exponentially (e.g., 5 booleans = 32 states). FSM forces you to define a finite set of **valid states and transitions** (e.g., exactly 4 states).
|
|
435
|
+
- **Embedded Actor Architecture**: Statecharts can spawn long-running, asynchronous, isolated lifecycles called **Actors** on state entry, complete with auto-cleanup on exit.
|
|
436
|
+
- **Declarative Synergy**: The FSM's internal `stateSignal` integrates flawlessly with the `@alwatr/flux` unidirectional loop, feeding directly into your rendering template.
|
|
437
|
+
|
|
380
438
|
---
|
|
381
439
|
|
|
382
440
|
## 🏗️ Architecture Overview
|
|
383
441
|
|
|
384
|
-
Flux implements a **strict layered architecture** where each layer has a single responsibility
|
|
442
|
+
Flux implements a **strict layered architecture** where each layer has a single responsibility. It supports both standard Unidirectional Data Flow (UDF) and the decentralized **Actor Model** (where components/features behave as isolated micro-services powered by FSMs).
|
|
385
443
|
|
|
444
|
+
### 1. Unidirectional Data Flow (UDF) Layering
|
|
386
445
|
```
|
|
387
446
|
┌───────────────────────────────────────────────────────────┐
|
|
388
447
|
│ VIEW LAYER │
|
|
@@ -406,13 +465,13 @@ Flux implements a **strict layered architecture** where each layer has a single
|
|
|
406
465
|
│ • Resolves payload ($value, $formdata) │
|
|
407
466
|
│ • Dispatches full Action {type, payload, context, meta} │
|
|
408
467
|
└──────────────────┬────────────────────────────────────────┘
|
|
409
|
-
│
|
|
468
|
+
│ actionService.dispatch({type: 'ui_add_to_cart', payload: 42, context: 'cart'})
|
|
410
469
|
▼
|
|
411
470
|
┌───────────────────────────────────────────────────────────┐
|
|
412
471
|
│ CONTROLLER LAYER │
|
|
413
472
|
│ (Business Logic, Services, Use Cases) │
|
|
414
473
|
│ │
|
|
415
|
-
│ • Subscribes to Actions via
|
|
474
|
+
│ • Subscribes to Actions via actionService.on() │
|
|
416
475
|
│ • Receives full Action object (type, payload, context, │
|
|
417
476
|
│ meta) — no need to pass context separately │
|
|
418
477
|
│ • Executes business logic │
|
|
@@ -439,13 +498,68 @@ Flux implements a **strict layered architecture** where each layer has a single
|
|
|
439
498
|
└───────────────────────────────────────────────────────────┘
|
|
440
499
|
```
|
|
441
500
|
|
|
501
|
+
### 2. Decoupled Actor Model Flow
|
|
502
|
+
|
|
503
|
+
In complex applications, you can structure features as **Actors** using `@alwatr/fsm` + `@alwatr/action`. An Actor maintains private local state, receives inbound event packets from the global action bus, and broadcasts changes asynchronously back to the ecosystem.
|
|
504
|
+
|
|
505
|
+
```mermaid
|
|
506
|
+
graph TD
|
|
507
|
+
%% Styling
|
|
508
|
+
classDef view fill:#E1F5FE,stroke:#03A9F4,stroke-width:2px,color:#01579B;
|
|
509
|
+
classDef action fill:#FFF3E0,stroke:#FF9800,stroke-width:2px,color:#E65100;
|
|
510
|
+
classDef fsm fill:#E8F5E9,stroke:#4CAF50,stroke-width:2px,color:#1B5E20;
|
|
511
|
+
classDef actor fill:#F3E5F5,stroke:#9C27B0,stroke-width:2px,color:#4A148C;
|
|
512
|
+
classDef global fill:#ECEFF1,stroke:#607D8B,stroke-width:2px,color:#263238;
|
|
513
|
+
|
|
514
|
+
subgraph View ["View Layer (lit-html & Directives)"]
|
|
515
|
+
V[DOM / UI Elements]
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
subgraph Actions ["Action Bus (Global Delegation)"]
|
|
519
|
+
AB[actionService.dispatch]
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
subgraph Controllers ["Actor Controller Layer"]
|
|
523
|
+
C[Input Controller / Message Router]
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
subgraph ActorDomain ["Decoupled Actor (FSM Brain)"]
|
|
527
|
+
direction TB
|
|
528
|
+
FSM[FsmService]
|
|
529
|
+
Ctx[(Extended Context)]
|
|
530
|
+
Actors[Spawning State Actors]
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
subgraph State ["Reactive State Layer (Signals)"]
|
|
534
|
+
SS[stateSignal]
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
%% Flow
|
|
538
|
+
V -->|on-click / on-input| AB
|
|
539
|
+
AB -->|Global AFSA Message| C
|
|
540
|
+
C -->|dispatch Event| FSM
|
|
541
|
+
FSM -->|1. Run-to-Completion| Ctx
|
|
542
|
+
FSM -->|2. Entry/Exit Effects| Actors
|
|
543
|
+
FSM -->|3. Update State| SS
|
|
544
|
+
SS -->|Re-render / Update UI| V
|
|
545
|
+
Actors -.->|Asynchronous Events| AB
|
|
546
|
+
|
|
547
|
+
%% Apply Classes
|
|
548
|
+
class V view;
|
|
549
|
+
class AB action;
|
|
550
|
+
class C global;
|
|
551
|
+
class FSM,Ctx fsm;
|
|
552
|
+
class Actors actor;
|
|
553
|
+
class SS view;
|
|
554
|
+
```
|
|
555
|
+
|
|
442
556
|
**Key architectural benefits:**
|
|
443
557
|
|
|
444
|
-
- **Zero coupling** — layers communicate only through
|
|
445
|
-
- **
|
|
446
|
-
- **
|
|
447
|
-
- **
|
|
448
|
-
- **
|
|
558
|
+
- **Zero coupling** — layers and actors communicate only through events, guaranteeing that features can be refactored, extended, or replaced without side effects.
|
|
559
|
+
- **Run-to-Completion (RTC)** — State machines evaluate transitions atomically in a synchronous queue. Race conditions are mathematically impossible.
|
|
560
|
+
- **High Testability** — Since business logic is isolated inside declarative statecharts (`StateMachineConfig`), you can test all transitions and side-effects in node/bun without mounting a DOM.
|
|
561
|
+
- **Surgical Reactivity** — Only the DOM elements bound to the FSM's `stateSignal` or Context properties re-render, keeping CPU usage and memory footprints exceptionally low.
|
|
562
|
+
- **AI-Agent and Developer Friendly** — The explicit separation of routing, transition, and effect layers provides clean boundaries that humans and AI agents can navigate with zero ambiguity.
|
|
449
563
|
|
|
450
564
|
---
|
|
451
565
|
|
|
@@ -480,7 +594,7 @@ interface Action<K extends keyof ActionRecord> {
|
|
|
480
594
|
This unified structure replaces the previous two-argument `(id, payload)` API. Every handler now receives the full picture:
|
|
481
595
|
|
|
482
596
|
```typescript
|
|
483
|
-
|
|
597
|
+
actionService.on('ui_add_to_cart', (action) => {
|
|
484
598
|
console.log(action.type); // 'ui_add_to_cart'
|
|
485
599
|
console.log(action.payload); // {productId: 42, qty: 1} — fully typed
|
|
486
600
|
console.log(action.context); // 'product-list' — from [action-context] ancestor
|
|
@@ -491,9 +605,9 @@ onAction('ui_add_to_cart', (action) => {
|
|
|
491
605
|
Modifiers can enrich `meta` before the action reaches subscribers:
|
|
492
606
|
|
|
493
607
|
```typescript
|
|
494
|
-
import {
|
|
608
|
+
import {actionService} from '@alwatr/flux';
|
|
495
609
|
|
|
496
|
-
registerModifier('trace', (_event, _element, action) => {
|
|
610
|
+
actionService.registerModifier('trace', (_event, _element, action) => {
|
|
497
611
|
action.meta ??= {};
|
|
498
612
|
action.meta['traceId'] = crypto.randomUUID();
|
|
499
613
|
return true;
|
|
@@ -531,10 +645,10 @@ bun add @alwatr/flux
|
|
|
531
645
|
### 1. Bootstrap the Application
|
|
532
646
|
|
|
533
647
|
```typescript
|
|
534
|
-
import {
|
|
648
|
+
import {actionService, dispatchPageReady} from '@alwatr/flux';
|
|
535
649
|
|
|
536
650
|
// Activate global event delegation (call once at app start)
|
|
537
|
-
|
|
651
|
+
actionService.setupDelegation();
|
|
538
652
|
|
|
539
653
|
// Dispatch page-ready signal (for MPA routing)
|
|
540
654
|
dispatchPageReady();
|
|
@@ -569,19 +683,19 @@ export const counterSignal = createStateSignal({
|
|
|
569
683
|
|
|
570
684
|
```typescript
|
|
571
685
|
// src/controllers.ts
|
|
572
|
-
import {
|
|
686
|
+
import {actionService} from '@alwatr/flux';
|
|
573
687
|
import {counterSignal} from './state.js';
|
|
574
688
|
|
|
575
|
-
|
|
689
|
+
actionService.on('ui_increment', () => {
|
|
576
690
|
counterSignal.update((count) => count + 1);
|
|
577
691
|
});
|
|
578
692
|
|
|
579
|
-
|
|
693
|
+
actionService.on('ui_decrement', () => {
|
|
580
694
|
counterSignal.update((count) => count - 1);
|
|
581
695
|
});
|
|
582
696
|
|
|
583
697
|
// Handler receives the full Action object — payload is typed from ActionRecord
|
|
584
|
-
|
|
698
|
+
actionService.on('ui_set_count', (action) => {
|
|
585
699
|
counterSignal.set(action.payload); // action.payload: number
|
|
586
700
|
});
|
|
587
701
|
```
|
|
@@ -616,11 +730,11 @@ onAction('ui_set_count', (action) => {
|
|
|
616
730
|
|
|
617
731
|
```typescript
|
|
618
732
|
// main.js
|
|
619
|
-
import {
|
|
733
|
+
import {actionService} from '@alwatr/flux';
|
|
620
734
|
import {counterSignal} from './state.js';
|
|
621
735
|
import './controllers.js'; // Register action handlers
|
|
622
736
|
|
|
623
|
-
|
|
737
|
+
actionService.setupDelegation();
|
|
624
738
|
|
|
625
739
|
// Subscribe to state changes and update DOM
|
|
626
740
|
counterSignal.subscribe((count) => {
|
|
@@ -732,38 +846,66 @@ const mapped = createMappedSignal(source, {
|
|
|
732
846
|
});
|
|
733
847
|
```
|
|
734
848
|
|
|
849
|
+
#### `createFsmService(config)`
|
|
850
|
+
|
|
851
|
+
Instantiates a Finite State Machine service using the given configuration:
|
|
852
|
+
|
|
853
|
+
```typescript
|
|
854
|
+
import {createFsmService} from '@alwatr/flux';
|
|
855
|
+
|
|
856
|
+
const myService = createFsmService({
|
|
857
|
+
name: 'my-fsm',
|
|
858
|
+
initial: 'idle',
|
|
859
|
+
context: {retries: 0},
|
|
860
|
+
states: {
|
|
861
|
+
idle: {
|
|
862
|
+
on: {START: {target: 'working'}},
|
|
863
|
+
},
|
|
864
|
+
working: {
|
|
865
|
+
on: {SUCCESS: {target: 'idle'}},
|
|
866
|
+
},
|
|
867
|
+
},
|
|
868
|
+
});
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
See the complete [FSM Package README](https://github.com/Alwatr/alwatr/tree/next/pkg/fsm#readme) for advanced FSM configuration details (Persistence, Guards, Actors, etc.).
|
|
872
|
+
|
|
735
873
|
---
|
|
736
874
|
|
|
737
875
|
### Actions
|
|
738
876
|
|
|
739
|
-
#### `
|
|
877
|
+
#### `actionService`
|
|
878
|
+
|
|
879
|
+
The pre-instantiated singleton instance of `ActionService` exported for declarative event delegation and action dispatching.
|
|
880
|
+
|
|
881
|
+
##### `actionService.setupDelegation(eventTypes?)`
|
|
740
882
|
|
|
741
|
-
Activates global event delegation
|
|
883
|
+
Activates global event delegation capture listeners on `document.body`.
|
|
742
884
|
|
|
743
885
|
```typescript
|
|
744
|
-
import {
|
|
886
|
+
import {actionService, DEFAULT_DELEGATED_EVENTS} from '@alwatr/flux';
|
|
745
887
|
|
|
746
888
|
// Use defaults (click, submit, input, change)
|
|
747
|
-
|
|
889
|
+
actionService.setupDelegation();
|
|
748
890
|
|
|
749
891
|
// Or add custom events
|
|
750
|
-
|
|
892
|
+
actionService.setupDelegation([...DEFAULT_DELEGATED_EVENTS, 'keydown', 'focus']);
|
|
751
893
|
```
|
|
752
894
|
|
|
753
|
-
|
|
895
|
+
##### `actionService.on(type, handler)`
|
|
754
896
|
|
|
755
|
-
Subscribes to a single typed action or an array of actions.
|
|
897
|
+
Subscribes to a single typed action or an array of actions.
|
|
756
898
|
|
|
757
899
|
```typescript
|
|
758
900
|
// Subscribe to a single action
|
|
759
|
-
const sub =
|
|
901
|
+
const sub = actionService.on('ui_add_to_cart', (action) => {
|
|
760
902
|
cartService.add(action.payload.productId, action.payload.qty);
|
|
761
903
|
console.log(action.context); // e.g. 'product-list' or undefined
|
|
762
904
|
console.log(action.meta); // any metadata set by modifiers
|
|
763
905
|
});
|
|
764
906
|
|
|
765
907
|
// Subscribe to multiple actions with a single handler
|
|
766
|
-
const multiSub =
|
|
908
|
+
const multiSub = actionService.on(['ui_increment', 'ui_decrement'], (action) => {
|
|
767
909
|
console.log('Action triggered:', action.type);
|
|
768
910
|
});
|
|
769
911
|
|
|
@@ -771,16 +913,16 @@ sub.unsubscribe(); // Clean up when done
|
|
|
771
913
|
multiSub.unsubscribe();
|
|
772
914
|
```
|
|
773
915
|
|
|
774
|
-
|
|
916
|
+
##### `actionService.dispatch(action)`
|
|
775
917
|
|
|
776
|
-
Dispatches a typed action programmatically.
|
|
918
|
+
Dispatches a typed action programmatically.
|
|
777
919
|
|
|
778
920
|
```typescript
|
|
779
|
-
|
|
780
|
-
|
|
921
|
+
actionService.dispatch({type: 'navigate', payload: '/home'});
|
|
922
|
+
actionService.dispatch({type: 'auth_expired', payload: undefined}); // void payload
|
|
781
923
|
|
|
782
924
|
// With context and meta
|
|
783
|
-
|
|
925
|
+
actionService.dispatch({
|
|
784
926
|
type: 'upload_complete',
|
|
785
927
|
payload: fileId,
|
|
786
928
|
context: 'product-list',
|
|
@@ -788,17 +930,17 @@ dispatchAction({
|
|
|
788
930
|
});
|
|
789
931
|
```
|
|
790
932
|
|
|
791
|
-
|
|
933
|
+
##### `actionService.registerModifier(name, handler)`
|
|
792
934
|
|
|
793
|
-
Adds a custom modifier for `on-<event>` attributes.
|
|
935
|
+
Adds a custom modifier for `on-<event>` attributes.
|
|
794
936
|
|
|
795
937
|
```typescript
|
|
796
|
-
registerModifier('confirm', () => {
|
|
938
|
+
actionService.registerModifier('confirm', () => {
|
|
797
939
|
return window.confirm('Are you sure?');
|
|
798
940
|
});
|
|
799
941
|
|
|
800
942
|
// A modifier that stamps a trace ID into meta
|
|
801
|
-
registerModifier('trace', (_event, _element, action) => {
|
|
943
|
+
actionService.registerModifier('trace', (_event, _element, action) => {
|
|
802
944
|
action.meta ??= {};
|
|
803
945
|
action.meta['traceId'] = crypto.randomUUID();
|
|
804
946
|
return true;
|
|
@@ -809,12 +951,12 @@ registerModifier('trace', (_event, _element, action) => {
|
|
|
809
951
|
<button on-click="ui_delete_item:42; confirm,trace">Delete</button>
|
|
810
952
|
```
|
|
811
953
|
|
|
812
|
-
|
|
954
|
+
##### `actionService.registerPayloadResolver(name, resolver)`
|
|
813
955
|
|
|
814
956
|
Adds a custom payload resolver.
|
|
815
957
|
|
|
816
958
|
```typescript
|
|
817
|
-
registerPayloadResolver('$data-id', (_event, element) => {
|
|
959
|
+
actionService.registerPayloadResolver('$data-id', (_event, element) => {
|
|
818
960
|
return element.dataset.id;
|
|
819
961
|
});
|
|
820
962
|
```
|
|
@@ -1238,19 +1380,19 @@ export const todosSignal = createStateSignal<Todo[]>({
|
|
|
1238
1380
|
});
|
|
1239
1381
|
|
|
1240
1382
|
// controllers.ts
|
|
1241
|
-
import {
|
|
1383
|
+
import {actionService} from '@alwatr/flux';
|
|
1242
1384
|
import {todosSignal} from './state.js';
|
|
1243
1385
|
|
|
1244
1386
|
let nextId = 1;
|
|
1245
1387
|
|
|
1246
|
-
|
|
1388
|
+
actionService.on('ui_add_todo', (action) => {
|
|
1247
1389
|
todosSignal.update((todos) => [
|
|
1248
1390
|
...todos,
|
|
1249
1391
|
{id: nextId++, text: action.payload, done: false},
|
|
1250
1392
|
]);
|
|
1251
1393
|
});
|
|
1252
1394
|
|
|
1253
|
-
|
|
1395
|
+
actionService.on('ui_toggle_todo', (action) => {
|
|
1254
1396
|
todosSignal.update((todos) =>
|
|
1255
1397
|
todos.map((todo) =>
|
|
1256
1398
|
todo.id === action.payload ? {...todo, done: !todo.done} : todo
|
|
@@ -1258,7 +1400,7 @@ onAction('ui_toggle_todo', (action) => {
|
|
|
1258
1400
|
);
|
|
1259
1401
|
});
|
|
1260
1402
|
|
|
1261
|
-
|
|
1403
|
+
actionService.on('ui_remove_todo', (action) => {
|
|
1262
1404
|
todosSignal.update((todos) => todos.filter((t) => t.id !== action.payload));
|
|
1263
1405
|
});
|
|
1264
1406
|
|
|
@@ -1269,11 +1411,11 @@ onAction('ui_remove_todo', (action) => {
|
|
|
1269
1411
|
</div>
|
|
1270
1412
|
|
|
1271
1413
|
// main.ts
|
|
1272
|
-
import {
|
|
1414
|
+
import {actionService, html, render} from '@alwatr/flux';
|
|
1273
1415
|
import {todosSignal} from './state.js';
|
|
1274
1416
|
import './controllers.js';
|
|
1275
1417
|
|
|
1276
|
-
|
|
1418
|
+
actionService.setupDelegation();
|
|
1277
1419
|
|
|
1278
1420
|
todosSignal.subscribe((todos) => {
|
|
1279
1421
|
render(
|
|
@@ -1307,7 +1449,7 @@ todosSignal.subscribe((todos) => {
|
|
|
1307
1449
|
- **[@alwatr/action](https://github.com/Alwatr/alwatr/tree/next/pkg/nanolib/action)** — Global event delegation action bus (part of Flux)
|
|
1308
1450
|
- **[@alwatr/directive](https://github.com/Alwatr/alwatr/tree/next/pkg/nanolib/directive)** — Attribute-based DOM directives (part of Flux)
|
|
1309
1451
|
- **[@alwatr/embedded-data](https://github.com/Alwatr/alwatr/tree/next/pkg/nanolib/embedded-data)** — Extract and validate embedded JSON from DOM script tags for SSR hydration (part of Flux)
|
|
1310
|
-
- **[@alwatr/fsm](https://github.com/Alwatr/alwatr/tree/next/pkg/fsm)** — Type-safe Finite State Machine
|
|
1452
|
+
- **[@alwatr/fsm](https://github.com/Alwatr/alwatr/tree/next/pkg/fsm)** — Type-safe Finite State Machine (part of Flux)
|
|
1311
1453
|
- **[@alwatr/nanotron](https://github.com/Alwatr/alwatr/tree/next/pkg/nanotron)** — Lightweight API server framework
|
|
1312
1454
|
- **[@alwatr/nitrobase](https://github.com/Alwatr/alwatr/tree/next/pkg/nitrobase)** — In-memory JSON database
|
|
1313
1455
|
- **[@alwatr/fetch](https://github.com/Alwatr/alwatr/tree/next/pkg/nanolib/fetch)** — Enhanced fetch with retry, cache, deduplication
|
package/dist/main.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from '@alwatr/signal';
|
|
|
2
2
|
export * from '@alwatr/action';
|
|
3
3
|
export * from '@alwatr/directive';
|
|
4
4
|
export * from '@alwatr/embedded-data';
|
|
5
|
+
export * from '@alwatr/fsm';
|
|
5
6
|
export * from '@alwatr/keyboard-shortcut';
|
|
6
7
|
export * from '@alwatr/render-state';
|
|
7
8
|
export * from '@alwatr/local-storage';
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,mBAAmB,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,mBAAmB,qBAAqB,CAAC"}
|
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/* 📦 @alwatr/flux v9.
|
|
2
|
-
export*from"@alwatr/signal";export*from"@alwatr/action";export*from"@alwatr/directive";export*from"@alwatr/embedded-data";export*from"@alwatr/keyboard-shortcut";export*from"@alwatr/render-state";export*from"@alwatr/local-storage";export*from"@alwatr/session-storage";export*from"@alwatr/page-ready";import{html as e,svg as p,mathml as a,render as f,noChange as t,nothing as
|
|
1
|
+
/* 📦 @alwatr/flux v9.28.0 */
|
|
2
|
+
export*from"@alwatr/signal";export*from"@alwatr/action";export*from"@alwatr/directive";export*from"@alwatr/embedded-data";export*from"@alwatr/fsm";export*from"@alwatr/keyboard-shortcut";export*from"@alwatr/render-state";export*from"@alwatr/local-storage";export*from"@alwatr/session-storage";export*from"@alwatr/page-ready";import{html as e,svg as p,mathml as a,render as f,noChange as t,nothing as x}from"lit-html";import{unsafeSVG as m}from"lit-html/directives/unsafe-svg.js";import{ifDefined as i}from"lit-html/directives/if-defined.js";import{cache as s}from"lit-html/directives/cache.js";import{classMap as b}from"lit-html/directives/class-map.js";import{when as d}from"lit-html/directives/when.js";import{repeat as I}from"lit-html/directives/repeat.js";export{d as when,m as unsafeSVG,p as svg,I as repeat,f as render,x as nothing,t as noChange,a as mathml,i as ifDefined,e as html,b as classMap,s as cache};
|
|
3
3
|
|
|
4
|
-
//# debugId=
|
|
4
|
+
//# debugId=F1AB5B823C55946064756E2164756E21
|
|
5
5
|
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/main.ts", "../src/lit-html.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"// UI and reactive bundle — signals, actions, directives, and client-side storage.\n// This package aggregates all UI-layer nanolibs for convenient single-import usage.\n\nexport * from '@alwatr/signal';\nexport * from '@alwatr/action';\nexport * from '@alwatr/directive';\nexport * from '@alwatr/embedded-data';\nexport * from '@alwatr/keyboard-shortcut';\nexport * from '@alwatr/render-state';\nexport * from '@alwatr/local-storage';\nexport * from '@alwatr/session-storage';\nexport * from '@alwatr/page-ready';\nexport * from './lit-html.js';\nexport type * from '@alwatr/type-helper';\n",
|
|
5
|
+
"// UI and reactive bundle — signals, actions, directives, and client-side storage.\n// This package aggregates all UI-layer nanolibs for convenient single-import usage.\n\nexport * from '@alwatr/signal';\nexport * from '@alwatr/action';\nexport * from '@alwatr/directive';\nexport * from '@alwatr/embedded-data';\nexport * from '@alwatr/fsm';\nexport * from '@alwatr/keyboard-shortcut';\nexport * from '@alwatr/render-state';\nexport * from '@alwatr/local-storage';\nexport * from '@alwatr/session-storage';\nexport * from '@alwatr/page-ready';\nexport * from './lit-html.js';\nexport type * from '@alwatr/type-helper';\n",
|
|
6
6
|
"/**\n * Curated re-exports from `lit-html` for use within `@alwatr/flux`.\n *\n * Only the subset of `lit-html` APIs that are commonly needed in a Flux-based\n * application is exported here. This keeps the public surface minimal and\n * avoids pulling in advanced directive utilities that most consumers never use.\n *\n * **Exported APIs:**\n * - `html` — tagged template literal that produces a `TemplateResult`\n * - `render` — renders a `TemplateResult` into a DOM container\n * - `noChange` — sentinel that tells lit-html to leave the current part value unchanged\n * - `nothing` — sentinel that renders nothing (removes the node/attribute)\n * - `ifDefined` — renders a value only when it is not `undefined`\n * - `cache` — caches rendered templates to avoid re-parsing on state changes\n * - `classMap` — efficiently sets/removes CSS classes from an object map\n * - `when` — conditional rendering helper (`when(condition, trueCase, falseCase)`)\n *\n * @example\n * ```typescript\n * import {html, render, classMap, when} from '@alwatr/flux';\n *\n * const template = (isActive: boolean) => html`\n * <div class=${classMap({active: isActive, hidden: !isActive})}>\n * ${when(isActive, () => html`<span>Active</span>`, () => html`<span>Inactive</span>`)}\n * </div>\n * `;\n *\n * render(template(true), document.getElementById('app')!);\n * ```\n */\nexport {\n html,\n svg,\n mathml,\n render,\n noChange,\n nothing,\n type TemplateResult,\n type HTMLTemplateResult,\n type SVGTemplateResult,\n type MathMLTemplateResult,\n} from 'lit-html';\nexport {unsafeSVG} from 'lit-html/directives/unsafe-svg.js';\nexport {ifDefined} from 'lit-html/directives/if-defined.js';\nexport {cache} from 'lit-html/directives/cache.js';\nexport {classMap, type ClassInfo} from 'lit-html/directives/class-map.js';\nexport {when} from 'lit-html/directives/when.js';\nexport {repeat, type RepeatDirectiveFn, type KeyFn, type ItemTemplate} from 'lit-html/directives/repeat.js';\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";AAGA,4BACA,4BACA,+BACA,mCACA,uCACA,kCACA,mCACA,qCACA,
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": ";AAGA,4BACA,4BACA,+BACA,mCACA,yBACA,uCACA,kCACA,mCACA,qCACA,gCCkBA,eACE,SACA,YACA,YACA,cACA,aACA,iBAMF,oBAAQ,0CACR,oBAAQ,0CACR,gBAAQ,qCACR,mBAAQ,yCACR,eAAQ,oCACR,iBAAQ",
|
|
9
|
+
"debugId": "F1AB5B823C55946064756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alwatr/flux",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.28.0",
|
|
4
4
|
"description": "UI and reactive library bundle for ECMAScript (JavaScript/TypeScript) projects — signals, actions, directives, and storage.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "S. Ali Mihandoost <ali.mihandoost@gmail.com> (https://ali.mihandoost.com)",
|
|
@@ -21,12 +21,13 @@
|
|
|
21
21
|
},
|
|
22
22
|
"sideEffects": false,
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@alwatr/action": "9.
|
|
24
|
+
"@alwatr/action": "9.28.0",
|
|
25
25
|
"@alwatr/directive": "9.26.0",
|
|
26
26
|
"@alwatr/embedded-data": "9.25.0",
|
|
27
|
-
"@alwatr/
|
|
27
|
+
"@alwatr/fsm": "9.28.0",
|
|
28
|
+
"@alwatr/keyboard-shortcut": "9.28.0",
|
|
28
29
|
"@alwatr/local-storage": "9.25.0",
|
|
29
|
-
"@alwatr/page-ready": "9.
|
|
30
|
+
"@alwatr/page-ready": "9.27.0",
|
|
30
31
|
"@alwatr/render-state": "9.25.0",
|
|
31
32
|
"@alwatr/session-storage": "9.25.0",
|
|
32
33
|
"@alwatr/signal": "9.26.0",
|
|
@@ -84,5 +85,5 @@
|
|
|
84
85
|
"ui",
|
|
85
86
|
"unidirectional-data-flow"
|
|
86
87
|
],
|
|
87
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "d60f1d986e5ff44f871d38231f1f2be60833140b"
|
|
88
89
|
}
|
package/src/main.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from '@alwatr/signal';
|
|
|
5
5
|
export * from '@alwatr/action';
|
|
6
6
|
export * from '@alwatr/directive';
|
|
7
7
|
export * from '@alwatr/embedded-data';
|
|
8
|
+
export * from '@alwatr/fsm';
|
|
8
9
|
export * from '@alwatr/keyboard-shortcut';
|
|
9
10
|
export * from '@alwatr/render-state';
|
|
10
11
|
export * from '@alwatr/local-storage';
|