@figliolia/galena 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.
@@ -0,0 +1,13 @@
1
+ import type { State } from "./State";
2
+
3
+ export type MutationEvent<T extends any> = {
4
+ [key: State<T>["name"]]: State<T>;
5
+ };
6
+
7
+ export enum Priority {
8
+ "IMMEDIATE" = 1,
9
+ "MICROTASK" = 2,
10
+ "BATCHED" = 3,
11
+ }
12
+
13
+ export type Task = () => void;
@@ -0,0 +1,72 @@
1
+ import { EventEmitter } from "@figliolia/event-emitter";
2
+
3
+ import type { State } from "Galena/State";
4
+ import type { MiddlewareEvent } from "Middleware/types";
5
+ import { MiddlewareEvents } from "Middleware/types";
6
+
7
+ /**
8
+ * # Middleware
9
+ *
10
+ * A root interface for all `Galena` Middleware. When creating
11
+ * a middleware for your `Galena` state, simply extend this
12
+ * class any override any of its public lifecycle methods.
13
+ *
14
+ * ### Creating a Profiling Middleware
15
+ *
16
+ * ```typescript
17
+ * export class ProfilerMiddleware extends Middleware {
18
+ * updateState: number | null = null;
19
+ *
20
+ * override onBeforeUpdate(state: State) {
21
+ * this.updateStart = performance.now();
22
+ * }
23
+ *
24
+ * override onUpdate(state: State) {
25
+ * if(this.updateStart) {
26
+ * const timeToUpdate = performance.now() - this.updateStart;
27
+ * if(timeToUpdate > 16) {
28
+ * console.warn("A state transition took more than 16 milliseconds!", State);
29
+ * }
30
+ * }
31
+ * }
32
+ * }
33
+ * ```
34
+ */
35
+ export class Middleware<T extends any = any> {
36
+ public static Emitter = new EventEmitter<MiddlewareEvent<any>>();
37
+ constructor() {
38
+ const extension = Object.getPrototypeOf(this);
39
+ const methods = Object.getOwnPropertyNames(extension);
40
+ methods.forEach((event) => {
41
+ if (Middleware.validateEvent(event)) {
42
+ Middleware.Emitter.on(MiddlewareEvents[event], this[event]);
43
+ }
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Validate Event
49
+ *
50
+ * Asserts that a given method on an extending class prototype
51
+ * is one of the supported `Galena` lifecycle events
52
+ */
53
+ private static validateEvent(event: string): event is MiddlewareEvents {
54
+ return event in MiddlewareEvents;
55
+ }
56
+
57
+ /* Life Cycle Events */
58
+
59
+ /**
60
+ * On Before Update
61
+ *
62
+ * An event emitted each time a `State` mutation is enqueued
63
+ */
64
+ public onBeforeUpdate(state: State<T>) {}
65
+
66
+ /**
67
+ * On Update
68
+ *
69
+ * An event emitted each time a `State` instance is mutated
70
+ */
71
+ public onUpdate(state: State<T>) {}
72
+ }
@@ -0,0 +1,2 @@
1
+ export { Middleware } from "./Middleware";
2
+ export * from "./types";
@@ -0,0 +1,11 @@
1
+ import type { State } from "Galena/State";
2
+
3
+ export enum MiddlewareEvents {
4
+ "onUpdate" = "onUpdate",
5
+ "onBeforeUpdate" = "onBeforeUpdate",
6
+ }
7
+
8
+ export type MiddlewareEvent<T extends any = any> = Record<
9
+ MiddlewareEvents,
10
+ State<T>
11
+ >;
@@ -0,0 +1,62 @@
1
+ import { State } from "Galena/State";
2
+ import { Middleware } from "Middleware/Middleware";
3
+
4
+ /**
5
+ * Logger
6
+ *
7
+ * A middleware for Redux-style logging! Each state transition
8
+ * will log to the console the `State` instance that changed
9
+ * along with a before and after snapshot of the current state:
10
+ *
11
+ * ```typescript
12
+ * const State = new Galena([new Logger()]);
13
+ * // if using isolated state instances:
14
+ * const MyState = new State(...args);
15
+ * MyState.registerMiddleware(new Logger())
16
+ * ```
17
+ */
18
+ export class Logger extends Middleware {
19
+ private previousState: Record<string, any> | null = null;
20
+
21
+ override onBeforeUpdate(state: State) {
22
+ this.previousState = State.clone(state.state);
23
+ }
24
+
25
+ override onUpdate(state: State) {
26
+ console.log(
27
+ "%cMutation:",
28
+ "color: rgb(187, 186, 186); font-weight: bold",
29
+ state.name,
30
+ "@",
31
+ this.time
32
+ );
33
+ console.log(
34
+ " %cPrevious State",
35
+ "color: #26ad65; font-weight: bold",
36
+ this.previousState
37
+ );
38
+ console.log(
39
+ " %cNext State ",
40
+ "color: rgb(17, 118, 249); font-weight: bold",
41
+ state.getState()
42
+ );
43
+ this.previousState = null;
44
+ }
45
+
46
+ /**
47
+ * Time
48
+ *
49
+ * Returns the time in which a given state transition completed
50
+ */
51
+ private get time() {
52
+ const date = new Date();
53
+ const mHours = date.getHours();
54
+ const hours = mHours > 12 ? mHours - 12 : mHours;
55
+ const mins = date.getMinutes();
56
+ const minutes = mins.toString().length === 1 ? `0${mins}` : mins;
57
+ const secs = date.getSeconds();
58
+ const seconds = secs.toString().length === 1 ? `0${secs}` : secs;
59
+ const milliseconds = date.getMilliseconds();
60
+ return `${hours}:${minutes}:${seconds}:${milliseconds}`;
61
+ }
62
+ }
@@ -0,0 +1,38 @@
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("Slow state transition detected", nextState);
34
+ console.warn(`The last transition took ${diff}ms`);
35
+ }
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,2 @@
1
+ export { Logger } from "./Logger";
2
+ export { Profiler } from "./Profiler";
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "Galena";
2
+ export * from "Middleware";
3
+ export * from "Middlewares";