@powerhousedao/academy 4.1.0-dev.7 → 4.1.0-dev.70
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/.vscode/settings.json +1 -1
- package/CHANGELOG.md +551 -0
- package/README.md +3 -3
- package/babel.config.js +1 -1
- package/blog/BeyondCommunication-ABlueprintForDevelopment.md +25 -24
- package/blog/TheChallengeOfChange.md +21 -21
- package/docs/academy/01-GetStarted/00-ExploreDemoPackage.mdx +67 -30
- package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +36 -19
- package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +24 -19
- package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +44 -41
- package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +10 -10
- package/docs/academy/01-GetStarted/05-VetraStudio.md +164 -0
- package/docs/academy/01-GetStarted/06-ReactorMCP.md +58 -0
- package/docs/academy/01-GetStarted/home.mdx +185 -90
- package/docs/academy/01-GetStarted/images/Modules.png +0 -0
- package/docs/academy/01-GetStarted/images/VetraStudioDrive.png +0 -0
- package/docs/academy/01-GetStarted/styles.module.css +5 -5
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/01-Prerequisites.md +46 -18
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +118 -68
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +75 -33
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/_category_.json +6 -6
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +30 -21
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +41 -37
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +29 -25
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +36 -37
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +128 -109
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +95 -86
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +7 -9
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/_category_.json +6 -6
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +65 -47
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/02-ConfiguringDrives.md +77 -62
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/03-BuildingADriveExplorer.md +360 -349
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/00-DocumentToolbar.mdx +16 -10
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/01-OperationHistory.md +10 -7
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/02-RevisionHistoryTimeline.md +26 -11
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/_category_.json +6 -6
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/07-Authorization/01-RenownAuthenticationFlow.md +14 -7
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/07-Authorization/02-Authorization.md +0 -1
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/07-Authorization/_category_.json +5 -5
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/_category_.json +1 -1
- package/docs/academy/02-MasteryTrack/04-WorkWithData/01-GraphQLAtPowerhouse.md +45 -33
- package/docs/academy/02-MasteryTrack/04-WorkWithData/02-UsingTheAPI.mdx +61 -18
- package/docs/academy/02-MasteryTrack/04-WorkWithData/03-UsingSubgraphs.md +105 -456
- package/docs/academy/02-MasteryTrack/04-WorkWithData/04-analytics-processor.md +126 -110
- package/docs/academy/02-MasteryTrack/04-WorkWithData/05-RelationalDbProcessor.md +98 -65
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/GraphQL References/QueryingADocumentWithGraphQL.md +23 -21
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/best-practices.md +9 -9
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/graphql/index.md +11 -23
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/graphql/integration.md +25 -9
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/intro.md +10 -10
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/benchmarks.md +1 -1
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/index.md +16 -11
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/memory.md +6 -5
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/schema.md +2 -2
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/typescript/utilities.md +7 -5
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/use-cases/maker.md +32 -58
- package/docs/academy/02-MasteryTrack/04-WorkWithData/06-Analytics Engine/use-cases/processors.md +1 -1
- package/docs/academy/02-MasteryTrack/04-WorkWithData/07-drive-analytics.md +105 -71
- package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_01-SetupBuilderEnvironment.md +22 -0
- package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_02-CreateNewPowerhouseProject.md +9 -8
- package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_03-GenerateAnAnalyticsProcessor.md +28 -32
- package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_04-UpdateAnalyticsProcessor.md +25 -26
- package/docs/academy/02-MasteryTrack/04-WorkWithData/_ARCHIVE-AnalyticsProcessorTutorial/_category_.json +1 -1
- package/docs/academy/02-MasteryTrack/04-WorkWithData/_category_.json +7 -7
- package/docs/academy/02-MasteryTrack/05-Launch/01-IntroductionToPackages.md +3 -4
- package/docs/academy/02-MasteryTrack/05-Launch/02-PublishYourProject.md +69 -45
- package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +70 -40
- package/docs/academy/02-MasteryTrack/05-Launch/04-ConfigureEnvironment.md +1 -0
- package/docs/academy/02-MasteryTrack/05-Launch/_category_.json +7 -7
- package/docs/academy/02-MasteryTrack/_category_.json +6 -6
- package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +5 -3
- package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +38 -37
- package/docs/academy/03-ExampleUsecases/Chatroom/04-ImplementOperationReducers.md +45 -41
- package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +14 -14
- package/docs/academy/03-ExampleUsecases/Chatroom/06-LaunchALocalReactor.md +6 -6
- package/docs/academy/03-ExampleUsecases/Chatroom/_category_.json +1 -1
- package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +143 -61
- package/docs/academy/04-APIReferences/01-ReactHooks.md +649 -141
- package/docs/academy/04-APIReferences/04-RelationalDatabase.md +121 -113
- package/docs/academy/04-APIReferences/05-PHDocumentMigrationGuide.md +48 -41
- package/docs/academy/04-APIReferences/_category_.json +6 -6
- package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +1 -2
- package/docs/academy/05-Architecture/01-WorkingWithTheReactor.md +11 -8
- package/docs/academy/05-Architecture/05-DocumentModelTheory/_category_.json +1 -1
- package/docs/academy/05-Architecture/_category_.json +6 -6
- package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +25 -23
- package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +105 -93
- package/docs/academy/06-ComponentLibrary/03-IntegrateIntoAReactComponent.md +1 -0
- package/docs/academy/06-ComponentLibrary/_category_.json +7 -7
- package/docs/academy/07-Cookbook.md +267 -34
- package/docs/academy/08-Glossary.md +7 -1
- package/docs/bookofpowerhouse/01-Overview.md +2 -2
- package/docs/bookofpowerhouse/02-GeneralFrameworkAndPhilosophy.md +1 -7
- package/docs/bookofpowerhouse/03-PowerhouseSoftwareArchitecture.md +10 -7
- package/docs/bookofpowerhouse/04-DevelopmentApproaches.md +10 -4
- package/docs/bookofpowerhouse/05-SNOsandANewModelForOSSandPublicGoods.md +23 -30
- package/docs/bookofpowerhouse/06-SNOsInActionAndPlatformEconomies.md +0 -7
- package/docusaurus.config.ts +64 -66
- package/package.json +9 -7
- package/scripts/generate-combined-cli-docs.ts +43 -13
- package/sidebars.ts +2 -0
- package/src/components/HomepageFeatures/index.tsx +171 -78
- package/src/components/HomepageFeatures/styles.module.css +1 -2
- package/src/css/custom.css +89 -89
- package/src/pages/_archive-homepage.tsx +17 -16
- package/src/theme/DocCardList/index.tsx +9 -8
- package/static.json +6 -6
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -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,418 +25,146 @@ 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
|
|
|
32
|
+
## Example: Implement a search subgraph based on data from the reactor
|
|
30
33
|
|
|
34
|
+
In this example we implement a subgraph which allows to search through todo-list documents in a specific document drive.
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
context: {
|
|
34
|
-
admin: async (session) => {
|
|
35
|
-
const admins = await operationalStore.get("admins");
|
|
36
|
-
return admins.includes(session.user);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
```
|
|
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.
|
|
40
37
|
|
|
41
|
-
|
|
38
|
+
### 1. Generate the subgraph
|
|
42
39
|
|
|
43
|
-
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.
|
|
44
41
|
Open your project and start your terminal.
|
|
45
|
-
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.
|
|
46
43
|
|
|
47
44
|
```bash title="Run the following command to generate a new subgraph"
|
|
48
|
-
ph generate --subgraph
|
|
45
|
+
ph generate --subgraph search-todos
|
|
49
46
|
```
|
|
50
47
|
|
|
51
48
|
```bash title="Expected Output"
|
|
52
|
-
Loaded templates:
|
|
53
|
-
FORCED: ./subgraphs/
|
|
49
|
+
Loaded templates: /projects/powerhouse/powerhouse/packages/codegen/dist/src/codegen/.hygen/templates
|
|
50
|
+
FORCED: ./subgraphs/search-todos/index.ts
|
|
54
51
|
skipped: ./subgraphs/index.ts
|
|
55
52
|
inject: ./subgraphs/index.ts
|
|
53
|
+
|
|
54
|
+
Loaded templates: /projects/powerhouse/powerhouse/packages/codegen/dist/src/codegen/.hygen/templates
|
|
55
|
+
FORCED: ./subgraphs/search-todos/resolvers.ts
|
|
56
|
+
FORCED: ./subgraphs/search-todos/schema.ts
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
### What happened?
|
|
59
|
-
|
|
60
|
+
|
|
61
|
+
1. A new subgraph was created in `./subgraphs/search-todos/`
|
|
60
62
|
2. The subgraph was automatically registered in your project's registry
|
|
61
63
|
3. Basic boilerplate code was generated with an example query
|
|
62
64
|
|
|
63
65
|
If we now run `ph reactor` we will see the new subgraph being registered during the startup of the Reactor.
|
|
64
|
-
> Registered /todolist subgraph.
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
> Registered /graphql/search-todos subgraph.
|
|
67
68
|
|
|
68
69
|
```
|
|
69
70
|
Initializing Subgraph Manager...
|
|
70
|
-
> Registered /graphql/auth subgraph.
|
|
71
|
+
> Registered /graphql/auth subgraph.
|
|
71
72
|
> Registered /graphql/system subgraph.
|
|
72
73
|
> Registered /graphql/analytics subgraph.
|
|
73
74
|
> Registered /d/:drive subgraph.
|
|
74
75
|
> Updating router
|
|
75
|
-
> Registered /graphql supergraph
|
|
76
|
-
> Registered /graphql/
|
|
76
|
+
> Registered /graphql supergraph
|
|
77
|
+
> Registered /graphql/search-todos subgraph.
|
|
77
78
|
> Updating router
|
|
78
|
-
> Registered /graphql supergraph
|
|
79
|
+
> Registered /graphql supergraph
|
|
79
80
|
➜ Reactor: http://localhost:4001/d/powerhouse
|
|
80
81
|
```
|
|
81
82
|
|
|
82
|
-
## 2. Building a
|
|
83
|
-
|
|
84
|
-
Now that we've generated our subgraph, let's build a complete To-do List subgraph that extends the functionality of our To-do List document model. This subgraph will provide additional querying capabilities and demonstrate how subgraphs work with document models.
|
|
85
|
-
|
|
86
|
-
### 2.1 Understanding the to-do list document model
|
|
83
|
+
## 2. Building a search subgraph
|
|
87
84
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
```graphql
|
|
91
|
-
type ToDoListState {
|
|
92
|
-
items: [ToDoItem!]!
|
|
93
|
-
stats: ToDoListStats!
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
type ToDoItem {
|
|
97
|
-
id: ID!
|
|
98
|
-
text: String!
|
|
99
|
-
checked: Boolean!
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
type ToDoListStats {
|
|
103
|
-
total: Int!
|
|
104
|
-
checked: Int!
|
|
105
|
-
unchecked: Int
|
|
106
|
-
}
|
|
107
|
-
```
|
|
85
|
+
Now that we've generated our subgraph its tome to define the GraphQL schema and implement the resolvers.
|
|
108
86
|
|
|
109
|
-
|
|
110
|
-
- `ADD_TODO_ITEM`: Adds a new to-do item
|
|
111
|
-
- `UPDATE_TODO_ITEM`: Updates an existing to-do item
|
|
112
|
-
- `DELETE_TODO_ITEM`: Deletes a to-do item
|
|
113
|
-
|
|
114
|
-
### 2.2 Define the subgraph schema
|
|
115
|
-
|
|
116
|
-
Now let's create a subgraph that provides enhanced querying capabilities for our To-do List documents.
|
|
117
|
-
|
|
118
|
-
**Step 1: Define the schema in `subgraphs/to-do-list/schema.ts` by creating the file:**
|
|
87
|
+
**Step 1: Define the schema in `subgraphs/search-todos/schema.ts` by creating the file:**
|
|
119
88
|
|
|
120
89
|
```typescript
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
# Dashboard-style summary query - returns high-level metrics
|
|
124
|
-
# Similar to ToDoListStats from document model but optimized for quick queries
|
|
125
|
-
todoList: TodoListSummary
|
|
126
|
-
|
|
127
|
-
# Filtered list query - lets you get items by completion status
|
|
128
|
-
# More flexible than the basic document model - can filter checked/unchecked
|
|
129
|
-
todoItems(checked: Boolean): [TodoItem!]!
|
|
130
|
-
|
|
131
|
-
# Count-only query - when you just need numbers, not full data
|
|
132
|
-
# Faster than getting full list when you only need totals for dashboards
|
|
133
|
-
todoItemsCount(checked: Boolean): Int!
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
# This mirrors ToDoListStats from the document model
|
|
137
|
-
# But it's a "view" optimized for summary reports and dashboards
|
|
138
|
-
type TodoListSummary {
|
|
139
|
-
total: Int! # Total number of items
|
|
140
|
-
checked: Int! # Number of completed items
|
|
141
|
-
unchecked: Int! # Number of pending items
|
|
142
|
-
}
|
|
90
|
+
import { gql } from "graphql-tag";
|
|
91
|
+
import type { DocumentNode } from "graphql";
|
|
143
92
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
93
|
+
export const schema: DocumentNode = gql`
|
|
94
|
+
"""
|
|
95
|
+
Subgraph definition
|
|
96
|
+
"""
|
|
97
|
+
type Query {
|
|
98
|
+
searchTodos(driveId: String!, searchTerm: String!): [String!]!
|
|
150
99
|
}
|
|
151
|
-
|
|
100
|
+
`;
|
|
152
101
|
```
|
|
153
102
|
|
|
154
|
-
|
|
155
|
-
<details>
|
|
156
|
-
<summary> #### Understanding resolvers </summary>
|
|
157
|
-
|
|
158
|
-
Before diving into the technical implementation, let's understand why these three different query types matter for your product.
|
|
159
|
-
Think of resolvers as custom API endpoints that are automatically created based on what your users actually need to know about your data.
|
|
160
|
-
|
|
161
|
-
When someone asks your system a question through GraphQL, the resolver:
|
|
162
|
-
|
|
163
|
-
1. **Understands the request** - "The user wants unchecked items"
|
|
164
|
-
2. **Knows where to get the data** - "I need to check the todo_items database table"
|
|
165
|
-
3. **Applies the right filters** - "Only get items where checked = false"
|
|
166
|
-
4. **Returns the answer** - "Here are the 5 unchecked items"
|
|
167
|
-
|
|
168
|
-
**The three resolvers serve different business needs:**
|
|
169
|
-
|
|
170
|
-
- **`todoList` Resolver - The Dashboard**
|
|
171
|
-
- **Business value**: Perfect for executive dashboards or KPI displays
|
|
172
|
-
- **Use case**: "We have 150 total tasks, 89 completed, 61 pending"
|
|
173
|
-
- **Users**: Executives, managers, anyone needing high-level metrics
|
|
174
|
-
|
|
175
|
-
- **`todoItems` Resolver - The Detailed List**
|
|
176
|
-
- **Business value**: Great for operational views where people need to see actual tasks
|
|
177
|
-
- **Use case**: "Show me all pending tasks" or "Show me everything"
|
|
178
|
-
- **Users**: Workers, operators, anyone who needs to act on specific items
|
|
179
|
-
|
|
180
|
-
- **`todoItemsCount` Resolver - The Counter**
|
|
181
|
-
- **Business value**: Super fast for analytics or when you only need numbers
|
|
182
|
-
- **Use case**: "How many completed tasks do we have?" → "47"
|
|
183
|
-
- **Users**: Analysts, automated systems, performance dashboards
|
|
184
|
-
|
|
185
|
-
**Why this architecture matters:**
|
|
186
|
-
- **Performance**: Count queries are much faster than getting full lists when you only need numbers
|
|
187
|
-
- **User Experience**: Different resolvers serve different user needs efficiently
|
|
188
|
-
- **Flexibility**: Users can ask for exactly what they need, nothing more, nothing less
|
|
189
|
-
|
|
190
|
-
</details>
|
|
191
|
-
|
|
192
|
-
**Step 2: Create resolvers in `subgraphs/to-do-list/resolvers.ts`:**
|
|
103
|
+
**Step 2: Create resolvers in `subgraphs/search-todos/resolvers.ts`:**
|
|
193
104
|
|
|
194
105
|
```typescript
|
|
195
|
-
// subgraphs/
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
106
|
+
// subgraphs/search-todos/resolvers.ts
|
|
107
|
+
import { type Subgraph } from "@powerhousedao/reactor-api";
|
|
108
|
+
import { type ToDoListDocument } from "document-models/to-do-list/index.js";
|
|
109
|
+
|
|
110
|
+
export const getResolvers = (subgraph: Subgraph) => {
|
|
111
|
+
const reactor = subgraph.reactor;
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
Query: {
|
|
115
|
+
searchTodos: async (
|
|
116
|
+
parent: unknown,
|
|
117
|
+
args: { driveId: string; searchTerm: string },
|
|
118
|
+
) => {
|
|
119
|
+
const documents = await reactor.getDocuments(args.driveId);
|
|
120
|
+
const todoItems: string[] = [];
|
|
121
|
+
for (const docId of documents) {
|
|
122
|
+
const doc: ToDoListDocument = await reactor.getDocument(docId);
|
|
123
|
+
if (doc.header.documentType !== "powerhouse/todolist") {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const amountEntries = doc.state.global.items.filter((e) =>
|
|
128
|
+
e.text.includes(args.searchTerm),
|
|
129
|
+
).length;
|
|
130
|
+
if (amountEntries > 0) {
|
|
131
|
+
todoItems.push(docId);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return todoItems;
|
|
135
|
+
},
|
|
224
136
|
},
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
let query = subgraphInstance.operationalStore.count("* as count").from("todo_items");
|
|
228
|
-
|
|
229
|
-
if (checked !== undefined) {
|
|
230
|
-
query = query.where("checked", checked);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const result = await query.first();
|
|
234
|
-
return result?.count || 0;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
**Step 3: Implement the main class in `subgraphs/to-do-list/index.ts`:**
|
|
241
|
-
|
|
242
|
-
```typescript
|
|
243
|
-
// subgraphs/to-do-list/index.ts
|
|
244
|
-
import { typeDefs } from './schema.js';
|
|
245
|
-
import { createResolvers } from './resolvers.js';
|
|
246
|
-
|
|
247
|
-
export default class ToDoListSubgraph {
|
|
248
|
-
// Define the API endpoint where this subgraph will be accessible
|
|
249
|
-
// Users can query this at: http://localhost:4001/graphql/to-do-list
|
|
250
|
-
path = '/to-do-list';
|
|
251
|
-
|
|
252
|
-
// GraphQL schema definition (what queries are available)
|
|
253
|
-
typeDefs = typeDefs;
|
|
254
|
-
|
|
255
|
-
// Query handlers (how to fetch the data)
|
|
256
|
-
resolvers: any;
|
|
257
|
-
|
|
258
|
-
// Database interface (injected by Powerhouse framework)
|
|
259
|
-
operationalStore: any;
|
|
260
|
-
|
|
261
|
-
constructor() {
|
|
262
|
-
// Connect the resolvers to this subgraph instance
|
|
263
|
-
// This gives resolvers access to the database through this.operationalStore
|
|
264
|
-
this.resolvers = createResolvers(this);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Called once when the subgraph starts up
|
|
268
|
-
async onSetup() {
|
|
269
|
-
await this.createOperationalTables();
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Create the database tables we need for storing todo items
|
|
273
|
-
async createOperationalTables() {
|
|
274
|
-
await this.operationalStore.schema.createTableIfNotExists(
|
|
275
|
-
"todo_items", // Table name
|
|
276
|
-
(table: any) => {
|
|
277
|
-
table.string("id").primary(); // Unique identifier for each todo item
|
|
278
|
-
table.string("text").notNullable(); // The actual todo task text
|
|
279
|
-
table.boolean("checked").defaultTo(false); // Completion status (unchecked by default)
|
|
280
|
-
table.timestamp("created_at").defaultTo(this.operationalStore.fn.now()); // When item was created
|
|
281
|
-
table.timestamp("updated_at").defaultTo(this.operationalStore.fn.now()); // When item was last modified
|
|
282
|
-
}
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Event processor: Keeps subgraph data synchronized with document model changes
|
|
287
|
-
// When users add/update/delete todos in Connect, this method handles the updates
|
|
288
|
-
async process(event: any) {
|
|
289
|
-
// Handle new todo item creation
|
|
290
|
-
if (event.type === "ADD_TODO_ITEM") {
|
|
291
|
-
await this.operationalStore.insert("todo_items", {
|
|
292
|
-
id: event.input.id,
|
|
293
|
-
text: event.input.text,
|
|
294
|
-
checked: false,
|
|
295
|
-
created_at: new Date(),
|
|
296
|
-
updated_at: new Date()
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
console.log(`Added todo item: ${event.input.text}`);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Handle todo item updates (text changes, checking/unchecking)
|
|
303
|
-
if (event.type === "UPDATE_TODO_ITEM") {
|
|
304
|
-
const updateData: any = {
|
|
305
|
-
updated_at: new Date() // Always update the timestamp
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
// Only update fields that were actually changed
|
|
309
|
-
if (event.input.text !== undefined) {
|
|
310
|
-
updateData.text = event.input.text;
|
|
311
|
-
}
|
|
312
|
-
if (event.input.checked !== undefined) {
|
|
313
|
-
updateData.checked = event.input.checked;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
await this.operationalStore.update("todo_items")
|
|
317
|
-
.where("id", event.input.id)
|
|
318
|
-
.update(updateData);
|
|
319
|
-
|
|
320
|
-
console.log(`Updated todo item: ${event.input.id}`);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Handle todo item deletion
|
|
324
|
-
if (event.type === "DELETE_TODO_ITEM") {
|
|
325
|
-
await this.operationalStore.delete("todo_items")
|
|
326
|
-
.where("id", event.input.id);
|
|
327
|
-
|
|
328
|
-
console.log(`Deleted todo item: ${event.input.id}`);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### 2.3 Understanding the implementation
|
|
335
|
-
|
|
336
|
-
**What this multi-file approach provides:**
|
|
337
|
-
|
|
338
|
-
1. **Schema separation** (`schema.ts`): Clean GraphQL type definitions
|
|
339
|
-
2. **Resolver isolation** (`resolvers.ts`): Business logic separated from structure
|
|
340
|
-
3. **Main orchestration** (`index.ts`): Combines everything and handles lifecycle methods
|
|
341
|
-
|
|
342
|
-
**Key features implemented:**
|
|
343
|
-
- A `todo_items` operational table to store individual to-do items
|
|
344
|
-
- Fields that match our document model structure
|
|
345
|
-
- Timestamps for tracking when items were created and updated
|
|
346
|
-
- Resolvers that fetch and filter todo items from the operational store
|
|
347
|
-
- Event processing to keep the subgraph data synchronized with document model changes
|
|
348
|
-
|
|
349
|
-
### 2.4 Understanding the document model event integration
|
|
350
|
-
|
|
351
|
-
Notice that our `index.ts` file already includes a `process` method - this is the **processor integration** that keeps our subgraph synchronized with To-do List document model events. When users interact with To-do List documents through Connect, this method automatically handles the updates.
|
|
352
|
-
|
|
353
|
-
**How the existing processor integration works:**
|
|
354
|
-
|
|
355
|
-
The `process` method in our `index.ts` file handles three types of document model events:
|
|
356
|
-
|
|
357
|
-
**1. Adding new todo items:**
|
|
358
|
-
```typescript
|
|
359
|
-
if (event.type === "ADD_TODO_ITEM") {
|
|
360
|
-
await this.operationalStore.insert("todo_items", {
|
|
361
|
-
id: event.input.id,
|
|
362
|
-
text: event.input.text,
|
|
363
|
-
checked: false,
|
|
364
|
-
created_at: new Date(),
|
|
365
|
-
updated_at: new Date()
|
|
366
|
-
});
|
|
367
|
-
}
|
|
137
|
+
};
|
|
138
|
+
};
|
|
368
139
|
```
|
|
369
140
|
|
|
370
|
-
**2. Updating existing items:**
|
|
371
|
-
```typescript
|
|
372
|
-
if (event.type === "UPDATE_TODO_ITEM") {
|
|
373
|
-
// Only update fields that were actually changed
|
|
374
|
-
const updateData = { updated_at: new Date() };
|
|
375
|
-
if (event.input.text !== undefined) updateData.text = event.input.text;
|
|
376
|
-
if (event.input.checked !== undefined) updateData.checked = event.input.checked;
|
|
377
|
-
|
|
378
|
-
await this.operationalStore.update("todo_items")
|
|
379
|
-
.where("id", event.input.id)
|
|
380
|
-
.update(updateData);
|
|
381
|
-
}
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
**3. Deleting items:**
|
|
385
|
-
```typescript
|
|
386
|
-
if (event.type === "DELETE_TODO_ITEM") {
|
|
387
|
-
await this.operationalStore.delete("todo_items")
|
|
388
|
-
.where("id", event.input.id);
|
|
389
|
-
}
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
**The integration happens automatically:**
|
|
393
|
-
1. **User action**: Someone adds a todo item in Connect
|
|
394
|
-
2. **Document model**: Processes the `ADD_TODO_ITEM` operation
|
|
395
|
-
3. **Framework routing**: Powerhouse automatically calls your subgraph's `process` method
|
|
396
|
-
4. **Subgraph response**: Your `process` method updates the operational store
|
|
397
|
-
5. **Query availability**: Users can now query the updated data via GraphQL
|
|
398
|
-
|
|
399
|
-
### 2.5 Summary of what we've built
|
|
400
|
-
|
|
401
|
-
Our complete To-do List subgraph includes:
|
|
402
|
-
|
|
403
|
-
- **GraphQL schema** (`schema.ts`): Defines `todoList`, `todoItems`, and `todoItemsCount` queries
|
|
404
|
-
- **Resolvers** (`resolvers.ts`): Handle data fetching and filtering from the operational store
|
|
405
|
-
- **Main subgraph class** (`index.ts`): Coordinates everything and includes:
|
|
406
|
-
- **Operational table creation**: Sets up the `todo_items` table with proper schema
|
|
407
|
-
- **Event processing**: The `process` method keeps subgraph data synchronized with document model changes
|
|
408
|
-
- **Real-time updates**: Automatically handles `ADD_TODO_ITEM`, `UPDATE_TODO_ITEM`, and `DELETE_TODO_ITEM` events
|
|
409
|
-
|
|
410
|
-
**Key features:**
|
|
411
|
-
- **Filtering capability**: The `todoItems` query accepts an optional `checked` parameter
|
|
412
|
-
- **Performance optimization**: The `todoItemsCount` query returns just numbers when you don't need full data
|
|
413
|
-
- **Real-time synchronization**: Changes in Connect immediately appear in subgraph queries
|
|
414
|
-
- **Complete statistics**: The `todoList` query returns total, checked, and unchecked counts
|
|
415
|
-
|
|
416
141
|
## 3. Testing the to-do list subgraph
|
|
417
142
|
|
|
418
143
|
### 3.1. Start the reactor
|
|
144
|
+
|
|
419
145
|
To activate the subgraph, run:
|
|
420
146
|
|
|
421
147
|
```bash
|
|
422
148
|
ph reactor
|
|
423
149
|
```
|
|
424
|
-
Or, for full system startup:
|
|
425
|
-
|
|
426
|
-
```bash title="Start the Reactor & Connect in Studio or Locally"
|
|
427
|
-
ph dev
|
|
428
|
-
```
|
|
429
150
|
|
|
430
151
|
You should see the subgraph being registered in the console output:
|
|
152
|
+
|
|
431
153
|
```
|
|
432
|
-
> Registered /graphql/
|
|
154
|
+
> Registered /graphql/search-todos subgraph.
|
|
433
155
|
```
|
|
434
156
|
|
|
435
157
|
### 3.2. Create some test data
|
|
158
|
+
|
|
436
159
|
Before testing queries, let's create some To-do List documents with test data:
|
|
437
160
|
|
|
438
|
-
1.
|
|
161
|
+
1. Start Connect
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
ph connect
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
1. Open Connect at `http://localhost:3000` in the browser
|
|
439
168
|
2. Add the 'remote' drive that is running locally via the (+) 'Add Drive' button. Add 'http://localhost:4001/d/powerhouse'
|
|
440
169
|
3. Create a new To-do List document
|
|
441
170
|
4. Add some test items:
|
|
@@ -444,95 +173,26 @@ Before testing queries, let's create some To-do List documents with test data:
|
|
|
444
173
|
- "Test the subgraph" (leave unchecked)
|
|
445
174
|
|
|
446
175
|
### 3.3. Access GraphQL playground
|
|
176
|
+
|
|
447
177
|
Open your browser and go to:
|
|
448
178
|
|
|
449
179
|
```bash
|
|
450
|
-
http://localhost:4001/graphql
|
|
180
|
+
http://localhost:4001/graphql
|
|
451
181
|
```
|
|
452
182
|
|
|
453
183
|
### 3.4. Test the queries
|
|
454
184
|
|
|
455
|
-
**Query 1:
|
|
456
|
-
```graphql
|
|
457
|
-
query {
|
|
458
|
-
todoList {
|
|
459
|
-
total
|
|
460
|
-
checked
|
|
461
|
-
unchecked
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
**Query 2: Get all to-do items**
|
|
467
|
-
```graphql
|
|
468
|
-
query {
|
|
469
|
-
todoItems {
|
|
470
|
-
id
|
|
471
|
-
text
|
|
472
|
-
checked
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
**Query 3: Get only unchecked items**
|
|
478
|
-
```graphql
|
|
479
|
-
query {
|
|
480
|
-
todoItems(checked: false) {
|
|
481
|
-
id
|
|
482
|
-
text
|
|
483
|
-
checked
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
```
|
|
185
|
+
**Query 1: Search for Todos **
|
|
487
186
|
|
|
488
|
-
**Query 4: Get count of completed items**
|
|
489
187
|
```graphql
|
|
490
188
|
query {
|
|
491
|
-
|
|
189
|
+
searchTodos(driveId: "powerhouse", searchTerm: "Test")
|
|
492
190
|
}
|
|
493
191
|
```
|
|
494
192
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
**For the statistics query:**
|
|
498
|
-
```json
|
|
499
|
-
{
|
|
500
|
-
"data": {
|
|
501
|
-
"todoList": {
|
|
502
|
-
"total": 3,
|
|
503
|
-
"checked": 1,
|
|
504
|
-
"unchecked": 2
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
**For the items query:**
|
|
511
|
-
```json
|
|
512
|
-
{
|
|
513
|
-
"data": {
|
|
514
|
-
"todoItems": [
|
|
515
|
-
{
|
|
516
|
-
"id": "item-1",
|
|
517
|
-
"text": "Learn about subgraphs",
|
|
518
|
-
"checked": false
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
"id": "item-2",
|
|
522
|
-
"text": "Build a To-do List subgraph",
|
|
523
|
-
"checked": true
|
|
524
|
-
},
|
|
525
|
-
{
|
|
526
|
-
"id": "item-3",
|
|
527
|
-
"text": "Test the subgraph",
|
|
528
|
-
"checked": false
|
|
529
|
-
}
|
|
530
|
-
]
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
```
|
|
193
|
+
You should get a list of the document Ids which contain the search term "Test".
|
|
534
194
|
|
|
535
|
-
### 3.
|
|
195
|
+
### 3.5. Test real-time updates
|
|
536
196
|
|
|
537
197
|
To verify that your subgraph stays synchronized with document changes:
|
|
538
198
|
|
|
@@ -544,27 +204,26 @@ To verify that your subgraph stays synchronized with document changes:
|
|
|
544
204
|
|
|
545
205
|
This demonstrates the real-time synchronization between the document model and the subgraph through event processing.
|
|
546
206
|
|
|
547
|
-
## 4. Working with the
|
|
207
|
+
## 4. Working with the GraphQL Gateway
|
|
548
208
|
|
|
549
|
-
|
|
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.
|
|
550
210
|
|
|
551
211
|
### 4.1 Key concepts
|
|
552
212
|
|
|
553
|
-
|
|
554
|
-
|
|
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.
|
|
555
215
|
|
|
556
216
|
### 4.2 Benefits of using a supergraph
|
|
557
217
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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.
|
|
564
223
|
|
|
565
224
|
### 4.3 Use the Powerhouse supergraph
|
|
566
225
|
|
|
567
|
-
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.
|
|
568
227
|
|
|
569
228
|
- Start the reactor:
|
|
570
229
|
|
|
@@ -578,18 +237,16 @@ The Powerhouse supergraph for any given remote drive or reactor can be found und
|
|
|
578
237
|
http://localhost:4001/graphql
|
|
579
238
|
```
|
|
580
239
|
|
|
581
|
-
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.
|
|
582
241
|
|
|
583
242
|
**Example: Using the supergraph with To-do List documents**
|
|
584
243
|
|
|
585
244
|
1. Create a todo document in the `powerhouse` drive using the `ToDoList_createDocument` mutation:
|
|
245
|
+
|
|
586
246
|
```graphql
|
|
587
247
|
mutation {
|
|
588
248
|
ToDoList_createDocument(
|
|
589
|
-
input: {
|
|
590
|
-
documentId: "my-todo-list"
|
|
591
|
-
name: "My Test To-do List"
|
|
592
|
-
}
|
|
249
|
+
input: { documentId: "my-todo-list", name: "My Test To-do List" }
|
|
593
250
|
) {
|
|
594
251
|
id
|
|
595
252
|
name
|
|
@@ -598,19 +255,18 @@ The supergraph allows you to both query & mutate data from the same endpoint.
|
|
|
598
255
|
```
|
|
599
256
|
|
|
600
257
|
2. Add some items to your to-do list using the `ToDoList_addTodoItem` mutation:
|
|
258
|
+
|
|
601
259
|
```graphql
|
|
602
260
|
mutation {
|
|
603
261
|
ToDoList_addTodoItem(
|
|
604
262
|
docId: "my-todo-list"
|
|
605
|
-
input: {
|
|
606
|
-
id: "item-1"
|
|
607
|
-
text: "Learn about supergraphs"
|
|
608
|
-
}
|
|
263
|
+
input: { id: "item-1", text: "Learn about supergraphs" }
|
|
609
264
|
)
|
|
610
265
|
}
|
|
611
266
|
```
|
|
612
267
|
|
|
613
268
|
3. Query the document state using the `GetDocument` query:
|
|
269
|
+
|
|
614
270
|
```graphql
|
|
615
271
|
query {
|
|
616
272
|
ToDoList {
|
|
@@ -650,13 +306,14 @@ The supergraph allows you to both query & mutate data from the same endpoint.
|
|
|
650
306
|
}
|
|
651
307
|
```
|
|
652
308
|
|
|
653
|
-
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.
|
|
654
310
|
|
|
655
311
|
## 5. Summary
|
|
656
312
|
|
|
657
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:
|
|
658
314
|
|
|
659
315
|
### Key concepts learned:
|
|
316
|
+
|
|
660
317
|
- **Subgraphs extend document models** with additional querying and data processing capabilities
|
|
661
318
|
- **Operational data stores** provide efficient storage for subgraph data
|
|
662
319
|
- **Event processing** enables real-time synchronization between document models and subgraphs
|
|
@@ -670,7 +327,7 @@ This tutorial has provided you with a solid foundation for building sophisticate
|
|
|
670
327
|
- When an invoice-related task is marked complete, update the invoice status
|
|
671
328
|
- When an invoice is paid, automatically check off related tasks
|
|
672
329
|
|
|
673
|
-
2. **External Integrations**:
|
|
330
|
+
2. **External Integrations**:
|
|
674
331
|
- Sync tasks with external project management tools
|
|
675
332
|
- Connect with notification systems
|
|
676
333
|
- Integrate with analytics platforms
|
|
@@ -680,14 +337,6 @@ This tutorial has provided you with a solid foundation for building sophisticate
|
|
|
680
337
|
- Add automated task assignments
|
|
681
338
|
- Create custom reporting functionality
|
|
682
339
|
|
|
683
|
-
|
|
684
340
|
### Future enhancements
|
|
685
341
|
|
|
686
342
|
Bridge Processors and Subgraphs — Currently, there's a gap in how processors and subgraphs interact. Powerhouse might improve this in future updates.
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|