@alwatr/action 9.16.0 → 9.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/type.ts ADDED
@@ -0,0 +1,165 @@
1
+ import type {DictionaryOpt} from '@alwatr/type-helper';
2
+
3
+ /**
4
+ * Global registry mapping action identifiers to their payload types.
5
+ *
6
+ * Extend this interface via declaration merging to register your application's
7
+ * actions and gain full type safety in `onAction` and `dispatchAction`.
8
+ *
9
+ * This interface is intentionally empty in the base package — all actions are
10
+ * application-specific and should be declared in a dedicated `action-record.ts`
11
+ * file within each feature package.
12
+ *
13
+ * @example — registering actions in a feature package
14
+ * ```ts
15
+ * // pkg/my-feature/src/action-record.ts
16
+ * declare module '@alwatr/action' {
17
+ * interface ActionRecord {
18
+ * 'open_drawer': string;
19
+ * 'add_to_cart': {productId: number; qty: number};
20
+ * 'logout': void;
21
+ * }
22
+ * }
23
+ * ```
24
+ */
25
+ export interface ActionRecord {}
26
+
27
+ /**
28
+ * Builds the parameter type for `dispatchAction`.
29
+ *
30
+ * When `ActionRecord[K]` is `void`, the `payload` field becomes optional so
31
+ * callers can omit it entirely. For all other payload types the field remains
32
+ * required, preserving full type safety.
33
+ *
34
+ * @example — void action (payload omitted)
35
+ * ```ts
36
+ * dispatchAction({type: 'logout'});
37
+ * ```
38
+ *
39
+ * @example — typed action (payload required)
40
+ * ```ts
41
+ * dispatchAction({type: 'add_to_cart', payload: {productId: 42, qty: 1}});
42
+ * ```
43
+ */
44
+ export type DispatchParam<K extends keyof ActionRecord> =
45
+ ActionRecord[K] extends void ? Omit<Action<K>, 'payload'> & {payload?: void} : Action<K>;
46
+
47
+ /**
48
+ * Alwatr Flux Standard Action (AFSA).
49
+ *
50
+ * The single, canonical object passed to every `dispatchAction` call and
51
+ * received by every `onAction` handler. Keeping all action data in one
52
+ * structure makes the bus extensible without breaking existing call sites.
53
+ *
54
+ * @template K - A key of `ActionRecord`; constrains `type` and `payload` together.
55
+ *
56
+ * @example — dispatching
57
+ * ```ts
58
+ * dispatchAction({type: 'add_to_cart', payload: {productId: 42, qty: 1}});
59
+ * ```
60
+ *
61
+ * @example — subscribing
62
+ * ```ts
63
+ * onAction('add_to_cart', (action) => {
64
+ * console.log(action.type); // 'add_to_cart'
65
+ * console.log(action.payload); // {productId: 42, qty: 1}
66
+ * console.log(action.context); // e.g. 'product-list' (from DOM) or undefined
67
+ * });
68
+ * ```
69
+ */
70
+ export interface Action<K extends keyof ActionRecord = keyof ActionRecord> {
71
+ /**
72
+ * Unique action identifier — must be a key of `ActionRecord`.
73
+ *
74
+ * @example 'cart:add-item', 'open_drawer', 'logout'
75
+ */
76
+ readonly type: K;
77
+
78
+ /**
79
+ * The DOM context in which the action was triggered.
80
+ *
81
+ * Extracted at delegation time from the nearest ancestor element that carries
82
+ * an `action-context` attribute. Useful for scoping the same action type to
83
+ * different UI regions (e.g. two sliders on the same page both dispatching
84
+ * `'slider:change'` but with different context values).
85
+ *
86
+ * `undefined` when the action is dispatched programmatically (no DOM involved)
87
+ * or when no `[action-context]` ancestor exists.
88
+ *
89
+ * @example 'slider-123', 'product-list', 'checkout-form'
90
+ */
91
+ readonly context?: string;
92
+
93
+ /**
94
+ * The pure business payload carried by this action.
95
+ *
96
+ * Type is inferred from `ActionRecord[K]` — the compiler enforces the correct
97
+ * shape at every call site. No manual generic annotation is needed.
98
+ */
99
+ readonly payload: ActionRecord[K];
100
+
101
+ /**
102
+ * Open-ended metadata bag for cross-cutting concerns.
103
+ *
104
+ * Intentionally untyped so that future infrastructure layers (tracing,
105
+ * analytics, A/B testing) can attach data without touching the typed API.
106
+ * Modifiers in the delegation pipeline may also write to `meta` before the
107
+ * action reaches subscribers.
108
+ *
109
+ * Treat values here as `unknown` and validate before use.
110
+ *
111
+ * @example {traceId: 'abc-123', timestamp: Date.now()}
112
+ */
113
+ meta?: DictionaryOpt<unknown>;
114
+ }
115
+
116
+ /**
117
+ * A modifier handler used in `on-<eventType>` attribute syntax.
118
+ *
119
+ * Receives the triggering DOM `event`, the `element` that owns the
120
+ * `on-<eventType>` attribute, and the **mutable** `action` object being built.
121
+ * The handler may mutate `action.meta` to attach cross-cutting data (e.g. a
122
+ * trace ID, a timestamp, or an A/B flag) before the action reaches subscribers.
123
+ *
124
+ * Return `true` (or any truthy value) to allow the action to proceed, or
125
+ * `false` to cancel the dispatch entirely.
126
+ *
127
+ * Using explicit parameters instead of `this` binding makes handlers
128
+ * compatible with arrow functions and easier to test in isolation.
129
+ *
130
+ * @example — a modifier that stamps a timestamp into meta
131
+ * ```ts
132
+ * const timestampHandler: ModifierHandler = (_event, _element, action) => {
133
+ * action.meta ??= {};
134
+ * action.meta['timestamp'] = Date.now();
135
+ * return true;
136
+ * };
137
+ * ```
138
+ *
139
+ * @example — a modifier that cancels dispatch when the element is disabled
140
+ * ```ts
141
+ * const notDisabledHandler: ModifierHandler = (_event, element) => {
142
+ * return !(element as HTMLButtonElement).disabled;
143
+ * };
144
+ * ```
145
+ */
146
+ export type ModifierHandler = (event: Event, element: HTMLElement, action: Action) => boolean;
147
+
148
+ /**
149
+ * A payload resolver used in `on-<eventType>` attribute syntax.
150
+ *
151
+ * Receives the triggering DOM `event` and the `element` that owns the
152
+ * `on-<eventType>` attribute. The return value becomes the `payload` field of
153
+ * the `Action` object passed to `onAction` subscribers.
154
+ *
155
+ * Using explicit parameters instead of `this` binding makes resolvers
156
+ * compatible with arrow functions and easier to test in isolation.
157
+ *
158
+ * @example — a resolver that returns the element's dataset id
159
+ * ```ts
160
+ * const dataIdResolver: PayloadResolver = (_event, element) => {
161
+ * return (element as HTMLElement).dataset.id ?? null;
162
+ * };
163
+ * ```
164
+ */
165
+ export type PayloadResolver = (event: Event, element: HTMLElement) => unknown;
@@ -1,54 +0,0 @@
1
- /**
2
- * @file action-record.ts
3
- *
4
- * Global action type registry via TypeScript declaration merging.
5
- *
6
- * ## How it works
7
- *
8
- * `ActionRecord` is an open interface — any package in the monorepo (or any
9
- * consumer application) can extend it with their own action names and payload
10
- * types using declaration merging, without modifying this file:
11
- *
12
- * ```ts
13
- * // In your package: src/action-record.ts
14
- * declare module '@alwatr/action' {
15
- * interface ActionRecord {
16
- * 'open_drawer': string;
17
- * 'add_to_cart': {productId: number; qty: number};
18
- * 'logout': void;
19
- * }
20
- * }
21
- * ```
22
- *
23
- * Once declared, `onAction` and `dispatchAction` become fully type-safe for
24
- * those action names — the compiler enforces the correct payload type at every
25
- * call site and provides autocomplete for action identifiers.
26
- *
27
- * Only actions declared in `ActionRecord` are accepted. Passing an unknown
28
- * action name is a **compile error** — there is no string fallback.
29
- */
30
- /**
31
- * Global registry mapping action identifiers to their payload types.
32
- *
33
- * Extend this interface via declaration merging to register your application's
34
- * actions and gain full type safety in `onAction` and `dispatchAction`.
35
- *
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.
39
- *
40
- * @example — registering actions in a feature package
41
- * ```ts
42
- * // pkg/my-feature/src/action-record.ts
43
- * declare module '@alwatr/action' {
44
- * interface ActionRecord {
45
- * 'open_drawer': string;
46
- * 'add_to_cart': {productId: number; qty: number};
47
- * 'logout': void;
48
- * }
49
- * }
50
- * ```
51
- */
52
- export interface ActionRecord {
53
- }
54
- //# sourceMappingURL=action-record.d.ts.map
@@ -1 +0,0 @@
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"}
package/dist/lib.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,OAAO,uCAAgC,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,iEAAwE,CAAC"}
@@ -1,61 +0,0 @@
1
- /**
2
- * A modifier handler used in `on-action` attribute syntax.
3
- *
4
- * Receives the triggering DOM `event` and the `element` that owns the
5
- * `on-action` attribute. Return `true` (or any truthy value) to allow the
6
- * action to proceed, or `false` to cancel the dispatch.
7
- *
8
- * Using explicit parameters instead of `this` binding makes handlers
9
- * compatible with arrow functions and easier to test in isolation.
10
- *
11
- * @example
12
- * ```ts
13
- * // A modifier that only allows the action when the element is not disabled
14
- * const notDisabledHandler: ModifierHandler = (_event, element) => {
15
- * return !(element as HTMLButtonElement).disabled;
16
- * };
17
- * ```
18
- */
19
- export type ModifierHandler = (event: Event, element: HTMLElement) => boolean;
20
- /**
21
- * A payload resolver used in `on-action` attribute syntax.
22
- *
23
- * Receives the triggering DOM `event` and the `element` that owns the
24
- * `on-action` attribute. The return value becomes the `actionPayload` passed
25
- * to `onAction` subscribers. Use this to compute dynamic payloads from DOM state.
26
- *
27
- * Using explicit parameters instead of `this` binding makes resolvers
28
- * compatible with arrow functions and easier to test in isolation.
29
- *
30
- * @example
31
- * ```ts
32
- * // A resolver that returns the element's dataset id
33
- * const dataIdResolver: PayloadResolver = (_event, element) => {
34
- * return (element as HTMLElement).dataset.id ?? null;
35
- * };
36
- * ```
37
- */
38
- export type PayloadResolver = (event: Event, element: HTMLElement) => unknown;
39
- /**
40
- * Registry of all named modifier handlers.
41
- *
42
- * Keys are modifier names used in the `on-<eventType>` attribute syntax
43
- * (e.g. `on-click="action-id; prevent"`). Values are `ModifierHandler` functions.
44
- * Populated at module load with built-in modifiers; extended at runtime via
45
- * `registerModifier`.
46
- *
47
- * @internal
48
- */
49
- export declare const modifierRegistry: Map<string, ModifierHandler>;
50
- /**
51
- * Registry of all named payload resolvers.
52
- *
53
- * Keys are resolver tokens used in the `on-<eventType>` attribute syntax
54
- * (e.g. `on-input="search_query:$value"`). Values are `PayloadResolver` functions.
55
- * Populated at module load with built-in resolvers; extended at runtime via
56
- * `registerPayloadResolver`.
57
- *
58
- * @internal
59
- */
60
- export declare const payloadRegistry: Map<string, PayloadResolver>;
61
- //# sourceMappingURL=registry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC;AAI9E;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,8BAAqC,CAAC;AAEnE;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,8BAAqC,CAAC"}
@@ -1,54 +0,0 @@
1
- /**
2
- * @file action-record.ts
3
- *
4
- * Global action type registry via TypeScript declaration merging.
5
- *
6
- * ## How it works
7
- *
8
- * `ActionRecord` is an open interface — any package in the monorepo (or any
9
- * consumer application) can extend it with their own action names and payload
10
- * types using declaration merging, without modifying this file:
11
- *
12
- * ```ts
13
- * // In your package: src/action-record.ts
14
- * declare module '@alwatr/action' {
15
- * interface ActionRecord {
16
- * 'open_drawer': string;
17
- * 'add_to_cart': {productId: number; qty: number};
18
- * 'logout': void;
19
- * }
20
- * }
21
- * ```
22
- *
23
- * Once declared, `onAction` and `dispatchAction` become fully type-safe for
24
- * those action names — the compiler enforces the correct payload type at every
25
- * call site and provides autocomplete for action identifiers.
26
- *
27
- * Only actions declared in `ActionRecord` are accepted. Passing an unknown
28
- * action name is a **compile error** — there is no string fallback.
29
- */
30
-
31
- /**
32
- * Global registry mapping action identifiers to their payload types.
33
- *
34
- * Extend this interface via declaration merging to register your application's
35
- * actions and gain full type safety in `onAction` and `dispatchAction`.
36
- *
37
- * This interface is intentionally empty in the base package — all actions are
38
- * application-specific and should be declared in a dedicated `action-record.ts`
39
- * file within each feature package.
40
- *
41
- * @example — registering actions in a feature package
42
- * ```ts
43
- * // pkg/my-feature/src/action-record.ts
44
- * declare module '@alwatr/action' {
45
- * interface ActionRecord {
46
- * 'open_drawer': string;
47
- * 'add_to_cart': {productId: number; qty: number};
48
- * 'logout': void;
49
- * }
50
- * }
51
- * ```
52
- */
53
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
54
- export interface ActionRecord {}