@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.
- package/README.md +534 -0
- package/dist/Galena/Galena.d.ts +152 -0
- package/dist/Galena/Galena.js +219 -0
- package/dist/Galena/Scheduler.d.ts +51 -0
- package/dist/Galena/Scheduler.js +84 -0
- package/dist/Galena/State.d.ts +228 -0
- package/dist/Galena/State.js +286 -0
- package/dist/Galena/index.d.ts +3 -0
- package/dist/Galena/index.js +22 -0
- package/dist/Galena/types.d.ts +10 -0
- package/dist/Galena/types.js +9 -0
- package/dist/Middleware/Middleware.d.ts +54 -0
- package/dist/Middleware/Middleware.js +68 -0
- package/dist/Middleware/index.d.ts +2 -0
- package/dist/Middleware/index.js +20 -0
- package/dist/Middleware/types.d.ts +6 -0
- package/dist/Middleware/types.js +8 -0
- package/dist/Middlewares/Logger.d.ts +27 -0
- package/dist/Middlewares/Logger.js +51 -0
- package/dist/Middlewares/Profiler.d.ts +22 -0
- package/dist/Middlewares/Profiler.js +38 -0
- package/dist/Middlewares/index.d.ts +2 -0
- package/dist/Middlewares/index.js +7 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +19 -0
- package/package.json +60 -0
- package/src/Galena/Galena.ts +253 -0
- package/src/Galena/Scheduler.ts +85 -0
- package/src/Galena/State.ts +311 -0
- package/src/Galena/index.ts +3 -0
- package/src/Galena/types.ts +13 -0
- package/src/Middleware/Middleware.ts +72 -0
- package/src/Middleware/index.ts +2 -0
- package/src/Middleware/types.ts +11 -0
- package/src/Middlewares/Logger.ts +62 -0
- package/src/Middlewares/Profiler.ts +38 -0
- package/src/Middlewares/index.ts +2 -0
- package/src/index.ts +3 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.State = void 0;
|
|
4
|
+
const types_1 = require("../Middleware/types");
|
|
5
|
+
const event_emitter_1 = require("@figliolia/event-emitter");
|
|
6
|
+
const types_2 = require("./types");
|
|
7
|
+
const Scheduler_1 = require("./Scheduler");
|
|
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
|
+
class State extends Scheduler_1.Scheduler {
|
|
64
|
+
constructor(name, initialState) {
|
|
65
|
+
super();
|
|
66
|
+
this.middleware = [];
|
|
67
|
+
this.emitter = new event_emitter_1.EventEmitter();
|
|
68
|
+
/**
|
|
69
|
+
* Update
|
|
70
|
+
*
|
|
71
|
+
* Mutates state and notifies any open subscriptions. This method
|
|
72
|
+
* by default uses task batching for optimized performance. In almost
|
|
73
|
+
* every use-case, this method is the correct way to mutate state. If
|
|
74
|
+
* you need to bypass batching for higher-priority state updates, you
|
|
75
|
+
* can use `State.priorityUpdate()` or `State.backgroundUpdate()`
|
|
76
|
+
*
|
|
77
|
+
* ##### Synchronous updates
|
|
78
|
+
* ```typescript
|
|
79
|
+
* MyState.update((state, initialState) => {
|
|
80
|
+
* state.listItems.push(5);
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
* ##### Asynchronous updates
|
|
84
|
+
* ```typescript
|
|
85
|
+
* MyState.update(async (state, initialState) => {
|
|
86
|
+
* const listItems = await fetch("/list-items");
|
|
87
|
+
* state.listItems = listItems;
|
|
88
|
+
* });
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
this.update = this.mutation((func) => {
|
|
92
|
+
return func(this.state, this.initialState);
|
|
93
|
+
}, types_2.Priority.BATCHED);
|
|
94
|
+
/**
|
|
95
|
+
* Background Update
|
|
96
|
+
*
|
|
97
|
+
* Mutates state and notifies any open subscriptions. This method
|
|
98
|
+
* bypasses Galena's internal task batching for a more immediate
|
|
99
|
+
* state update and propagation of state to consumers. It utilizes
|
|
100
|
+
* a micro-task that allows for the current call stack to clear
|
|
101
|
+
* ahead of propagating state updates to consumers
|
|
102
|
+
*
|
|
103
|
+
* ##### Synchronous updates
|
|
104
|
+
* ```typescript
|
|
105
|
+
* MyState.backgroundUpdate((state, initialState) => {
|
|
106
|
+
* state.listItems.push(5);
|
|
107
|
+
* });
|
|
108
|
+
* ```
|
|
109
|
+
* ##### Asynchronous updates
|
|
110
|
+
* ```typescript
|
|
111
|
+
* MyState.backgroundUpdate(async (state, initialState) => {
|
|
112
|
+
* const listItems = await fetch("/list-items");
|
|
113
|
+
* state.listItems = listItems;
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
this.backgroundUpdate = this.mutation((func) => {
|
|
118
|
+
return func(this.state, this.initialState);
|
|
119
|
+
}, types_2.Priority.MICROTASK);
|
|
120
|
+
/**
|
|
121
|
+
* Priority Update
|
|
122
|
+
*
|
|
123
|
+
* Mutates state and notifies any open subscriptions. This method
|
|
124
|
+
* bypasses optimizations for task batching and scheduling. This means
|
|
125
|
+
* that state updates made with this method propagate to subscriptions
|
|
126
|
+
* as immediately as possible. Overusing this method can cause your
|
|
127
|
+
* state updates to perform slower in certain cases. The usage of this
|
|
128
|
+
* method should be conserved for state mutations that need to occur
|
|
129
|
+
* at a certain frame rate
|
|
130
|
+
*
|
|
131
|
+
* ##### Synchronous updates
|
|
132
|
+
* ```typescript
|
|
133
|
+
* MyState.priorityUpdate((state, initialState) => {
|
|
134
|
+
* state.listItems.push(5);
|
|
135
|
+
* });
|
|
136
|
+
* ```
|
|
137
|
+
* ##### Asynchronous updates
|
|
138
|
+
* ```typescript
|
|
139
|
+
* MyState.priorityUpdate(async (state, initialState) => {
|
|
140
|
+
* const listItems = await fetch("/list-items");
|
|
141
|
+
* state.listItems = listItems;
|
|
142
|
+
* });
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
this.priorityUpdate = this.mutation((func) => {
|
|
146
|
+
return func(this.state, this.initialState);
|
|
147
|
+
}, types_2.Priority.IMMEDIATE);
|
|
148
|
+
/**
|
|
149
|
+
* Reset
|
|
150
|
+
*
|
|
151
|
+
* Resets the current state to its initial state
|
|
152
|
+
*/
|
|
153
|
+
this.reset = this.mutation(() => {
|
|
154
|
+
this.state = State.clone(this.initialState);
|
|
155
|
+
});
|
|
156
|
+
this.name = name;
|
|
157
|
+
this.state = initialState;
|
|
158
|
+
this.initialState = State.clone(initialState);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get State
|
|
162
|
+
*
|
|
163
|
+
* Returns a readonly snapshot of the current state
|
|
164
|
+
*/
|
|
165
|
+
getState() {
|
|
166
|
+
return this.state;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Mutation
|
|
170
|
+
*
|
|
171
|
+
* This method can be used to wrap arbitrary functions that when invoked
|
|
172
|
+
* will:
|
|
173
|
+
* 1. Notify your subscriptions with the latest state
|
|
174
|
+
* 2. Execute any registered middleware (such as loggers or profiling tools)
|
|
175
|
+
*
|
|
176
|
+
* Using this method, developers can compose and extend `Galena`'s internal
|
|
177
|
+
* infrastructure for state mutations to create proprietary models for your
|
|
178
|
+
* state
|
|
179
|
+
*
|
|
180
|
+
* ```typescript
|
|
181
|
+
* import { State } from "../galena";
|
|
182
|
+
*
|
|
183
|
+
* // Extend of Galena State
|
|
184
|
+
* class MyState extends State {
|
|
185
|
+
* addListItem = mutation((newListItem) => {
|
|
186
|
+
* this.state.list.push(newListItem);
|
|
187
|
+
* });
|
|
188
|
+
* }
|
|
189
|
+
*
|
|
190
|
+
* // Create an instance
|
|
191
|
+
* const myState = new MyState("myState", { list: [] });
|
|
192
|
+
*
|
|
193
|
+
* // Invoke your custom mutation method
|
|
194
|
+
* myState.addListItem("new-item");
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
mutation(func, priority = types_2.Priority.BATCHED) {
|
|
198
|
+
return (...args) => {
|
|
199
|
+
this.lifeCycleEvent(types_1.MiddlewareEvents.onBeforeUpdate);
|
|
200
|
+
const returnValue = func(...args);
|
|
201
|
+
if (returnValue instanceof Promise) {
|
|
202
|
+
return returnValue.then((v) => {
|
|
203
|
+
this.scheduleUpdate(priority);
|
|
204
|
+
return v;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
this.scheduleUpdate(priority);
|
|
208
|
+
return returnValue;
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Schedule Update
|
|
213
|
+
*
|
|
214
|
+
* Schedules an update to State subscribers and emits the
|
|
215
|
+
* `onUpdate` lifecycle hook
|
|
216
|
+
*/
|
|
217
|
+
scheduleUpdate(priority) {
|
|
218
|
+
this.lifeCycleEvent(types_1.MiddlewareEvents.onUpdate);
|
|
219
|
+
void this.scheduleTask(() => this.emitter.emit(this.name, this), priority);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Register Middleware
|
|
223
|
+
*
|
|
224
|
+
* Caches a `Middleware` instance and invokes its
|
|
225
|
+
* lifecycle subscriptions on all state transitions
|
|
226
|
+
*/
|
|
227
|
+
registerMiddleware(...middleware) {
|
|
228
|
+
this.middleware.push(...middleware);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Subscribe
|
|
232
|
+
*
|
|
233
|
+
* Registers a subscription on the state instance. The
|
|
234
|
+
* callback you provide will execute each time state changes.
|
|
235
|
+
* Returns a unique identifier for your subscription
|
|
236
|
+
*/
|
|
237
|
+
subscribe(callback) {
|
|
238
|
+
return this.emitter.on(this.name, callback);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Unsubscribe
|
|
242
|
+
*
|
|
243
|
+
* Given a subscription ID, removes a registered subscription
|
|
244
|
+
* from the `State` instance
|
|
245
|
+
*/
|
|
246
|
+
unsubscribe(ID) {
|
|
247
|
+
return this.emitter.off(this.name, ID);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Life Cycle Event
|
|
251
|
+
*
|
|
252
|
+
* Triggers a life cycle event for each registered middleware
|
|
253
|
+
*/
|
|
254
|
+
lifeCycleEvent(event) {
|
|
255
|
+
const maxIndex = this.middleware.length - 1;
|
|
256
|
+
for (let i = maxIndex; i > -1; i--) {
|
|
257
|
+
this.middleware[i][event](this);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Clone
|
|
262
|
+
*
|
|
263
|
+
* `State` instances accept any value as a form of reactive
|
|
264
|
+
* state. In order to maintain the initial state past any state
|
|
265
|
+
* transitions, this method clones the initial values provided
|
|
266
|
+
* to the `State` constructor and caches them to allow for
|
|
267
|
+
* developers to easily reset their current state back to its
|
|
268
|
+
* initial value
|
|
269
|
+
*/
|
|
270
|
+
static clone(state) {
|
|
271
|
+
if (Array.isArray(state)) {
|
|
272
|
+
return [...state];
|
|
273
|
+
}
|
|
274
|
+
if (state instanceof Set) {
|
|
275
|
+
return new Set(state);
|
|
276
|
+
}
|
|
277
|
+
if (state instanceof Map) {
|
|
278
|
+
return new Map(state);
|
|
279
|
+
}
|
|
280
|
+
if (state && typeof state === "object") {
|
|
281
|
+
return Object.assign({}, state);
|
|
282
|
+
}
|
|
283
|
+
return state;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
exports.State = State;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.State = exports.Galena = void 0;
|
|
18
|
+
var Galena_1 = require("./Galena");
|
|
19
|
+
Object.defineProperty(exports, "Galena", { enumerable: true, get: function () { return Galena_1.Galena; } });
|
|
20
|
+
var State_1 = require("./State");
|
|
21
|
+
Object.defineProperty(exports, "State", { enumerable: true, get: function () { return State_1.State; } });
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Priority = void 0;
|
|
4
|
+
var Priority;
|
|
5
|
+
(function (Priority) {
|
|
6
|
+
Priority[Priority["IMMEDIATE"] = 1] = "IMMEDIATE";
|
|
7
|
+
Priority[Priority["MICROTASK"] = 2] = "MICROTASK";
|
|
8
|
+
Priority[Priority["BATCHED"] = 3] = "BATCHED";
|
|
9
|
+
})(Priority = exports.Priority || (exports.Priority = {}));
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { EventEmitter } from "@figliolia/event-emitter";
|
|
2
|
+
import type { State } from "../Galena/State";
|
|
3
|
+
import type { MiddlewareEvent } from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* # Middleware
|
|
6
|
+
*
|
|
7
|
+
* A root interface for all `Galena` Middleware. When creating
|
|
8
|
+
* a middleware for your `Galena` state, simply extend this
|
|
9
|
+
* class any override any of its public lifecycle methods.
|
|
10
|
+
*
|
|
11
|
+
* ### Creating a Profiling Middleware
|
|
12
|
+
*
|
|
13
|
+
* ```typescript
|
|
14
|
+
* export class ProfilerMiddleware extends Middleware {
|
|
15
|
+
* updateState: number | null = null;
|
|
16
|
+
*
|
|
17
|
+
* override onBeforeUpdate(state: State) {
|
|
18
|
+
* this.updateStart = performance.now();
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* override onUpdate(state: State) {
|
|
22
|
+
* if(this.updateStart) {
|
|
23
|
+
* const timeToUpdate = performance.now() - this.updateStart;
|
|
24
|
+
* if(timeToUpdate > 16) {
|
|
25
|
+
* console.warn("A state transition took more than 16 milliseconds!", State);
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class Middleware<T extends any = any> {
|
|
33
|
+
static Emitter: EventEmitter<MiddlewareEvent<any>>;
|
|
34
|
+
constructor();
|
|
35
|
+
/**
|
|
36
|
+
* Validate Event
|
|
37
|
+
*
|
|
38
|
+
* Asserts that a given method on an extending class prototype
|
|
39
|
+
* is one of the supported `Galena` lifecycle events
|
|
40
|
+
*/
|
|
41
|
+
private static validateEvent;
|
|
42
|
+
/**
|
|
43
|
+
* On Before Update
|
|
44
|
+
*
|
|
45
|
+
* An event emitted each time a `State` mutation is enqueued
|
|
46
|
+
*/
|
|
47
|
+
onBeforeUpdate(state: State<T>): void;
|
|
48
|
+
/**
|
|
49
|
+
* On Update
|
|
50
|
+
*
|
|
51
|
+
* An event emitted each time a `State` instance is mutated
|
|
52
|
+
*/
|
|
53
|
+
onUpdate(state: State<T>): void;
|
|
54
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Middleware = void 0;
|
|
4
|
+
const event_emitter_1 = require("@figliolia/event-emitter");
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
/**
|
|
7
|
+
* # Middleware
|
|
8
|
+
*
|
|
9
|
+
* A root interface for all `Galena` Middleware. When creating
|
|
10
|
+
* a middleware for your `Galena` state, simply extend this
|
|
11
|
+
* class any override any of its public lifecycle methods.
|
|
12
|
+
*
|
|
13
|
+
* ### Creating a Profiling Middleware
|
|
14
|
+
*
|
|
15
|
+
* ```typescript
|
|
16
|
+
* export class ProfilerMiddleware extends Middleware {
|
|
17
|
+
* updateState: number | null = null;
|
|
18
|
+
*
|
|
19
|
+
* override onBeforeUpdate(state: State) {
|
|
20
|
+
* this.updateStart = performance.now();
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* override onUpdate(state: State) {
|
|
24
|
+
* if(this.updateStart) {
|
|
25
|
+
* const timeToUpdate = performance.now() - this.updateStart;
|
|
26
|
+
* if(timeToUpdate > 16) {
|
|
27
|
+
* console.warn("A state transition took more than 16 milliseconds!", State);
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
class Middleware {
|
|
35
|
+
constructor() {
|
|
36
|
+
const extension = Object.getPrototypeOf(this);
|
|
37
|
+
const methods = Object.getOwnPropertyNames(extension);
|
|
38
|
+
methods.forEach((event) => {
|
|
39
|
+
if (Middleware.validateEvent(event)) {
|
|
40
|
+
Middleware.Emitter.on(types_1.MiddlewareEvents[event], this[event]);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validate Event
|
|
46
|
+
*
|
|
47
|
+
* Asserts that a given method on an extending class prototype
|
|
48
|
+
* is one of the supported `Galena` lifecycle events
|
|
49
|
+
*/
|
|
50
|
+
static validateEvent(event) {
|
|
51
|
+
return event in types_1.MiddlewareEvents;
|
|
52
|
+
}
|
|
53
|
+
/* Life Cycle Events */
|
|
54
|
+
/**
|
|
55
|
+
* On Before Update
|
|
56
|
+
*
|
|
57
|
+
* An event emitted each time a `State` mutation is enqueued
|
|
58
|
+
*/
|
|
59
|
+
onBeforeUpdate(state) { }
|
|
60
|
+
/**
|
|
61
|
+
* On Update
|
|
62
|
+
*
|
|
63
|
+
* An event emitted each time a `State` instance is mutated
|
|
64
|
+
*/
|
|
65
|
+
onUpdate(state) { }
|
|
66
|
+
}
|
|
67
|
+
exports.Middleware = Middleware;
|
|
68
|
+
Middleware.Emitter = new event_emitter_1.EventEmitter();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.Middleware = void 0;
|
|
18
|
+
var Middleware_1 = require("./Middleware");
|
|
19
|
+
Object.defineProperty(exports, "Middleware", { enumerable: true, get: function () { return Middleware_1.Middleware; } });
|
|
20
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MiddlewareEvents = void 0;
|
|
4
|
+
var MiddlewareEvents;
|
|
5
|
+
(function (MiddlewareEvents) {
|
|
6
|
+
MiddlewareEvents["onUpdate"] = "onUpdate";
|
|
7
|
+
MiddlewareEvents["onBeforeUpdate"] = "onBeforeUpdate";
|
|
8
|
+
})(MiddlewareEvents = exports.MiddlewareEvents || (exports.MiddlewareEvents = {}));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { State } from "../Galena/State";
|
|
2
|
+
import { Middleware } from "../Middleware/Middleware";
|
|
3
|
+
/**
|
|
4
|
+
* Logger
|
|
5
|
+
*
|
|
6
|
+
* A middleware for Redux-style logging! Each state transition
|
|
7
|
+
* will log to the console the `State` instance that changed
|
|
8
|
+
* along with a before and after snapshot of the current state:
|
|
9
|
+
*
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const State = new Galena([new Logger()]);
|
|
12
|
+
* // if using isolated state instances:
|
|
13
|
+
* const MyState = new State(...args);
|
|
14
|
+
* MyState.registerMiddleware(new Logger())
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare class Logger extends Middleware {
|
|
18
|
+
private previousState;
|
|
19
|
+
onBeforeUpdate(state: State): void;
|
|
20
|
+
onUpdate(state: State): void;
|
|
21
|
+
/**
|
|
22
|
+
* Time
|
|
23
|
+
*
|
|
24
|
+
* Returns the time in which a given state transition completed
|
|
25
|
+
*/
|
|
26
|
+
private get time();
|
|
27
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Logger = void 0;
|
|
4
|
+
const State_1 = require("../Galena/State");
|
|
5
|
+
const Middleware_1 = require("../Middleware/Middleware");
|
|
6
|
+
/**
|
|
7
|
+
* Logger
|
|
8
|
+
*
|
|
9
|
+
* A middleware for Redux-style logging! Each state transition
|
|
10
|
+
* will log to the console the `State` instance that changed
|
|
11
|
+
* along with a before and after snapshot of the current state:
|
|
12
|
+
*
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const State = new Galena([new Logger()]);
|
|
15
|
+
* // if using isolated state instances:
|
|
16
|
+
* const MyState = new State(...args);
|
|
17
|
+
* MyState.registerMiddleware(new Logger())
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
class Logger extends Middleware_1.Middleware {
|
|
21
|
+
constructor() {
|
|
22
|
+
super(...arguments);
|
|
23
|
+
this.previousState = null;
|
|
24
|
+
}
|
|
25
|
+
onBeforeUpdate(state) {
|
|
26
|
+
this.previousState = State_1.State.clone(state.state);
|
|
27
|
+
}
|
|
28
|
+
onUpdate(state) {
|
|
29
|
+
console.log("%cMutation:", "color: rgb(187, 186, 186); font-weight: bold", state.name, "@", this.time);
|
|
30
|
+
console.log(" %cPrevious State", "color: #26ad65; font-weight: bold", this.previousState);
|
|
31
|
+
console.log(" %cNext State ", "color: rgb(17, 118, 249); font-weight: bold", state.getState());
|
|
32
|
+
this.previousState = null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Time
|
|
36
|
+
*
|
|
37
|
+
* Returns the time in which a given state transition completed
|
|
38
|
+
*/
|
|
39
|
+
get time() {
|
|
40
|
+
const date = new Date();
|
|
41
|
+
const mHours = date.getHours();
|
|
42
|
+
const hours = mHours > 12 ? mHours - 12 : mHours;
|
|
43
|
+
const mins = date.getMinutes();
|
|
44
|
+
const minutes = mins.toString().length === 1 ? `0${mins}` : mins;
|
|
45
|
+
const secs = date.getSeconds();
|
|
46
|
+
const seconds = secs.toString().length === 1 ? `0${secs}` : secs;
|
|
47
|
+
const milliseconds = date.getMilliseconds();
|
|
48
|
+
return `${hours}:${minutes}:${seconds}:${milliseconds}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.Logger = Logger;
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Profiler = void 0;
|
|
4
|
+
const Middleware_1 = require("../Middleware/Middleware");
|
|
5
|
+
/**
|
|
6
|
+
* Profiler
|
|
7
|
+
*
|
|
8
|
+
* A logger for state transitions exceeding a given threshold
|
|
9
|
+
* for duration:
|
|
10
|
+
*
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const State = new Galena([new Profiler()]);
|
|
13
|
+
* // if using isolated state instances:
|
|
14
|
+
* const MyState = new State(...args);
|
|
15
|
+
* MyState.registerMiddleware(new Profiler())
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
class Profiler extends Middleware_1.Middleware {
|
|
19
|
+
constructor(threshold = 16) {
|
|
20
|
+
super();
|
|
21
|
+
this.startTime = null;
|
|
22
|
+
this.threshold = threshold;
|
|
23
|
+
}
|
|
24
|
+
onBeforeUpdate(_) {
|
|
25
|
+
this.startTime = performance.now();
|
|
26
|
+
}
|
|
27
|
+
onUpdate(nextState) {
|
|
28
|
+
if (this.startTime) {
|
|
29
|
+
const endTime = performance.now();
|
|
30
|
+
const diff = endTime - this.startTime;
|
|
31
|
+
if (diff > this.threshold) {
|
|
32
|
+
console.warn("Slow state transition detected", nextState);
|
|
33
|
+
console.warn(`The last transition took ${diff}ms`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.Profiler = Profiler;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Profiler = exports.Logger = void 0;
|
|
4
|
+
var Logger_1 = require("./Logger");
|
|
5
|
+
Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return Logger_1.Logger; } });
|
|
6
|
+
var Profiler_1 = require("./Profiler");
|
|
7
|
+
Object.defineProperty(exports, "Profiler", { enumerable: true, get: function () { return Profiler_1.Profiler; } });
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./Galena"), exports);
|
|
18
|
+
__exportStar(require("./Middleware"), exports);
|
|
19
|
+
__exportStar(require("./Middlewares"), exports);
|