@inglorious/store 6.1.0 → 6.1.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.
Files changed (2) hide show
  1. package/README.md +101 -22
  2. package/package.json +7 -1
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  A Redux-compatible, ECS-inspired state library that makes state management as elegant as game logic.
7
7
 
8
- **Drop-in replacement for Redux.** Works with `react-redux` and Redux DevTools. Adds **entity-based state management (ECS)** for simpler, more predictable code.
8
+ **Drop-in replacement for Redux.** Works with `react-redux` and Redux DevTools. Borrows concepts from Entity-Component-System architectures and Functional Programming to provide an environment where you can write simple, predictable, and testable code.
9
9
 
10
10
  ```javascript
11
11
  // from redux
@@ -20,7 +20,7 @@ import { createStore } from "@inglorious/store"
20
20
 
21
21
  Redux is powerful but verbose. You need action creators, reducers, middleware for async operations, and a bunch of decisions about where logic should live. Redux Toolkit cuts the boilerplate, but you're still writing a lot of ceremony.
22
22
 
23
- Inglorious Store ditches the ceremony entirely with an **entity-based architecture** inspired by game engines. The same ECS patterns that power AAA games power your state management.
23
+ Inglorious Store eliminates the boilerplate entirely with an **entity-based architecture** inspired by game engines. Some of the patterns that power AAA games now power your state management.
24
24
 
25
25
  Game engines solved state complexity years ago — Inglorious Store brings those lessons to web development.
26
26
 
@@ -187,7 +187,7 @@ const entities = {
187
187
 
188
188
  Even though it looks like types expose methods, they are actually **event handlers**, very similar to Redux reducers. There are a few differences though:
189
189
 
190
- 1. Just like RTK reducers, you can mutate the entity directly since event handlers are using an immutability library under the hood. Not Immer, but Mutative, which claims to be 10x faster than Immer.
190
+ 1. Just like RTK reducers, you can mutate the entity directly since event handlers are using an immutability library under the hood. Not Immer, but Mutative which claims to be 10x faster than Immer.
191
191
 
192
192
  ```javascript
193
193
  const types = {
@@ -218,7 +218,7 @@ const types = {
218
218
 
219
219
  ## Installation & Setup
220
220
 
221
- The Inglorious store, just like Redux, can be used standalone. But a common usage is together with a component library such as React.
221
+ The Inglorious store, just like Redux, can be used standalone. However, it's commonly used together with component libraries such as React.
222
222
 
223
223
  ### Basic Setup with `react-redux`
224
224
 
@@ -272,6 +272,8 @@ function Counter() {
272
272
 
273
273
  ### With `@inglorious/react-store` (Recommended)
274
274
 
275
+ For React applications, `@inglorious/react-store` provides a set of hooks and a Provider that are tightly integrated with the store. It's a lightweight wrapper around `react-redux` that offers a more ergonomic API.
276
+
275
277
  ```javascript
276
278
  import { createStore } from "@inglorious/store"
277
279
  import { createReactStore } from "@inglorious/react-store"
@@ -296,13 +298,16 @@ function Counter() {
296
298
  return (
297
299
  <div>
298
300
  <p>{count}</p>
299
- <button onClick={() => notify("increment")}>+</button> // cleaner API
301
+ <button onClick={() => notify("increment")}>+</button> // simplified
302
+ syntax
300
303
  <button onClick={() => notify("decrement")}>-</button>
301
304
  </div>
302
305
  )
303
306
  }
304
307
  ```
305
308
 
309
+ The package is fully typed, providing a great developer experience with TypeScript.
310
+
306
311
  ---
307
312
 
308
313
  ## Core Features
@@ -382,7 +387,7 @@ const types = {
382
387
 
383
388
  ### 🔊 Event Broadcasting
384
389
 
385
- Events are broadcast to all entities via pub/sub. Every entity handler receives every event of that type, just like in Redux.
390
+ Events are broadcast to all entities via pub/sub. Every entity handler receives every event of that type, just like it does in Redux.
386
391
 
387
392
  ```javascript
388
393
  const types = {
@@ -433,7 +438,7 @@ store.notify("toggle", "todo1")
433
438
 
434
439
  ### ⚡ Async Operations
435
440
 
436
- In **Redux/RTK**, logic should be written inside of pure functions as much as possible. Not even action creators, just in the reducers. But what if I need to access some other part of the state that is not visible to the reducer? What if I need to combine async behavior with sync behavior? This is where the choice of "where does my logic live?" matters.
441
+ In **Redux/RTK**, logic should be written inside pure functions as much as possible specifically in reducers, not action creators. But what if I need to access some other part of the state that is not visible to the reducer? What if I need to combine async behavior with sync behavior? This is where the choice of "where does my logic live?" matters.
437
442
 
438
443
  In **Inglorious Store:** your event handlers can be async, and you get deterministic behavior automatically. Inside an async handler, you can access other parts of state (read-only), and you can trigger other events via `api.notify()`. Even if we give up on some purity, everything still maintains predictability because of the underlying **event queue**:
439
444
 
@@ -661,32 +666,77 @@ store.notify("eventName", payload)
661
666
  store.dispatch({ type: "eventName", payload }) // Redux-compatible alternative
662
667
  ```
663
668
 
664
- ### 🧩 Type Safety (WIP)
669
+ ### 🧩 Type Safety
665
670
 
666
- Inglorious Store aims for **type inference similar to Redux Toolkit** but without verbose builder callbacks.
671
+ Inglorious Store is written in JavaScript but comes with powerful TypeScript support out of the box, allowing for a fully type-safe experience similar to Redux Toolkit, but with less boilerplate.
667
672
 
668
- You’ll soon be able to infer payload types and entity shapes directly from the `types` definition.
673
+ You can achieve strong type safety by defining an interface for your `types` configuration. This allows you to statically define the shape of your entity handlers, ensuring that all required handlers are present and correctly typed.
669
674
 
670
- ```ts
671
- const types = {
672
- counter: {
673
- increment(entity: { value: number }, value: number) {
674
- entity.value += value
675
+ Here’s how you can set it up for a TodoMVC-style application:
676
+
677
+ **1. Define Your Types**
678
+
679
+ First, create an interface that describes your entire `types` configuration. This interface will enforce the structure of your event handlers.
680
+
681
+ ```typescript
682
+ // src/store/types.ts
683
+ import type {
684
+ FormEntity,
685
+ ListEntity,
686
+ FooterEntity,
687
+ // ... other payload types
688
+ } from "../../types"
689
+
690
+ // Define the static shape of the types configuration
691
+ interface TodoListTypes {
692
+ form: {
693
+ inputChange: (entity: FormEntity, value: string) => void
694
+ formSubmit: (entity: FormEntity) => void
695
+ }
696
+ list: {
697
+ formSubmit: (entity: ListEntity, value: string) => void
698
+ toggleClick: (entity: ListEntity, id: number) => void
699
+ // ... other handlers
700
+ }
701
+ footer: {
702
+ filterClick: (entity: FooterEntity, id: string) => void
703
+ }
704
+ }
705
+
706
+ export const types: TodoListTypes = {
707
+ form: {
708
+ inputChange(entity, value) {
709
+ entity.value = value
710
+ },
711
+ formSubmit(entity) {
712
+ entity.value = ""
675
713
  },
676
714
  },
715
+ // ... other type implementations
677
716
  }
717
+ ```
678
718
 
679
- const entities = {
680
- c1: { type: "counter", value: 0 },
681
- }
719
+ With `TodoListTypes`, TypeScript will throw an error if you forget a handler (e.g., `formSubmit`) or if its signature is incorrect.
682
720
 
683
- const store = createStore({ types, entities })
721
+ **2. Create the Store**
722
+
723
+ When creating your store, you'll pass the `types` object. To satisfy the store's generic `TypesConfig`, you may need to use a double cast (`as unknown as`). This is a safe and intentional way to bridge your specific, statically-checked configuration with the store's more generic type.
724
+
725
+ ```typescript
726
+ // src/store/index.ts
727
+ import { createStore, type TypesConfig } from "@inglorious/store"
728
+ import { types } from "./types"
729
+ import type { TodoListEntity, TodoListState } from "../../types"
684
730
 
685
- store.notify("increment", 1) // ✅ type-checked
686
- store.notify("decrement", "oops") // type error
731
+ export const store = createStore<TodoListEntity, TodoListState>({
732
+ types: types as unknown as TypesConfig<TodoListEntity>,
733
+ // ... other store config
734
+ })
687
735
  ```
688
736
 
689
- RTK added the builder callback syntax mostly for type safety — Inglorious Store aims to provide the same guarantees with a cleaner API.
737
+ **3. Enjoy Full Type Safety**
738
+
739
+ Now, your store is fully type-safe. The hooks provided by `@inglorious/react-store` will also be correctly typed.
690
740
 
691
741
  ---
692
742
 
@@ -714,11 +764,40 @@ Check out the following demos to see the Inglorious Store in action on real-case
714
764
  - **[TodoMVC](https://github.com/IngloriousCoderz/inglorious-engine/tree/main/examples/apps/todomvc)** - An (ugly) clone of Kent Dodds' [TodoMVC](https://todomvc.com/) experiments, showing the full compatibility with react-redux and The Redux DevTools.
715
765
  - **[TodoMVC-CS](https://github.com/IngloriousCoderz/inglorious-engine/tree/main/examples/apps/todomvc-cs)** - A client-server version of the TodoMVC, which showcases the use of `notify` as a cleaner alternative to `dispatch` and async event handlers.
716
766
  - **[TodoMVC-RT](https://github.com/IngloriousCoderz/inglorious-engine/tree/main/examples/apps/todomvc-rt)** - A "multiplayer" version, in which multiple clients are able to synchronize through a real-time server.
767
+ - **[TodoMVC-TS](https://github.com/IngloriousCoderz/inglorious-engine/tree/main/examples/apps/todomvc-ts)** - A typesafe version of the base TodoMVC.
768
+
769
+ ---
717
770
 
718
771
  ## Part of the Inglorious Engine
719
772
 
720
773
  This store powers the [Inglorious Engine](https://github.com/IngloriousCoderz/inglorious-engine), a functional game engine. The same patterns that power games power your web apps.
721
774
 
775
+ ## Frequently Unsolicited Complaints (FUCs)
776
+
777
+ It's hard to accept the new, especially on Reddit. Here are the main objections to the Inglorious Store.
778
+
779
+ **"This is not ECS."**
780
+
781
+ It's not. The Inglorious Store is _inspired_ by ECS, but doesn't strictly follow ECS. Heck, not even the major game engines out there follow ECS by the book!
782
+
783
+ Let's compare the two:
784
+
785
+ | ECS Architecture | Inglorious Store |
786
+ | ------------------------------------- | -------------------------------------- |
787
+ | Entities are ids | Entities have an id |
788
+ | Components are pure, consecutive data | Entities are pure bags of related data |
789
+ | Data and behavior are separated | Data and behavior are separated |
790
+ | Systems operate on the whole state | Systems operate on the whole state |
791
+ | Usually written in an OOP environment | Written in an FP environment |
792
+
793
+ **"This is not FP."**
794
+
795
+ It looks like it's not, and that's a feature. If you're used to classes and instances, the Inglorious Store will feel natural to you. Even behavior composition looks like inheritance, but it's actually function composition. The same [Three Principles](https://redux.js.org/understanding/thinking-in-redux/three-principles) that describe Redux are applied here (with some degree of freedom on function purity).
796
+
797
+ **"This is not Data-Oriented Design."**
798
+
799
+ It's not. Please grep this README and count how many occurrences of DoD you can find. This is not [Data-Oriented Design](https://en.wikipedia.org/wiki/Data-oriented_design), which is related to low-level CPU cache optimization. It's more similar to [Data-Driven Programming](https://en.wikipedia.org/wiki/Data-driven_programming), which is related to separating data and behavior. The Inglorious Engine separates behavior in... behaviors (grouped into so-called types), while the data is stored in plain objects called entities.
800
+
722
801
  ---
723
802
 
724
803
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/store",
3
- "version": "6.1.0",
3
+ "version": "6.1.2",
4
4
  "description": "A state manager for real-time, collaborative apps, inspired by game development patterns and compatible with Redux.",
5
5
  "author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
@@ -28,9 +28,15 @@
28
28
  "type": "module",
29
29
  "exports": {
30
30
  ".": {
31
+ "types": "./types/index.d.ts",
31
32
  "import": "./src/store.js"
32
33
  },
34
+ "./client/devtools": {
35
+ "types": "./types/client/devtools.d.ts",
36
+ "import": "./src/client/devtools.js"
37
+ },
33
38
  "./*": {
39
+ "types": "./types/*.d.ts",
34
40
  "import": "./src/*"
35
41
  }
36
42
  },