@inglorious/store 5.0.0 → 5.0.1

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
@@ -3,9 +3,100 @@
3
3
  [![NPM version](https://img.shields.io/npm/v/@inglorious/store.svg)](https://www.npmjs.com/package/@inglorious/store)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- The core, environment-agnostic state management library for the [Inglorious Engine](https://github.com/IngloriousCoderz/inglorious-engine).
6
+ **State management inspired by video games.**
7
7
 
8
- This package provides a powerful and predictable state container based on an **Entity-Component-System (ECS)** architecture, inspired by **[Redux](https://redux.js.org/)** but tailored for game development. It can be used in any JavaScript environment, including Node.js servers and browsers.
8
+ Inglorious Store brings battle-tested patterns from game development to modern web applications. If your app needs real-time updates, multiplayer features, or complex interactive state, you'll benefit from the same techniques that power multiplayer games and collaborative tools like Figma.
9
+
10
+ Perfect for: real-time collaboration, live dashboards, chat apps, interactive visualizations, and any application where state synchronization matters.
11
+
12
+ ---
13
+
14
+ ## Why Video Game Patterns?
15
+
16
+ Video games solved distributed state synchronization decades ago. They handle:
17
+
18
+ - **60fps updates** with thousands of entities
19
+ - **Multiplayer** with lag compensation and state sync
20
+ - **Deterministic simulation** for replays and debugging
21
+ - **Complex interactions** between many objects
22
+
23
+ If it works for games, it'll handle your app's state with ease.
24
+
25
+ ---
26
+
27
+ ## Key Features
28
+
29
+ ### 🎮 **Entity-Based State**
30
+
31
+ Define behavior once, reuse it across all instances of the same type. Perfect for managing collections (todos, messages, cart items).
32
+
33
+ ```javascript
34
+ // Define behavior for ALL todos
35
+ const todoType = {
36
+ toggle(todo, id) {
37
+ if (todo.id !== id) return
38
+ todo.completed = !todo.completed
39
+ },
40
+ }
41
+
42
+ // Toggle specific todos
43
+ store.notify("toggle", "todo-1")
44
+ store.notify("toggle", "todo-2")
45
+ ```
46
+
47
+ > **Important:** `toggle` is not a method—it's an **event handler**. When you notify an event, it's broadcast to **all entities** that have that handler (pub/sub pattern). Use the payload to filter which entities should respond.
48
+
49
+ ### 🔄 **Event Queue with Batching**
50
+
51
+ Events are queued and processed together, preventing cascading updates and enabling predictable state changes.
52
+
53
+ ```javascript
54
+ // Dispatch multiple events
55
+ store.notify("increment", "counter-1")
56
+ store.notify("increment", "counter-2")
57
+ store.notify("increment", "counter-3")
58
+
59
+ // Process all at once (single React re-render)
60
+ store.update()
61
+ ```
62
+
63
+ ### ⏱️ **Time-Travel Debugging**
64
+
65
+ Save and replay state at any point—built-in, not an afterthought.
66
+
67
+ ```javascript
68
+ const snapshot = store.getState()
69
+ // ... user makes changes ...
70
+ store.setState(snapshot) // Instant undo
71
+ ```
72
+
73
+ ### 🌐 **Multiplayer-Ready**
74
+
75
+ Synchronize state across clients by sending serializable events. Same events + same handlers = guaranteed sync.
76
+
77
+ ```javascript
78
+ socket.on("userAction", (event) => {
79
+ store.notify(event.type, event.payload)
80
+ // All clients stay in perfect sync
81
+ })
82
+ ```
83
+
84
+ ### ✍️ **Ergonomic Immutability**
85
+
86
+ Write code that looks mutable, get immutable updates automatically via [Mutative](https://mutative.js.org/).
87
+
88
+ ```javascript
89
+ // Looks like mutation, but creates new immutable state
90
+ const todoType = {
91
+ rename(todo, text) {
92
+ todo.text = text // So clean!
93
+ },
94
+ }
95
+ ```
96
+
97
+ ### 🔗 **Redux-Compatible**
98
+
99
+ Works with `react-redux` and Redux DevTools. Provides both `notify()` and `dispatch()` for compatibility.
9
100
 
10
101
  ---
11
102
 
@@ -17,154 +108,474 @@ npm install @inglorious/store
17
108
 
18
109
  ---
19
110
 
111
+ ## Quick Start
112
+
113
+ ### Simple Counter Example
114
+
115
+ ```javascript
116
+ import { createStore } from "@inglorious/store"
117
+
118
+ // Types can be a single behavior (not an array) for simplicity
119
+ const types = {
120
+ counter: {
121
+ increment(counter) {
122
+ counter.value++
123
+ },
124
+ decrement(counter) {
125
+ counter.value--
126
+ },
127
+ },
128
+ }
129
+
130
+ const entities = {
131
+ "counter-1": { type: "counter", value: 0 },
132
+ "counter-2": { type: "counter", value: 10 },
133
+ }
134
+
135
+ const store = createStore({ types, entities })
136
+
137
+ // One event updates ALL counters
138
+ store.notify("increment")
139
+ store.update()
140
+
141
+ console.log(store.getState().entities["counter-1"].value) // => 1
142
+ console.log(store.getState().entities["counter-2"].value) // => 11
143
+
144
+ // To update just one counter, add filtering logic in the handler
145
+ ```
146
+
147
+ ### Complete Todo App Example
148
+
149
+ ```javascript
150
+ import { createStore } from "@inglorious/store"
151
+ import { createSelector } from "@inglorious/store/select"
152
+
153
+ // 1. Define types (can be a single behavior or array of behaviors)
154
+ const types = {
155
+ form: {
156
+ inputChange(entity, value) {
157
+ entity.value = value
158
+ },
159
+ formSubmit(entity) {
160
+ entity.value = ""
161
+ },
162
+ },
163
+
164
+ list: {
165
+ formSubmit(entity, value) {
166
+ entity.tasks.push({
167
+ id: entity.tasks.length + 1,
168
+ text: value,
169
+ completed: false,
170
+ })
171
+ },
172
+ toggleClick(entity, id) {
173
+ const task = entity.tasks.find((task) => task.id === id)
174
+ task.completed = !task.completed
175
+ },
176
+ deleteClick(entity, id) {
177
+ const index = entity.tasks.findIndex((task) => task.id === id)
178
+ entity.tasks.splice(index, 1)
179
+ },
180
+ clearClick(entity) {
181
+ entity.tasks = entity.tasks.filter((task) => !task.completed)
182
+ },
183
+ },
184
+
185
+ footer: {
186
+ filterClick(entity, filter) {
187
+ entity.activeFilter = filter
188
+ },
189
+ },
190
+ }
191
+
192
+ // 2. Define initial entities
193
+ const entities = {
194
+ form: {
195
+ type: "form",
196
+ value: "",
197
+ },
198
+ list: {
199
+ type: "list",
200
+ tasks: [],
201
+ },
202
+ footer: {
203
+ type: "footer",
204
+ activeFilter: "all",
205
+ },
206
+ }
207
+
208
+ // 3. Create store
209
+ const store = createStore({ types, entities })
210
+
211
+ // 4. Create selectors
212
+ const selectTasks = ({ entities }) => entities.list.tasks
213
+ const selectActiveFilter = ({ entities }) => entities.footer.activeFilter
214
+
215
+ const selectFilteredTasks = createSelector(
216
+ [selectTasks, selectActiveFilter],
217
+ (tasks, activeFilter) => {
218
+ switch (activeFilter) {
219
+ case "active":
220
+ return tasks.filter((t) => !t.completed)
221
+ case "completed":
222
+ return tasks.filter((t) => t.completed)
223
+ default:
224
+ return tasks
225
+ }
226
+ },
227
+ )
228
+
229
+ // 5. Subscribe to changes
230
+ store.subscribe(() => {
231
+ console.log("Filtered tasks:", selectFilteredTasks(store.getState()))
232
+ })
233
+
234
+ // 6. Dispatch events (use notify or dispatch - both work!)
235
+ store.notify("inputChange", "Buy milk")
236
+ store.notify("formSubmit", store.getState().entities.form.value)
237
+ store.notify("toggleClick", 1) // Only todo with id=1 will respond
238
+ store.notify("filterClick", "active")
239
+
240
+ // 7. Process event queue
241
+ store.update()
242
+ ```
243
+
244
+ ---
245
+
20
246
  ## Core Concepts
21
247
 
22
- The state management is built on a few simple principles:
248
+ ### Pub/Sub Event Architecture
249
+
250
+ **This is not OOP with methods—it's a pub/sub (publish/subscribe) event system.**
23
251
 
24
- 1. **Entities and Properties**: The state is composed of **entities**, which are unique objects. Each entity has a **type** and a set of properties (e.g., `position: v(0, 0, 0)`, `health: 100`). Unlike a traditional ECS, properties are not grouped into explicit components.
252
+ When you call `store.notify('toggle', 'todo-1')`, the `toggle` event is broadcast to **all entities**. Any entity that has a `toggle` handler will process the event and decide whether to respond based on the payload.
25
253
 
26
- 2. **Types and Behaviors**: The logic for how entities and the overall state change is defined in **types** and **systems**.
27
- - **Types** are arrays of **behaviors**. A behavior is an object that contains event handlers (e.g., `update(entity, dt) { ... }`). Behaviors are composable, allowing you to define a type by combining multiple sets of properties and event handlers.
28
- - **Systems** are objects that contain event handlers to operate on the global state or manage interactions between entities.
254
+ ```javascript
255
+ const todoType = {
256
+ // This handler runs for EVERY todo when 'toggle' is notified
257
+ toggle(todo, id) {
258
+ if (todo.id !== id) return // Filter: only this todo responds
259
+ todo.completed = !todo.completed
260
+ },
261
+ }
29
262
 
30
- 3. **Events and State Updates**: The only way to change the state is by issuing an **event**. An event is a plain object describing what happened (e.g., `{ type: 'move', payload: { id: 'player1', dx: 1 } }`).
31
- - The store processes events by first applying behaviors defined on an entity's type, and then running the logic in the systems.
32
- - An `update` event with a `dt` (delta time) payload is automatically dispatched on every `store.update()` call, making it suitable for a game loop.
263
+ // This broadcasts 'toggle' to all entities
264
+ store.notify("toggle", "todo-1") // Only todo-1 actually updates
265
+ ```
33
266
 
34
- 4. **Immutability**: The state is immutable. Updates are handled internally by **[Mutative](https://mutative.js.org/)**, so you can "mutate" the state directly within a type's or system's behavior function, and a new, immutable state will be created.
267
+ **Why this matters:**
268
+
269
+ - ✅ Multiple entities of different types can respond to the same event
270
+ - ✅ Enables reactive, decoupled behavior
271
+ - ✅ Perfect for coordinating related entities
272
+ - ✅ Natural fit for multiplayer/real-time sync
273
+
274
+ **Example of multiple entities responding:**
275
+
276
+ ```javascript
277
+ const types = {
278
+ player: {
279
+ gameOver(player) {
280
+ player.active = false
281
+ },
282
+ },
283
+ enemy: {
284
+ gameOver(enemy) {
285
+ enemy.active = false
286
+ },
287
+ },
288
+ ui: {
289
+ gameOver(ui) {
290
+ ui.showGameOverScreen = true
291
+ },
292
+ },
293
+ }
294
+
295
+ // One event, all three entity types respond (if they have the handler)
296
+ store.notify("gameOver")
297
+ ```
298
+
299
+ ### Entities and Types
300
+
301
+ Your state is a collection of **entities** (instances) organized by **type** (like classes or models).
302
+
303
+ ```javascript
304
+ const entities = {
305
+ "item-1": { type: "cartItem", name: "Shoes", quantity: 1, price: 99 },
306
+ "item-2": { type: "cartItem", name: "Shirt", quantity: 2, price: 29 },
307
+ }
308
+ ```
309
+
310
+ ### Behaviors
311
+
312
+ Define how entities respond to events. Behaviors can be a single object or an array of composable objects.
313
+
314
+ ```javascript
315
+ // Single behavior (simple)
316
+ const counterType = {
317
+ increment(counter) {
318
+ counter.value++
319
+ },
320
+ decrement(counter) {
321
+ counter.value--
322
+ },
323
+ }
324
+
325
+ // Array of behaviors (composable)
326
+ const cartItemType = [
327
+ {
328
+ incrementQuantity(item) {
329
+ item.quantity++
330
+ },
331
+ decrementQuantity(item) {
332
+ if (item.quantity > 1) item.quantity--
333
+ },
334
+ },
335
+ {
336
+ applyDiscount(item, percent) {
337
+ item.price = item.price * (1 - percent / 100)
338
+ },
339
+ },
340
+ ]
341
+ ```
342
+
343
+ ### Events
344
+
345
+ Events are broadcast to all relevant handlers in a pub/sub pattern.
346
+
347
+ ```javascript
348
+ // Simplest form - just the entity ID
349
+ store.notify("increment", "counter-1")
350
+
351
+ // With additional data
352
+ store.notify("applyDiscount", { id: "item-1", percent: 10 })
353
+
354
+ // Also supports dispatch() for Redux compatibility
355
+ store.dispatch({ type: "increment", payload: "counter-1" })
356
+
357
+ // Process the queue - this is when handlers actually run
358
+ store.update()
359
+ ```
360
+
361
+ **Key insight:** Events go into a queue and are processed together during `update()`. This enables batching and prevents cascading updates within a single frame.
362
+
363
+ ### Systems (Optional)
364
+
365
+ For global state logic that doesn't belong to a specific entity type.
366
+
367
+ ```javascript
368
+ const systems = [
369
+ {
370
+ calculateTotal(state) {
371
+ state.cartTotal = Object.values(state.entities)
372
+ .filter((e) => e.type === "cartItem")
373
+ .reduce((sum, item) => sum + item.price * item.quantity, 0)
374
+ },
375
+ },
376
+ ]
377
+ ```
35
378
 
36
379
  ---
37
380
 
38
- ## API
381
+ ## API Reference
39
382
 
40
383
  ### `createStore(options)`
41
384
 
42
385
  Creates a new store instance.
43
386
 
44
- **Parameters:**
387
+ **Options:**
45
388
 
46
- - `options` (object):
47
- - `types` (object): A map of entity types. Keys are type names (e.g., `'player'`), and values are arrays of behaviors.
48
- - `entities` (object): A map of initial entities. Keys are entity IDs, and values are objects containing the entity's properties.
49
- - `systems` (array, optional): An array of system objects, which define behaviors for the whole state.
389
+ - `types` (object): Map of type names to behaviors (single object or array)
390
+ - `entities` (object): Initial entities by ID
391
+ - `systems` (array, optional): Global event handlers
50
392
 
51
393
  **Returns:**
52
394
 
53
- - A `store` object with the following methods:
54
- - `subscribe(listener)`: Subscribes a `listener` to state changes. The listener is called after `store.update()` is complete. Returns an `unsubscribe` function.
55
- - `update(dt, api)`: Processes the event queue and updates the state. This is typically called once per frame. `dt` is the time elapsed since the last frame, and `api` is the engine's public API.
56
- - `notify(type, payload)`: Adds a new event to the queue to be processed on the next `update` call.
57
- - `dispatch(event)`: A Redux-compatible alias for `notify`.
58
- - `getState()`: Returns the current, immutable state.
59
- - `setState(newState)`: Replaces the entire state with a new one. Use with caution.
60
- - `getTypes()`: Returns the augmented types configuration. Augmenting here means that the array of behaviors is merged into one single behavior.
61
- - `getOriginalTypes()`: Returns the original, un-augmented behavior arrays.
62
- - `reset()`: Resets the state to its initial configuration.
395
+ - `subscribe(listener)`: Subscribe to state changes
396
+ - `update(dt)`: Process event queue (optional `dt` for time-based logic)
397
+ - `notify(type, payload)`: Queue an event
398
+ - `dispatch(event)`: Redux-compatible event dispatch
399
+ - `getState()`: Get current immutable state
400
+ - `setState(newState)`: Replace entire state
401
+ - `reset()`: Reset to initial state
63
402
 
64
- ---
403
+ ### `createApi(store)`
404
+
405
+ Creates a convenience wrapper with utility methods.
406
+
407
+ **Returns:**
408
+
409
+ - `createSelector(inputSelectors, resultFunc)`: Memoized selectors
410
+ - `getTypes()`, `getEntities()`, `getEntity(id)`: State accessors
411
+ - `notify(type, payload)`: Dispatch events
65
412
 
66
413
  ### `createSelector(inputSelectors, resultFunc)`
67
414
 
68
- Creates a memoized selector to efficiently compute derived data from the state. It only recomputes the result if the inputs to the `resultFunc` have changed.
415
+ Create memoized, performant selectors.
416
+
417
+ ```javascript
418
+ const selectCompletedTodos = createSelector(
419
+ [(state) => state.entities],
420
+ (entities) => Object.values(entities).filter((e) => e.completed),
421
+ )
422
+ ```
423
+
424
+ ---
69
425
 
70
- **Parameters:**
426
+ ## Use Cases
71
427
 
72
- - `inputSelectors` (array of functions): An array of selector functions that take the state and return a slice of it.
73
- - `resultFunc` (function): A function that takes the results of the `inputSelectors` and returns the final computed value.
428
+ ### Perfect For
74
429
 
75
- **Returns:**
430
+ - **Real-time collaboration** (like Figma, Google Docs)
431
+ - **Chat and messaging apps**
432
+ - **Live dashboards and monitoring**
433
+ - **Interactive data visualizations**
434
+ - **Apps with undo/redo**
435
+ - **Multiplayer features**
436
+ - **Collection-based UIs** (lists, feeds, boards)
437
+ - **...and games!**
76
438
 
77
- - A memoized selector function that takes the `state` as its only argument and returns the selected data.
439
+ ### 🤔 Maybe Overkill For
440
+
441
+ - Simple forms with local state
442
+ - Static marketing pages
443
+ - Basic CRUD with no real-time needs
78
444
 
79
445
  ---
80
446
 
81
- ### `createApi(store)`
447
+ ## Comparison
82
448
 
83
- Creates a convenient API object that encapsulates the store's methods and provides common utility functions for accessing state.
449
+ | Feature | Inglorious Store | Redux | Redux Toolkit | Zustand | Jotai | Pinia | MobX |
450
+ | --------------------------- | ----------------- | ------------------- | ---------------- | ------------- | ------------- | --------------- | --------------- |
451
+ | **Integrated Immutability** | ✅ Mutative | ❌ Manual | ✅ Immer | ❌ Manual | ✅ Optional | ✅ Built-in | ✅ Observables |
452
+ | **Event Queue/Batching** | ✅ Built-in | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ Automatic |
453
+ | **Dispatch from Handlers** | ✅ Safe (queued) | ❌ Not allowed | ❌ Not allowed | ✅ | ✅ | ✅ | ✅ |
454
+ | **Redux DevTools** | ⚠️ Limited | ✅ Native | ✅ Native | ✅ Middleware | ⚠️ Limited | ✅ Vue DevTools | ⚠️ Limited |
455
+ | **react-redux Compatible** | ✅ Yes | ✅ Yes | ✅ Yes | ❌ | ❌ | ❌ Vue only | ❌ |
456
+ | **Time-Travel Debug** | ✅ Built-in | ✅ Via DevTools | ✅ Via DevTools | ⚠️ Manual | ❌ | ⚠️ Limited | ❌ |
457
+ | **Entity-Based State** | ✅ First-class | ⚠️ Manual normalize | ✅ EntityAdapter | ❌ | ❌ | ❌ | ❌ |
458
+ | **Pub/Sub Events** | ✅ Core pattern | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
459
+ | **Multiplayer-Ready** | ✅ Deterministic | ⚠️ With work | ⚠️ With work | ⚠️ With work | ❌ | ❌ | ❌ |
460
+ | **Testability** | ✅ Pure functions | ✅ Pure reducers | ✅ Pure reducers | ⚠️ With mocks | ⚠️ With mocks | ⚠️ With mocks | ❌ Side effects |
461
+ | **Learning Curve** | Medium | High | Medium | Low | Medium | Low | Medium |
462
+ | **Bundle Size** | Small | Small | Medium | Tiny | Small | Medium | Medium |
84
463
 
85
- **Parameters:**
464
+ ### Key Differences
86
465
 
87
- - `store` (object): The store instance created with `createStore`.
466
+ **vs Redux/RTK:**
88
467
 
89
- **Returns:**
468
+ - Integrated immutability (no manual spreads)
469
+ - Event queue with automatic batching
470
+ - Can dispatch from handlers safely
471
+ - Entity-based architecture built-in
472
+ - Reusable handlers across instances
90
473
 
91
- - An `api` object with methods for interacting with the store and state, including:
92
- - `createSelector(inputSelectors, resultFunc)`: A helper function that automatically binds the store's state to a new selector.
93
- - `getTypes()`, `getEntities()`, `getEntity(id)`: Utility functions for accessing state.
94
- - `notify(type, payload)`, `dispatch(action)`: Aliases to the store's event dispatching methods.
474
+ **vs Zustand:**
475
+
476
+ - Deterministic event processing (better for multiplayer)
477
+ - Built-in time-travel debugging
478
+ - Entity/type architecture for collections
479
+ - Event queue prevents cascading updates
480
+ - Redux DevTools compatible
481
+
482
+ **vs Jotai:**
483
+
484
+ - Different paradigm (events vs atoms)
485
+ - Better for entity collections
486
+ - Built-in normalization
487
+ - Explicit event flow
488
+
489
+ **vs Pinia:**
490
+
491
+ - React-compatible (Pinia is Vue-only)
492
+ - Event queue system
493
+ - Deterministic updates for multiplayer
494
+
495
+ **vs MobX:**
496
+
497
+ - Explicit events (less magic)
498
+ - Serializable state (easier persistence/sync)
499
+ - Deterministic (better for debugging)
500
+ - Redux DevTools compatible
95
501
 
96
502
  ---
97
503
 
98
- ## Basic Usage
504
+ **When to choose Inglorious Store:**
505
+
506
+ - Building real-time/collaborative features
507
+ - Managing collections of similar items
508
+ - Need deterministic state for multiplayer
509
+ - Want built-in time-travel debugging
510
+ - Coming from Redux and want better DX
511
+
512
+ **When to choose alternatives:**
513
+
514
+ - **Zustand/Jotai**: Simple apps, prefer minimal API
515
+ - **Redux Toolkit**: Large team, established Redux patterns
516
+ - **Pinia**: Vue ecosystem
517
+ - **MobX**: Prefer reactive/observable patterns
518
+
519
+ ---
99
520
 
100
- Here is a simple example of a player entity that moves based on events.
521
+ ## Advanced: Real-Time Sync
101
522
 
102
523
  ```javascript
103
- import { createStore, createApi } from "@inglorious/store"
104
- import { add, scale } from "@inglorious/utils/math/vectors.js"
524
+ // Client-side
525
+ socket.on("server-event", (event) => {
526
+ store.notify(event.type, event.payload)
527
+ store.update()
528
+ })
105
529
 
106
- // 1. Define the behaviors
107
- const transform = {
108
- // The second parameter of an event handler is the payload
109
- move: (entity, payload) => {
110
- entity.position[0] += payload.dx || 0
111
- entity.position[1] += payload.dy || 0
112
- entity.position[2] += payload.dz || 0
113
- },
114
- }
530
+ // Send local events to server
531
+ store.subscribe(() => {
532
+ const state = store.getState()
533
+ socket.emit("state-update", serializeState(state))
534
+ })
535
+ ```
115
536
 
116
- const kinematic = {
117
- update: (entity, dt) => {
118
- // You can use utility functions for easy vector operations
119
- entity.position = add(entity.position, scale(entity.velocity, dt))
120
- },
121
- }
537
+ ---
122
538
 
123
- // 2. Define the entity types by composing behaviors
539
+ ## Advanced: Time-Based Updates
540
+
541
+ For animations or continuous updates (like in games):
542
+
543
+ ```javascript
124
544
  const types = {
125
- player: [
126
- // Composed behaviors
127
- transform,
128
- kinematic,
545
+ particle: [
546
+ {
547
+ update(particle, dt) {
548
+ // dt = delta time in milliseconds
549
+ particle.x += particle.velocityX * dt
550
+ particle.y += particle.velocityY * dt
551
+ particle.life -= dt
552
+ },
553
+ },
129
554
  ],
130
555
  }
131
556
 
132
- // 3. Define the initial entities
133
- const entities = {
134
- player1: {
135
- type: "player",
136
- position: v(0, 0, 0),
137
- velocity: v(0.0625, 0, 0),
138
- },
557
+ // In your game/animation loop
558
+ function loop(timestamp) {
559
+ const dt = timestamp - lastTime
560
+ store.update(dt)
561
+ requestAnimationFrame(loop)
139
562
  }
563
+ ```
140
564
 
141
- // 4. Create the store and a unified API
142
- const store = createStore({ types, entities })
143
- const api = createApi(store)
565
+ ---
144
566
 
145
- // 5. Create selectors to get data from the state
146
- const selectPlayerPosition = api.createSelector(
147
- [(state) => state.entities.player1],
148
- (player) => player.position,
149
- )
567
+ ## Part of the Inglorious Engine
150
568
 
151
- // 6. Subscribe to changes
152
- store.subscribe(() => {
153
- console.log("State updated!", selectPlayerPosition())
154
- })
569
+ This store powers the [Inglorious Engine](https://github.com/IngloriousCoderz/inglorious-engine), a functional game engine. But you don't need to build games to benefit from game development patterns!
570
+
571
+ ---
155
572
 
156
- // 7. Notify the store of an event
157
- console.log("Initial player position:", selectPlayerPosition()) // => v(0, 0, 0)
573
+ ## License
158
574
 
159
- // Dispatch a custom `move` event with a payload
160
- api.notify("move", { id: "player1", dx: 5, dz: 5 })
575
+ MIT
161
576
 
162
- // Events are queued but not yet processed
163
- console.log("Position after notify:", selectPlayerPosition()) // => v(0, 0, 0)
577
+ ---
164
578
 
165
- // 8. Run the update loop to process the queue and trigger `update` behaviors
166
- store.update(16) // Pass delta time
167
- // Console output from subscriber: "State updated! v(6, 0, 5)"
579
+ ## Contributing
168
580
 
169
- console.log("Final position:", selectPlayerPosition()) // => v(6, 0, 5)
170
- ```
581
+ Contributions welcome! Please read our [Contributing Guidelines](../../CONTRIBUTING.md) first.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/store",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "A state manager inspired by Redux, but tailored for the specific needs of game development.",
5
5
  "author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
package/src/entities.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { map } from "@inglorious/utils/data-structures/object.js"
2
2
 
3
3
  /**
4
- * @typedef {Object.<string, any>} Entity - An object representing a game entity.
4
+ * @typedef {Object.<string, any>} Entity - An object representing an entity.
5
5
  * @typedef {Object.<string, Entity>} Entities - A collection of named entities.
6
6
  */
7
7
 
package/src/event-map.js CHANGED
@@ -1,18 +1,18 @@
1
1
  /**
2
2
  * @typedef {Object.<string, any>} Type - An object representing an augmented entity type.
3
- * @typedef {Object.<string, any>} Entity - An object representing a game entity.
3
+ * @typedef {Object.<string, any>} Entity - An object representing a entity.
4
4
  */
5
5
 
6
6
  /**
7
7
  * A class to manage the mapping of event names to the entity IDs that handle them.
8
- * This is used for optimized event handling in a game loop.
8
+ * This is used for optimized event handling.
9
9
  */
10
10
  export class EventMap {
11
11
  /**
12
12
  * Creates an instance of EventMap and initializes it with entities and their types.
13
13
  *
14
14
  * @param {Object.<string, Type>} types - An object containing all augmented type definitions.
15
- * @param {Object.<string, Entity>} entities - An object containing all game entities.
15
+ * @param {Object.<string, Entity>} entities - An object containing all entities.
16
16
  */
17
17
  constructor(types, entities) {
18
18
  /**