@figliolia/galena 2.3.5 → 3.0.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.
Files changed (106) hide show
  1. package/README.md +172 -476
  2. package/dist/Galena.cjs +102 -0
  3. package/dist/Galena.d.cts +80 -0
  4. package/dist/Galena.d.cts.map +1 -0
  5. package/dist/Galena.d.mts +80 -0
  6. package/dist/Galena.d.mts.map +1 -0
  7. package/dist/Galena.mjs +103 -0
  8. package/dist/Galena.mjs.map +1 -0
  9. package/dist/Logger.cjs +46 -0
  10. package/dist/Logger.d.cts +35 -0
  11. package/dist/Logger.d.cts.map +1 -0
  12. package/dist/Logger.d.mts +35 -0
  13. package/dist/Logger.d.mts.map +1 -0
  14. package/dist/Logger.mjs +48 -0
  15. package/dist/Logger.mjs.map +1 -0
  16. package/dist/Middleware.cjs +62 -0
  17. package/dist/Middleware.d.cts +65 -0
  18. package/dist/Middleware.d.cts.map +1 -0
  19. package/dist/Middleware.d.mts +65 -0
  20. package/dist/Middleware.d.mts.map +1 -0
  21. package/dist/Middleware.mjs +64 -0
  22. package/dist/Middleware.mjs.map +1 -0
  23. package/dist/Profiler.cjs +41 -0
  24. package/dist/Profiler.d.cts +31 -0
  25. package/dist/Profiler.d.cts.map +1 -0
  26. package/dist/Profiler.d.mts +31 -0
  27. package/dist/Profiler.d.mts.map +1 -0
  28. package/dist/Profiler.mjs +43 -0
  29. package/dist/Profiler.mjs.map +1 -0
  30. package/dist/State.cjs +147 -0
  31. package/dist/State.d.cts +114 -0
  32. package/dist/State.d.cts.map +1 -0
  33. package/dist/State.d.mts +114 -0
  34. package/dist/State.d.mts.map +1 -0
  35. package/dist/State.mjs +148 -0
  36. package/dist/State.mjs.map +1 -0
  37. package/dist/index.cjs +13 -0
  38. package/dist/index.d.cts +7 -0
  39. package/dist/index.d.mts +7 -0
  40. package/dist/index.mjs +6 -0
  41. package/dist/types.d.cts +16 -0
  42. package/dist/types.d.cts.map +1 -0
  43. package/dist/types.d.mts +16 -0
  44. package/dist/types.d.mts.map +1 -0
  45. package/media/Logging.png +0 -0
  46. package/media/Profiling.png +0 -0
  47. package/package.json +38 -59
  48. package/src/Galena.ts +120 -0
  49. package/src/{Middlewares/Logger.ts → Logger.ts} +15 -14
  50. package/src/Middleware.ts +62 -0
  51. package/src/Profiler.ts +53 -0
  52. package/src/State.ts +167 -0
  53. package/src/index.ts +6 -3
  54. package/src/types.ts +28 -0
  55. package/dist/cjs/Galena/Galena.js +0 -223
  56. package/dist/cjs/Galena/Guards.js +0 -40
  57. package/dist/cjs/Galena/Scheduler.js +0 -84
  58. package/dist/cjs/Galena/State.js +0 -314
  59. package/dist/cjs/Galena/index.js +0 -22
  60. package/dist/cjs/Galena/types.js +0 -9
  61. package/dist/cjs/Middleware/Middleware.js +0 -46
  62. package/dist/cjs/Middleware/index.js +0 -20
  63. package/dist/cjs/Middleware/types.js +0 -8
  64. package/dist/cjs/Middlewares/Logger.js +0 -51
  65. package/dist/cjs/Middlewares/Profiler.js +0 -38
  66. package/dist/cjs/Middlewares/index.js +0 -7
  67. package/dist/cjs/index.js +0 -19
  68. package/dist/cjs/package.json +0 -3
  69. package/dist/mjs/Galena/Galena.js +0 -218
  70. package/dist/mjs/Galena/Guards.js +0 -36
  71. package/dist/mjs/Galena/Scheduler.js +0 -79
  72. package/dist/mjs/Galena/State.js +0 -313
  73. package/dist/mjs/Galena/index.js +0 -3
  74. package/dist/mjs/Galena/types.js +0 -6
  75. package/dist/mjs/Middleware/Middleware.js +0 -42
  76. package/dist/mjs/Middleware/index.js +0 -2
  77. package/dist/mjs/Middleware/types.js +0 -5
  78. package/dist/mjs/Middlewares/Logger.js +0 -44
  79. package/dist/mjs/Middlewares/Profiler.js +0 -35
  80. package/dist/mjs/Middlewares/index.js +0 -2
  81. package/dist/mjs/index.js +0 -3
  82. package/dist/mjs/package.json +0 -4
  83. package/dist/types/Galena/Galena.d.ts +0 -160
  84. package/dist/types/Galena/Guards.d.ts +0 -29
  85. package/dist/types/Galena/Scheduler.d.ts +0 -51
  86. package/dist/types/Galena/State.d.ts +0 -235
  87. package/dist/types/Galena/index.d.ts +0 -3
  88. package/dist/types/Galena/types.d.ts +0 -13
  89. package/dist/types/Middleware/Middleware.d.ts +0 -43
  90. package/dist/types/Middleware/index.d.ts +0 -2
  91. package/dist/types/Middleware/types.d.ts +0 -4
  92. package/dist/types/Middlewares/Logger.d.ts +0 -27
  93. package/dist/types/Middlewares/Profiler.d.ts +0 -22
  94. package/dist/types/Middlewares/index.d.ts +0 -2
  95. package/dist/types/index.d.ts +0 -3
  96. package/src/Galena/Galena.ts +0 -252
  97. package/src/Galena/Guards.ts +0 -49
  98. package/src/Galena/Scheduler.ts +0 -85
  99. package/src/Galena/State.ts +0 -344
  100. package/src/Galena/index.ts +0 -3
  101. package/src/Galena/types.ts +0 -18
  102. package/src/Middleware/Middleware.ts +0 -45
  103. package/src/Middleware/index.ts +0 -2
  104. package/src/Middleware/types.ts +0 -4
  105. package/src/Middlewares/Profiler.ts +0 -41
  106. package/src/Middlewares/index.ts +0 -2
@@ -1,22 +0,0 @@
1
- import type { State } from "../Galena/State";
2
- import { Middleware } from "../Middleware/Middleware";
3
- /**
4
- * Profiler
5
- *
6
- * A logger for state transitions exceeding a given threshold
7
- * for duration:
8
- *
9
- * ```typescript
10
- * const State = new Galena([new Profiler()]);
11
- * // if using isolated state instances:
12
- * const MyState = new State(...args);
13
- * MyState.registerMiddleware(new Profiler())
14
- * ```
15
- */
16
- export declare class Profiler extends Middleware {
17
- private threshold;
18
- private startTime;
19
- constructor(threshold?: number);
20
- onBeforeUpdate(_: State): void;
21
- onUpdate(nextState: State): void;
22
- }
@@ -1,2 +0,0 @@
1
- export { Logger } from "./Logger";
2
- export { Profiler } from "./Profiler";
@@ -1,3 +0,0 @@
1
- export * from "./Galena";
2
- export * from "./Middleware";
3
- export * from "./Middlewares";
@@ -1,252 +0,0 @@
1
- import { AutoIncrementingID } from "@figliolia/event-emitter";
2
- import { State } from "Galena/State";
3
- import type { Middleware } from "Middleware/Middleware";
4
- import { Guards } from "./Guards";
5
- import type { Subscription, SubscriptionTuple } from "./types";
6
-
7
- /**
8
- * ## Galena
9
- *
10
- * A performant global state solution that scales
11
- *
12
- * ### Creating State
13
- *
14
- * ```typescript
15
- * // AppState.ts
16
- * import { Galena } from "@figliolia/galena";
17
- *
18
- * const AppState = new Galena([...middleware]);
19
- *
20
- * const NavigationState = AppState.composeState("navigation", {
21
- * currentRoute: "/",
22
- * userID: "12345",
23
- * permittedRoutes: ["/*"]
24
- * });
25
- * ```
26
- *
27
- * ### Subscribing to State Changes
28
- * #### Using the Galena Instance
29
- * ```typescript
30
- * import { AppState } from "./AppState";
31
- *
32
- * AppState.subscribe(appState => {
33
- * const navState = appState.get("navigation");
34
- * const { currentRoute } = navState.state;
35
- * // do something with state changes!
36
- * });
37
- * ```
38
- * #### Using the State Instance
39
- * ```typescript
40
- * NavigationState.subscribe(navigation => {
41
- * const { currentRoute } = navigation
42
- * // do something with state changes!
43
- * });
44
- * ```
45
- *
46
- * #### Using Global Subscriptions
47
- * ```typescript
48
- * NavigationState.subscribeAll(nextState => {
49
- * const { currentRoute } = nextState.navigation
50
- * // do something with state changes!
51
- * });
52
- * ```
53
- *
54
- * ### Mutating State
55
- * ```typescript
56
- * NavigationState.update(state => {
57
- * state.currentRoute = "/profile";
58
- * // You can mutate state without creating new objects!
59
- * });
60
- * ```
61
- */
62
- export class Galena<
63
- T extends Record<string, State<any>> = Record<string, State<any>>,
64
- > extends Guards {
65
- public readonly state = {} as T;
66
- private readonly middleware: Middleware[] = [];
67
- private readonly IDs = new AutoIncrementingID();
68
- private readonly subscriptions = new Map<string, SubscriptionTuple[]>();
69
- constructor(middleware: Middleware[] = []) {
70
- super();
71
- this.middleware = middleware;
72
- }
73
-
74
- /**
75
- * Compose State
76
- *
77
- * Creates a new `State` instance and returns it. Your new state
78
- * becomes immediately available on your `Galena` instance and
79
- * is wired into your middleware. All existing subscriptions to
80
- * state will automatically receive updates when your new unit of
81
- * state updates
82
- */
83
- public composeState<S extends Record<string, any>, M extends typeof State<S>>(
84
- name: string,
85
- initialState: S,
86
- Model?: M,
87
- ) {
88
- this.guardDuplicateStates(name, this.state);
89
- const StateModel = Model || State<S>;
90
- const state = new StateModel(name, initialState);
91
- state.registerMiddleware(...this.middleware);
92
- this.mutable[name] = state;
93
- this.reIndexSubscriptions(name);
94
- return state as InstanceType<M>;
95
- }
96
-
97
- /**
98
- * Get State
99
- *
100
- * Returns a mutable state instance
101
- */
102
- public getState() {
103
- return this.state as Readonly<T>;
104
- }
105
-
106
- /**
107
- * Get
108
- *
109
- * Returns a unit of `State` by name
110
- */
111
- public get<K extends Extract<keyof T, string>>(name: K): T[K] {
112
- this.warnForUndefinedStates(name, this.state);
113
- return this.state[name];
114
- }
115
-
116
- /**
117
- * Mutable
118
- *
119
- * Returns a mutable state instance
120
- */
121
- private get mutable() {
122
- return this.state as Record<string, State>;
123
- }
124
-
125
- /**
126
- * Update
127
- *
128
- * Runs a mutation on the specified unit of state
129
- */
130
- public update<K extends Extract<keyof T, string>>(
131
- name: K,
132
- mutation: Parameters<T[K]["update"]>["0"],
133
- ) {
134
- return this.get(name).update(mutation);
135
- }
136
-
137
- /**
138
- * Background Update
139
- *
140
- * Runs a higher priority mutation on the specified unit of
141
- * state
142
- */
143
- public backgroundUpdate<K extends Extract<keyof T, string>>(
144
- name: K,
145
- mutation: Parameters<T[K]["backgroundUpdate"]>["0"],
146
- ) {
147
- return this.get(name).backgroundUpdate(mutation);
148
- }
149
-
150
- /**
151
- * Priority Update
152
- *
153
- * Runs an immediate priority mutation on the specified unit
154
- * of state
155
- */
156
- public priorityUpdate<K extends Extract<keyof T, string>>(
157
- name: K,
158
- mutation: Parameters<T[K]["priorityUpdate"]>["0"],
159
- ) {
160
- return this.get(name).priorityUpdate(mutation);
161
- }
162
-
163
- /**
164
- * Subscribe
165
- *
166
- * Given the name of a unit of state, this method registers
167
- * a subscription on the target state instance. The callback
168
- * you provide will execute each time state changes. Returns
169
- * a unique identifier for your subscription. To clean up your
170
- * subscription, call `Galena.unsubscribe()` with the ID returned
171
- * by this method
172
- */
173
- public subscribe<K extends Extract<keyof T, string>>(
174
- name: K,
175
- callback: Parameters<T[K]["subscribe"]>["0"],
176
- ) {
177
- return this.get(name).subscribe(callback);
178
- }
179
-
180
- /**
181
- * Unsubscribe
182
- *
183
- * Given a subscription ID returned from the `subscribe` method,
184
- * this method removes and cleans up the corresponding subscription
185
- */
186
- public unsubscribe<K extends Extract<keyof T, string>>(name: K, ID: string) {
187
- return this.get(name).unsubscribe(ID);
188
- }
189
-
190
- /**
191
- * Subscribe All
192
- *
193
- * Registers a callback on each registered `State` instance and
194
- * is invoked each time your state changes. Using `Galena`'s
195
- * `subscribeAll` method, although performant, can be less
196
- * performant than subscribing directly to a target `State`
197
- * instance using `Galena.subscribe()`. To clean up your
198
- * subscription, call `Galena.unsubscribeAll()` with the ID
199
- * returned
200
- */
201
- public subscribeAll(callback: Subscription<T>) {
202
- const stateSubscriptions: [state: string, ID: string][] = [];
203
- for (const key in this.state) {
204
- stateSubscriptions.push([
205
- key,
206
- this.state[key].subscribe(() => {
207
- void callback(this.state);
208
- }),
209
- ]);
210
- }
211
- const subscriptionID = this.IDs.get();
212
- this.subscriptions.set(subscriptionID, stateSubscriptions);
213
- return subscriptionID;
214
- }
215
-
216
- /**
217
- * Unsubscribe
218
- *
219
- * Given a subscription ID returned from the `subscribeAll()` method,
220
- * this method removes and cleans up the corresponding subscription
221
- */
222
- public unsubscribeAll(ID: string) {
223
- const IDs = this.subscriptions.get(ID);
224
- if (IDs) {
225
- for (const [state, ID] of IDs) {
226
- this.state[state].unsubscribe(ID);
227
- }
228
- this.subscriptions.delete(ID);
229
- }
230
- }
231
-
232
- /**
233
- * ReIndex Subscriptions
234
- *
235
- * When units of state are created lazily, this method updates
236
- * each existing subscription to receive mutations occurring on
237
- * recently created `State` instances that post-date prior
238
- * subscriptions
239
- */
240
- private reIndexSubscriptions(name: string) {
241
- for (const [ID, unitSubscriptions] of this.subscriptions) {
242
- const [stateName, listenerID] = unitSubscriptions[0];
243
- const subscriptions =
244
- this.state[stateName]["emitter"].storage.get(stateName);
245
- const listener = subscriptions?.storage?.get?.(listenerID);
246
- if (listener) {
247
- unitSubscriptions.push([name, this.state[name].subscribe(listener)]);
248
- this.subscriptions.set(ID, unitSubscriptions);
249
- }
250
- }
251
- }
252
- }
@@ -1,49 +0,0 @@
1
- import type { State } from "./State";
2
-
3
- /**
4
- * Guards
5
- *
6
- * Development-only warnings and runtime errors designed to
7
- * guard developers against possible pitfalls when using
8
- * Galena. This interface provides composable error and
9
- * warning methods that can be used to prevent invalid usage
10
- * of the library
11
- */
12
- export class Guards {
13
- /**
14
- * Warn For Undefined States
15
- *
16
- * In Galena, it's normal to lazy initialize a unit of state
17
- * in attached to a `Galena` instance. This warning lets
18
- * developers know that they are attempting to manipulate a
19
- * unit of state that has not yet been initialized
20
- */
21
- protected warnForUndefinedStates<T extends Record<string, State<any>>>(
22
- name: string,
23
- state: T,
24
- ) {
25
- if (!(name in state)) {
26
- console.warn(
27
- `A unit of state with the name "${name}" does not yet exist on this Galena instance. If this is expected, you can ignore this warning`,
28
- );
29
- }
30
- }
31
-
32
- /**
33
- * Guard Duplicate States
34
- *
35
- * Throws an error if a developer attempts to create
36
- * more than one state with the same name on a single
37
- * `Galena` instance
38
- */
39
- protected guardDuplicateStates<T extends Record<string, State<any>>>(
40
- name: string,
41
- state: T,
42
- ) {
43
- if (name in state) {
44
- console.warn(
45
- `A unit of state with the name "${name}" already exists on this Galena instance. Please re-name this new unit of state to something unique`,
46
- );
47
- }
48
- }
49
- }
@@ -1,85 +0,0 @@
1
- import type { Task } from "./types";
2
- import { Priority } from "./types";
3
-
4
- /**
5
- * Scheduler
6
- *
7
- * Scheduling dispatched events to state consumers is how Galena
8
- * out-performs just about every state management library out there.
9
- * The scheduler offers the ability to dispatch state updates on 3
10
- * priorities:
11
- *
12
- * 1. Immediate - Immediate synchronous task execution and propagation of
13
- * changes to consumers
14
- * 2. Microtask - Immediate task execution and scheduled propagation of
15
- * changes to consumers
16
- * 3. Batched - Immediate task execution and batched propagation of
17
- * changes to consumers
18
- *
19
- * This module manages the propagation of changes to State consumers
20
- * by implementing the three priorities outlined above
21
- */
22
- export class Scheduler<T extends Task = Task> {
23
- private task: null | T = null;
24
- private schedule: ReturnType<typeof setTimeout> | null = null;
25
- constructor() {
26
- this.executeTasks = this.executeTasks.bind(this);
27
- }
28
-
29
- /**
30
- * Schedule Task
31
- *
32
- * Given a task (the emission of state changes to consumers)
33
- * and a priority, this method executes the task on the priority
34
- * level specified
35
- */
36
- protected scheduleTask(task: T, priority: Priority) {
37
- this.task = task;
38
- switch (priority) {
39
- case Priority.IMMEDIATE:
40
- return this.executeTasks();
41
- case Priority.MICROTASK:
42
- return Promise.resolve().then(() => {
43
- return this.executeTasks();
44
- });
45
- case Priority.BATCHED:
46
- default:
47
- if (!this.schedule) {
48
- this.createSchedule();
49
- }
50
- }
51
- }
52
-
53
- /**
54
- * Create Schedule
55
- *
56
- * Schedules the execution of the current task after 5 milliseconds
57
- */
58
- private createSchedule() {
59
- this.clearSchedule();
60
- this.schedule = setTimeout(this.executeTasks, 5);
61
- }
62
-
63
- /**
64
- * Clear Schedule
65
- *
66
- * Clears the schedule if it exists
67
- */
68
- private clearSchedule() {
69
- if (this.schedule !== null) {
70
- clearTimeout(this.schedule);
71
- this.schedule = null;
72
- }
73
- }
74
-
75
- /**
76
- * Execute Tasks
77
- *
78
- * Clears the schedule if it exists and executes the current task
79
- */
80
- private executeTasks() {
81
- this.clearSchedule();
82
- this.task?.();
83
- this.task = null;
84
- }
85
- }