@figliolia/galena 2.0.0 → 2.0.2

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 CHANGED
@@ -531,4 +531,5 @@ As the application scales with more state updates and connected components, the
531
531
  ### Support for Frontend Frameworks!
532
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
533
 
534
-
534
+ #### Demo Application
535
+ To see some basic usage using Galena with React, please check out this [Example App](https://github.com/alexfigliolia/galena-quick-start)
@@ -1,5 +1,6 @@
1
1
  import type { Middleware } from "../Middleware/Middleware";
2
2
  import { State } from "./State";
3
+ import { Guards } from "./Guards";
3
4
  /**
4
5
  * ## Galena
5
6
  *
@@ -55,11 +56,11 @@ import { State } from "./State";
55
56
  * });
56
57
  * ```
57
58
  */
58
- export declare class Galena<T extends Record<string, State<any>> = Record<string, State<any>>> {
59
+ export declare class Galena<T extends Record<string, State<any>> = Record<string, State<any>>> extends Guards {
59
60
  readonly state: T;
61
+ private readonly subscriptions;
60
62
  private readonly middleware;
61
63
  private readonly IDs;
62
- private readonly subscriptions;
63
64
  constructor(middleware?: Middleware[]);
64
65
  /**
65
66
  * Compose State
@@ -76,7 +77,7 @@ export declare class Galena<T extends Record<string, State<any>> = Record<string
76
77
  *
77
78
  * Returns a unit of `State` by name
78
79
  */
79
- get<K extends keyof T>(name: K): T[K];
80
+ get<K extends Extract<keyof T, string>>(name: K): T[K];
80
81
  /**
81
82
  * Mutable
82
83
  *
@@ -88,21 +89,21 @@ export declare class Galena<T extends Record<string, State<any>> = Record<string
88
89
  *
89
90
  * Runs a mutation on the specified unit of state
90
91
  */
91
- update<K extends keyof T>(name: K, mutation: Parameters<T[K]["update"]>["0"]): any;
92
+ update<K extends Extract<keyof T, string>>(name: K, mutation: Parameters<T[K]["update"]>["0"]): any;
92
93
  /**
93
94
  * Background Update
94
95
  *
95
96
  * Runs a higher priority mutation on the specified unit of
96
97
  * state
97
98
  */
98
- backgroundUpdate<K extends keyof T>(name: K, mutation: Parameters<T[K]["backgroundUpdate"]>["0"]): any;
99
+ backgroundUpdate<K extends Extract<keyof T, string>>(name: K, mutation: Parameters<T[K]["backgroundUpdate"]>["0"]): any;
99
100
  /**
100
101
  * Priority Update
101
102
  *
102
103
  * Runs an immediate priority mutation on the specified unit
103
104
  * of state
104
105
  */
105
- priorityUpdate<K extends keyof T>(name: K, mutation: Parameters<T[K]["priorityUpdate"]>["0"]): any;
106
+ priorityUpdate<K extends Extract<keyof T, string>>(name: K, mutation: Parameters<T[K]["priorityUpdate"]>["0"]): any;
106
107
  /**
107
108
  * Subscribe
108
109
  *
@@ -113,14 +114,14 @@ export declare class Galena<T extends Record<string, State<any>> = Record<string
113
114
  * subscription, call `Galena.unsubscribe()` with the ID returned
114
115
  * by this method
115
116
  */
116
- subscribe<K extends keyof T>(name: K, callback: Parameters<T[K]["subscribe"]>["0"]): string;
117
+ subscribe<K extends Extract<keyof T, string>>(name: K, callback: Parameters<T[K]["subscribe"]>["0"]): string;
117
118
  /**
118
119
  * Unsubscribe
119
120
  *
120
121
  * Given a subscription ID returned from the `subscribe` method,
121
122
  * this method removes and cleans up the corresponding subscription
122
123
  */
123
- unsubscribe<K extends keyof T>(name: K, ID: string): boolean | undefined;
124
+ unsubscribe<K extends Extract<keyof T, string>>(name: K, ID: string): boolean | undefined;
124
125
  /**
125
126
  * Subscribe All
126
127
  *
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Galena = void 0;
4
4
  const event_emitter_1 = require("@figliolia/event-emitter");
5
5
  const State_1 = require("./State");
6
+ const Guards_1 = require("./Guards");
6
7
  /**
7
8
  * ## Galena
8
9
  *
@@ -58,12 +59,13 @@ const State_1 = require("./State");
58
59
  * });
59
60
  * ```
60
61
  */
61
- class Galena {
62
+ class Galena extends Guards_1.Guards {
62
63
  constructor(middleware = []) {
64
+ super();
63
65
  this.state = {};
66
+ this.subscriptions = new Map();
64
67
  this.middleware = [];
65
68
  this.IDs = new event_emitter_1.AutoIncrementingID();
66
- this.subscriptions = new Map();
67
69
  this.middleware = middleware;
68
70
  }
69
71
  /**
@@ -78,6 +80,7 @@ class Galena {
78
80
  composeState(name, initialState,
79
81
  // @ts-ignore
80
82
  Model = (State_1.State)) {
83
+ this.guardDuplicateStates(name, this.state);
81
84
  const state = new Model(name, initialState);
82
85
  state.registerMiddleware(...this.middleware);
83
86
  this.mutable[name] = state;
@@ -90,6 +93,7 @@ class Galena {
90
93
  * Returns a unit of `State` by name
91
94
  */
92
95
  get(name) {
96
+ this.warnForUndefinedStates(name, this.state);
93
97
  return this.state[name];
94
98
  }
95
99
  /**
@@ -160,7 +164,6 @@ class Galena {
160
164
  * returned
161
165
  */
162
166
  subscribeAll(callback) {
163
- const subscriptionID = this.IDs.get();
164
167
  const stateSubscriptions = [];
165
168
  for (const key in this.state) {
166
169
  stateSubscriptions.push([
@@ -170,6 +173,7 @@ class Galena {
170
173
  }),
171
174
  ]);
172
175
  }
176
+ const subscriptionID = this.IDs.get();
173
177
  this.subscriptions.set(subscriptionID, stateSubscriptions);
174
178
  return subscriptionID;
175
179
  }
@@ -0,0 +1,36 @@
1
+ import type { State } from "./State";
2
+ /**
3
+ * Guards
4
+ *
5
+ * Development-only warnings and runtime errors designed to
6
+ * guard developers against possible pitfalls when using
7
+ * Galena. This interface provides composable error and
8
+ * warning methods that can be used to prevent invalid usage
9
+ * of the library
10
+ */
11
+ export declare class Guards {
12
+ /**
13
+ * Warn For Undefined States
14
+ *
15
+ * In Galena, it's normal to lazy initialize a unit of state
16
+ * in attached to a `Galena` instance. This warning lets
17
+ * developers know that they are attempting to manipulate a
18
+ * unit of state that has not yet been initialized
19
+ */
20
+ protected warnForUndefinedStates: (name: string, state: Record<string, State<any>>) => void;
21
+ /**
22
+ * Guard Duplicate States
23
+ *
24
+ * Throws an error if a developer attempts to create
25
+ * more than one state with the same name on a single
26
+ * `Galena` instance
27
+ */
28
+ protected guardDuplicateStates: (name: string, state: Record<string, State<any>>) => void;
29
+ /**
30
+ * Development Guard
31
+ *
32
+ * Wraps a provided function in a check for `process.env.NODE_ENV`
33
+ * equaling "development". Returns the callback
34
+ */
35
+ private developmentGuard;
36
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Guards = void 0;
4
+ /**
5
+ * Guards
6
+ *
7
+ * Development-only warnings and runtime errors designed to
8
+ * guard developers against possible pitfalls when using
9
+ * Galena. This interface provides composable error and
10
+ * warning methods that can be used to prevent invalid usage
11
+ * of the library
12
+ */
13
+ class Guards {
14
+ constructor() {
15
+ /**
16
+ * Warn For Undefined States
17
+ *
18
+ * In Galena, it's normal to lazy initialize a unit of state
19
+ * in attached to a `Galena` instance. This warning lets
20
+ * developers know that they are attempting to manipulate a
21
+ * unit of state that has not yet been initialized
22
+ */
23
+ this.warnForUndefinedStates = this.developmentGuard((name, state) => {
24
+ if (!process.env.IGNORE_LAZY_STATE_WARNINGS && !(name in state)) {
25
+ console.warn(`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. To make these warnings go away, you can enable this process argument - "IGNORE_WARNINGS_FOR_LAZY_STATES=1"`);
26
+ }
27
+ });
28
+ /**
29
+ * Guard Duplicate States
30
+ *
31
+ * Throws an error if a developer attempts to create
32
+ * more than one state with the same name on a single
33
+ * `Galena` instance
34
+ */
35
+ this.guardDuplicateStates = this.developmentGuard((name, state) => {
36
+ if (name in state) {
37
+ throw new Error(`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`);
38
+ }
39
+ });
40
+ }
41
+ /**
42
+ * Development Guard
43
+ *
44
+ * Wraps a provided function in a check for `process.env.NODE_ENV`
45
+ * equaling "development". Returns the callback
46
+ */
47
+ developmentGuard(guard) {
48
+ return (...args) => {
49
+ if (process.env.NODE_ENV === "development") {
50
+ guard(...args);
51
+ }
52
+ };
53
+ }
54
+ }
55
+ exports.Guards = Guards;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@figliolia/galena",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "A performant state management library supporting mutable state, batched updates, middleware and a rich development API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -3,6 +3,7 @@ import { AutoIncrementingID } from "@figliolia/event-emitter";
3
3
  import type { Middleware } from "Middleware/Middleware";
4
4
 
5
5
  import { State } from "Galena/State";
6
+ import { Guards } from "./Guards";
6
7
 
7
8
  /**
8
9
  * ## Galena
@@ -61,15 +62,16 @@ import { State } from "Galena/State";
61
62
  */
62
63
  export class Galena<
63
64
  T extends Record<string, State<any>> = Record<string, State<any>>
64
- > {
65
+ > extends Guards {
65
66
  public readonly state = {} as T;
66
- private readonly middleware: Middleware[] = [];
67
- private readonly IDs = new AutoIncrementingID();
68
67
  private readonly subscriptions = new Map<
69
68
  string,
70
69
  [state: string, ID: string][]
71
70
  >();
71
+ private readonly middleware: Middleware[] = [];
72
+ private readonly IDs = new AutoIncrementingID();
72
73
  constructor(middleware: Middleware[] = []) {
74
+ super();
73
75
  this.middleware = middleware;
74
76
  }
75
77
 
@@ -91,6 +93,7 @@ export class Galena<
91
93
  // @ts-ignore
92
94
  Model: M = State<S>
93
95
  ) {
96
+ this.guardDuplicateStates(name, this.state);
94
97
  const state = new Model(name, initialState);
95
98
  state.registerMiddleware(...this.middleware);
96
99
  this.mutable[name] = state;
@@ -103,7 +106,8 @@ export class Galena<
103
106
  *
104
107
  * Returns a unit of `State` by name
105
108
  */
106
- public get<K extends keyof T>(name: K): T[K] {
109
+ public get<K extends Extract<keyof T, string>>(name: K): T[K] {
110
+ this.warnForUndefinedStates(name, this.state);
107
111
  return this.state[name];
108
112
  }
109
113
 
@@ -121,7 +125,7 @@ export class Galena<
121
125
  *
122
126
  * Runs a mutation on the specified unit of state
123
127
  */
124
- public update<K extends keyof T>(
128
+ public update<K extends Extract<keyof T, string>>(
125
129
  name: K,
126
130
  mutation: Parameters<T[K]["update"]>["0"]
127
131
  ) {
@@ -134,7 +138,7 @@ export class Galena<
134
138
  * Runs a higher priority mutation on the specified unit of
135
139
  * state
136
140
  */
137
- public backgroundUpdate<K extends keyof T>(
141
+ public backgroundUpdate<K extends Extract<keyof T, string>>(
138
142
  name: K,
139
143
  mutation: Parameters<T[K]["backgroundUpdate"]>["0"]
140
144
  ) {
@@ -147,7 +151,7 @@ export class Galena<
147
151
  * Runs an immediate priority mutation on the specified unit
148
152
  * of state
149
153
  */
150
- public priorityUpdate<K extends keyof T>(
154
+ public priorityUpdate<K extends Extract<keyof T, string>>(
151
155
  name: K,
152
156
  mutation: Parameters<T[K]["priorityUpdate"]>["0"]
153
157
  ) {
@@ -164,7 +168,7 @@ export class Galena<
164
168
  * subscription, call `Galena.unsubscribe()` with the ID returned
165
169
  * by this method
166
170
  */
167
- public subscribe<K extends keyof T>(
171
+ public subscribe<K extends Extract<keyof T, string>>(
168
172
  name: K,
169
173
  callback: Parameters<T[K]["subscribe"]>["0"]
170
174
  ) {
@@ -177,7 +181,7 @@ export class Galena<
177
181
  * Given a subscription ID returned from the `subscribe` method,
178
182
  * this method removes and cleans up the corresponding subscription
179
183
  */
180
- public unsubscribe<K extends keyof T>(name: K, ID: string) {
184
+ public unsubscribe<K extends Extract<keyof T, string>>(name: K, ID: string) {
181
185
  return this.get(name).unsubscribe(ID);
182
186
  }
183
187
 
@@ -193,7 +197,6 @@ export class Galena<
193
197
  * returned
194
198
  */
195
199
  public subscribeAll(callback: (nextState: T) => void) {
196
- const subscriptionID = this.IDs.get();
197
200
  const stateSubscriptions: [state: string, ID: string][] = [];
198
201
  for (const key in this.state) {
199
202
  stateSubscriptions.push([
@@ -203,6 +206,7 @@ export class Galena<
203
206
  }),
204
207
  ]);
205
208
  }
209
+ const subscriptionID = this.IDs.get();
206
210
  this.subscriptions.set(subscriptionID, stateSubscriptions);
207
211
  return subscriptionID;
208
212
  }
@@ -0,0 +1,67 @@
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 = this.developmentGuard(
22
+ <T extends Record<string, State<any>>, K extends Extract<keyof T, string>>(
23
+ name: K,
24
+ state: T
25
+ ) => {
26
+ if (!process.env.IGNORE_LAZY_STATE_WARNINGS && !(name in state)) {
27
+ console.warn(
28
+ `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. To make these warnings go away, you can enable this process argument - "IGNORE_LAZY_STATE_WARNING=1"`
29
+ );
30
+ }
31
+ }
32
+ );
33
+
34
+ /**
35
+ * Guard Duplicate States
36
+ *
37
+ * Throws an error if a developer attempts to create
38
+ * more than one state with the same name on a single
39
+ * `Galena` instance
40
+ */
41
+ protected guardDuplicateStates = this.developmentGuard(
42
+ <T extends Record<string, State<any>>, K extends Extract<keyof T, string>>(
43
+ name: K,
44
+ state: T
45
+ ) => {
46
+ if (name in state) {
47
+ throw new Error(
48
+ `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`
49
+ );
50
+ }
51
+ }
52
+ );
53
+
54
+ /**
55
+ * Development Guard
56
+ *
57
+ * Wraps a provided function in a check for `process.env.NODE_ENV`
58
+ * equaling "development". Returns the callback
59
+ */
60
+ private developmentGuard<F extends (...args: any[]) => void>(guard: F) {
61
+ return (...args: Parameters<F>) => {
62
+ if (process.env.NODE_ENV === "development") {
63
+ guard(...args);
64
+ }
65
+ };
66
+ }
67
+ }