@powerhousedao/academy 4.1.0-staging.1 → 5.0.0-staging.10

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 (104) hide show
  1. package/.vscode/settings.json +1 -1
  2. package/CHANGELOG.md +127 -1
  3. package/README.md +3 -3
  4. package/babel.config.js +1 -1
  5. package/blog/BeyondCommunication-ABlueprintForDevelopment.md +25 -24
  6. package/blog/TheChallengeOfChange.md +21 -21
  7. package/docs/academy/01-GetStarted/00-ExploreDemoPackage.mdx +61 -24
  8. package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +21 -12
  9. package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +24 -19
  10. package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +44 -41
  11. package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +10 -10
  12. package/docs/academy/01-GetStarted/05-SpecDrivenAI.md +143 -0
  13. package/docs/academy/01-GetStarted/home.mdx +185 -90
  14. package/docs/academy/01-GetStarted/styles.module.css +5 -5
  15. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/01-Prerequisites.md +46 -18
  16. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +118 -68
  17. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +75 -33
  18. package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/_category_.json +6 -6
  19. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +30 -21
  20. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +41 -37
  21. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +29 -25
  22. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +36 -37
  23. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +128 -109
  24. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +95 -86
  25. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +7 -9
  26. package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/_category_.json +6 -6
  27. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +65 -47
  28. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/02-ConfiguringDrives.md +77 -62
  29. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/03-BuildingADriveExplorer.md +360 -349
  30. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/00-DocumentToolbar.mdx +16 -10
  31. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/01-OperationHistory.md +10 -7
  32. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/02-RevisionHistoryTimeline.md +26 -11
  33. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/_category_.json +6 -6
  34. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/07-Authorization/01-RenownAuthenticationFlow.md +14 -7
  35. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/07-Authorization/02-Authorization.md +0 -1
  36. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/07-Authorization/_category_.json +5 -5
  37. package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/_category_.json +1 -1
  38. package/docs/academy/02-MasteryTrack/04-WorkWithData/01-GraphQLAtPowerhouse.md +45 -33
  39. package/docs/academy/02-MasteryTrack/04-WorkWithData/02-UsingTheAPI.mdx +61 -18
  40. package/docs/academy/02-MasteryTrack/04-WorkWithData/03-UsingSubgraphs.md +50 -54
  41. package/docs/academy/02-MasteryTrack/04-WorkWithData/04-analytics-processor.md +126 -110
  42. package/docs/academy/02-MasteryTrack/04-WorkWithData/05-RelationalDbProcessor.md +75 -45
  43. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/GraphQL References/QueryingADocumentWithGraphQL.md +23 -21
  44. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/best-practices.md +9 -9
  45. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/graphql/index.md +11 -23
  46. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/graphql/integration.md +25 -9
  47. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/intro.md +10 -10
  48. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/benchmarks.md +1 -1
  49. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/index.md +16 -11
  50. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/memory.md +6 -5
  51. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/schema.md +2 -2
  52. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/utilities.md +7 -5
  53. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/use-cases/maker.md +32 -58
  54. package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/use-cases/processors.md +1 -1
  55. package/docs/academy/02-MasteryTrack/04-WorkWithData/07-drive-analytics.md +105 -71
  56. package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_01-SetupBuilderEnvironment.md +22 -0
  57. package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_02-CreateNewPowerhouseProject.md +9 -8
  58. package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_03-GenerateAnAnalyticsProcessor.md +28 -32
  59. package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_04-UpdateAnalyticsProcessor.md +25 -26
  60. package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_category_.json +1 -1
  61. package/docs/academy/02-MasteryTrack/04-WorkWithData/_category_.json +7 -7
  62. package/docs/academy/02-MasteryTrack/05-Launch/01-IntroductionToPackages.md +3 -4
  63. package/docs/academy/02-MasteryTrack/05-Launch/02-PublishYourProject.md +69 -45
  64. package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +70 -40
  65. package/docs/academy/02-MasteryTrack/05-Launch/04-ConfigureEnvironment.md +1 -0
  66. package/docs/academy/02-MasteryTrack/05-Launch/_category_.json +7 -7
  67. package/docs/academy/02-MasteryTrack/_category_.json +6 -6
  68. package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +5 -3
  69. package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +38 -37
  70. package/docs/academy/03-ExampleUsecases/Chatroom/04-ImplementOperationReducers.md +45 -41
  71. package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +14 -14
  72. package/docs/academy/03-ExampleUsecases/Chatroom/06-LaunchALocalReactor.md +6 -6
  73. package/docs/academy/03-ExampleUsecases/Chatroom/_category_.json +1 -1
  74. package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +9 -7
  75. package/docs/academy/04-APIReferences/01-ReactHooks.md +177 -129
  76. package/docs/academy/04-APIReferences/04-RelationalDatabase.md +121 -113
  77. package/docs/academy/04-APIReferences/05-PHDocumentMigrationGuide.md +48 -41
  78. package/docs/academy/04-APIReferences/_category_.json +6 -6
  79. package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +1 -2
  80. package/docs/academy/05-Architecture/01-WorkingWithTheReactor.md +11 -8
  81. package/docs/academy/05-Architecture/05-DocumentModelTheory/_category_.json +1 -1
  82. package/docs/academy/05-Architecture/_category_.json +6 -6
  83. package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +25 -23
  84. package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +105 -93
  85. package/docs/academy/06-ComponentLibrary/03-IntegrateIntoAReactComponent.md +1 -0
  86. package/docs/academy/06-ComponentLibrary/_category_.json +7 -7
  87. package/docs/academy/07-Cookbook.md +267 -34
  88. package/docs/academy/08-Glossary.md +7 -1
  89. package/docs/bookofpowerhouse/01-Overview.md +2 -2
  90. package/docs/bookofpowerhouse/02-GeneralFrameworkAndPhilosophy.md +1 -7
  91. package/docs/bookofpowerhouse/03-PowerhouseSoftwareArchitecture.md +10 -7
  92. package/docs/bookofpowerhouse/04-DevelopmentApproaches.md +10 -4
  93. package/docs/bookofpowerhouse/05-SNOsandANewModelForOSSandPublicGoods.md +23 -30
  94. package/docs/bookofpowerhouse/06-SNOsInActionAndPlatformEconomies.md +0 -7
  95. package/docusaurus.config.ts +64 -66
  96. package/package.json +1 -1
  97. package/scripts/generate-combined-cli-docs.ts +43 -13
  98. package/sidebars.ts +1 -0
  99. package/src/components/HomepageFeatures/index.tsx +171 -78
  100. package/src/components/HomepageFeatures/styles.module.css +1 -2
  101. package/src/css/custom.css +89 -89
  102. package/src/pages/_archive-homepage.tsx +17 -16
  103. package/src/theme/DocCardList/index.tsx +9 -8
  104. package/static.json +6 -6
@@ -6,6 +6,7 @@ Let's start with the basics and gradually add more complex features and function
6
6
  ## What is a subgraph?
7
7
 
8
8
  A subgraph in Powerhouse is a **GraphQL-based modular data component** that extends the functionality of your document models. While document models handle the core state and operations, subgraphs can:
9
+
9
10
  1. Connect to external APIs or databases
10
11
  2. Add custom queries and mutations
11
12
  3. Automate interactions between different document models
@@ -13,8 +14,8 @@ A subgraph in Powerhouse is a **GraphQL-based modular data component** that exte
13
14
 
14
15
  ### Subgraphs can retrieve data from
15
16
 
16
- - **The Reactor** – The core Powerhouse data system or network node.
17
- - **Relational Data Stores** – Structured data storage for operational processes, offering real-time updates, for querying structured data.
17
+ - **The Reactor** – The core Powerhouse data system or network node.
18
+ - **Relational Data Stores** – Structured data storage for operational processes, offering real-time updates, for querying structured data.
18
19
  - **Analytics Stores** – Aggregated historical data, useful for insights, reporting and business intelligence.
19
20
 
20
21
  ### Subgraphs consist of
@@ -24,22 +25,21 @@ A subgraph in Powerhouse is a **GraphQL-based modular data component** that exte
24
25
  - **Context Fields** — Additional metadata that helps in resolving data efficiently.
25
26
 
26
27
  #### Additionally, context fields allow resolvers to access extra information, such as:
28
+
27
29
  - **User authentication** (e.g., checking if a user is an admin).
28
30
  - **External data sources** (e.g., analytics).
29
31
 
30
-
31
-
32
32
  ## Example: Implement a search subgraph based on data from the reactor
33
33
 
34
- In this example we implement a subgraph which allows to search through todo-list documents in a specific document drive.
34
+ In this example we implement a subgraph which allows to search through todo-list documents in a specific document drive.
35
35
 
36
36
  First we will generate the subgraph with the help of the ph cli, then we will define the GraphQL schema and implement the resolvers and finally we will start the reactor and execute a query through the GraphQL Gateway.
37
37
 
38
38
  ### 1. Generate the subgraph
39
39
 
40
- Let's start by generating a new subgraph. For our tutorial we will create a new subgraph within our To-do List project.
40
+ Let's start by generating a new subgraph. For our tutorial we will create a new subgraph within our To-do List project.
41
41
  Open your project and start your terminal.
42
- The Powerhouse toolkit provides a command-line utility to create new subgraphs easily.
42
+ The Powerhouse toolkit provides a command-line utility to create new subgraphs easily.
43
43
 
44
44
  ```bash title="Run the following command to generate a new subgraph"
45
45
  ph generate --subgraph search-todos
@@ -57,24 +57,26 @@ Loaded templates: /projects/powerhouse/powerhouse/packages/codegen/dist/src/code
57
57
  ```
58
58
 
59
59
  ### What happened?
60
+
60
61
  1. A new subgraph was created in `./subgraphs/search-todos/`
61
62
  2. The subgraph was automatically registered in your project's registry
62
63
  3. Basic boilerplate code was generated with an example query
63
64
 
64
65
  If we now run `ph reactor` we will see the new subgraph being registered during the startup of the Reactor.
65
- > Registered /graphql/search-todos subgraph.
66
+
67
+ > Registered /graphql/search-todos subgraph.
66
68
 
67
69
  ```
68
70
  Initializing Subgraph Manager...
69
- > Registered /graphql/auth subgraph.
71
+ > Registered /graphql/auth subgraph.
70
72
  > Registered /graphql/system subgraph.
71
73
  > Registered /graphql/analytics subgraph.
72
74
  > Registered /d/:drive subgraph.
73
75
  > Updating router
74
- > Registered /graphql supergraph
75
- > Registered /graphql/search-todos subgraph.
76
+ > Registered /graphql supergraph
77
+ > Registered /graphql/search-todos subgraph.
76
78
  > Updating router
77
- > Registered /graphql supergraph
79
+ > Registered /graphql supergraph
78
80
  ➜ Reactor: http://localhost:4001/d/powerhouse
79
81
  ```
80
82
 
@@ -89,16 +91,13 @@ import { gql } from "graphql-tag";
89
91
  import type { DocumentNode } from "graphql";
90
92
 
91
93
  export const schema: DocumentNode = gql`
92
- """
93
- Subgraph definition
94
- """
95
-
96
- type Query {
94
+ """
95
+ Subgraph definition
96
+ """
97
+ type Query {
97
98
  searchTodos(driveId: String!, searchTerm: String!): [String!]!
98
- }
99
-
99
+ }
100
100
  `;
101
-
102
101
  ```
103
102
 
104
103
  **Step 2: Create resolvers in `subgraphs/search-todos/resolvers.ts`:**
@@ -113,7 +112,10 @@ export const getResolvers = (subgraph: Subgraph) => {
113
112
 
114
113
  return {
115
114
  Query: {
116
- searchTodos: async (parent: unknown, args: { driveId: string, searchTerm: string }) => {
115
+ searchTodos: async (
116
+ parent: unknown,
117
+ args: { driveId: string; searchTerm: string },
118
+ ) => {
117
119
  const documents = await reactor.getDocuments(args.driveId);
118
120
  const todoItems: string[] = [];
119
121
  for (const docId of documents) {
@@ -122,7 +124,9 @@ export const getResolvers = (subgraph: Subgraph) => {
122
124
  continue;
123
125
  }
124
126
 
125
- const amountEntries = doc.state.global.items.filter(e => e.text.includes(args.searchTerm)).length;
127
+ const amountEntries = doc.state.global.items.filter((e) =>
128
+ e.text.includes(args.searchTerm),
129
+ ).length;
126
130
  if (amountEntries > 0) {
127
131
  todoItems.push(docId);
128
132
  }
@@ -132,13 +136,12 @@ export const getResolvers = (subgraph: Subgraph) => {
132
136
  },
133
137
  };
134
138
  };
135
-
136
139
  ```
137
140
 
138
-
139
141
  ## 3. Testing the to-do list subgraph
140
142
 
141
143
  ### 3.1. Start the reactor
144
+
142
145
  To activate the subgraph, run:
143
146
 
144
147
  ```bash
@@ -146,14 +149,17 @@ ph reactor
146
149
  ```
147
150
 
148
151
  You should see the subgraph being registered in the console output:
152
+
149
153
  ```
150
154
  > Registered /graphql/search-todos subgraph.
151
155
  ```
152
156
 
153
157
  ### 3.2. Create some test data
158
+
154
159
  Before testing queries, let's create some To-do List documents with test data:
155
160
 
156
- 1. Start Connect
161
+ 1. Start Connect
162
+
157
163
  ```bash
158
164
  ph connect
159
165
  ```
@@ -167,6 +173,7 @@ ph connect
167
173
  - "Test the subgraph" (leave unchecked)
168
174
 
169
175
  ### 3.3. Access GraphQL playground
176
+
170
177
  Open your browser and go to:
171
178
 
172
179
  ```bash
@@ -176,6 +183,7 @@ http://localhost:4001/graphql
176
183
  ### 3.4. Test the queries
177
184
 
178
185
  **Query 1: Search for Todos **
186
+
179
187
  ```graphql
180
188
  query {
181
189
  searchTodos(driveId: "powerhouse", searchTerm: "Test")
@@ -196,28 +204,26 @@ To verify that your subgraph stays synchronized with document changes:
196
204
 
197
205
  This demonstrates the real-time synchronization between the document model and the subgraph through event processing.
198
206
 
199
-
200
207
  ## 4. Working with the GraphQL Gateway
201
208
 
202
209
  The GraphQL Gateway is a GraphQL schema that combines multiple underlying GraphQL APIs, known as subgraphs, into a single, unified graph. This architecture allows different teams to work independently on their respective services (subgraphs) while providing a single entry point for clients or users to query all available data.
203
210
 
204
211
  ### 4.1 Key concepts
205
212
 
206
- * **Subgraph:** An independent GraphQL service with its own schema. Each subgraph typically represents a specific domain or microservice within a larger system.
207
- * **Gateway/Router:** A server that sits in front of the subgraphs. It receives client queries, consults the supergraph schema, and routes parts of the query to the relevant subgraphs. It then stitches the results back together before sending the final response to the client.
213
+ - **Subgraph:** An independent GraphQL service with its own schema. Each subgraph typically represents a specific domain or microservice within a larger system.
214
+ - **Gateway/Router:** A server that sits in front of the subgraphs. It receives client queries, consults the supergraph schema, and routes parts of the query to the relevant subgraphs. It then stitches the results back together before sending the final response to the client.
208
215
 
209
216
  ### 4.2 Benefits of using a supergraph
210
217
 
211
- * **Federated Architecture:** Enables a microservices-based approach where different teams can own and operate their services independently.
212
- * **Scalability:** Individual subgraphs can be scaled independently based on their specific needs.
213
- * **Improved Developer Experience:** Clients interact with a single, consistent GraphQL API, simplifying data fetching and reducing the need to manage multiple endpoints.
214
- * **Schema Evolution:** Subgraphs can evolve their schemas independently, and the supergraph can be updated without breaking existing clients, as long as breaking changes are managed carefully.
215
- * **Clear Separation of Concerns:** Each subgraph focuses on a specific domain, leading to more maintainable and understandable codebases.
216
-
218
+ - **Federated Architecture:** Enables a microservices-based approach where different teams can own and operate their services independently.
219
+ - **Scalability:** Individual subgraphs can be scaled independently based on their specific needs.
220
+ - **Improved Developer Experience:** Clients interact with a single, consistent GraphQL API, simplifying data fetching and reducing the need to manage multiple endpoints.
221
+ - **Schema Evolution:** Subgraphs can evolve their schemas independently, and the supergraph can be updated without breaking existing clients, as long as breaking changes are managed carefully.
222
+ - **Clear Separation of Concerns:** Each subgraph focuses on a specific domain, leading to more maintainable and understandable codebases.
217
223
 
218
224
  ### 4.3 Use the Powerhouse supergraph
219
225
 
220
- The Powerhouse supergraph for any given remote drive or reactor can be found under `http://localhost:4001/graphql`. The gateway / supergraph available on `/graphql` combines all the subgraphs, except for the drive subgraph (which is accessible via `/d/:driveId`). To access the endpoint, start the reactor and navigate to the URL with `graphql` appended. The following commands explain how you can test & try the supergraph.
226
+ The Powerhouse supergraph for any given remote drive or reactor can be found under `http://localhost:4001/graphql`. The gateway / supergraph available on `/graphql` combines all the subgraphs, except for the drive subgraph (which is accessible via `/d/:driveId`). To access the endpoint, start the reactor and navigate to the URL with `graphql` appended. The following commands explain how you can test & try the supergraph.
221
227
 
222
228
  - Start the reactor:
223
229
 
@@ -231,18 +237,16 @@ The Powerhouse supergraph for any given remote drive or reactor can be found und
231
237
  http://localhost:4001/graphql
232
238
  ```
233
239
 
234
- The supergraph allows you to both query & mutate data from the same endpoint.
240
+ The supergraph allows you to both query & mutate data from the same endpoint.
235
241
 
236
242
  **Example: Using the supergraph with To-do List documents**
237
243
 
238
244
  1. Create a todo document in the `powerhouse` drive using the `ToDoList_createDocument` mutation:
245
+
239
246
  ```graphql
240
247
  mutation {
241
248
  ToDoList_createDocument(
242
- input: {
243
- documentId: "my-todo-list"
244
- name: "My Test To-do List"
245
- }
249
+ input: { documentId: "my-todo-list", name: "My Test To-do List" }
246
250
  ) {
247
251
  id
248
252
  name
@@ -251,19 +255,18 @@ The supergraph allows you to both query & mutate data from the same endpoint.
251
255
  ```
252
256
 
253
257
  2. Add some items to your to-do list using the `ToDoList_addTodoItem` mutation:
258
+
254
259
  ```graphql
255
260
  mutation {
256
261
  ToDoList_addTodoItem(
257
262
  docId: "my-todo-list"
258
- input: {
259
- id: "item-1"
260
- text: "Learn about supergraphs"
261
- }
263
+ input: { id: "item-1", text: "Learn about supergraphs" }
262
264
  )
263
265
  }
264
266
  ```
265
267
 
266
268
  3. Query the document state using the `GetDocument` query:
269
+
267
270
  ```graphql
268
271
  query {
269
272
  ToDoList {
@@ -303,13 +306,14 @@ The supergraph allows you to both query & mutate data from the same endpoint.
303
306
  }
304
307
  ```
305
308
 
306
- This demonstrates how the supergraph provides a unified interface to both your document models and your custom subgraphs, allowing you to query and mutate data from the same endpoint.
309
+ This demonstrates how the supergraph provides a unified interface to both your document models and your custom subgraphs, allowing you to query and mutate data from the same endpoint.
307
310
 
308
311
  ## 5. Summary
309
312
 
310
313
  Congratulations! You've successfully built a complete To-do List subgraph that demonstrates the power of extending document models with custom GraphQL functionality. Let's recap what you've accomplished:
311
314
 
312
315
  ### Key concepts learned:
316
+
313
317
  - **Subgraphs extend document models** with additional querying and data processing capabilities
314
318
  - **Operational data stores** provide efficient storage for subgraph data
315
319
  - **Event processing** enables real-time synchronization between document models and subgraphs
@@ -323,7 +327,7 @@ This tutorial has provided you with a solid foundation for building sophisticate
323
327
  - When an invoice-related task is marked complete, update the invoice status
324
328
  - When an invoice is paid, automatically check off related tasks
325
329
 
326
- 2. **External Integrations**:
330
+ 2. **External Integrations**:
327
331
  - Sync tasks with external project management tools
328
332
  - Connect with notification systems
329
333
  - Integrate with analytics platforms
@@ -333,14 +337,6 @@ This tutorial has provided you with a solid foundation for building sophisticate
333
337
  - Add automated task assignments
334
338
  - Create custom reporting functionality
335
339
 
336
-
337
340
  ### Future enhancements
338
341
 
339
342
  Bridge Processors and Subgraphs — Currently, there's a gap in how processors and subgraphs interact. Powerhouse might improve this in future updates.
340
-
341
-
342
-
343
-
344
-
345
-
346
-
@@ -152,9 +152,7 @@ const dimensions = {
152
152
  asset: AnalyticsPath.fromString(
153
153
  `sky/rwas/assets/t-bills/${fixedIncomeTransaction.assetId}`,
154
154
  ),
155
- portfolio: AnalyticsPath.fromString(
156
- `sky/rwas/portfolios/${documentId}`,
157
- ),
155
+ portfolio: AnalyticsPath.fromString(`sky/rwas/portfolios/${documentId}`),
158
156
  };
159
157
 
160
158
  // create the series values
@@ -202,9 +200,7 @@ With variables:
202
200
  "granularity": "annual",
203
201
  "start": "2024-01-01",
204
202
  "end": "2025-01-01",
205
- "metrics": [
206
- "AssetBalance",
207
- ],
203
+ "metrics": ["AssetBalance"],
208
204
  "dimensions": [
209
205
  {
210
206
  "name": "asset",
@@ -220,14 +216,17 @@ With variables:
220
216
 
221
217
  The RWA processor example pulls information from operation _inputs_ to insert analytics data. Another use case might be to capture meta-analytics from the states themselves.
222
218
 
223
- This processor listens to all documents of type `powerhouse/document-drive`, and since the `document-drive` is itself implemented on top of the document model core systems, this means that we can process all virtual file system operations. This processor count basic usage metrics using document operations and states.
219
+ This processor listens to all documents of type `powerhouse/document-drive`, and since the `document-drive` is itself implemented on top of the document model core systems, this means that we can process all virtual file system operations. This processor count basic usage metrics using document operations and states.
224
220
 
225
221
  ```typescript
226
222
  import { IAnalyticsStore } from "@powerhousedao/reactor-api";
227
223
  import { AnalyticsPath } from "@powerhousedao/reactor-api";
228
224
  import { AnalyticsSeriesInput } from "@powerhousedao/reactor-api";
229
225
  import { InternalTransmitterUpdate, IProcessor } from "document-drive";
230
- import { AddFileInput, DeleteNodeInput } from "document-drive/drive-document-model/gen/types";
226
+ import {
227
+ AddFileInput,
228
+ DeleteNodeInput,
229
+ } from "document-drive/drive-document-model/gen/types";
231
230
  import { PHDocument } from "document-model";
232
231
  import { DateTime } from "luxon";
233
232
 
@@ -247,91 +246,103 @@ export class DriveProcessorProcessor implements IProcessor {
247
246
  constructor(private readonly analyticsStore: IAnalyticsStore) {
248
247
  //
249
248
  }
250
-
249
+
251
250
  async onStrands<TDocument extends PHDocument>(
252
- strands: InternalTransmitterUpdate<TDocument>[]
251
+ strands: InternalTransmitterUpdate<TDocument>[],
253
252
  ): Promise<void> {
254
253
  if (strands.length === 0) {
255
254
  return;
256
255
  }
257
256
 
258
- const values:AnalyticsSeriesInput[] = [];
257
+ const values: AnalyticsSeriesInput[] = [];
259
258
 
260
259
  for (const strand of strands) {
261
260
  const operations = strand.operations;
262
- await Promise.all(operations.map((operation) => {
263
- const source = AnalyticsPath.fromString(`switchboard/default/${strand.driveId}`);
264
-
265
- const start = DateTime.fromISO(operation.timestamp);
266
- const dimensions: any = {
267
- documentType: AnalyticsPath.fromString(`document/type/powerhouse/document-drive`),
268
- };
269
-
270
- if (operation.index === 0) {
271
- this.analyticsStore.clearSeriesBySource(source);
272
- }
273
-
274
- switch (operation.type) {
275
- case "ADD_FILE": {
276
- // count documents of each type (ADD_FILE, input.documentType)
277
-
278
- // lookup node in state
279
- const input = operation.input as AddFileInput;
280
- const node = findNode(strand.state, input.id);
281
- if (!node) {
282
- return Promise.resolve();
283
- }
284
-
285
- dimensions["kind"] = AnalyticsPath.fromString(`document/kind/${node.kind}`);
286
-
287
- // increment by adding a 1
288
- values.push({
289
- source,
290
- start,
291
- value: 1,
292
- metric: "Count",
293
- dimensions,
294
- });
295
-
296
- break;
297
- }
298
- case "ADD_FOLDER": {
299
- dimensions["kind"] = AnalyticsPath.fromString("document/kind/folder");
300
-
301
- // increment by adding a 1
302
- values.push({
303
- source,
304
- start,
305
- value: 1,
306
- metric: "Count",
307
- dimensions,
308
- });
309
-
310
- break;
261
+ await Promise.all(
262
+ operations.map((operation) => {
263
+ const source = AnalyticsPath.fromString(
264
+ `switchboard/default/${strand.driveId}`,
265
+ );
266
+
267
+ const start = DateTime.fromISO(operation.timestamp);
268
+ const dimensions: any = {
269
+ documentType: AnalyticsPath.fromString(
270
+ `document/type/powerhouse/document-drive`,
271
+ ),
272
+ };
273
+
274
+ if (operation.index === 0) {
275
+ this.analyticsStore.clearSeriesBySource(source);
311
276
  }
312
- case "DELETE_NODE": {
313
- // the operation only contains the id, so lookup deleted item type in previous state
314
- const input = operation.input as DeleteNodeInput;
315
- const node = findNode(operation.previousState, input.id);
316
- if (!node) {
317
- return Promise.resolve();
277
+
278
+ switch (operation.type) {
279
+ case "ADD_FILE": {
280
+ // count documents of each type (ADD_FILE, input.documentType)
281
+
282
+ // lookup node in state
283
+ const input = operation.input as AddFileInput;
284
+ const node = findNode(strand.state, input.id);
285
+ if (!node) {
286
+ return Promise.resolve();
287
+ }
288
+
289
+ dimensions["kind"] = AnalyticsPath.fromString(
290
+ `document/kind/${node.kind}`,
291
+ );
292
+
293
+ // increment by adding a 1
294
+ values.push({
295
+ source,
296
+ start,
297
+ value: 1,
298
+ metric: "Count",
299
+ dimensions,
300
+ });
301
+
302
+ break;
303
+ }
304
+ case "ADD_FOLDER": {
305
+ dimensions["kind"] = AnalyticsPath.fromString(
306
+ "document/kind/folder",
307
+ );
308
+
309
+ // increment by adding a 1
310
+ values.push({
311
+ source,
312
+ start,
313
+ value: 1,
314
+ metric: "Count",
315
+ dimensions,
316
+ });
317
+
318
+ break;
319
+ }
320
+ case "DELETE_NODE": {
321
+ // the operation only contains the id, so lookup deleted item type in previous state
322
+ const input = operation.input as DeleteNodeInput;
323
+ const node = findNode(operation.previousState, input.id);
324
+ if (!node) {
325
+ return Promise.resolve();
326
+ }
327
+
328
+ dimensions["kind"] = AnalyticsPath.fromString(
329
+ `document/kind/${node.kind}`,
330
+ );
331
+
332
+ // decrement by adding a -1
333
+ values.push({
334
+ source,
335
+ start,
336
+ value: -1,
337
+ metric: "Count",
338
+ dimensions,
339
+ });
340
+
341
+ break;
318
342
  }
319
-
320
- dimensions["kind"] = AnalyticsPath.fromString(`document/kind/${node.kind}`);
321
-
322
- // decrement by adding a -1
323
- values.push({
324
- source,
325
- start,
326
- value: -1,
327
- metric: "Count",
328
- dimensions,
329
- });
330
-
331
- break;
332
343
  }
333
- }
334
- }));
344
+ }),
345
+ );
335
346
  }
336
347
 
337
348
  await this.analyticsStore.addSeriesValues(values);
@@ -462,9 +473,12 @@ Now, we can define the dimensions we want to group by. We can imagine that we wi
462
473
 
463
474
  ```ts
464
475
  const totalSpendOnHeadcount = useAnalyticsQuery({
465
- start, end, granularity, metrics,
476
+ start,
477
+ end,
478
+ granularity,
479
+ metrics,
466
480
  select: {
467
- contributor: "/billing-statement/contributor"
481
+ contributor: "/billing-statement/contributor",
468
482
  },
469
483
  lod: {
470
484
  contributor: 3,
@@ -483,7 +497,7 @@ const totalSpend = useAnalyticsQuery({
483
497
  granularity: "total", // <--- this means we'll get results for the entire time period
484
498
  metrics: ["Cash", "Powt"],
485
499
  select: {
486
- budget: "/billing-statement"
500
+ budget: "/billing-statement",
487
501
  },
488
502
  lod: {
489
503
  budget: 0, // <--- this means we'll get all results lumped together
@@ -496,7 +510,7 @@ const monthlySpendByBudget = useAnalyticsQuery({
496
510
  granularity: "monthly", // <--- this means we'll get results grouped by month
497
511
  metrics: ["Cash", "Powt"],
498
512
  select: {
499
- budget: "/billing-statement/budget"
513
+ budget: "/billing-statement/budget",
500
514
  },
501
515
  lod: {
502
516
  budget: 3, // <--- this means we'll get results grouped by "/billing-statement/budget/budget1", "/billing-statement/budget/budget2", etc.
@@ -509,7 +523,7 @@ const monthlySpendByCategory = useAnalyticsQuery({
509
523
  granularity: "monthly", // <--- this means we'll get results grouped by month
510
524
  metrics: ["Cash", "Powt"],
511
525
  select: {
512
- category: "/billing-statement/category"
526
+ category: "/billing-statement/category",
513
527
  },
514
528
  lod: {
515
529
  category: 3, // <--- this means we'll get results grouped by "/billing-statement/category/category1", "/billing-statement/category/category2", etc.
@@ -522,7 +536,7 @@ const yearlySpendByBudget = useAnalyticsQuery({
522
536
  granularity: "yearly", // <--- this means we'll get results grouped by year
523
537
  metrics: ["Cash", "Powt"],
524
538
  select: {
525
- budget: "/billing-statement/budget"
539
+ budget: "/billing-statement/budget",
526
540
  },
527
541
  lod: {
528
542
  budget: 3, // <--- this means we'll get results grouped by "/billing-statement/budget/budget1", "/billing-statement/budget/budget2", etc.
@@ -535,7 +549,7 @@ const monthlySpendByBudget = useAnalyticsQuery({
535
549
  granularity: "monthly", // <--- this means we'll get results grouped by month
536
550
  metrics: ["Cash", "Powt"],
537
551
  select: {
538
- budget: "/billing-statement/budget"
552
+ budget: "/billing-statement/budget",
539
553
  },
540
554
  lod: {
541
555
  budget: 3, // <--- this means we'll get results grouped by "/billing-statement/budget/budget1", "/billing-statement/budget/budget2", etc.
@@ -548,7 +562,7 @@ const last30DaysSpendByBudget = useAnalyticsQuery({
548
562
  granularity: "day", // <--- this means we'll get results grouped by day
549
563
  metrics: ["Cash", "Powt"],
550
564
  select: {
551
- budget: "/billing-statement/budget"
565
+ budget: "/billing-statement/budget",
552
566
  },
553
567
  lod: {
554
568
  budget: 3, // <--- this means we'll get results grouped by "/billing-statement/budget/budget1", "/billing-statement/budget/budget2", etc.
@@ -568,10 +582,10 @@ For instance, say we take our monthly spend by category query:
568
582
  const monthlySpendByCategory = useAnalyticsQuery({
569
583
  start,
570
584
  end,
571
- granularity: "monthly",
585
+ granularity: "monthly",
572
586
  metrics: ["Cash", "Powt"],
573
587
  select: {
574
- category: "/billing-statement/category"
588
+ category: "/billing-statement/category",
575
589
  },
576
590
  lod: {
577
591
  category: 3,
@@ -583,31 +597,36 @@ This gives us the results we're looking for but, by design, there may be many di
583
597
 
584
598
  ```ts
585
599
  // this source will match all analytics updates from any document in the drive
586
- const driveSource = AnalyticsPath.fromString(`billing-statement/${drive.header.id}`);
600
+ const driveSource = AnalyticsPath.fromString(
601
+ `billing-statement/${drive.header.id}`,
602
+ );
587
603
 
588
604
  // this source will match all analytics updates from a specific document in a drive
589
- const documentSource = AnalyticsPath.fromString(`billing-statement/${drive.header.id}/${document.header.id}`);
605
+ const documentSource = AnalyticsPath.fromString(
606
+ `billing-statement/${drive.header.id}/${document.header.id}`,
607
+ );
590
608
  ```
591
609
 
592
610
  ```ts
593
611
  const { state, data: drive } = useSelectedDrive();
594
612
 
595
- const results = useAnalyticsQuery({
596
- start, end,
597
- granularity: "monthly",
598
- metrics: ["Cash", "Powt"],
599
- select: {
600
- category: "/billing-statement/category"
613
+ const results = useAnalyticsQuery(
614
+ {
615
+ start,
616
+ end,
617
+ granularity: "monthly",
618
+ metrics: ["Cash", "Powt"],
619
+ select: {
620
+ category: "/billing-statement/category",
621
+ },
622
+ lod: {
623
+ category: 3,
624
+ },
601
625
  },
602
- lod: {
603
- category: 3,
626
+ {
627
+ sources: [`/billing-statement/${drive.header.id}/`],
604
628
  },
605
- },
606
- {
607
- sources: [
608
- `/billing-statement/${drive.header.id}/`
609
- ],
610
- });
629
+ );
611
630
  ```
612
631
 
613
632
  ### `IProcessor`
@@ -617,6 +636,3 @@ Now that we have designed out our data, we can open up `line-item-processor/inde
617
636
  ```ts
618
637
 
619
638
  ```
620
-
621
-
622
-