@figliolia/galena 2.3.5 → 3.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 (144) 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/GalenaProvider.cjs +50 -0
  10. package/dist/GalenaProvider.d.cts +35 -0
  11. package/dist/GalenaProvider.d.cts.map +1 -0
  12. package/dist/GalenaProvider.d.mts +35 -0
  13. package/dist/GalenaProvider.d.mts.map +1 -0
  14. package/dist/GalenaProvider.mjs +52 -0
  15. package/dist/GalenaProvider.mjs.map +1 -0
  16. package/dist/Logger.cjs +46 -0
  17. package/dist/Logger.d.cts +35 -0
  18. package/dist/Logger.d.cts.map +1 -0
  19. package/dist/Logger.d.mts +35 -0
  20. package/dist/Logger.d.mts.map +1 -0
  21. package/dist/Logger.mjs +48 -0
  22. package/dist/Logger.mjs.map +1 -0
  23. package/dist/Middleware.cjs +62 -0
  24. package/dist/Middleware.d.cts +65 -0
  25. package/dist/Middleware.d.cts.map +1 -0
  26. package/dist/Middleware.d.mts +65 -0
  27. package/dist/Middleware.d.mts.map +1 -0
  28. package/dist/Middleware.mjs +64 -0
  29. package/dist/Middleware.mjs.map +1 -0
  30. package/dist/Profiler.cjs +41 -0
  31. package/dist/Profiler.d.cts +31 -0
  32. package/dist/Profiler.d.cts.map +1 -0
  33. package/dist/Profiler.d.mts +31 -0
  34. package/dist/Profiler.d.mts.map +1 -0
  35. package/dist/Profiler.mjs +43 -0
  36. package/dist/Profiler.mjs.map +1 -0
  37. package/dist/State.cjs +147 -0
  38. package/dist/State.d.cts +114 -0
  39. package/dist/State.d.cts.map +1 -0
  40. package/dist/State.d.mts +114 -0
  41. package/dist/State.d.mts.map +1 -0
  42. package/dist/State.mjs +148 -0
  43. package/dist/State.mjs.map +1 -0
  44. package/dist/commonHooks.cjs +17 -0
  45. package/dist/commonHooks.mjs +18 -0
  46. package/dist/commonHooks.mjs.map +1 -0
  47. package/dist/connect-multi.cjs +42 -0
  48. package/dist/connect-multi.d.cts +10 -0
  49. package/dist/connect-multi.d.cts.map +1 -0
  50. package/dist/connect-multi.d.mts +10 -0
  51. package/dist/connect-multi.d.mts.map +1 -0
  52. package/dist/connect-multi.mjs +44 -0
  53. package/dist/connect-multi.mjs.map +1 -0
  54. package/dist/connect.cjs +41 -0
  55. package/dist/connect.d.cts +9 -0
  56. package/dist/connect.d.cts.map +1 -0
  57. package/dist/connect.d.mts +9 -0
  58. package/dist/connect.d.mts.map +1 -0
  59. package/dist/connect.mjs +43 -0
  60. package/dist/connect.mjs.map +1 -0
  61. package/dist/createUseState.cjs +47 -0
  62. package/dist/createUseState.d.cts +46 -0
  63. package/dist/createUseState.d.cts.map +1 -0
  64. package/dist/createUseState.d.mts +46 -0
  65. package/dist/createUseState.d.mts.map +1 -0
  66. package/dist/createUseState.mjs +49 -0
  67. package/dist/createUseState.mjs.map +1 -0
  68. package/dist/index.cjs +23 -0
  69. package/dist/index.d.cts +12 -0
  70. package/dist/index.d.mts +12 -0
  71. package/dist/index.mjs +11 -0
  72. package/dist/types.d.cts +23 -0
  73. package/dist/types.d.cts.map +1 -0
  74. package/dist/types.d.mts +23 -0
  75. package/dist/types.d.mts.map +1 -0
  76. package/dist/useState.cjs +45 -0
  77. package/dist/useState.d.cts +43 -0
  78. package/dist/useState.d.cts.map +1 -0
  79. package/dist/useState.d.mts +43 -0
  80. package/dist/useState.d.mts.map +1 -0
  81. package/dist/useState.mjs +47 -0
  82. package/dist/useState.mjs.map +1 -0
  83. package/media/Logging.png +0 -0
  84. package/media/Profiling.png +0 -0
  85. package/package.json +38 -59
  86. package/src/Galena.ts +120 -0
  87. package/src/{Middlewares/Logger.ts → Logger.ts} +15 -14
  88. package/src/Middleware.ts +62 -0
  89. package/src/Profiler.ts +53 -0
  90. package/src/State.ts +167 -0
  91. package/src/index.ts +6 -3
  92. package/src/types.ts +28 -0
  93. package/dist/cjs/Galena/Galena.js +0 -223
  94. package/dist/cjs/Galena/Guards.js +0 -40
  95. package/dist/cjs/Galena/Scheduler.js +0 -84
  96. package/dist/cjs/Galena/State.js +0 -314
  97. package/dist/cjs/Galena/index.js +0 -22
  98. package/dist/cjs/Galena/types.js +0 -9
  99. package/dist/cjs/Middleware/Middleware.js +0 -46
  100. package/dist/cjs/Middleware/index.js +0 -20
  101. package/dist/cjs/Middleware/types.js +0 -8
  102. package/dist/cjs/Middlewares/Logger.js +0 -51
  103. package/dist/cjs/Middlewares/Profiler.js +0 -38
  104. package/dist/cjs/Middlewares/index.js +0 -7
  105. package/dist/cjs/index.js +0 -19
  106. package/dist/cjs/package.json +0 -3
  107. package/dist/mjs/Galena/Galena.js +0 -218
  108. package/dist/mjs/Galena/Guards.js +0 -36
  109. package/dist/mjs/Galena/Scheduler.js +0 -79
  110. package/dist/mjs/Galena/State.js +0 -313
  111. package/dist/mjs/Galena/index.js +0 -3
  112. package/dist/mjs/Galena/types.js +0 -6
  113. package/dist/mjs/Middleware/Middleware.js +0 -42
  114. package/dist/mjs/Middleware/index.js +0 -2
  115. package/dist/mjs/Middleware/types.js +0 -5
  116. package/dist/mjs/Middlewares/Logger.js +0 -44
  117. package/dist/mjs/Middlewares/Profiler.js +0 -35
  118. package/dist/mjs/Middlewares/index.js +0 -2
  119. package/dist/mjs/index.js +0 -3
  120. package/dist/mjs/package.json +0 -4
  121. package/dist/types/Galena/Galena.d.ts +0 -160
  122. package/dist/types/Galena/Guards.d.ts +0 -29
  123. package/dist/types/Galena/Scheduler.d.ts +0 -51
  124. package/dist/types/Galena/State.d.ts +0 -235
  125. package/dist/types/Galena/index.d.ts +0 -3
  126. package/dist/types/Galena/types.d.ts +0 -13
  127. package/dist/types/Middleware/Middleware.d.ts +0 -43
  128. package/dist/types/Middleware/index.d.ts +0 -2
  129. package/dist/types/Middleware/types.d.ts +0 -4
  130. package/dist/types/Middlewares/Logger.d.ts +0 -27
  131. package/dist/types/Middlewares/Profiler.d.ts +0 -22
  132. package/dist/types/Middlewares/index.d.ts +0 -2
  133. package/dist/types/index.d.ts +0 -3
  134. package/src/Galena/Galena.ts +0 -252
  135. package/src/Galena/Guards.ts +0 -49
  136. package/src/Galena/Scheduler.ts +0 -85
  137. package/src/Galena/State.ts +0 -344
  138. package/src/Galena/index.ts +0 -3
  139. package/src/Galena/types.ts +0 -18
  140. package/src/Middleware/Middleware.ts +0 -45
  141. package/src/Middleware/index.ts +0 -2
  142. package/src/Middleware/types.ts +0 -4
  143. package/src/Middlewares/Profiler.ts +0 -41
  144. package/src/Middlewares/index.ts +0 -2
@@ -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
- }
@@ -1,344 +0,0 @@
1
- import { EventEmitter } from "@figliolia/event-emitter";
2
- import type { Middleware } from "Middleware/Middleware";
3
- import { MiddlewareEvents } from "Middleware/types";
4
- import { Scheduler } from "./Scheduler";
5
- import type { MutationEvent, Subscription } from "./types";
6
- import { Priority } from "./types";
7
-
8
- /**
9
- * ### State
10
- *
11
- * The root of all reactivity in Galena. State instances can
12
- * operate in isolation by calling `new State(...args)` or as
13
- * part of your application's larger global state by using
14
- * `new Galena().composeState()`.
15
- *
16
- * `State` instances operate on the premise of pub-sub and mutability.
17
- * This provides significant performance improvement over more traditional
18
- * state management tools because
19
- *
20
- * 1. Mutations can occur in O(1) space
21
- * 2. Mutations can be batched when dispatching updates to subscribers
22
- *
23
- * When deciding how many `State` instances are required for your
24
- * applications needs, we suggest creating and organizing state in
25
- * accordance with your application logic. Meaning, you might have a
26
- * `State` instance for navigation/routing, another `State` instance
27
- * for storing user information, and so on. Performance can improve
28
- * significantly when state is dispersed amongst multiple instances
29
- *
30
- * #### Creating State Instances
31
- *
32
- * ```typescript
33
- * const MyState = new State("MyState", {
34
- * someData: true,
35
- * listItems: [1, 2, 3, 4];
36
- * // ...etc
37
- * });
38
- * ```
39
- *
40
- * #### Updating State
41
- * ##### Synchronous updates
42
- * ```typescript
43
- * MyState.update((state) => {
44
- * state.listItems.push(5);
45
- * });
46
- * ```
47
- * ##### Asynchronous updates
48
- * ```typescript
49
- * MyState.update(async (state) => {
50
- * const listItems = await fetch("/list-items");
51
- * state.listItems = listItems;
52
- * });
53
- * ```
54
- *
55
- * #### Subscribing to State Changes
56
- * ```typescript
57
- * MyState.subscribe((state) => {
58
- * const { listItems } = state
59
- * // Do something with your list items!
60
- * });
61
- * ```
62
- */
63
- export class State<T extends any = any> extends Scheduler {
64
- public state: T;
65
- public readonly name: string;
66
- public readonly initialState: T;
67
- private readonly middleware: Middleware[] = [];
68
- private readonly emitter = new EventEmitter<MutationEvent<T>>();
69
- constructor(name: string, initialState: T) {
70
- super();
71
- this.name = name;
72
- this.state = initialState;
73
- this.initialState = State.clone(initialState);
74
- }
75
-
76
- /**
77
- * Get State
78
- *
79
- * Returns a readonly snapshot of the current state
80
- */
81
- public getState() {
82
- return this.state as Readonly<T>;
83
- }
84
-
85
- /**
86
- * Update
87
- *
88
- * Mutates state and notifies any open subscriptions. This method
89
- * by default uses task batching for optimized performance. In almost
90
- * every use-case, this method is the correct way to mutate state. If
91
- * you need to bypass batching for higher-priority state updates, you
92
- * can use `State.priorityUpdate()` or `State.backgroundUpdate()`
93
- *
94
- * ##### Synchronous updates
95
- * ```typescript
96
- * MyState.update((state, initialState) => {
97
- * state.listItems.push(5);
98
- * });
99
- * ```
100
- * ##### Asynchronous updates
101
- * ```typescript
102
- * MyState.update(async (state, initialState) => {
103
- * const listItems = await fetch("/list-items");
104
- * state.listItems = listItems;
105
- * });
106
- * ```
107
- */
108
- public update = this.mutation(
109
- (func: (state: T, initialState: T) => void | Promise<void>) => {
110
- return func(this.state, this.initialState);
111
- },
112
- Priority.BATCHED,
113
- );
114
-
115
- /**
116
- * Background Update
117
- *
118
- * Mutates state and notifies any open subscriptions. This method
119
- * bypasses Galena's internal task batching for a more immediate
120
- * state update and propagation of state to consumers. It utilizes
121
- * a micro-task that allows for the current call stack to clear
122
- * ahead of propagating state updates to consumers
123
- *
124
- * ##### Synchronous updates
125
- * ```typescript
126
- * MyState.backgroundUpdate((state, initialState) => {
127
- * state.listItems.push(5);
128
- * });
129
- * ```
130
- * ##### Asynchronous updates
131
- * ```typescript
132
- * MyState.backgroundUpdate(async (state, initialState) => {
133
- * const listItems = await fetch("/list-items");
134
- * state.listItems = listItems;
135
- * });
136
- * ```
137
- */
138
- public backgroundUpdate = this.mutation(
139
- (func: (state: T, initialState: T) => void | Promise<void>) => {
140
- return func(this.state, this.initialState);
141
- },
142
- Priority.MICROTASK,
143
- );
144
-
145
- /**
146
- * Priority Update
147
- *
148
- * Mutates state and notifies any open subscriptions. This method
149
- * bypasses optimizations for task batching and scheduling. This means
150
- * that state updates made with this method propagate to subscriptions
151
- * as immediately as possible. Overusing this method can cause your
152
- * state updates to perform slower in certain cases. The usage of this
153
- * method should be conserved for state mutations that need to occur
154
- * at a certain frame rate
155
- *
156
- * ##### Synchronous updates
157
- * ```typescript
158
- * MyState.priorityUpdate((state, initialState) => {
159
- * state.listItems.push(5);
160
- * });
161
- * ```
162
- * ##### Asynchronous updates
163
- * ```typescript
164
- * MyState.priorityUpdate(async (state, initialState) => {
165
- * const listItems = await fetch("/list-items");
166
- * state.listItems = listItems;
167
- * });
168
- * ```
169
- */
170
- public priorityUpdate = this.mutation(
171
- (func: (state: T, initialState: T) => void | Promise<void>) => {
172
- return func(this.state, this.initialState);
173
- },
174
- Priority.IMMEDIATE,
175
- );
176
-
177
- /**
178
- * Reset
179
- *
180
- * Resets the current state to its initial state
181
- */
182
- public reset = this.mutation(() => {
183
- this.state = State.clone(this.initialState);
184
- });
185
-
186
- /**
187
- * Mutation
188
- *
189
- * This method can be used to wrap arbitrary functions that when invoked
190
- * will:
191
- * 1. Notify your subscriptions with the latest state
192
- * 2. Execute any registered middleware (such as loggers or profiling tools)
193
- *
194
- * Using this method, developers can compose and extend `Galena`'s internal
195
- * infrastructure for state mutations to create proprietary models for your
196
- * state
197
- *
198
- * ```typescript
199
- * import { State } from "@figliolia/galena";
200
- *
201
- * // Extend of Galena State
202
- * class MyState extends State {
203
- * addListItem = mutation((newListItem) => {
204
- * this.state.list.push(newListItem);
205
- * });
206
- * }
207
- *
208
- * // Create an instance
209
- * const myState = new MyState("myState", { list: [] });
210
- *
211
- * // Invoke your custom mutation method
212
- * myState.addListItem("new-item");
213
- * ```
214
- */
215
- protected mutation<F extends (...args: any[]) => any>(
216
- func: F,
217
- priority: Priority = Priority.BATCHED,
218
- ) {
219
- return (...args: Parameters<F>) => {
220
- this.lifeCycleEvent(MiddlewareEvents.onBeforeUpdate);
221
- const returnValue = func(...args);
222
- if (returnValue instanceof Promise) {
223
- return returnValue.then(v => {
224
- this.scheduleUpdate(priority);
225
- return v;
226
- });
227
- }
228
- this.scheduleUpdate(priority);
229
- return returnValue;
230
- };
231
- }
232
-
233
- /**
234
- * Schedule Update
235
- *
236
- * Schedules an update to State subscribers and emits the
237
- * `onUpdate` lifecycle hook
238
- */
239
- private scheduleUpdate(priority: Priority) {
240
- this.lifeCycleEvent(MiddlewareEvents.onUpdate);
241
- void this.scheduleTask(
242
- () => this.emitter.emit(this.name, this.state),
243
- priority,
244
- );
245
- }
246
-
247
- /**
248
- * Register Middleware
249
- *
250
- * Caches a `Middleware` instance and invokes its
251
- * lifecycle subscriptions on all state transitions
252
- */
253
- public registerMiddleware(...middleware: Middleware[]) {
254
- this.middleware.push(...middleware);
255
- }
256
-
257
- /**
258
- * Subscribe
259
- *
260
- * Registers a subscription on the state instance. The
261
- * callback you provide will execute each time state changes.
262
- * Returns a unique identifier for your subscription
263
- */
264
- public subscribe(callback: Subscription<T>) {
265
- return this.emitter.on(this.name, callback);
266
- }
267
-
268
- /**
269
- * Unsubscribe
270
- *
271
- * Given a subscription ID, removes a registered subscription
272
- * from the `State` instance
273
- */
274
- public unsubscribe(ID: string) {
275
- return this.emitter.off(this.name, ID);
276
- }
277
-
278
- /**
279
- * Clear All Subscriptions
280
- *
281
- * Removes all open subscriptions to the `State` instance
282
- */
283
- public clearAllSubscriptions() {
284
- return this.emitter.storage.clear();
285
- }
286
-
287
- /**
288
- * Life Cycle Event
289
- *
290
- * Triggers a life cycle event for each registered middleware
291
- */
292
- private lifeCycleEvent<E extends MiddlewareEvents>(event: E) {
293
- const maxIndex = this.middleware.length - 1;
294
- for (let i = maxIndex; i > -1; i--) {
295
- this.middleware[i][event](this);
296
- }
297
- }
298
-
299
- /**
300
- * Clone
301
- *
302
- * `State` instances accept any value as a form of reactive
303
- * state. In order to maintain the initial state past any state
304
- * transitions, this method clones the initial values provided
305
- * to the `State` constructor and caches them to allow for
306
- * developers to easily reset their current state back to its
307
- * initial value
308
- */
309
- public static clone<T>(state: T): T {
310
- switch (typeof state) {
311
- case "string":
312
- return String(state) as T;
313
- case "bigint":
314
- return BigInt(state) as T;
315
- case "boolean":
316
- return Boolean(state) as T;
317
- case "number":
318
- return Number(state) as T;
319
- case "symbol":
320
- case "function":
321
- return state;
322
- case "undefined":
323
- return undefined as T;
324
- case "object":
325
- default:
326
- if (!state) {
327
- return null as T;
328
- }
329
- if (Array.isArray(state)) {
330
- return [...state] as T;
331
- }
332
- if (state instanceof Set) {
333
- return new Set(state) as T;
334
- }
335
- if (state instanceof Map) {
336
- return new Map(state) as T;
337
- }
338
- if (state && typeof state === "object") {
339
- return { ...state } as T;
340
- }
341
- return state;
342
- }
343
- }
344
- }
@@ -1,3 +0,0 @@
1
- export { Galena } from "./Galena";
2
- export { State } from "./State";
3
- export * from "./types";
@@ -1,18 +0,0 @@
1
- import type { Callback } from "@figliolia/event-emitter";
2
- import type { State } from "./State";
3
-
4
- export type MutationEvent<T extends any> = {
5
- [key: State<T>["name"]]: T;
6
- };
7
-
8
- export enum Priority {
9
- "IMMEDIATE" = 1,
10
- "MICROTASK" = 2,
11
- "BATCHED" = 3,
12
- }
13
-
14
- export type Task = () => void;
15
-
16
- export type SubscriptionTuple = [state: string, ID: string];
17
-
18
- export type Subscription<T> = Callback<[nextState: T]>;
@@ -1,45 +0,0 @@
1
- import type { State } from "Galena/State";
2
-
3
- /**
4
- * # Middleware
5
- *
6
- * A root interface for all `Galena` Middleware. When creating
7
- * a middleware for your `Galena` state, simply extend this
8
- * class any override any of its public lifecycle methods.
9
- *
10
- * ### Creating a Profiling Middleware
11
- *
12
- * ```typescript
13
- * export class ProfilerMiddleware extends Middleware {
14
- * updateState: number | null = null;
15
- *
16
- * override onBeforeUpdate(state: State) {
17
- * this.updateStart = performance.now();
18
- * }
19
- *
20
- * override onUpdate(state: State) {
21
- * if(this.updateStart) {
22
- * const timeToUpdate = performance.now() - this.updateStart;
23
- * if(timeToUpdate > 16) {
24
- * console.warn("A state transition took more than 16 milliseconds!", State);
25
- * }
26
- * }
27
- * }
28
- * }
29
- * ```
30
- */
31
- export class Middleware<T extends any = any> {
32
- /**
33
- * On Before Update
34
- *
35
- * An event emitted each time a `State` mutation is enqueued
36
- */
37
- public onBeforeUpdate(_state: State<T>) {}
38
-
39
- /**
40
- * On Update
41
- *
42
- * An event emitted each time a `State` instance is mutated
43
- */
44
- public onUpdate(_state: State<T>) {}
45
- }
@@ -1,2 +0,0 @@
1
- export { Middleware } from "./Middleware";
2
- export * from "./types";
@@ -1,4 +0,0 @@
1
- export enum MiddlewareEvents {
2
- "onUpdate" = "onUpdate",
3
- "onBeforeUpdate" = "onBeforeUpdate",
4
- }
@@ -1,41 +0,0 @@
1
- import type { State } from "Galena/State";
2
- import { Middleware } from "Middleware/Middleware";
3
-
4
- /**
5
- * Profiler
6
- *
7
- * A logger for state transitions exceeding a given threshold
8
- * for duration:
9
- *
10
- * ```typescript
11
- * const State = new Galena([new Profiler()]);
12
- * // if using isolated state instances:
13
- * const MyState = new State(...args);
14
- * MyState.registerMiddleware(new Profiler())
15
- * ```
16
- */
17
- export class Profiler extends Middleware {
18
- private threshold: number;
19
- private startTime: number | null = null;
20
- constructor(threshold = 16) {
21
- super();
22
- this.threshold = threshold;
23
- }
24
- onBeforeUpdate(_: State) {
25
- this.startTime = performance.now();
26
- }
27
-
28
- onUpdate(nextState: State) {
29
- if (this.startTime) {
30
- const endTime = performance.now();
31
- const diff = endTime - this.startTime;
32
- if (diff > this.threshold) {
33
- console.warn(
34
- `A slow state transition was detected on ${nextState.name}`,
35
- nextState.getState(),
36
- );
37
- console.warn(`The last transition took ${diff}ms`);
38
- }
39
- }
40
- }
41
- }
@@ -1,2 +0,0 @@
1
- export { Logger } from "./Logger";
2
- export { Profiler } from "./Profiler";