@alwatr/flux 9.25.0 → 9.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
- onAction('ui_add_to_cart', (action) => {
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
- dispatchAction({type: 'ui_add_to_cart', payload: {productId: 42, qty: 1}}); // ✅
76
- dispatchAction({type: 'ui_add_to_cart', payload: 'wrong'}); // ❌ Compile error
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
- onAction('ui_slider_change', (action) => {
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
  });
@@ -406,13 +406,13 @@ Flux implements a **strict layered architecture** where each layer has a single
406
406
  │ • Resolves payload ($value, $formdata) │
407
407
  │ • Dispatches full Action {type, payload, context, meta} │
408
408
  └──────────────────┬────────────────────────────────────────┘
409
- dispatchAction({type: 'ui_add_to_cart', payload: 42, context: 'cart'})
409
+ actionService.dispatch({type: 'ui_add_to_cart', payload: 42, context: 'cart'})
410
410
 
411
411
  ┌───────────────────────────────────────────────────────────┐
412
412
  │ CONTROLLER LAYER │
413
413
  │ (Business Logic, Services, Use Cases) │
414
414
  │ │
415
- │ • Subscribes to Actions via onAction()
415
+ │ • Subscribes to Actions via actionService.on()
416
416
  │ • Receives full Action object (type, payload, context, │
417
417
  │ meta) — no need to pass context separately │
418
418
  │ • Executes business logic │
@@ -480,7 +480,7 @@ interface Action<K extends keyof ActionRecord> {
480
480
  This unified structure replaces the previous two-argument `(id, payload)` API. Every handler now receives the full picture:
481
481
 
482
482
  ```typescript
483
- onAction('ui_add_to_cart', (action) => {
483
+ actionService.on('ui_add_to_cart', (action) => {
484
484
  console.log(action.type); // 'ui_add_to_cart'
485
485
  console.log(action.payload); // {productId: 42, qty: 1} — fully typed
486
486
  console.log(action.context); // 'product-list' — from [action-context] ancestor
@@ -491,9 +491,9 @@ onAction('ui_add_to_cart', (action) => {
491
491
  Modifiers can enrich `meta` before the action reaches subscribers:
492
492
 
493
493
  ```typescript
494
- import {registerModifier} from '@alwatr/flux';
494
+ import {actionService} from '@alwatr/flux';
495
495
 
496
- registerModifier('trace', (_event, _element, action) => {
496
+ actionService.registerModifier('trace', (_event, _element, action) => {
497
497
  action.meta ??= {};
498
498
  action.meta['traceId'] = crypto.randomUUID();
499
499
  return true;
@@ -531,10 +531,10 @@ bun add @alwatr/flux
531
531
  ### 1. Bootstrap the Application
532
532
 
533
533
  ```typescript
534
- import {setupActionDelegation, dispatchPageReady} from '@alwatr/flux';
534
+ import {actionService, dispatchPageReady} from '@alwatr/flux';
535
535
 
536
536
  // Activate global event delegation (call once at app start)
537
- setupActionDelegation();
537
+ actionService.setupDelegation();
538
538
 
539
539
  // Dispatch page-ready signal (for MPA routing)
540
540
  dispatchPageReady();
@@ -569,19 +569,19 @@ export const counterSignal = createStateSignal({
569
569
 
570
570
  ```typescript
571
571
  // src/controllers.ts
572
- import {onAction} from '@alwatr/flux';
572
+ import {actionService} from '@alwatr/flux';
573
573
  import {counterSignal} from './state.js';
574
574
 
575
- onAction('ui_increment', () => {
575
+ actionService.on('ui_increment', () => {
576
576
  counterSignal.update((count) => count + 1);
577
577
  });
578
578
 
579
- onAction('ui_decrement', () => {
579
+ actionService.on('ui_decrement', () => {
580
580
  counterSignal.update((count) => count - 1);
581
581
  });
582
582
 
583
583
  // Handler receives the full Action object — payload is typed from ActionRecord
584
- onAction('ui_set_count', (action) => {
584
+ actionService.on('ui_set_count', (action) => {
585
585
  counterSignal.set(action.payload); // action.payload: number
586
586
  });
587
587
  ```
@@ -616,11 +616,11 @@ onAction('ui_set_count', (action) => {
616
616
 
617
617
  ```typescript
618
618
  // main.js
619
- import {setupActionDelegation} from '@alwatr/flux';
619
+ import {actionService} from '@alwatr/flux';
620
620
  import {counterSignal} from './state.js';
621
621
  import './controllers.js'; // Register action handlers
622
622
 
623
- setupActionDelegation();
623
+ actionService.setupDelegation();
624
624
 
625
625
  // Subscribe to state changes and update DOM
626
626
  counterSignal.subscribe((count) => {
@@ -736,34 +736,38 @@ const mapped = createMappedSignal(source, {
736
736
 
737
737
  ### Actions
738
738
 
739
- #### `setupActionDelegation(eventTypes?)`
739
+ #### `actionService`
740
740
 
741
- Activates global event delegation. Call once at app bootstrap.
741
+ The pre-instantiated singleton instance of `ActionService` exported for declarative event delegation and action dispatching.
742
+
743
+ ##### `actionService.setupDelegation(eventTypes?)`
744
+
745
+ Activates global event delegation capture listeners on `document.body`.
742
746
 
743
747
  ```typescript
744
- import {setupActionDelegation, DEFAULT_DELEGATED_EVENTS} from '@alwatr/flux';
748
+ import {actionService, DEFAULT_DELEGATED_EVENTS} from '@alwatr/flux';
745
749
 
746
750
  // Use defaults (click, submit, input, change)
747
- setupActionDelegation();
751
+ actionService.setupDelegation();
748
752
 
749
753
  // Or add custom events
750
- setupActionDelegation([...DEFAULT_DELEGATED_EVENTS, 'keydown', 'focus']);
754
+ actionService.setupDelegation([...DEFAULT_DELEGATED_EVENTS, 'keydown', 'focus']);
751
755
  ```
752
756
 
753
- #### `onAction<K>(type, handler)`
757
+ ##### `actionService.on(type, handler)`
754
758
 
755
- Subscribes to a single typed action or an array of actions. The handler receives the full `Action<K>` object.
759
+ Subscribes to a single typed action or an array of actions.
756
760
 
757
761
  ```typescript
758
762
  // Subscribe to a single action
759
- const sub = onAction('ui_add_to_cart', (action) => {
763
+ const sub = actionService.on('ui_add_to_cart', (action) => {
760
764
  cartService.add(action.payload.productId, action.payload.qty);
761
765
  console.log(action.context); // e.g. 'product-list' or undefined
762
766
  console.log(action.meta); // any metadata set by modifiers
763
767
  });
764
768
 
765
769
  // Subscribe to multiple actions with a single handler
766
- const multiSub = onAction(['ui_increment', 'ui_decrement'], (action) => {
770
+ const multiSub = actionService.on(['ui_increment', 'ui_decrement'], (action) => {
767
771
  console.log('Action triggered:', action.type);
768
772
  });
769
773
 
@@ -771,16 +775,16 @@ sub.unsubscribe(); // Clean up when done
771
775
  multiSub.unsubscribe();
772
776
  ```
773
777
 
774
- #### `dispatchAction<K>(action)`
778
+ ##### `actionService.dispatch(action)`
775
779
 
776
- Dispatches a typed action programmatically. Takes a full `Action<K>` object.
780
+ Dispatches a typed action programmatically.
777
781
 
778
782
  ```typescript
779
- dispatchAction({type: 'navigate', payload: '/home'});
780
- dispatchAction({type: 'auth_expired', payload: undefined}); // void payload
783
+ actionService.dispatch({type: 'navigate', payload: '/home'});
784
+ actionService.dispatch({type: 'auth_expired', payload: undefined}); // void payload
781
785
 
782
786
  // With context and meta
783
- dispatchAction({
787
+ actionService.dispatch({
784
788
  type: 'upload_complete',
785
789
  payload: fileId,
786
790
  context: 'product-list',
@@ -788,17 +792,17 @@ dispatchAction({
788
792
  });
789
793
  ```
790
794
 
791
- #### `registerModifier(name, handler)`
795
+ ##### `actionService.registerModifier(name, handler)`
792
796
 
793
- Adds a custom modifier for `on-<event>` attributes. The handler receives the mutable `action` object and may write to `action.meta`.
797
+ Adds a custom modifier for `on-<event>` attributes.
794
798
 
795
799
  ```typescript
796
- registerModifier('confirm', () => {
800
+ actionService.registerModifier('confirm', () => {
797
801
  return window.confirm('Are you sure?');
798
802
  });
799
803
 
800
804
  // A modifier that stamps a trace ID into meta
801
- registerModifier('trace', (_event, _element, action) => {
805
+ actionService.registerModifier('trace', (_event, _element, action) => {
802
806
  action.meta ??= {};
803
807
  action.meta['traceId'] = crypto.randomUUID();
804
808
  return true;
@@ -809,12 +813,12 @@ registerModifier('trace', (_event, _element, action) => {
809
813
  <button on-click="ui_delete_item:42; confirm,trace">Delete</button>
810
814
  ```
811
815
 
812
- #### `registerPayloadResolver(name, resolver)`
816
+ ##### `actionService.registerPayloadResolver(name, resolver)`
813
817
 
814
818
  Adds a custom payload resolver.
815
819
 
816
820
  ```typescript
817
- registerPayloadResolver('$data-id', (_event, element) => {
821
+ actionService.registerPayloadResolver('$data-id', (_event, element) => {
818
822
  return element.dataset.id;
819
823
  });
820
824
  ```
@@ -1238,19 +1242,19 @@ export const todosSignal = createStateSignal<Todo[]>({
1238
1242
  });
1239
1243
 
1240
1244
  // controllers.ts
1241
- import {onAction} from '@alwatr/flux';
1245
+ import {actionService} from '@alwatr/flux';
1242
1246
  import {todosSignal} from './state.js';
1243
1247
 
1244
1248
  let nextId = 1;
1245
1249
 
1246
- onAction('ui_add_todo', (action) => {
1250
+ actionService.on('ui_add_todo', (action) => {
1247
1251
  todosSignal.update((todos) => [
1248
1252
  ...todos,
1249
1253
  {id: nextId++, text: action.payload, done: false},
1250
1254
  ]);
1251
1255
  });
1252
1256
 
1253
- onAction('ui_toggle_todo', (action) => {
1257
+ actionService.on('ui_toggle_todo', (action) => {
1254
1258
  todosSignal.update((todos) =>
1255
1259
  todos.map((todo) =>
1256
1260
  todo.id === action.payload ? {...todo, done: !todo.done} : todo
@@ -1258,7 +1262,7 @@ onAction('ui_toggle_todo', (action) => {
1258
1262
  );
1259
1263
  });
1260
1264
 
1261
- onAction('ui_remove_todo', (action) => {
1265
+ actionService.on('ui_remove_todo', (action) => {
1262
1266
  todosSignal.update((todos) => todos.filter((t) => t.id !== action.payload));
1263
1267
  });
1264
1268
 
@@ -1269,11 +1273,11 @@ onAction('ui_remove_todo', (action) => {
1269
1273
  </div>
1270
1274
 
1271
1275
  // main.ts
1272
- import {setupActionDelegation, html, render} from '@alwatr/flux';
1276
+ import {actionService, html, render} from '@alwatr/flux';
1273
1277
  import {todosSignal} from './state.js';
1274
1278
  import './controllers.js';
1275
1279
 
1276
- setupActionDelegation();
1280
+ actionService.setupDelegation();
1277
1281
 
1278
1282
  todosSignal.subscribe((todos) => {
1279
1283
  render(
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/keyboard-shortcut';
5
6
  export * from '@alwatr/render-state';
6
7
  export * from '@alwatr/local-storage';
7
8
  export * from '@alwatr/session-storage';
@@ -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,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,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.25.0 */
2
- export*from"@alwatr/signal";export*from"@alwatr/action";export*from"@alwatr/directive";export*from"@alwatr/embedded-data";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 a,mathml as p,render as f,noChange as n,nothing as t}from"lit-html";import{unsafeSVG as g}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,g as unsafeSVG,a as svg,I as repeat,f as render,t as nothing,n as noChange,p as mathml,i as ifDefined,e as html,b as classMap,s as cache};
1
+ /* 📦 @alwatr/flux v9.27.0 */
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 n}from"lit-html";import{unsafeSVG as g}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,g as unsafeSVG,p as svg,I as repeat,f as render,n as nothing,t as noChange,a as mathml,i as ifDefined,e as html,b as classMap,s as cache};
3
3
 
4
- //# debugId=93BBA3F8B762FC8564756E2164756E21
4
+ //# debugId=48C4CA5B471A0B6264756E2164756E21
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/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/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,kCACA,mCACA,qCACA,gCCoBA,eACE,SACA,YACA,YACA,cACA,aACA,iBAMF,oBAAQ,0CACR,oBAAQ,0CACR,gBAAQ,qCACR,mBAAQ,yCACR,eAAQ,oCACR,iBAAQ",
9
- "debugId": "93BBA3F8B762FC8564756E2164756E21",
8
+ "mappings": ";AAGA,4BACA,4BACA,+BACA,mCACA,uCACA,kCACA,mCACA,qCACA,gCCmBA,eACE,SACA,YACA,YACA,cACA,aACA,iBAMF,oBAAQ,0CACR,oBAAQ,0CACR,gBAAQ,qCACR,mBAAQ,yCACR,eAAQ,oCACR,iBAAQ",
9
+ "debugId": "48C4CA5B471A0B6264756E2164756E21",
10
10
  "names": []
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alwatr/flux",
3
- "version": "9.25.0",
3
+ "version": "9.27.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,14 +21,15 @@
21
21
  },
22
22
  "sideEffects": false,
23
23
  "dependencies": {
24
- "@alwatr/action": "9.25.0",
25
- "@alwatr/directive": "9.25.0",
24
+ "@alwatr/action": "9.27.0",
25
+ "@alwatr/directive": "9.26.0",
26
26
  "@alwatr/embedded-data": "9.25.0",
27
+ "@alwatr/keyboard-shortcut": "9.27.0",
27
28
  "@alwatr/local-storage": "9.25.0",
28
- "@alwatr/page-ready": "9.25.0",
29
+ "@alwatr/page-ready": "9.27.0",
29
30
  "@alwatr/render-state": "9.25.0",
30
31
  "@alwatr/session-storage": "9.25.0",
31
- "@alwatr/signal": "9.25.0",
32
+ "@alwatr/signal": "9.26.0",
32
33
  "@alwatr/type-helper": "9.14.0",
33
34
  "lit-html": "^3.3.3"
34
35
  },
@@ -83,5 +84,5 @@
83
84
  "ui",
84
85
  "unidirectional-data-flow"
85
86
  ],
86
- "gitHead": "a49ae304180faab79539b5ddfe62d18ac91a232e"
87
+ "gitHead": "a5591e8230a1a33b5414297345ae5f6fd3e5a168"
87
88
  }
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/keyboard-shortcut';
8
9
  export * from '@alwatr/render-state';
9
10
  export * from '@alwatr/local-storage';
10
11
  export * from '@alwatr/session-storage';