@cplieger/actions 1.0.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.
Files changed (50) hide show
  1. package/.editorconfig +19 -0
  2. package/.htmlvalidate.json +28 -0
  3. package/.stylelintrc.json +67 -0
  4. package/LICENSE +692 -0
  5. package/README.md +176 -0
  6. package/dist/src/api.d.ts +56 -0
  7. package/dist/src/api.js +158 -0
  8. package/dist/src/cleanup.d.ts +11 -0
  9. package/dist/src/cleanup.js +63 -0
  10. package/dist/src/debounce.d.ts +28 -0
  11. package/dist/src/debounce.js +91 -0
  12. package/dist/src/define-helpers.d.ts +20 -0
  13. package/dist/src/define-helpers.js +83 -0
  14. package/dist/src/define.d.ts +14 -0
  15. package/dist/src/define.js +627 -0
  16. package/dist/src/error.d.ts +42 -0
  17. package/dist/src/error.js +174 -0
  18. package/dist/src/index.d.ts +17 -0
  19. package/dist/src/index.js +22 -0
  20. package/dist/src/loading.d.ts +15 -0
  21. package/dist/src/loading.js +114 -0
  22. package/dist/src/notifier.d.ts +21 -0
  23. package/dist/src/notifier.js +21 -0
  24. package/dist/src/poll.d.ts +23 -0
  25. package/dist/src/poll.js +120 -0
  26. package/dist/src/registry.d.ts +18 -0
  27. package/dist/src/registry.js +210 -0
  28. package/dist/src/retry.d.ts +9 -0
  29. package/dist/src/retry.js +78 -0
  30. package/dist/src/transport.d.ts +46 -0
  31. package/dist/src/transport.js +70 -0
  32. package/dist/src/types.d.ts +157 -0
  33. package/dist/src/types.js +7 -0
  34. package/eslint.config.base.mjs +186 -0
  35. package/jsr.json +22 -0
  36. package/package.json +41 -0
  37. package/src/api.ts +209 -0
  38. package/src/cleanup.ts +76 -0
  39. package/src/debounce.ts +128 -0
  40. package/src/define-helpers.ts +97 -0
  41. package/src/define.ts +699 -0
  42. package/src/error.ts +184 -0
  43. package/src/index.ts +60 -0
  44. package/src/loading.ts +149 -0
  45. package/src/notifier.ts +41 -0
  46. package/src/poll.ts +150 -0
  47. package/src/registry.ts +225 -0
  48. package/src/retry.ts +80 -0
  49. package/src/transport.ts +112 -0
  50. package/src/types.ts +198 -0
@@ -0,0 +1,97 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Pure utility helpers extracted from define.ts — independently testable
3
+ // without instantiating the full action framework.
4
+ // ---------------------------------------------------------------------------
5
+
6
+ import type { NotificationSpec } from "./types.js";
7
+
8
+ /** Invoke a callback safely — errors are caught and logged without
9
+ * disrupting the dispatch lifecycle. */
10
+ export function safeInvoke(actionName: string, hookName: string, fn: () => void): void {
11
+ try {
12
+ fn();
13
+ } catch (e) {
14
+ console.error(`[actions] ${hookName} callback for ${actionName} threw`, e);
15
+ }
16
+ }
17
+
18
+ /** Monotonic counter for symbol identity in dedupe keys. Symbols with
19
+ * the same description are distinct values but String(sym) is identical,
20
+ * so we assign each unique symbol a stable numeric ID. */
21
+ let _symbolCounter = 0;
22
+ export const _symbolMap = new Map<symbol, number>();
23
+ export function symbolId(sym: symbol): number {
24
+ let id = _symbolMap.get(sym);
25
+ if (id === undefined) {
26
+ id = ++_symbolCounter;
27
+ _symbolMap.set(sym, id);
28
+ }
29
+ return id;
30
+ }
31
+
32
+ /** Reset symbol state — test-only. */
33
+ export function _resetSymbols(): void {
34
+ _symbolCounter = 0;
35
+ _symbolMap.clear();
36
+ }
37
+
38
+ /** Defensive JSON.stringify — falls back to String(args) on cycles
39
+ * or non-serializable values (DOM elements, functions). Used by
40
+ * the default dedupe key computation. */
41
+ export function safeStringify(args: unknown): string {
42
+ if (args === undefined) {
43
+ return "undefined";
44
+ }
45
+ if (args === null || typeof args === "number" || typeof args === "boolean") {
46
+ return String(args);
47
+ }
48
+ if (typeof args === "string") {
49
+ return JSON.stringify(args);
50
+ }
51
+ if (typeof args === "bigint") {
52
+ return `${String(args)}n`;
53
+ }
54
+ if (typeof args === "symbol") {
55
+ return `@@sym${String(symbolId(args))}`;
56
+ }
57
+ try {
58
+ return JSON.stringify(args, (_key, value: unknown) =>
59
+ value === undefined ? "__undef__" : value,
60
+ );
61
+ } catch {
62
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string -- intentional fallback for cyclic objects
63
+ return String(args);
64
+ }
65
+ }
66
+
67
+ /** Resolve a NotificationSpec to its message string. Returns null when
68
+ * the spec is `false` (suppressed) or undefined and no fallback. */
69
+ export function resolveNotification<TArgs, TPayload>(
70
+ spec: NotificationSpec<TArgs, TPayload> | undefined,
71
+ args: TArgs,
72
+ payload: TPayload,
73
+ fallback?: string,
74
+ ): string | null {
75
+ if (spec === false) {
76
+ return null;
77
+ }
78
+ if (spec === undefined) {
79
+ return fallback ?? null;
80
+ }
81
+ if (typeof spec === "string") {
82
+ return spec;
83
+ }
84
+ return spec(args, payload);
85
+ }
86
+
87
+ /** @deprecated Use {@link resolveNotification} instead. */
88
+ export const resolveToast = resolveNotification;
89
+
90
+ /** Build a default error notification prefix from the action name.
91
+ * Converts "chat.delete" -> "Delete failed". */
92
+ export function defaultErrorPrefix(name: string): string {
93
+ const parts = name.split(".");
94
+ const tail = parts[parts.length - 1] ?? name;
95
+ const readable = tail.replace(/[_-]/g, " ");
96
+ return readable.charAt(0).toUpperCase() + readable.slice(1) + " failed";
97
+ }