@powerhousedao/academy 2.5.0-dev.3 → 2.5.0-dev.30

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 (49) hide show
  1. package/CHANGELOG.md +187 -0
  2. package/docs/academy/01-GetStarted/00-ExploreDemoPackage.md +19 -15
  3. package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +38 -39
  4. package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +22 -7
  5. package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +9 -4
  6. package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +146 -422
  7. package/docs/academy/01-GetStarted/_04-BuildToDoListEditor +360 -0
  8. package/docs/academy/01-GetStarted/home.mdx +16 -24
  9. package/docs/academy/01-GetStarted/styles.module.css +31 -0
  10. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +7 -3
  11. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +1 -1
  12. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +33 -16
  13. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +73 -0
  14. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +59 -4
  15. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +32 -12
  16. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +103 -38
  17. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +90 -228
  18. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +41 -1
  19. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +342 -67
  20. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/02-ConfiguringDrives.md +5 -3
  21. package/docs/academy/02-MasteryTrack/05-Launch/02-PublishYourProject.md +70 -5
  22. package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +162 -73
  23. package/docs/academy/02-MasteryTrack/05-Launch/{03-RunOnACloudServer.md → _03-RunOnACloudServer} +8 -5
  24. package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +7 -40
  25. package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +3 -0
  26. package/docs/academy/05-Architecture/images/PowerhouseArchitecture.png +0 -0
  27. package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +86 -29
  28. package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +404 -0
  29. package/docs/academy/06-ComponentLibrary/03-IntegrateIntoAReactComponent.md +124 -0
  30. package/docs/academy/07-Cookbook.md +209 -4
  31. package/docs/academy/08-Glossary.md +20 -18
  32. package/docs/academy/09-AIResources +131 -0
  33. package/package.json +1 -1
  34. package/sidebars.ts +3 -45
  35. package/docs/academy/06-ComponentLibrary/02-BuildingWithScalars.md +0 -54
  36. package/docs/academy/06-ComponentLibrary/03-Scalar-Components/01-phid-field.mdx +0 -72
  37. package/docs/academy/06-ComponentLibrary/03-Scalar-Components/02-input-field.mdx +0 -0
  38. package/docs/academy/06-ComponentLibrary/04-Complex-Components/01-sidebar.mdx +0 -36
  39. package/docs/academy/06-ComponentLibrary/05-Layout-Components/01-test-toupdate.mdx +0 -61
  40. package/docs/academy/06-ComponentLibrary/06-Fragments/01-test-toupdate.mdx +0 -61
  41. /package/docs/academy/02-MasteryTrack/05-Launch/{02-IntroductionToPackages.md → 01-IntroductionToPackages.md} +0 -0
  42. /package/docs/academy/02-MasteryTrack/05-Launch/{00-IntegrateInAFront-End → _00-IntegrateInAFront-End} +0 -0
  43. /package/docs/academy/02-MasteryTrack/05-Launch/{01-IntroducingFusion → _01-IntroducingFusion} +0 -0
  44. /package/docs/academy/02-MasteryTrack/05-Launch/{04-GraphQLNamespacing → _04-GraphQLNamespacing} +0 -0
  45. /package/docs/academy/02-MasteryTrack/05-Launch/{05-LaunchYourBackend.md → _05-LaunchYourBackend} +0 -0
  46. /package/docs/academy/02-MasteryTrack/05-Launch/{06-LaunchYourFrontend.md → _06-LaunchYourFrontend} +0 -0
  47. /package/docs/academy/04-APIReferences/{01-ReactHooks.md → 01-ReactHooks} +0 -0
  48. /package/docs/academy/04-APIReferences/{02-ReactorAPI.md → 02-ReactorAPI} +0 -0
  49. /package/docs/academy/04-APIReferences/{03-Configuration.md → 03-Configuration} +0 -0
@@ -21,6 +21,8 @@ GraphQL has a set of built-in **scalar types**:
21
21
  * `Boolean`: `true` or `false`.
22
22
  * `ID`: A unique identifier, often used as a key for a field. It is serialized in the same way as a String; however, it is not intended to be human-readable.
23
23
 
24
+ In addition to these standard types, the Powerhouse Document-Engineering system introduces custom scalars that are linked to reusable front-end components. These scalars are tailored for the web3 ecosystem and will be explored in the Component Library section of the documentation.
25
+
24
26
  ### Lists and Non-Null
25
27
  You can modify types using lists and non-null indicators:
26
28
  * **Lists**: To indicate that a field will return a list of a certain type, you wrap the type in square brackets, e.g., `[ToDoItem!]!`. This means the field `items` in `ToDoListState` will be a list of `ToDoItem` objects.
@@ -29,6 +31,7 @@ You can modify types using lists and non-null indicators:
29
31
  ## Example: ToDoList State Schema
30
32
 
31
33
  Let's revisit the `ToDoList` example from the "Define the ToDoList document specification" tutorial.
34
+ Only this time, we'll also add a 'Stats' type. Since we want to keep track of the number of completed To-Do's.
32
35
 
33
36
  ```graphql
34
37
  # The state of our ToDoList
@@ -42,6 +45,12 @@ type ToDoItem {
42
45
  text: String!
43
46
  checked: Boolean!
44
47
  }
48
+ # The statistics on our to-do's
49
+ type ToDoListStats {
50
+ total: Int!
51
+ checked: Int!
52
+ unchecked: Int!
53
+ }
45
54
  ```
46
55
 
47
56
  ### Breakdown:
@@ -57,6 +66,11 @@ type ToDoItem {
57
66
  * `text: String!`: The textual description of the to-do item. It cannot be null, ensuring every to-do has a description.
58
67
  * `checked: Boolean!`: Indicates whether the to-do item is completed. It defaults to a boolean value (true or false) and cannot be null.
59
68
 
69
+ * **`ToDoListStats` type**: This type holds the summary statistics for the to-do list.
70
+ * `total: Int!`: The total count of all to-do items. This field must be an integer and cannot be null.
71
+ * `checked: Int!`: The number of to-do items that are marked as completed. This must be an integer and cannot be null.
72
+ * `unchecked: Int!`: The number of to-do items that are still pending. This also must be an integer and cannot be null.
73
+
60
74
  ## Best Practices for Designing Your State Schema
61
75
 
62
76
  1. **Start Simple, Iterate**: Begin with the core entities and properties. You can always expand and refine your schema as your understanding of the document's requirements grows.
@@ -70,3 +84,62 @@ type ToDoItem {
70
84
  6. **Root State Type**: It's a common pattern to have a single root type for your document state (e.g., `ToDoListState`). This provides a clear entry point for accessing all document data.
71
85
 
72
86
  By carefully defining your state schema, you lay a solid foundation for your Powerhouse document model, making it robust, maintainable, and easy to work with. The schema dictates not only how data is stored but also how it can be queried and mutated through operations, which will be covered in the next section.
87
+
88
+ ## Practical Implementation: Defining the State Schema in Connect
89
+
90
+ Now that you understand the concepts behind the state schema, let's put it into practice. This section will guide you through creating a document model specification for the advanced ToDoList example discussed above.
91
+
92
+ <details>
93
+ <summary>Tutorial: The State Schema Specification</summary>
94
+
95
+ ### Prerequisites
96
+
97
+ - You have a Powerhouse project set up. If not, please follow the [Create a new Powerhouse Project](../../GetStarted/CreateNewPowerhouseProject) tutorial.
98
+ - Connect Studio is running. If not, navigate to your project directory in the terminal and run `ph connect`.
99
+
100
+ ### Steps
101
+
102
+ 1. **Create a New Document Model**:
103
+ - With Connect Studio open in your browser, navigate into your local drive.
104
+ - At the bottom of the page in the 'New Document' section, click the `DocumentModel` button to create a new document model specification.
105
+
106
+ 2. **Define Document Metadata**:
107
+ - **Name**: Give your document model a descriptive name, for example, `ToDoList`. **Pay close attention to capitalization, as it influences our code.**
108
+ - **Document Type**: In the 'Document Type' field, enter a unique identifier for this document type, for instance, `powerhouse/todolist`.
109
+
110
+ 3. **Specify the State Schema**:
111
+ - In the code editor provided, you'll see a template for a GraphQL schema.
112
+ - Replace the entire content of the editor with the advanced `ToDoList` schema we've designed in this chapter:
113
+
114
+ ```graphql
115
+ # The state of our ToDoList
116
+ type ToDoListState {
117
+ items: [ToDoItem!]!
118
+ }
119
+
120
+ # A single to-do item
121
+ type ToDoItem {
122
+ id: ID!
123
+ text: String!
124
+ checked: Boolean!
125
+ }
126
+ # The statistics on our to-do's
127
+ type ToDoListStats {
128
+ total: Int!
129
+ checked: Int!
130
+ unchecked: Int!
131
+ }
132
+ ```
133
+
134
+ 4. **Sync Schema and View Initial State**:
135
+ - After pasting the schema, click the **'Sync with schema'** button.
136
+ - This action processes your schema and generates an initial JSON state for your document model based on the `ToDoListState` type. You can view this initial state, which helps you verify that your schema is structured correctly.
137
+
138
+ For now, you can ignore the "Modules & Operations" section. We will define and implement the operations that modify this state in the upcoming sections of this Mastery Track.
139
+
140
+ By completing these steps, you have successfully specified the data structure for the advanced ToDoList document model. The next step is to define the operations that will allow users to interact with and change this state.
141
+
142
+ </details>
143
+
144
+ For a complete, working example, you can always refer to the [Example ToDoList Repository](/academy/MasteryTrack/DocumentModelCreation/ExampleToDoListRepository) which contains the full implementation of the concepts discussed in this Mastery Track.
145
+
@@ -16,10 +16,9 @@ Each operation acts as a command that, when applied, transitions the document fr
16
16
 
17
17
  ## Connecting Operations to the Schema
18
18
 
19
- In the "Define ToDoList Document Model" guide, we used GraphQL `input` types to define the structure of the data required for each operation. Let's revisit that:
19
+ In the "Define ToDoList Document Model" chapter in the "Get Started" guide, we used GraphQL `input` types to define the structure of the data required for each operation. Let's revisit that:
20
20
 
21
21
  ```graphql
22
- # From 02-DefineToDoListDocumentModel.md
23
22
  # Defines a GraphQL input type for adding a new to-do item
24
23
  input AddTodoItemInput {
25
24
  id: ID!
@@ -96,7 +95,6 @@ The generated code from `ph generate` (as seen in `03-ImplementOperationReducers
96
95
  For example, the `ToDoListToDoListOperations` type generated by Powerhouse will expect methods corresponding to `addTodoItemOperation`, `updateTodoItemOperation`, and `deleteTodoItemOperation`.
97
96
 
98
97
  ```typescript
99
- // From 03-ImplementOperationReducers.md
100
98
  import { ToDoListToDoListOperations } from '../../gen/to-do-list/operations.js';
101
99
 
102
100
  export const reducer: ToDoListToDoListOperations = {
@@ -112,8 +110,65 @@ export const reducer: ToDoListToDoListOperations = {
112
110
  };
113
111
  ```
114
112
 
113
+ ## Practical Implementation: Defining Operations in Connect
114
+
115
+ Now that you understand the theory, let's walk through the practical steps of defining these operations for our `ToDoList` document model within the Powerhouse Connect application.
116
+
117
+ <details>
118
+ <summary>Tutorial: Specifying ToDoList Operations</summary>
119
+
120
+ Assuming you have already defined the state schema for the `ToDoList` as covered in the previous section, follow these steps to add the operations:
121
+
122
+ 1. **Create a Module for Operations:**
123
+ Below the schema editor in Connect, find the input field labeled `Add module`. Modules help organize your operations.
124
+ * Type `to_do_list` into the field and press Enter.
125
+
126
+ 2. **Add the `ADD_TODO_ITEM` Operation:**
127
+ A new field, `Add operation`, will appear under your new module.
128
+ * Type `ADD_TODO_ITEM` into this field and press Enter.
129
+ * An editor will appear for the operation's input type. You need to define the data required for this operation. Paste the following GraphQL `input` definition into the editor:
130
+
131
+ ```graphql
132
+ # Defines a GraphQL input type for adding a new to-do item
133
+ input AddTodoItemInput {
134
+ id: ID!
135
+ text: String!
136
+ }
137
+ ```
138
+
139
+ 3. **Add the `UPDATE_TODO_ITEM` Operation:**
140
+ * In the `Add operation` field again, type `UPDATE_TODO_ITEM` and press Enter.
141
+ * Paste the corresponding `input` definition into its editor:
142
+
143
+ ```graphql
144
+ # Defines a GraphQL input type for updating a to-do item
145
+ input UpdateTodoItemInput {
146
+ id: ID!
147
+ text: String
148
+ checked: Boolean
149
+ }
150
+ ```
151
+
152
+ 4. **Add the `DELETE_TODO_ITEM` Operation:**
153
+ * Finally, type `DELETE_TODO_ITEM` in the `Add operation` field and press Enter.
154
+ * Paste its `input` definition:
155
+
156
+ ```graphql
157
+ # Defines a GraphQL input type for deleting a to-do item
158
+ input DeleteTodoItemInput {
159
+ id: ID!
160
+ }
161
+ ```
162
+
163
+ 5. **Review and Export:**
164
+ After adding all three operations, your document model specification in Connect is complete for now. You can see how each operation (`ADD_TODO_ITEM`, etc.) is now explicitly linked to an input type that defines its payload.
165
+
166
+ The next step in a real project would be to click the `Export` button to save this specification file. In the next chapter, we will see how this exported file is used to generate code for our reducers.
167
+
168
+ </details>
169
+
115
170
  ## Conclusion
116
171
 
117
- Specifying document operations is a foundational step in building robust and predictable document models in Powerhouse. By clearly defining the "what" (the operation and its input) before implementing the "how" (the reducer logic), you create a clear contract for state transitions. This approach enhances type safety, testability, and the overall maintainability of your document model.
172
+ Specifying document operations is a foundational step in building robust and predictable document models in Powerhouse. By clearly defining the **"what" (the operation and its input)** before implementing the **"how" (the reducer logic)**, you create a clear contract for state transitions. This approach enhances type safety, testability, and the overall maintainability of your document model.
118
173
 
119
174
  In the next section, we will dive deeper into the implementation of the reducer functions for these specified operations.
@@ -90,22 +90,42 @@ Leveraging the `ph generate` command offers numerous advantages:
90
90
  5. **Alignment with Powerhouse Ecosystem:** The generated code is designed to integrate seamlessly with other parts of the Powerhouse ecosystem, such as the reducer execution engine and UI components.
91
91
  6. **Single Source of Truth:** Ensures that your codebase (especially types and action creators) stays synchronized with the document model specification defined in Connect. If the specification changes, regenerating the model will update these components accordingly.
92
92
 
93
- ## Example Workflow Snippet
93
+ ## Practical Implementation: Generating the `ToDoList` Model
94
94
 
95
- Let's assume you have defined a `ProjectTask` document model and exported `ProjectTask.phdm.zip`.
95
+ Now that you understand what the Document Model Generator does, let's walk through the practical steps of using it with our `ToDoList` example.
96
96
 
97
- 1. **Navigate to your Powerhouse project root in the terminal.**
98
- 2. **Run the generator:**
97
+ <details>
98
+ <summary>Tutorial: Generating the ToDoList Document Model</summary>
99
+
100
+ This tutorial assumes you have completed the previous steps in this Mastery Track, where you defined the state schema and operations for the `ToDoList` model in Connect and exported it.
101
+
102
+ ### Prerequisites
103
+
104
+ * **`ToDoList.phdm.zip` file**: You must have the document model specification file exported from Connect. If you do not have this file, please revisit the previous sections on specifying the state schema and operations.
105
+
106
+ ### Steps
107
+
108
+ 1. **Place the Specification File in Your Project**:
109
+ * Navigate to the root directory of your Powerhouse project.
110
+ * Move or copy your `ToDoList.phdm.zip` file into this directory.
111
+
112
+ 2. **Run the Generator Command**:
113
+ * Open your terminal in the root directory of your Powerhouse project.
114
+ * Execute the `ph generate` command, pointing to your specification file:
99
115
  ```bash
100
- ph generate ProjectTask.phdm.zip
116
+ ph generate ToDoList.phdm.zip
101
117
  ```
102
- 3. **Explore the generated files:**
103
- You would now find a new directory `document-models/project-task/` containing:
104
- * `project-task/spec.json`
105
- * `project-task/schema.graphql`
106
- * `project-task/gen/types.ts` (with `ProjectTaskState`, `AssignUserInput`, etc.)
107
- * `project-task/gen/operations.ts` (with `creators.assignUser(...)`, `creators.completeTask(...)`, etc.)
108
- * `project-task/src/reducers/project-task.ts` (with empty functions like `assignUserOperation`, `completeTaskOperation` awaiting your implementation).
118
+
119
+ 3. **Explore the Generated Files**:
120
+ * After the command completes successfully, you will find a new directory: `document-models/to-do-list/`.
121
+ * Take a moment to explore its contents, which will match the structure described earlier in this document:
122
+ * `spec.json` and `schema.graphql`: The definition of your model.
123
+ * `gen/`: Type-safe, generated code including `types.ts`, `operations.ts`, etc.
124
+ * `src/`: The skeleton for your implementation, most importantly `src/reducers/to-do-list.ts`, which will contain empty functions for `addTodoItemOperation`, `updateTodoItemOperation`, and `deleteTodoItemOperation`, ready for you to implement.
125
+
126
+ With these files generated, you have successfully scaffolded your document model. The project is now set up for you to implement the core business logic.
127
+
128
+ </details>
109
129
 
110
130
  ## Next Steps
111
131
 
@@ -47,7 +47,6 @@ Let's break down its components and principles:
47
47
  While you can write one large reducer that uses a `switch` statement or `if/else if` blocks based on `action.type`, Powerhouse's generated code typically encourages a more modular approach. You'll often implement a separate function for each operation, which are then combined into a main reducer object or map. The `ph generate` command usually sets up this structure for you. For example, in your `document-models/to-do-list/src/reducers/to-do-list.ts`, you'll find an object structure like this:
48
48
 
49
49
  ```typescript
50
- // Example structure from 01-GetStarted/03-ImplementOperationReducers.md
51
50
  import { ToDoListToDoListOperations } from '../../gen/to-do-list/operations.js'; // Generated type for operations
52
51
  import { ToDoListState } from '../../gen/types.js'; // Generated type for state
53
52
 
@@ -76,9 +75,9 @@ Let's break down its components and principles:
76
75
 
77
76
  ## Implementing Reducer Logic: A Practical Guide
78
77
 
79
- Let's use our familiar `ToDoList` example (as detailed in `01-GetStarted/03-ImplementOperationReducers.md`) to illustrate common patterns.
78
+ Let's use our familiar `ToDoList` example to illustrate common patterns. For this example, we'll assume our state schema has been updated to include a `stats` object to track the number of total, checked, and unchecked items.
80
79
 
81
- Assume our `ToDoListState` is:
80
+ Our `ToDoListState` now looks like this:
82
81
  ```typescript
83
82
  interface ToDoItem {
84
83
  id: string;
@@ -86,8 +85,15 @@ interface ToDoItem {
86
85
  checked: boolean;
87
86
  }
88
87
 
88
+ interface ToDoListStats {
89
+ total: number;
90
+ checked: number;
91
+ unchecked: number;
92
+ }
93
+
89
94
  interface ToDoListState {
90
95
  items: ToDoItem[];
96
+ stats: ToDoListStats;
91
97
  }
92
98
  ```
93
99
 
@@ -226,46 +232,105 @@ Using these types provides:
226
232
  * **Autocompletion and IntelliSense**: Improved developer experience in your IDE.
227
233
  * **Clearer code**: Types serve as documentation for the expected data structures.
228
234
 
229
- ## Testing Your Reducers
235
+ ## Practical Implementation: Writing the `ToDoList` Reducers
236
+
237
+ Now that you understand the principles, let's put them into practice by implementing the reducers for our `ToDoList` document model.
238
+
239
+ <details>
240
+ <summary>Tutorial: Implementing the ToDoList Reducers</summary>
230
241
 
231
- Reducers, being pure functions, are inherently easy to test. For each reducer:
232
- 1. Set up an initial state.
233
- 2. Create an action object (ideally using the generated action creators from `../../gen/creators.js` or `../../gen/operations.js`).
234
- 3. Call the reducer function with the initial state and the action.
235
- 4. Assert that the returned new state is what you expect it to be.
236
- 5. Crucially, also assert that the *original* state object was not modified.
242
+ This tutorial assumes you have followed the steps in the previous chapters, especially using `ph generate ToDoList.phdm.zip` to scaffold your document model's code.
237
243
 
238
- The `01-GetStarted/03-ImplementOperationReducers.md` document provides excellent examples of reducer tests:
244
+ ### Implement the Operation Reducers
245
+
246
+ Navigate to `document-models/to-do-list/src/reducers/to-do-list.ts`. The generator will have created a skeleton file. Replace its contents with the following logic.
239
247
 
240
248
  ```typescript
241
- // Example from 01-GetStarted/03-ImplementOperationReducers.md (simplified)
242
- import utils from '../../gen/utils'; // For createDocument
243
- import { reducer } from '../../gen/reducer'; // The combined reducer
244
- import * as creators from '../../gen/creators'; // Action creators
245
- import { ToDoListDocument, ToDoListState } from '../../gen/types';
246
-
247
- describe('Todolist Operations', () => {
248
- let document: ToDoListDocument; // Assuming ToDoListDocument wraps ToDoListState
249
-
250
- beforeEach(() => {
251
- document = utils.createDocument(); // Gets initial state
252
- });
253
-
254
- it('should handle addTodoItem operation', () => {
255
- const input = { id: '1', text: 'Buy milk' };
256
- const action = creators.addTodoItem(input); // Use action creator
257
-
258
- // Assuming your main reducer takes the whole document and action
259
- const updatedDocument = reducer(document, action);
260
-
261
- expect(updatedDocument.state.global.items).toHaveLength(1);
262
- expect(updatedDocument.state.global.items[0].text).toBe('Buy milk');
263
- // Also check that document.state.global.items is different from updatedDocument.state.global.items (immutability)
264
- });
265
- });
249
+ import { ToDoListToDoListOperations } from '../../gen/to-do-list/operations.js';
250
+ import { ToDoListState } from '../../gen/types.js'; // Assuming this now includes the 'stats' object
251
+
252
+ // REMARKS: This is our main reducer object. It implements all operations defined in the schema.
253
+ // The ToDoListToDoListOperations type is auto-generated from our GraphQL specification and ensures type safety.
254
+ export const reducer: ToDoListToDoListOperations = {
255
+ // REMARKS: The addTodoItemOperation adds a new item and updates our tracking statistics.
256
+ // - state: The current document state. Powerhouse uses a library like Immer.js,
257
+ // so you can write code that looks like it's mutating the state directly.
258
+ // Behind the scenes, Powerhouse ensures this results in an immutable update.
259
+ // - action: Contains the operation's 'type' and 'input' data from the client.
260
+ // - dispatch: A function to trigger subsequent operations (advanced, not used here).
261
+ addTodoItemOperation(state, action, dispatch) {
262
+ // REMARKS: We update our statistics for total and unchecked items.
263
+ state.stats.total += 1;
264
+ state.stats.unchecked += 1;
265
+
266
+ // REMARKS: We push the new to-do item into the items array.
267
+ // The data for the new item comes from the operation's input.
268
+ state.items.push({
269
+ id: action.input.id,
270
+ text: action.input.text,
271
+ checked: false, // New items always start as unchecked.
272
+ });
273
+ },
274
+
275
+ // REMARKS: The updateTodoItemOperation modifies an existing to-do item.
276
+ // It handles partial updates for text and checked status.
277
+ updateTodoItemOperation(state, action, dispatch) {
278
+ // REMARKS: First, we find the specific item we want to update using its ID.
279
+ const item = state.items.find(item => item.id === action.input.id);
280
+
281
+ // REMARKS: It's good practice to handle cases where the item might not be found.
282
+ if (!item) {
283
+ throw new Error(`Item with id ${action.input.id} not found`);
284
+ }
285
+
286
+ // REMARKS: We only update the text if it was provided in the input.
287
+ // This allows for partial updates (e.g., just checking an item without changing its text).
288
+ if (action.input.text) {
289
+ item.text = action.input.text;
290
+ }
291
+
292
+ // REMARKS: When the checked status changes, we also update our statistics.
293
+ // We check for `true` and `false` explicitly.
294
+ if (action.input.checked) { // This is true only if action.input.checked is true
295
+ // Note: This assumes the item was previously unchecked. For a more robust implementation,
296
+ // you could check `if (item.checked === false)` before updating stats to prevent inconsistencies.
297
+ state.stats.unchecked -= 1;
298
+ state.stats.checked += 1;
299
+ item.checked = action.input.checked;
300
+ }
301
+ if (action.input.checked === false) {
302
+ // Note: This assumes the item was previously checked.
303
+ state.stats.unchecked += 1;
304
+ state.stats.checked -= 1;
305
+ item.checked = action.input.checked;
306
+ }
307
+ },
308
+
309
+ // REMARKS: The deleteTodoItemOperation removes an item from the list.
310
+ deleteTodoItemOperation(state, action, dispatch) {
311
+ // REMARKS: Before removing the item, we find it to determine its checked status.
312
+ // This is necessary to correctly decrement our statistics.
313
+ const item = state.items.find(item => item.id === action.input.id);
314
+
315
+ // REMARKS: We always decrement the total count.
316
+ state.stats.total -= 1;
317
+
318
+ // REMARKS: We then decrement the 'checked' or 'unchecked' count based on the item's status.
319
+ if (item?.checked) { // This is shorthand for item?.checked === true
320
+ state.stats.checked -= 1;
321
+ }
322
+ if (item?.checked === false) {
323
+ state.stats.unchecked -= 1;
324
+ }
325
+
326
+ // REMARKS: Finally, we create a new 'items' array that excludes the deleted item.
327
+ // Assigning to 'state.items' is handled by Powerhouse to produce a new immutable state.
328
+ state.items = state.items.filter(item => item.id !== action.input.id);
329
+ },
330
+ };
266
331
  ```
267
332
 
268
- The generator typically creates a test file skeleton (e.g., `document-models/<YourModelName>/src/reducers/tests/<your-model-name>.test.ts`) to get you started. **Thoroughly testing your reducers is paramount for a robust document model.**
333
+ </details>
269
334
 
270
335
  ## Reducers and the Event Sourcing Model
271
336
 
@@ -279,4 +344,4 @@ This is why purity and immutability are so critical:
279
344
 
280
345
  Implementing document reducers is where you breathe life into your document model's specification. By adhering to the principles of purity and immutability, and by leveraging the type safety provided by Powerhouse's code generation, you can build predictable, testable, and maintainable business logic. These reducers form the immutable backbone of your document's state management, perfectly aligning with the event sourcing architecture that underpins Powerhouse.
281
346
 
282
- With your reducers implemented and tested, your document model is now functionally complete from a data manipulation perspective. The next stages often involve building user interfaces or integrations that interact with this model by dispatching the operations you've now so carefully implemented.
347
+ With your reducers implemented, your document model is now functionally complete from a data manipulation perspective. The next chapter covers how to write tests for this logic to ensure its correctness and reliability.