@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 ADDED
@@ -0,0 +1,534 @@
1
+ # Galena
2
+ Lightning fast platform agnostic state! Galena is a one-stop-shop for creating reactive state that supports your global application or individually isolated features. Galena operates on the premise of pub-sub and mutability allowing for significantly higher performance over more traditional state management utilities.
3
+
4
+ In Galena, your state architecture is a composition of reactive units that can be declared at any point in your application lifecycle. Your units of state can exist in isolation (similar to React Contexts) or as part of one or more global application states (similar to Redux). Galena offers a global application state solution that supports lazy initialization of state, performant updates, and a rich development API.
5
+
6
+ # Getting Started
7
+
8
+ ## Installation
9
+ ```bash
10
+ npm install --save galena
11
+ # or
12
+ yarn add galena
13
+ ```
14
+
15
+ ## Composing Your Application State
16
+ ### Global Application State Architecture
17
+ Creating a "global" application state begins with initializing a `Galena` instance. Your `Galena` instances act as a container from which units of state can be composed:
18
+
19
+ ```typescript
20
+ // AppState.ts
21
+ import { Galena, Logger, Profiler } from "galena";
22
+ import type { Middleware } from "galena";
23
+
24
+ const middleware: Middleware[] = [];
25
+
26
+ if(process.env.NODE_ENV === "development") {
27
+ middleware.push(new Logger(), new Profiler())
28
+ }
29
+
30
+ // Initialize Galena State!
31
+ export const AppState = new Galena(middleware);
32
+ ```
33
+
34
+ Now that we have our `AppState` instance, let's compose a unit of state for it!
35
+
36
+ ```typescript
37
+ // NavigationState.ts
38
+ import { AppState } from "./AppState.ts";
39
+
40
+ // Compose a unit of state attached to your Galena Instance
41
+ export const NavigationState = AppState.composeState("navigation", {
42
+ // initial state
43
+ currentRoute: "/",
44
+ userID: "123",
45
+ permittedRoutes: ["**/*"]
46
+ });
47
+ ```
48
+
49
+ Creating units of state using `AppState.composeState()` will scope your new unit of state to your `Galena` instance. You can then access, subscribe, and update your state using using your `Galena` instance or the unit of `State` returned from `AppState.composeState()`:
50
+
51
+ #### State Operations Using Your Galena Instance
52
+
53
+ ```typescript
54
+ // BusinessLogic.ts
55
+ import { AppState } from "./AppState.ts";
56
+
57
+ const subscription = AppState.subscribe("navigation", navigationState => {
58
+ // React to changes to Navigation state
59
+ });
60
+
61
+ // Set Navigation State!
62
+ AppState.update("navigation", state => {
63
+ state.currentRoute = "/contact-us";
64
+ });
65
+
66
+ // Clean up subscriptions
67
+ AppState.unsubscribe("navigation", subscription);
68
+ ```
69
+
70
+ #### State Operations Using Your Unit of State
71
+
72
+ ```typescript
73
+ // BusinessLogic.ts
74
+ import { NavigationState } from "./NavigationState";
75
+
76
+ const subscription = NavigationState.subscribe(state => {
77
+ // React to changes to Navigation state
78
+ });
79
+
80
+ // Set Navigation State!
81
+ NavigationState.update(state => {
82
+ state.currentRoute = "/contact-us";
83
+ });
84
+
85
+ // Clean up subscriptions
86
+ NavigationState.unsubscribe(subscription);
87
+ ```
88
+
89
+ Running mutations on individual units of state will automatically update your `Galena` instance's state! Your `Galena` instance will internally track changes to each unit of state that it composes.
90
+
91
+ ### Isolated State Architecture
92
+
93
+ You may also create units of state that are *not* connected to a "global" `Galena` instance. To promote flexibility for developers to organize their state however they wish, `Galena` exports its `State` object for usage directly:
94
+
95
+ ```typescript
96
+ import { State } from "galena";
97
+
98
+ // Create Your Isolated Unit of State
99
+ const FeatureState = new State("myFeature", {
100
+ // initial state
101
+ list: [1, 2, 3];
102
+ });
103
+
104
+ const subscription = FeatureState.subscribe((state) => {
105
+ // React to FeatureState changes!
106
+ });
107
+
108
+ FeatureState.update((state) => {
109
+ // Update feature state!
110
+ state.list.push(state.list.length);
111
+ });
112
+
113
+ // Clean up subscriptions
114
+ FeatureState.unsubscribe(subscription);
115
+ ```
116
+
117
+ The API for isolated units of State is the same as the API for units connected to a `Galena` instance. When using `Galena` it's totally normal to have one or more "global" states along side any number of island states for isolate-able features. The composition patterns that can be used for your application state are virtually limitless!
118
+
119
+ ## API Reference
120
+ ### Galena
121
+ Instances of `Galena` behave as a container for one or more units of `State`. Your `Galena` instance is designed to replicate the "global" application state pattern, but without the overhead of
122
+ 1. Declaring all of your units of state early in your application lifecycle
123
+ 2. Making complex mutations to large state objects.
124
+
125
+ In `Galena`, your "global" application state exists in the form of operable sub-structures that can be individually subscribed to and mutated. This means, mutating one piece of your `State` does not effect other units of your `State`. This allows for the relief of several performance bottlenecks that are common in state management libraries that offer "global" application states. In `Galena`, you get the performance of island architecture with the option to also have a predictable global application state.
126
+
127
+ #### Galena Public Methods
128
+
129
+ ```typescript
130
+ import { Galena, Logger, Profiler } from "galena";
131
+ import type { State } from "galena";
132
+
133
+ const AppState = new Galena(/* middleware */ [new Logger(), new Profiler()]);
134
+
135
+ /**
136
+ * Compose State
137
+ *
138
+ * Creates a unit of `State` connected to your `Galena` instance.
139
+ * Returns a unit of `State`
140
+ */
141
+ AppState.composeState("nameOfState" /* unique name */, /* initial state */, /* Optional Model */);
142
+
143
+ /**
144
+ * Get
145
+ *
146
+ * Returns a connected unit of `State` by name
147
+ */
148
+ AppState.get("nameOfState");
149
+
150
+ /**
151
+ * Update
152
+ *
153
+ * Mutates a unit of state by name. This method by default
154
+ * uses internal batching in order to optimize the dispatching
155
+ * of state changes to consumers. This method is the most
156
+ * performant way to mutate state in Galena!
157
+ */
158
+ AppState.update("nameOfState", (state) => {});
159
+
160
+ /**
161
+ * Background Update
162
+ *
163
+ * Runs a higher-priority mutation on a unit of state. This method
164
+ * will bypass batching in favor of a scheduled propagation of
165
+ * changes to subscribers of your state. This method is great for
166
+ * prioritizing state updates driven by frequent user-input such
167
+ * as typing into a form or game logic.
168
+ */
169
+ AppState.backgroundUpdate("nameOfState", (state) => {});
170
+
171
+ /**
172
+ * Priority Update
173
+ *
174
+ * Runs a highest-priority mutation on a unit of state. This method
175
+ * bypasses all batching and scheduling optimizations in Galena.
176
+ * When using `priorityUpdate()` your state changes are immediately
177
+ * propagated to your state subscribers ahead of all scheduled and
178
+ * batched updates. This method is great for usage with external
179
+ * scheduling mechanisms such as `requestAnimationFrame`, `intervals`,
180
+ * and/or `timeouts`
181
+ */
182
+ AppState.priorityUpdate("nameOfState", (state) => {});
183
+
184
+ /**
185
+ * Subscribe
186
+ *
187
+ * Registers a subscription on a unit of state
188
+ */
189
+ const subscription = AppState.subscribe("nameOfState", (state) => {});
190
+
191
+ /**
192
+ * Unsubscribe
193
+ *
194
+ * Closes an open subscription given a subscription ID
195
+ * returned by `new Galena().subscribe()`
196
+ */
197
+ AppState.unsubscribe(subscription);
198
+
199
+ /**
200
+ * Subscribe All
201
+ *
202
+ * Registers a global subscription on each State registered to
203
+ * your Galena instance. Your callback will be invoked any
204
+ * time a unit of state is updated
205
+ */
206
+ const subscription = AppState.subscribeAll(appState => {});
207
+
208
+ /**
209
+ * Unsubscribe All
210
+ *
211
+ * Closes an open global subscription by subscription ID
212
+ */
213
+ AppState.unsubscribeAll(subscription);
214
+ ```
215
+
216
+ ### State
217
+ While instances of `Galena` behave as a container for units of state, the `State` interface serves as the unit itself. The `State` interface has a predictable API designed to make composing your states simple and effective. Whether you compose your state using a "global" state or island architecture, the underlying API for your units of state look like the following:
218
+
219
+ ```typescript
220
+ import { State, Logger, Profiler } from "galena";
221
+
222
+ const MyState = new State(/* a unique name */ "myState", /* initial state */);
223
+
224
+ /**
225
+ * Get State
226
+ *
227
+ * Returns the current state
228
+ */
229
+ MyState.getState();
230
+
231
+ /**
232
+ * Update
233
+ *
234
+ * Mutates the unit of state using the callback provided. This method
235
+ * by default uses internal task batching in order to optimize
236
+ * dispatching state changes to consumers. This method is the most
237
+ * performant way to mutate state in Galena!
238
+ */
239
+ MyState.update((currentState, initialState) => {
240
+ currentState.someValue = "new value!"
241
+ });
242
+
243
+ /**
244
+ * Background Update
245
+ *
246
+ * Runs a higher-priority mutation on your state. This method
247
+ * will bypass batching in favor of a scheduled propagation of
248
+ * changes to subscribers of your state. This method is great for
249
+ * prioritizing state updates driven by frequent user-input such
250
+ * as typing into a form or game logic.
251
+ */
252
+ MyState.backgroundUpdate((currentState, initialState) => {
253
+ currentState.someValue = "new value!"
254
+ });
255
+
256
+ /**
257
+ * Priority Update
258
+ *
259
+ * Runs a highest-priority mutation on your state. This method
260
+ * bypasses all batching and scheduling optimizations in Galena.
261
+ * When using `priorityUpdate()` your state changes are immediately
262
+ * propagated to your state subscribers ahead of all scheduled and
263
+ * batched updates. This method is great for usage with external
264
+ * scheduling mechanisms such as `requestAnimationFrame`, `intervals`,
265
+ * and/or `timeouts`
266
+ */
267
+ MyState.priorityUpdate((currentState, initialState) => {
268
+ currentState.someValue = "new value!"
269
+ });
270
+
271
+ /**
272
+ * Reset
273
+ *
274
+ * Resets the current state back to its initial state
275
+ */
276
+ MyState.reset();
277
+
278
+ /**
279
+ * Register Middleware
280
+ *
281
+ * Applies any number of Middleware instances to your State
282
+ */
283
+ MyState.registerMiddleware(/* Middleware */ new Logger(), new Profiler());
284
+
285
+ /**
286
+ * Subscribe
287
+ *
288
+ * Given a callback, invokes the callback each time your state
289
+ * changes. Returns a subscription ID
290
+ */
291
+ const subscription = MyState.subscribe(state => {});
292
+
293
+ /**
294
+ * Unsubscribe
295
+ *
296
+ * Closes an open subscription given a subscription ID
297
+ * returned by `new State().subscribe()`
298
+ */
299
+ MyState.unsubscribe(subscription);
300
+
301
+ /**
302
+ * Mutation (protected)
303
+ *
304
+ * This method can be used to wrap arbitrary functions that
305
+ * when invoked will:
306
+ * 1. Notify your subscriptions with the latest state
307
+ * 2. Execute any registered middleware (such as loggers or
308
+ * profiling tools)
309
+ *
310
+ * Using this method, developers can trigger or extend `Galena`'s
311
+ * internals for dispatching events and mutating state to create
312
+ * proprietary processes for an individual unit of state.
313
+ *
314
+ * In order to access this method, you'll need to extend `State`
315
+ * using:
316
+ *
317
+ * ```typescript
318
+ * class MyState extends State {
319
+ * proprietaryMutation = this.mutation((...anyArgs) => {
320
+ * // any logic
321
+ * });
322
+ * }
323
+ * ```
324
+ */
325
+ MyState.mutation(/* callback */);
326
+ ```
327
+
328
+ ### Middleware
329
+ Galena supports developers creating enhancements for their usage of `Galena`. Out of the box `Galena` comes with middleware for Logging and Profiling that can be used for making development with `Galena` more intuitive. To opt into `Galena`'s built-in middleware, simply pass them to your `Galena` instance when calling `new Galena()`
330
+
331
+ #### Logging Middleware
332
+ Galena comes with a redux-style state transition logger that prints to the console each time state updates. The Logger will log the previous state, the current state, and tell you which unit of `State` has changed.
333
+
334
+ ```typescript
335
+ import { Galena, Logger } from "galena";
336
+
337
+ // Enable logging!
338
+ const AppState = new Galena([new Logger()]);
339
+ ```
340
+
341
+ #### Profiling Middleware
342
+ Galena also comes with a Profiler that can track the duration of all state transitions. When a state transition exceeds 16ms, a warning is printed to the console notifying the developer of a potential bottleneck in his or her application. By default the Profiler will log each time a state transition exceeds one full frame (16ms). This threshold can be adjusted by calling `new Profiler(/* any number of milliseconds */)`
343
+
344
+ ```typescript
345
+ import { Galena, Profiler } from "galena";
346
+
347
+ const AppState = new Galena([new Profiler()]);
348
+ ```
349
+
350
+ ### Middleware - Advanced Usage
351
+ Similar to a lot of stateful tools, `Galena` also exposes an API for creating your own Middleware. With it, you can do a lot of cool things for both development and productions environments. Let's first look at how to use middleware in `Galena`, then we'll walk through creating our own!
352
+
353
+ #### Applying Middleware
354
+ When applying middleware in `Galena`, you may choose to apply your middleware to *all* of your application state or just some of it. To apply middleware to each of your units of `State`, you can simply initialize `Galena` with the middleware that you enjoy using:
355
+ ```typescript
356
+ import { Galena, Profiler, Logger } from "galena";
357
+
358
+ export const AppState = new Galena([new Profiler(), new Logger()]);
359
+ ```
360
+ Using this method, whenever you create a new unit of state using `AppState.composeState()`, your `Profiler` and `Logger` will automatically register themselves on your new unit of State.
361
+
362
+ Alternatively, you may also choose to register a middleware on only some of your state:
363
+
364
+ ```typescript
365
+ import { Galena, Profiler, Logger } from "galena";
366
+
367
+ // Let's add logging to all of our units of State
368
+ export const AppState = new Galena([new Logger()]);
369
+
370
+ // Lets create an arbitrary unit of state and add profiling to it
371
+ export const FrequentlyUpdatedState = AppState.composeState("complexState", {
372
+ bigData: new Array(10000).fill("")
373
+ });
374
+
375
+ // Profiling is applied only to this unit of state
376
+ FrequentlyUpdatedState.registerMiddleware(new Profiler());
377
+ ```
378
+
379
+ #### Creating Middleware
380
+ Galena's middleware architecture operates on fixed set of events that are triggered each time a state mutation takes place. When state is mutated in your application, the `Middleware`'s `onBeforeUpdate()` and `onUpdate()` methods are called. Using these events, you can compose logic for auditing and enhancing your state updates! Let's take a look at a real world example.
381
+
382
+ Let's say we have an application that does not use typescript and we want to achieve type-safety for a unit of our application state. To achieve this, we'll create a middleware that validates changes to our state in real time.
383
+
384
+ In the example below, we'll create a unit of state holding unique identifiers for users that are connections of the current user:
385
+
386
+ ```typescript
387
+ export const CurrentUserState = AppState.composeState("currentUser", {
388
+ userID: 1,
389
+ username: "currentUser",
390
+ connectedUsers: ["2", "3", "4", "5"]
391
+ });
392
+ ```
393
+
394
+ Next, let's create our own custom middleware for ensuring that all entries in the `connectedUsers` array are strings:
395
+
396
+ ```typescript
397
+ import { Middleware } from "galena";
398
+
399
+ // Let's extend the Middleware class from the Galena library
400
+ export class ConnectedUsersMiddleware extends Middleware {
401
+ // A cache for the length of the array we want to audit
402
+ private totalArrayElements: number | null = null;
403
+
404
+ // On each update, let's cache the length of the array
405
+ override onBeforeUpdate({ state }: State) {
406
+ this.totalArrayElements = state.connectedUsers.length;
407
+ }
408
+
409
+ // When an update to state occurs let's check if the length
410
+ // of the connectedUsers array has changed
411
+ override onUpdate({ state }: State) {
412
+ const connectedUsers = state.connectedUsers
413
+ if(
414
+ this.totalArrayElements === null ||
415
+ connectedUsers.length === this.totalArrayElements
416
+ ) {
417
+ return;
418
+ }
419
+ // If the length of user connections has changed, let's validate that
420
+ // the new connection is, in fact, a string.
421
+ const newConnection = connectedUsers[connectedUsers.length - 1];
422
+ if(typeof newConnection !== "string") {
423
+ // If we find anything other than a string, let's log or throw an error
424
+ console.error(`A ${typeof newConnection} was added to the current user's connection array! This can create a bug in production!`)
425
+ }
426
+ }
427
+ }
428
+ ```
429
+
430
+ Next let's bring this middleware into our application!
431
+ ```typescript
432
+ import { State } from "galena";
433
+ import { ConnectedUsersMiddleware } from "./ConnectedUsersMiddleware";
434
+
435
+ export const CurrentUserState = AppState.composeState("currentUser", {
436
+ userID: 1,
437
+ username: "currentUser",
438
+ connectedUsers: ["2", "3", "4", "5"]
439
+ });
440
+
441
+ if(process.env.NODE_ENV !== "production") {
442
+ // Let's apply our custom middleware in development environments
443
+ CurrentUserState.registerMiddleware(new ConnectedUsersMiddleware());
444
+ }
445
+ ```
446
+
447
+ ### Let's Talk Architecture
448
+ The `Galena` library is designed to promote extension of its features. In doing so, it's possible to achieve a very strong Model/Controller layer for your applications. I'm going to demonstrate a few techniques for not only utilizing `Galena` as is, but building proprietary Models and Controllers for your applications.
449
+
450
+ #### Extending State
451
+ Galena's `State` interface is designed to be an out-of-the-box solution for housing any portion of your application's state. There are benefits however, to extending its functionality to compose proprietary models for your units of state:
452
+
453
+ ##### Creating State Models
454
+ ```typescript
455
+ // UserModel.ts
456
+ import { State } from "galena";
457
+
458
+ // Let's extend the `State` class for a hypothetical
459
+ // user schema
460
+ export class UserModel extends State<{
461
+ userID: string;
462
+ username: string;
463
+ connectedUsers: string[];
464
+ }> {
465
+ public addConnection(userID: string) {
466
+ this.update(state => {
467
+ state.connectedUsers.push(userID);
468
+ });
469
+ }
470
+
471
+ public updateUsername(username: string) {
472
+ this.update(state => {
473
+ state.username = username;
474
+ });
475
+ }
476
+ }
477
+ ```
478
+
479
+ Next, let's use our Model!
480
+
481
+ ```typescript
482
+ // AppState.ts
483
+ import { Galena, State } from "galena";
484
+ import { UserModel } from "./UserModel";
485
+
486
+ export const AppState = new Galena(/* middleware */);
487
+
488
+ // Let's apply our UserModel to AppState
489
+ export const UserState = AppState.composeState("currentUser", {
490
+ userID: 1,
491
+ username: "currentUser",
492
+ connectedUsers: ["2", "3", "4", "5"]
493
+ }, UserModel); // Specify the UserModel here so that our new unit is created using the `UserModel` instead of `State`
494
+ ```
495
+
496
+ Now that we have our current user in our Galena State, we can create subscriptions and updates!
497
+
498
+ ```tsx
499
+ import { AppState } from "./AppState";
500
+
501
+ const subscriptionID = AppState.subscribe("currentUser", state => {
502
+ // React to changes to the current user!
503
+ });
504
+
505
+ // The UserModel's custom methods are available on your Galena state!
506
+ AppState.get("currentUser").updateUsername("awesomeUser");
507
+
508
+ AppState.get("currentUser").addConnection("6");
509
+ ```
510
+
511
+ Using this extension pattern, each unit of `State` can exist as it's own model with abstractions for proprietary mutations and business logic. Although slightly more complex on the surface, this pattern in very large applications will reduce the complexity of state management significantly. It'll also replicate what one might find at the persistence layer of server-side code - where persisted data structures are often modeled along side their mutation logic when interacting with a database or GQL Resolver. Because of this, the extension pattern's syntactical uniformity may be beneficial for teams that lean fullstack instead of frontend/backend!
512
+
513
+ ### Let's talk Performance!
514
+ In several areas of the `Galena` readme, there are references to performance and the relief of bottlenecks. In this section I'd like to share some hard numbers relating to areas of `Galena`'s architecture.
515
+
516
+ #### In Place Mutations
517
+ In Galena, state mutations can occur in-place (`O(1)` space). While you can use immutable data structures if you like, it's not required when using this library - by design. This is because even the most basic immutable state updates are about 4-5x slower than mutable state updates. This `4-5x` balloons even larger the more your state grows. When building `Galena`, I wanted to remove the notion of immutability wherever possible.
518
+
519
+ #### Composition of State
520
+ To further promote efficient state mutations, `Galena`'s composition architecture allows units of state to be operable without effecting adjacent units of state. This means you can can safely make *extremely* frequent updates to your state and be sure that your updates are scoped to specific units - and not your *entire* application state. This optimization extends to subscriptions as well. The only subscriptions that will ever be triggered when state changes, are the ones directly bound to the unit that is changing.
521
+
522
+ #### Benchmarking
523
+ Using 2 identical applications, I've profiled the performance of Galena vs. Redux using 10,000 state updates and 10 connected React Components that'll rerender on each state change. The results looked like the following:
524
+
525
+ ##### Galena vs. Redux with no middleware
526
+ 1. Redux - `33.6ms` to complete 10,000 state updates
527
+ 2. Galena - `5.5ms` to complete 10,000 state updates
528
+
529
+ As the application scales with more state updates and connected components, the spread between `Galena` and Redux grows even further. Although I don't believe most applications will ever require 10,000 immediate state updates (unless building a game-like experience), `Galena` does relieve the bottle-necks of popular state management utilities quite well.
530
+
531
+ ### Support for Frontend Frameworks!
532
+ `Galena` provides bindings for React through [react-galena](https://github.com/alexfigliolia/react-galena). This package provides factories for generating HOC's and hooks from your Galena instances and units of State!
533
+
534
+
@@ -0,0 +1,152 @@
1
+ import type { Middleware } from "../Middleware/Middleware";
2
+ import { State } from "./State";
3
+ /**
4
+ * ## Galena
5
+ *
6
+ * A performant global state solution that scales
7
+ *
8
+ * ### Creating State
9
+ *
10
+ * ```typescript
11
+ * // AppState.ts
12
+ * import { Galena } from "../galena";
13
+ *
14
+ * const AppState = new Galena([...middleware]);
15
+ *
16
+ * const NavigationState = AppState.composeState("navigation", {
17
+ * currentRoute: "/",
18
+ * userID: "12345",
19
+ * permittedRoutes: ["/*"]
20
+ * });
21
+ * ```
22
+ *
23
+ * ### Subscribing to State Changes
24
+ * #### Using the Galena Instance
25
+ * ```typescript
26
+ * import { AppState } from "./AppState";
27
+ *
28
+ * AppState.subscribe(appState => {
29
+ * const navState = appState.get("navigation");
30
+ * const { currentRoute } = navState.state;
31
+ * // do something with state changes!
32
+ * });
33
+ * ```
34
+ * #### Using the State Instance
35
+ * ```typescript
36
+ * NavigationState.subscribe(navigation => {
37
+ * const { currentRoute } = navigation.state
38
+ * // do something with state changes!
39
+ * });
40
+ * ```
41
+ *
42
+ * #### Using Global Subscriptions
43
+ * ```typescript
44
+ * NavigationState.subscribeAll(galenaInstance => {
45
+ * const { currentRoute } = galenaInstance.get("navigation").state
46
+ * // do something with state changes!
47
+ * });
48
+ * ```
49
+ *
50
+ * ### Mutating State
51
+ * ```typescript
52
+ * NavigationState.update(state => {
53
+ * state.currentRoute = "/profile";
54
+ * // You can mutate state without creating new objects!
55
+ * });
56
+ * ```
57
+ */
58
+ export declare class Galena<T extends Record<string, State<any>> = Record<string, State<any>>> {
59
+ readonly state: T;
60
+ private readonly middleware;
61
+ private readonly IDs;
62
+ private readonly subscriptions;
63
+ constructor(middleware?: Middleware[]);
64
+ /**
65
+ * Compose State
66
+ *
67
+ * Creates a new `State` instance and returns it. Your new state
68
+ * becomes immediately available on your `Galena` instance and
69
+ * is wired into your middleware. All existing subscriptions to
70
+ * state will automatically receive updates when your new unit of
71
+ * state updates
72
+ */
73
+ composeState<S extends any, M extends typeof State<S> = typeof State<S>>(name: string, initialState: S, Model?: M): InstanceType<M>;
74
+ /**
75
+ * Get
76
+ *
77
+ * Returns a unit of `State` by name
78
+ */
79
+ get<K extends keyof T>(name: K): T[K];
80
+ /**
81
+ * Mutable
82
+ *
83
+ * Returns a mutable state instance
84
+ */
85
+ private get mutable();
86
+ /**
87
+ * Update
88
+ *
89
+ * Runs a mutation on the specified unit of state
90
+ */
91
+ update<K extends keyof T>(name: K, mutation: Parameters<T[K]["update"]>["0"]): any;
92
+ /**
93
+ * Background Update
94
+ *
95
+ * Runs a higher priority mutation on the specified unit of
96
+ * state
97
+ */
98
+ backgroundUpdate<K extends keyof T>(name: K, mutation: Parameters<T[K]["backgroundUpdate"]>["0"]): any;
99
+ /**
100
+ * Priority Update
101
+ *
102
+ * Runs an immediate priority mutation on the specified unit
103
+ * of state
104
+ */
105
+ priorityUpdate<K extends keyof T>(name: K, mutation: Parameters<T[K]["priorityUpdate"]>["0"]): any;
106
+ /**
107
+ * Subscribe
108
+ *
109
+ * Given the name of a unit of state, this method registers
110
+ * a subscription on the target state instance. The callback
111
+ * you provide will execute each time state changes. Returns
112
+ * a unique identifier for your subscription. To clean up your
113
+ * subscription, call `Galena.unsubscribe()` with the ID returned
114
+ * by this method
115
+ */
116
+ subscribe<K extends keyof T>(name: K, mutation: Parameters<T[K]["subscribe"]>["0"]): string;
117
+ /**
118
+ * Unsubscribe
119
+ *
120
+ * Given a subscription ID returned from the `subscribe` method,
121
+ * this method removes and cleans up the corresponding subscription
122
+ */
123
+ unsubscribe<K extends keyof T>(name: K, ID: string): boolean | undefined;
124
+ /**
125
+ * Subscribe All
126
+ *
127
+ * Registers a callback on each registered `State` instance and
128
+ * is invoked each time your state changes. Using `Galena`'s
129
+ * `subscribeAll` method, although performant, can be less
130
+ * performant than subscribing directly to a target `State`
131
+ * instance using `Galena.subscribe()`. To clean up your
132
+ * subscription, call `Galena.unsubscribeAll()` with the ID
133
+ * returned
134
+ */
135
+ subscribeAll(callback: (state: Galena<T>) => void): string;
136
+ /**
137
+ * Unsubscribe
138
+ *
139
+ * Given a subscription ID returned from the `subscribeAll()` method,
140
+ * this method removes and cleans up the corresponding subscription
141
+ */
142
+ unsubscribeAll(ID: string): void;
143
+ /**
144
+ * ReIndex Subscriptions
145
+ *
146
+ * When units of state are created lazily, this method updates
147
+ * each existing subscription to receive mutations occurring on
148
+ * recently created `State` instances that post-date prior
149
+ * subscriptions
150
+ */
151
+ private reIndexSubscriptions;
152
+ }