rexfect 0.0.7

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.
Files changed (190) hide show
  1. package/README.md +1756 -0
  2. package/dist/abortableContext.d.ts +3 -0
  3. package/dist/abortableContext.d.ts.map +1 -0
  4. package/dist/abortableContext.js +48 -0
  5. package/dist/abortableContext.js.map +1 -0
  6. package/dist/action.d.ts +64 -0
  7. package/dist/action.d.ts.map +1 -0
  8. package/dist/action.js +208 -0
  9. package/dist/action.js.map +1 -0
  10. package/dist/action.test.d.ts +2 -0
  11. package/dist/action.test.d.ts.map +1 -0
  12. package/dist/action.test.js +189 -0
  13. package/dist/action.test.js.map +1 -0
  14. package/dist/async/abortable-guard.d.ts +25 -0
  15. package/dist/async/abortable-guard.d.ts.map +1 -0
  16. package/dist/async/abortable-guard.js +33 -0
  17. package/dist/async/abortable-guard.js.map +1 -0
  18. package/dist/async/abortable.d.ts +331 -0
  19. package/dist/async/abortable.d.ts.map +1 -0
  20. package/dist/async/abortable.js +410 -0
  21. package/dist/async/abortable.js.map +1 -0
  22. package/dist/async/abortable.test.d.ts +2 -0
  23. package/dist/async/abortable.test.d.ts.map +1 -0
  24. package/dist/async/abortable.test.js +535 -0
  25. package/dist/async/abortable.test.js.map +1 -0
  26. package/dist/async/abortable.typeCheck.d.ts +8 -0
  27. package/dist/async/abortable.typeCheck.d.ts.map +1 -0
  28. package/dist/async/abortable.typeCheck.js +138 -0
  29. package/dist/async/abortable.typeCheck.js.map +1 -0
  30. package/dist/async/async.d.ts +18 -0
  31. package/dist/async/async.d.ts.map +1 -0
  32. package/dist/async/async.js +20 -0
  33. package/dist/async/async.js.map +1 -0
  34. package/dist/async/index.d.ts +15 -0
  35. package/dist/async/index.d.ts.map +1 -0
  36. package/dist/async/index.js +13 -0
  37. package/dist/async/index.js.map +1 -0
  38. package/dist/async/loadable.d.ts +7 -0
  39. package/dist/async/loadable.d.ts.map +1 -0
  40. package/dist/async/loadable.js +52 -0
  41. package/dist/async/loadable.js.map +1 -0
  42. package/dist/async/loadable.test.d.ts +2 -0
  43. package/dist/async/loadable.test.d.ts.map +1 -0
  44. package/dist/async/loadable.test.js +322 -0
  45. package/dist/async/loadable.test.js.map +1 -0
  46. package/dist/async/promiseCache.d.ts +14 -0
  47. package/dist/async/promiseCache.d.ts.map +1 -0
  48. package/dist/async/promiseCache.js +29 -0
  49. package/dist/async/promiseCache.js.map +1 -0
  50. package/dist/async/read.d.ts +120 -0
  51. package/dist/async/read.d.ts.map +1 -0
  52. package/dist/async/read.js +286 -0
  53. package/dist/async/read.js.map +1 -0
  54. package/dist/async/read.test.d.ts +2 -0
  55. package/dist/async/read.test.d.ts.map +1 -0
  56. package/dist/async/read.test.js +419 -0
  57. package/dist/async/read.test.js.map +1 -0
  58. package/dist/async/read.typeCheck.d.ts +6 -0
  59. package/dist/async/read.typeCheck.d.ts.map +1 -0
  60. package/dist/async/read.typeCheck.js +101 -0
  61. package/dist/async/read.typeCheck.js.map +1 -0
  62. package/dist/async/safe.d.ts +230 -0
  63. package/dist/async/safe.d.ts.map +1 -0
  64. package/dist/async/safe.js +247 -0
  65. package/dist/async/safe.js.map +1 -0
  66. package/dist/async/safe.test.d.ts +2 -0
  67. package/dist/async/safe.test.d.ts.map +1 -0
  68. package/dist/async/safe.test.js +447 -0
  69. package/dist/async/safe.test.js.map +1 -0
  70. package/dist/async/utils.d.ts +17 -0
  71. package/dist/async/utils.d.ts.map +1 -0
  72. package/dist/async/utils.js +38 -0
  73. package/dist/async/utils.js.map +1 -0
  74. package/dist/async/wait.d.ts +120 -0
  75. package/dist/async/wait.d.ts.map +1 -0
  76. package/dist/async/wait.js +112 -0
  77. package/dist/async/wait.js.map +1 -0
  78. package/dist/async/wait.test.d.ts +2 -0
  79. package/dist/async/wait.test.d.ts.map +1 -0
  80. package/dist/async/wait.test.js +122 -0
  81. package/dist/async/wait.test.js.map +1 -0
  82. package/dist/async/wait.typeCheck.d.ts +6 -0
  83. package/dist/async/wait.typeCheck.d.ts.map +1 -0
  84. package/dist/async/wait.typeCheck.js +104 -0
  85. package/dist/async/wait.typeCheck.js.map +1 -0
  86. package/dist/atom.d.ts +46 -0
  87. package/dist/atom.d.ts.map +1 -0
  88. package/dist/atom.js +86 -0
  89. package/dist/atom.js.map +1 -0
  90. package/dist/atom.test.d.ts +2 -0
  91. package/dist/atom.test.d.ts.map +1 -0
  92. package/dist/atom.test.js +75 -0
  93. package/dist/atom.test.js.map +1 -0
  94. package/dist/batch.d.ts +15 -0
  95. package/dist/batch.d.ts.map +1 -0
  96. package/dist/batch.js +45 -0
  97. package/dist/batch.js.map +1 -0
  98. package/dist/defer.d.ts +56 -0
  99. package/dist/defer.d.ts.map +1 -0
  100. package/dist/defer.js +49 -0
  101. package/dist/defer.js.map +1 -0
  102. package/dist/effect.d.ts +91 -0
  103. package/dist/effect.d.ts.map +1 -0
  104. package/dist/effect.js +311 -0
  105. package/dist/effect.js.map +1 -0
  106. package/dist/effect.test.d.ts +2 -0
  107. package/dist/effect.test.d.ts.map +1 -0
  108. package/dist/effect.test.js +123 -0
  109. package/dist/effect.test.js.map +1 -0
  110. package/dist/emitter.d.ts +129 -0
  111. package/dist/emitter.d.ts.map +1 -0
  112. package/dist/emitter.js +164 -0
  113. package/dist/emitter.js.map +1 -0
  114. package/dist/emitter.test.d.ts +2 -0
  115. package/dist/emitter.test.d.ts.map +1 -0
  116. package/dist/emitter.test.js +259 -0
  117. package/dist/emitter.test.js.map +1 -0
  118. package/dist/equality.d.ts +66 -0
  119. package/dist/equality.d.ts.map +1 -0
  120. package/dist/equality.js +145 -0
  121. package/dist/equality.js.map +1 -0
  122. package/dist/event.d.ts +18 -0
  123. package/dist/event.d.ts.map +1 -0
  124. package/dist/event.js +166 -0
  125. package/dist/event.js.map +1 -0
  126. package/dist/event.test.d.ts +2 -0
  127. package/dist/event.test.d.ts.map +1 -0
  128. package/dist/event.test.js +167 -0
  129. package/dist/event.test.js.map +1 -0
  130. package/dist/hooks.d.ts +152 -0
  131. package/dist/hooks.d.ts.map +1 -0
  132. package/dist/hooks.js +122 -0
  133. package/dist/hooks.js.map +1 -0
  134. package/dist/hooks.test.d.ts +2 -0
  135. package/dist/hooks.test.d.ts.map +1 -0
  136. package/dist/hooks.test.js +99 -0
  137. package/dist/hooks.test.js.map +1 -0
  138. package/dist/index.d.ts +33 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +35 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/isPromiseLike.d.ts +10 -0
  143. package/dist/isPromiseLike.d.ts.map +1 -0
  144. package/dist/isPromiseLike.js +15 -0
  145. package/dist/isPromiseLike.js.map +1 -0
  146. package/dist/pick.d.ts +22 -0
  147. package/dist/pick.d.ts.map +1 -0
  148. package/dist/pick.js +46 -0
  149. package/dist/pick.js.map +1 -0
  150. package/dist/react/index.d.ts +8 -0
  151. package/dist/react/index.d.ts.map +1 -0
  152. package/dist/react/index.js +8 -0
  153. package/dist/react/index.js.map +1 -0
  154. package/dist/react/useRx.d.ts +14 -0
  155. package/dist/react/useRx.d.ts.map +1 -0
  156. package/dist/react/useRx.js +110 -0
  157. package/dist/react/useRx.js.map +1 -0
  158. package/dist/react/useRx.test.d.ts +2 -0
  159. package/dist/react/useRx.test.d.ts.map +1 -0
  160. package/dist/react/useRx.test.js +457 -0
  161. package/dist/react/useRx.test.js.map +1 -0
  162. package/dist/strictModeTest.d.ts +11 -0
  163. package/dist/strictModeTest.d.ts.map +1 -0
  164. package/dist/strictModeTest.js +41 -0
  165. package/dist/strictModeTest.js.map +1 -0
  166. package/dist/types.d.ts +606 -0
  167. package/dist/types.d.ts.map +1 -0
  168. package/dist/types.js +5 -0
  169. package/dist/types.js.map +1 -0
  170. package/dist/untrack.d.ts +14 -0
  171. package/dist/untrack.d.ts.map +1 -0
  172. package/dist/untrack.js +17 -0
  173. package/dist/untrack.js.map +1 -0
  174. package/dist/utils/withUse.d.ts +10 -0
  175. package/dist/utils/withUse.d.ts.map +1 -0
  176. package/dist/utils/withUse.js +21 -0
  177. package/dist/utils/withUse.js.map +1 -0
  178. package/dist/utils/withUse.test.d.ts +2 -0
  179. package/dist/utils/withUse.test.d.ts.map +1 -0
  180. package/dist/utils/withUse.test.js +233 -0
  181. package/dist/utils/withUse.test.js.map +1 -0
  182. package/dist/utils.d.ts +7 -0
  183. package/dist/utils.d.ts.map +1 -0
  184. package/dist/utils.js +7 -0
  185. package/dist/utils.js.map +1 -0
  186. package/dist/utils.test.d.ts +2 -0
  187. package/dist/utils.test.d.ts.map +1 -0
  188. package/dist/utils.test.js +119 -0
  189. package/dist/utils.test.js.map +1 -0
  190. package/package.json +64 -0
@@ -0,0 +1,3 @@
1
+ import { type AbortableContext } from "./types";
2
+ export declare function abortableContext(controller: AbortController, prevController?: AbortController | null): AbortableContext;
3
+ //# sourceMappingURL=abortableContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abortableContext.d.ts","sourceRoot":"","sources":["../src/abortableContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,eAAe,EAC3B,cAAc,CAAC,EAAE,eAAe,GAAG,IAAI,GACtC,gBAAgB,CA0DlB"}
@@ -0,0 +1,48 @@
1
+ export function abortableContext(controller, prevController) {
2
+ const ctx = {
3
+ signal: controller.signal,
4
+ abort() {
5
+ controller.abort();
6
+ },
7
+ abortPrev(message) {
8
+ prevController?.abort(message);
9
+ },
10
+ safe(promiseOrCallback) {
11
+ if (typeof promiseOrCallback === "function") {
12
+ return (...args) => {
13
+ if (!controller.signal.aborted) {
14
+ return promiseOrCallback(...args);
15
+ }
16
+ };
17
+ }
18
+ return new Promise((resolve, reject) => {
19
+ promiseOrCallback.then((value) => {
20
+ if (!controller.signal.aborted) {
21
+ resolve(value);
22
+ }
23
+ }, (error) => {
24
+ if (!controller.signal.aborted) {
25
+ reject(error);
26
+ }
27
+ });
28
+ });
29
+ },
30
+ fork() {
31
+ const childController = new AbortController();
32
+ const handler = () => {
33
+ controller.signal.removeEventListener("abort", handler);
34
+ childController.abort();
35
+ };
36
+ controller.signal.addEventListener("abort", handler);
37
+ return abortableContext(childController);
38
+ },
39
+ spawn() {
40
+ return abortableContext(new AbortController());
41
+ },
42
+ use(plugin, ...args) {
43
+ return plugin(ctx, ...args);
44
+ },
45
+ };
46
+ return ctx;
47
+ }
48
+ //# sourceMappingURL=abortableContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abortableContext.js","sourceRoot":"","sources":["../src/abortableContext.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAC9B,UAA2B,EAC3B,cAAuC;IAEvC,MAAM,GAAG,GAAqB;QAC5B,MAAM,EAAE,UAAU,CAAC,MAAM;QAEzB,KAAK;YACH,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;QAED,SAAS,CAAC,OAAgB;YACxB,cAAc,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,CACF,iBAA8D;YAE9D,IAAI,OAAO,iBAAiB,KAAK,UAAU,EAAE,CAAC;gBAC5C,OAAO,CAAC,GAAG,IAAW,EAAE,EAAE;oBACxB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;wBAC/B,OAAO,iBAAiB,CAAC,GAAG,IAAI,CAAC,CAAC;oBACpC,CAAC;gBACH,CAAC,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACxC,iBAAiB,CAAC,IAAI,CACpB,CAAC,KAAK,EAAE,EAAE;oBACR,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;wBAC/B,OAAO,CAAC,KAAK,CAAC,CAAC;oBACjB,CAAC;gBACH,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;oBACR,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;wBAC/B,MAAM,CAAC,KAAK,CAAC,CAAC;oBAChB,CAAC;gBACH,CAAC,CACF,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;QAED,IAAI;YACF,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;YAC9C,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,UAAU,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACxD,eAAe,CAAC,KAAK,EAAE,CAAC;YAC1B,CAAC,CAAC;YACF,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACrD,OAAO,gBAAgB,CAAC,eAAe,CAAC,CAAC;QAC3C,CAAC;QAED,KAAK;YACH,OAAO,gBAAgB,CAAC,IAAI,eAAe,EAAE,CAAC,CAAC;QACjD,CAAC;QAED,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI;YACjB,OAAO,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC;IAEF,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,64 @@
1
+ import type { ActionOptions, Action } from "./types";
2
+ /**
3
+ * Creates an action - the core primitive for dispatch and notification.
4
+ *
5
+ * ## Two Patterns
6
+ *
7
+ * **Void action** (`action<T>()`):
8
+ * - For notifications/events (use "on" prefix naming: `onClick`, `onSubmit`)
9
+ * - Dispatching returns void
10
+ * - Listeners receive the payload
11
+ *
12
+ * **Action with handler** (`action<T, R>(handler)`):
13
+ * - For commands that return results (use verb naming: `addToCart`, `fetchUser`)
14
+ * - Handler executes first, returns result to caller
15
+ * - Listeners still receive the original payload (not the result)
16
+ *
17
+ * ## Key Features
18
+ *
19
+ * - **Thenable**: Actions implement PromiseLike, so `await action` waits for next dispatch
20
+ * - **Sealed**: With `{ sealed: true }`, action fires once then ignores subsequent calls
21
+ * - **Late subscribers**: On sealed actions, late `.on()` calls fire immediately with last payload
22
+ * - **Simple**: Handlers and listeners are plain functions - no context needed
23
+ *
24
+ * @example
25
+ * // Void action for notifications (use "on" prefix naming)
26
+ * const onClick = action<MouseEvent>();
27
+ * onClick.on((e) => {
28
+ * console.log("Clicked at:", e.clientX, e.clientY);
29
+ * });
30
+ * onClick(mouseEvent); // dispatch → void
31
+ *
32
+ * @example
33
+ * // Action with handler (use verb naming)
34
+ * const addToCart = action((product: Product) => {
35
+ * const item = createCartItem(product);
36
+ * setCart([...cart(), item]);
37
+ * return item; // returned to caller
38
+ * });
39
+ * const item = addToCart(product); // dispatch → CartItem
40
+ * addToCart.on((product) => {
41
+ * // Listeners receive payload, not result
42
+ * analytics.track('add_to_cart', product);
43
+ * });
44
+ *
45
+ * @example
46
+ * // Async handler (for cancellation, use abortable wrapper)
47
+ * const searchAbortable = abortable(async ({ signal, abort }, query: string) => {
48
+ * abort(); // Cancel previous
49
+ * const res = await fetch(`/search?q=${query}`, { signal });
50
+ * return res.json();
51
+ * });
52
+ * const search = action((query: string) => searchAbortable(query));
53
+ * const results = await search("hello");
54
+ *
55
+ * @example
56
+ * // Sealed action for one-time initialization
57
+ * const onAppReady = action<Config>({ sealed: true });
58
+ * onAppReady(config); // fires listeners
59
+ * onAppReady(config); // no-op (already sealed)
60
+ * onAppReady.on(cb); // late subscriber → cb fires immediately with last payload
61
+ */
62
+ export declare function action<T = void>(options?: ActionOptions): Action<T, void>;
63
+ export declare function action<H extends (payload: any) => any>(handler: H, options?: ActionOptions): Action<Parameters<H>[0], ReturnType<H>>;
64
+ //# sourceMappingURL=action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAuBrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,MAAM,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAC3E,wBAAgB,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,KAAK,GAAG,EACpD,OAAO,EAAE,CAAC,EACV,OAAO,CAAC,EAAE,aAAa,GACtB,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC"}
package/dist/action.js ADDED
@@ -0,0 +1,208 @@
1
+ import { batch } from "./batch";
2
+ import { defer } from "./defer";
3
+ /** No-op function returned for sealed action late subscribers */
4
+ const noop = () => { };
5
+ export function action(handlerOrOptions, maybeOptions) {
6
+ // ---------------------------------------------------------------------------
7
+ // Parse arguments: action(options?) or action(handler, options?)
8
+ // ---------------------------------------------------------------------------
9
+ const handler = typeof handlerOrOptions === "function" ? handlerOrOptions : undefined;
10
+ const options = typeof handlerOrOptions === "object"
11
+ ? handlerOrOptions
12
+ : maybeOptions ?? {};
13
+ // ---------------------------------------------------------------------------
14
+ // Internal State
15
+ // ---------------------------------------------------------------------------
16
+ /**
17
+ * Set of active listener entries.
18
+ * Lazily created on first `.on()` call to avoid allocation for actions
19
+ * that are only used for their handler (no listeners).
20
+ */
21
+ let listeners;
22
+ /** True after first dispatch if options.sealed is true */
23
+ let isSealed = false;
24
+ /** True after dispose() is called */
25
+ let disposed = false;
26
+ /**
27
+ * Stores the most recent dispatch payload and its associated promise.
28
+ * Used for:
29
+ * - `.latest()` to return last value without waiting
30
+ * - Late subscribers on sealed actions
31
+ * - `.fired()` check
32
+ */
33
+ let lastDispatch;
34
+ /**
35
+ * Deferred promise for the NEXT dispatch.
36
+ * Created lazily when someone awaits the action or calls `.then()`.
37
+ * Resolved when dispatch() is called, then cleared.
38
+ */
39
+ let nextDispatch;
40
+ // ---------------------------------------------------------------------------
41
+ // Internal Helpers
42
+ // ---------------------------------------------------------------------------
43
+ /**
44
+ * Returns a promise that resolves on the next dispatch.
45
+ * If already sealed, returns the last dispatch promise immediately.
46
+ */
47
+ const next = () => {
48
+ // Sealed actions always resolve to the last payload
49
+ if (isSealed && lastDispatch) {
50
+ return lastDispatch.promise;
51
+ }
52
+ // Create deferred if not exists (lazy)
53
+ nextDispatch = defer();
54
+ return nextDispatch.promise;
55
+ };
56
+ /**
57
+ * Invokes a single listener with the given payload.
58
+ *
59
+ * Simple function call - no context, no error handling.
60
+ * Errors propagate normally (can be caught by caller if needed).
61
+ */
62
+ const fire = (payload, entry) => {
63
+ if (!entry.active)
64
+ return;
65
+ entry.fn(payload);
66
+ };
67
+ // ---------------------------------------------------------------------------
68
+ // Dispatch Function (the callable part of the action)
69
+ // ---------------------------------------------------------------------------
70
+ /**
71
+ * Main dispatch function - called when you invoke the action: `myAction(payload)`.
72
+ *
73
+ * Execution order:
74
+ * 1. Execute handler (if exists) and capture result (wrapped in batch)
75
+ * 2. If sealed/disposed, return result early (skip listeners)
76
+ * 3. Resolve any pending `await action` promises
77
+ * 4. Store this dispatch as lastDispatch
78
+ * 5. If options.sealed, mark as sealed (subsequent dispatches become no-ops)
79
+ * 6. Fire all listeners with the payload
80
+ * 7. Return handler result (or undefined for void actions)
81
+ */
82
+ const dispatch = (payload) => {
83
+ // Step 1: Execute handler first (if exists), get result
84
+ // Handler runs in a batch to coalesce any atom updates it makes
85
+ let result = undefined;
86
+ if (handler) {
87
+ result = batch(() => handler(payload));
88
+ }
89
+ // Step 2: Early exit if sealed or disposed
90
+ // Note: handler still executes even if sealed (for actions with handlers)
91
+ // but listeners don't fire
92
+ if (isSealed || disposed)
93
+ return result;
94
+ // Step 3: Resolve any awaiting promises
95
+ nextDispatch?.resolve(payload);
96
+ // Step 4: Store this dispatch
97
+ lastDispatch = {
98
+ payload,
99
+ promise: nextDispatch?.promise ?? Promise.resolve(payload),
100
+ };
101
+ // Clear the deferred - next await will create a new one
102
+ nextDispatch = undefined;
103
+ // Step 5: Seal if configured
104
+ if (options.sealed) {
105
+ isSealed = true;
106
+ }
107
+ // Step 6: Fire all listeners
108
+ // Copy the set to avoid issues if listeners unsubscribe during iteration
109
+ if (listeners?.size) {
110
+ const copyListeners = [...listeners];
111
+ for (const entry of copyListeners) {
112
+ fire(payload, entry);
113
+ }
114
+ }
115
+ // Step 7: Return handler result
116
+ return result;
117
+ };
118
+ // ---------------------------------------------------------------------------
119
+ // Build the Action Object
120
+ // ---------------------------------------------------------------------------
121
+ /**
122
+ * The action object combines the dispatch function with additional methods.
123
+ * This uses Object.assign to make dispatch callable while adding properties.
124
+ */
125
+ const actionObj = Object.assign(dispatch, {
126
+ as() {
127
+ return dispatch;
128
+ },
129
+ /** Returns true if this action is sealed (no more dispatches will fire listeners) */
130
+ sealed: () => isSealed,
131
+ /** Returns true if this action has been dispatched at least once */
132
+ fired: () => !!lastDispatch,
133
+ /**
134
+ * Returns a promise that resolves with the last dispatched payload,
135
+ * or waits for the next dispatch if never fired.
136
+ *
137
+ * Unlike `await action` (which always waits for NEXT dispatch),
138
+ * `.latest()` returns immediately if there's already a value.
139
+ */
140
+ latest() {
141
+ if (lastDispatch) {
142
+ return lastDispatch.promise;
143
+ }
144
+ return next();
145
+ },
146
+ /**
147
+ * Subscribes a listener to this action.
148
+ *
149
+ * @param listener - Called on each dispatch with payload
150
+ * @returns Unsubscribe function
151
+ *
152
+ * Special behavior for sealed actions:
153
+ * - If already sealed AND fired, listener is called immediately
154
+ * with the last payload, and returns a no-op unsubscribe
155
+ */
156
+ on(listener) {
157
+ // Create entry for this subscription
158
+ const entry = {
159
+ fn: listener,
160
+ active: true,
161
+ };
162
+ // Late subscriber on sealed action: fire immediately
163
+ if (isSealed && lastDispatch) {
164
+ fire(lastDispatch.payload, entry);
165
+ return noop; // No need to unsubscribe - won't fire again
166
+ }
167
+ // Lazy create listeners Set on first subscription
168
+ if (!listeners)
169
+ listeners = new Set();
170
+ listeners.add(entry);
171
+ // Return unsubscribe function
172
+ return () => {
173
+ if (!entry.active)
174
+ return; // Already unsubscribed
175
+ entry.active = false;
176
+ listeners?.delete(entry);
177
+ };
178
+ },
179
+ /**
180
+ * Makes the action thenable (PromiseLike).
181
+ *
182
+ * This enables `await action` to wait for the next dispatch.
183
+ * Always waits for the NEXT dispatch, even if action has fired before.
184
+ * (Use `.latest()` to get the last value immediately if available)
185
+ */
186
+ then(onFulfilled, onRejected) {
187
+ return next().then(onFulfilled, onRejected);
188
+ },
189
+ /**
190
+ * Disposes the action, clearing all listeners and state.
191
+ *
192
+ * After dispose:
193
+ * - Dispatch still works (handler executes) but listeners don't fire
194
+ * - New `.on()` calls still work but listeners won't fire
195
+ * - `await action` will hang forever
196
+ */
197
+ dispose() {
198
+ if (disposed)
199
+ return;
200
+ disposed = true;
201
+ listeners?.clear();
202
+ lastDispatch = undefined;
203
+ nextDispatch = undefined;
204
+ },
205
+ });
206
+ return actionObj;
207
+ }
208
+ //# sourceMappingURL=action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.js","sourceRoot":"","sources":["../src/action.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=action.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.test.d.ts","sourceRoot":"","sources":["../src/action.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,189 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { action } from "./action";
3
+ describe("action", () => {
4
+ describe("void action (no handler)", () => {
5
+ describe("dispatch", () => {
6
+ it("should dispatch action to listeners", () => {
7
+ const onClick = action();
8
+ const listener = vi.fn();
9
+ onClick.on(listener);
10
+ onClick(42);
11
+ expect(listener).toHaveBeenCalledWith(42);
12
+ });
13
+ it("should support void payload", () => {
14
+ const onReset = action();
15
+ const listener = vi.fn();
16
+ onReset.on(listener);
17
+ onReset();
18
+ expect(listener).toHaveBeenCalled();
19
+ });
20
+ it("should return void for void actions", () => {
21
+ const onClick = action();
22
+ const result = onClick(42);
23
+ expect(result).toBeUndefined();
24
+ });
25
+ });
26
+ describe("fired", () => {
27
+ it("should track if action has been fired", () => {
28
+ const onClick = action();
29
+ expect(onClick.fired()).toBe(false);
30
+ expect(!onClick.fired()).toBe(true);
31
+ onClick(1);
32
+ expect(onClick.fired()).toBe(true);
33
+ expect(!onClick.fired()).toBe(false);
34
+ });
35
+ });
36
+ describe("sealed", () => {
37
+ it("should auto-seal after first dispatch", () => {
38
+ const onInit = action({ sealed: true });
39
+ expect(onInit.sealed()).toBe(false);
40
+ onInit("first");
41
+ expect(onInit.sealed()).toBe(true);
42
+ });
43
+ it("should ignore subsequent dispatches when sealed", () => {
44
+ const onInit = action({ sealed: true });
45
+ const listener = vi.fn();
46
+ onInit.on(listener);
47
+ onInit("first");
48
+ onInit("second");
49
+ expect(listener).toHaveBeenCalledTimes(1);
50
+ expect(listener).toHaveBeenCalledWith("first");
51
+ });
52
+ it("should call late listeners immediately with sealed value", () => {
53
+ const onInit = action({ sealed: true });
54
+ onInit("config");
55
+ const lateListener = vi.fn();
56
+ onInit.on(lateListener);
57
+ expect(lateListener).toHaveBeenCalledWith("config");
58
+ });
59
+ });
60
+ describe("latest", () => {
61
+ it("should resolve immediately if already dispatched", async () => {
62
+ const onData = action();
63
+ onData(42);
64
+ const result = await onData.latest();
65
+ expect(result).toBe(42);
66
+ });
67
+ it("should wait for dispatch if not fired", async () => {
68
+ const onData = action();
69
+ const promise = onData.latest();
70
+ setTimeout(() => onData(100), 10);
71
+ const result = await promise;
72
+ expect(result).toBe(100);
73
+ });
74
+ });
75
+ describe("thenable (await action)", () => {
76
+ it("should wait for next dispatch", async () => {
77
+ const onClick = action();
78
+ const promise = onClick.then((value) => value.toUpperCase());
79
+ setTimeout(() => onClick("hello"), 10);
80
+ const result = await promise;
81
+ expect(result).toBe("HELLO");
82
+ });
83
+ it("should resolve immediately if sealed", async () => {
84
+ const onInit = action({ sealed: true });
85
+ onInit("sealed-value");
86
+ const result = await onInit;
87
+ expect(result).toBe("sealed-value");
88
+ });
89
+ });
90
+ describe("on listener", () => {
91
+ it("should return unsubscribe function", () => {
92
+ const onClick = action();
93
+ const listener = vi.fn();
94
+ const unsubscribe = onClick.on(listener);
95
+ onClick(1);
96
+ expect(listener).toHaveBeenCalledTimes(1);
97
+ unsubscribe();
98
+ onClick(2);
99
+ expect(listener).toHaveBeenCalledTimes(1);
100
+ });
101
+ });
102
+ });
103
+ describe("action with handler", () => {
104
+ it("should execute handler and return result", () => {
105
+ const addToCart = action((product) => {
106
+ return {
107
+ ...product,
108
+ cartItemId: `cart-${product.id}`,
109
+ addedAt: Date.now(),
110
+ };
111
+ });
112
+ const result = addToCart({ id: "prod-1", price: 100 });
113
+ expect(result.cartItemId).toBe("cart-prod-1");
114
+ expect(result.id).toBe("prod-1");
115
+ expect(result.price).toBe(100);
116
+ expect(result.addedAt).toBeTypeOf("number");
117
+ });
118
+ it("should notify listeners with original payload", () => {
119
+ const listener = vi.fn();
120
+ const addToCart = action((product) => {
121
+ return { ...product, processed: true };
122
+ });
123
+ addToCart.on(listener);
124
+ addToCart({ id: "prod-1" });
125
+ // Listener receives original payload, not the result
126
+ expect(listener).toHaveBeenCalledWith({ id: "prod-1" });
127
+ });
128
+ it("should execute handler synchronously", () => {
129
+ const order = [];
130
+ const doSomething = action((value) => {
131
+ order.push(`handler:${value}`);
132
+ return value.toUpperCase();
133
+ });
134
+ doSomething.on((value) => {
135
+ order.push(`listener:${value}`);
136
+ });
137
+ const result = doSomething("test");
138
+ expect(result).toBe("TEST");
139
+ expect(order).toEqual(["handler:test", "listener:test"]);
140
+ });
141
+ it("should still support await for payload", async () => {
142
+ const addToCart = action((product) => {
143
+ return { ...product, cartItemId: `cart-${product.id}` };
144
+ });
145
+ const promise = addToCart.then((payload) => payload.id);
146
+ setTimeout(() => addToCart({ id: "prod-1" }), 10);
147
+ const result = await promise;
148
+ expect(result).toBe("prod-1"); // Gets payload.id, not result
149
+ });
150
+ it("should support async handlers", async () => {
151
+ const fetchUser = action(async (id) => {
152
+ // Simulating async operation
153
+ await new Promise((r) => setTimeout(r, 10));
154
+ return { id, name: `User ${id}` };
155
+ });
156
+ const resultPromise = fetchUser("123");
157
+ expect(resultPromise).toBeInstanceOf(Promise);
158
+ const result = await resultPromise;
159
+ expect(result).toEqual({ id: "123", name: "User 123" });
160
+ });
161
+ it("should execute handler even when sealed", () => {
162
+ let handlerCallCount = 0;
163
+ const onInit = action((value) => {
164
+ handlerCallCount++;
165
+ return value.toUpperCase();
166
+ }, { sealed: true });
167
+ const result1 = onInit("first");
168
+ expect(result1).toBe("FIRST");
169
+ expect(handlerCallCount).toBe(1);
170
+ // Handler still executes even when sealed
171
+ const result2 = onInit("second");
172
+ expect(result2).toBe("SECOND");
173
+ expect(handlerCallCount).toBe(2);
174
+ });
175
+ it("should not notify listeners after sealed", () => {
176
+ const listener = vi.fn();
177
+ const onInit = action((value) => value.toUpperCase(), {
178
+ sealed: true,
179
+ });
180
+ onInit.on(listener);
181
+ onInit("first");
182
+ onInit("second");
183
+ // Listener only called once (sealed after first)
184
+ expect(listener).toHaveBeenCalledTimes(1);
185
+ expect(listener).toHaveBeenCalledWith("first");
186
+ });
187
+ });
188
+ });
189
+ //# sourceMappingURL=action.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.test.js","sourceRoot":"","sources":["../src/action.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Abortable type guard.
3
+ *
4
+ * Extracted to a separate file to avoid circular dependencies
5
+ * between safe.ts and abortable.ts.
6
+ */
7
+ /**
8
+ * Symbol used to identify Abortable functions.
9
+ * @internal
10
+ */
11
+ export declare const abortableSymbol: unique symbol;
12
+ /**
13
+ * Check if a value is an Abortable function.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * if (isAbortable(fn)) {
18
+ * fn.withSignal(signal, ...args);
19
+ * }
20
+ * ```
21
+ */
22
+ export declare function isAbortable(fn: unknown): fn is {
23
+ withSignal: Function;
24
+ };
25
+ //# sourceMappingURL=abortable-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abortable-guard.d.ts","sourceRoot":"","sources":["../../src/async/abortable-guard.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;GAGG;AACH,eAAO,MAAM,eAAe,eAA0B,CAAC;AAMvD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI;IAAE,UAAU,EAAE,QAAQ,CAAA;CAAE,CAMvE"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Abortable type guard.
3
+ *
4
+ * Extracted to a separate file to avoid circular dependencies
5
+ * between safe.ts and abortable.ts.
6
+ */
7
+ // =============================================================================
8
+ // SYMBOL
9
+ // =============================================================================
10
+ /**
11
+ * Symbol used to identify Abortable functions.
12
+ * @internal
13
+ */
14
+ export const abortableSymbol = Symbol.for("abortable");
15
+ // =============================================================================
16
+ // TYPE GUARD
17
+ // =============================================================================
18
+ /**
19
+ * Check if a value is an Abortable function.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * if (isAbortable(fn)) {
24
+ * fn.withSignal(signal, ...args);
25
+ * }
26
+ * ```
27
+ */
28
+ export function isAbortable(fn) {
29
+ return (typeof fn === "function" &&
30
+ abortableSymbol in fn &&
31
+ fn[abortableSymbol] === true);
32
+ }
33
+ //# sourceMappingURL=abortable-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abortable-guard.js","sourceRoot":"","sources":["../../src/async/abortable-guard.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,gFAAgF;AAChF,SAAS;AACT,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAEvD,gFAAgF;AAChF,aAAa;AACb,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,EAAW;IACrC,OAAO,CACL,OAAO,EAAE,KAAK,UAAU;QACxB,eAAe,IAAI,EAAE;QACpB,EAAU,CAAC,eAAe,CAAC,KAAK,IAAI,CACtC,CAAC;AACJ,CAAC"}