@salesforce/graphiti 10.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT_GUIDE.md +424 -0
- package/CHANGELOG.md +448 -0
- package/LICENSE.txt +82 -0
- package/README.md +204 -0
- package/TASK.md +249 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +683 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/args.d.ts +13 -0
- package/dist/commands/args.js +207 -0
- package/dist/commands/args.js.map +1 -0
- package/dist/commands/build.d.ts +11 -0
- package/dist/commands/build.js +209 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/connect.d.ts +8 -0
- package/dist/commands/connect.js +55 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/describe.d.ts +6 -0
- package/dist/commands/describe.js +229 -0
- package/dist/commands/describe.js.map +1 -0
- package/dist/commands/meta.d.ts +9 -0
- package/dist/commands/meta.js +140 -0
- package/dist/commands/meta.js.map +1 -0
- package/dist/commands/navigate.d.ts +14 -0
- package/dist/commands/navigate.js +105 -0
- package/dist/commands/navigate.js.map +1 -0
- package/dist/commands/query-helpers.d.ts +80 -0
- package/dist/commands/query-helpers.js +865 -0
- package/dist/commands/query-helpers.js.map +1 -0
- package/dist/commands/query.d.ts +26 -0
- package/dist/commands/query.js +901 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/review.d.ts +18 -0
- package/dist/commands/review.js +533 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/session-mgmt.d.ts +25 -0
- package/dist/commands/session-mgmt.js +206 -0
- package/dist/commands/session-mgmt.js.map +1 -0
- package/dist/commands/type.d.ts +6 -0
- package/dist/commands/type.js +342 -0
- package/dist/commands/type.js.map +1 -0
- package/dist/commands/validate-input.d.ts +6 -0
- package/dist/commands/validate-input.js +32 -0
- package/dist/commands/validate-input.js.map +1 -0
- package/dist/intent/build-aggregate.d.ts +33 -0
- package/dist/intent/build-aggregate.js +134 -0
- package/dist/intent/build-aggregate.js.map +1 -0
- package/dist/intent/build-create.d.ts +14 -0
- package/dist/intent/build-create.js +16 -0
- package/dist/intent/build-create.js.map +1 -0
- package/dist/intent/build-delete.d.ts +30 -0
- package/dist/intent/build-delete.js +53 -0
- package/dist/intent/build-delete.js.map +1 -0
- package/dist/intent/build-detail.d.ts +32 -0
- package/dist/intent/build-detail.js +80 -0
- package/dist/intent/build-detail.js.map +1 -0
- package/dist/intent/build-discover.d.ts +30 -0
- package/dist/intent/build-discover.js +149 -0
- package/dist/intent/build-discover.js.map +1 -0
- package/dist/intent/build-list.d.ts +28 -0
- package/dist/intent/build-list.js +75 -0
- package/dist/intent/build-list.js.map +1 -0
- package/dist/intent/build-mutation.d.ts +23 -0
- package/dist/intent/build-mutation.js +54 -0
- package/dist/intent/build-mutation.js.map +1 -0
- package/dist/intent/build-output.d.ts +27 -0
- package/dist/intent/build-output.js +60 -0
- package/dist/intent/build-output.js.map +1 -0
- package/dist/intent/build-raw.d.ts +23 -0
- package/dist/intent/build-raw.js +54 -0
- package/dist/intent/build-raw.js.map +1 -0
- package/dist/intent/build-update.d.ts +14 -0
- package/dist/intent/build-update.js +16 -0
- package/dist/intent/build-update.js.map +1 -0
- package/dist/intent/get-schema-with-priming.d.ts +26 -0
- package/dist/intent/get-schema-with-priming.js +32 -0
- package/dist/intent/get-schema-with-priming.js.map +1 -0
- package/dist/intent/select-child-relationship.d.ts +19 -0
- package/dist/intent/select-child-relationship.js +38 -0
- package/dist/intent/select-child-relationship.js.map +1 -0
- package/dist/intent/types.d.ts +159 -0
- package/dist/intent/types.js +21 -0
- package/dist/intent/types.js.map +1 -0
- package/dist/lib/apply-command.d.ts +15 -0
- package/dist/lib/apply-command.js +238 -0
- package/dist/lib/apply-command.js.map +1 -0
- package/dist/lib/auth.d.ts +38 -0
- package/dist/lib/auth.js +113 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/codegen.d.ts +32 -0
- package/dist/lib/codegen.js +700 -0
- package/dist/lib/codegen.js.map +1 -0
- package/dist/lib/command-registry.d.ts +59 -0
- package/dist/lib/command-registry.js +366 -0
- package/dist/lib/command-registry.js.map +1 -0
- package/dist/lib/formatter.d.ts +76 -0
- package/dist/lib/formatter.js +419 -0
- package/dist/lib/formatter.js.map +1 -0
- package/dist/lib/fs-utils.d.ts +23 -0
- package/dist/lib/fs-utils.js +46 -0
- package/dist/lib/fs-utils.js.map +1 -0
- package/dist/lib/graphql-name.d.ts +27 -0
- package/dist/lib/graphql-name.js +32 -0
- package/dist/lib/graphql-name.js.map +1 -0
- package/dist/lib/interactive.d.ts +6 -0
- package/dist/lib/interactive.js +562 -0
- package/dist/lib/interactive.js.map +1 -0
- package/dist/lib/introspect.d.ts +40 -0
- package/dist/lib/introspect.js +280 -0
- package/dist/lib/introspect.js.map +1 -0
- package/dist/lib/object-info.d.ts +79 -0
- package/dist/lib/object-info.js +313 -0
- package/dist/lib/object-info.js.map +1 -0
- package/dist/lib/path-selection.d.ts +50 -0
- package/dist/lib/path-selection.js +146 -0
- package/dist/lib/path-selection.js.map +1 -0
- package/dist/lib/prime-schema.d.ts +59 -0
- package/dist/lib/prime-schema.js +158 -0
- package/dist/lib/prime-schema.js.map +1 -0
- package/dist/lib/query-builder.d.ts +10 -0
- package/dist/lib/query-builder.js +168 -0
- package/dist/lib/query-builder.js.map +1 -0
- package/dist/lib/query-commands.d.ts +19 -0
- package/dist/lib/query-commands.js +262 -0
- package/dist/lib/query-commands.js.map +1 -0
- package/dist/lib/session.d.ts +205 -0
- package/dist/lib/session.js +1075 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/tokenize.d.ts +12 -0
- package/dist/lib/tokenize.js +48 -0
- package/dist/lib/tokenize.js.map +1 -0
- package/dist/lib/uiapi.d.ts +109 -0
- package/dist/lib/uiapi.js +159 -0
- package/dist/lib/uiapi.js.map +1 -0
- package/dist/lib/validator.d.ts +27 -0
- package/dist/lib/validator.js +100 -0
- package/dist/lib/validator.js.map +1 -0
- package/dist/lib/variable-promotion.d.ts +69 -0
- package/dist/lib/variable-promotion.js +95 -0
- package/dist/lib/variable-promotion.js.map +1 -0
- package/dist/lib/walker.d.ts +147 -0
- package/dist/lib/walker.js +723 -0
- package/dist/lib/walker.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +34 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +7 -0
- package/dist/mcp/stdio.js +25 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/mcp/tools/echo.d.ts +7 -0
- package/dist/mcp/tools/echo.js +17 -0
- package/dist/mcp/tools/echo.js.map +1 -0
- package/dist/mcp/tools/sf-gql-aggregate.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-aggregate.js +75 -0
- package/dist/mcp/tools/sf-gql-aggregate.js.map +1 -0
- package/dist/mcp/tools/sf-gql-create.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-create.js +35 -0
- package/dist/mcp/tools/sf-gql-create.js.map +1 -0
- package/dist/mcp/tools/sf-gql-delete.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-delete.js +31 -0
- package/dist/mcp/tools/sf-gql-delete.js.map +1 -0
- package/dist/mcp/tools/sf-gql-detail.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-detail.js +58 -0
- package/dist/mcp/tools/sf-gql-detail.js.map +1 -0
- package/dist/mcp/tools/sf-gql-discover.d.ts +9 -0
- package/dist/mcp/tools/sf-gql-discover.js +51 -0
- package/dist/mcp/tools/sf-gql-discover.js.map +1 -0
- package/dist/mcp/tools/sf-gql-list.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-list.js +53 -0
- package/dist/mcp/tools/sf-gql-list.js.map +1 -0
- package/dist/mcp/tools/sf-gql-raw.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-raw.js +39 -0
- package/dist/mcp/tools/sf-gql-raw.js.map +1 -0
- package/dist/mcp/tools/sf-gql-update.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-update.js +35 -0
- package/dist/mcp/tools/sf-gql-update.js.map +1 -0
- package/dist/mcp/tools/shared/zod-schemas.d.ts +49 -0
- package/dist/mcp/tools/shared/zod-schemas.js +46 -0
- package/dist/mcp/tools/shared/zod-schemas.js.map +1 -0
- package/package.json +36 -0
- package/ralph-loop.sh +120 -0
- package/scripts/smoke-orderby.sh +190 -0
- package/src/__tests__/helpers/prime-deps.ts +46 -0
- package/src/__tests__/helpers/schema.ts +73 -0
- package/src/__tests__/helpers/stdout.ts +33 -0
- package/src/__tests__/setup.ts +19 -0
- package/src/cli.ts +764 -0
- package/src/commands/__tests__/query.spec.ts +137 -0
- package/src/commands/args.ts +306 -0
- package/src/commands/build.ts +288 -0
- package/src/commands/connect.ts +60 -0
- package/src/commands/describe.ts +246 -0
- package/src/commands/meta.ts +171 -0
- package/src/commands/navigate.ts +134 -0
- package/src/commands/query-helpers.ts +1202 -0
- package/src/commands/query.ts +1085 -0
- package/src/commands/review.ts +670 -0
- package/src/commands/session-mgmt.ts +256 -0
- package/src/commands/type.ts +437 -0
- package/src/commands/validate-input.ts +38 -0
- package/src/intent/__tests__/build-aggregate.spec.ts +931 -0
- package/src/intent/__tests__/build-create-validation.spec.ts +135 -0
- package/src/intent/__tests__/build-delete.spec.ts +121 -0
- package/src/intent/__tests__/build-detail.spec.ts +333 -0
- package/src/intent/__tests__/build-discover.spec.ts +432 -0
- package/src/intent/__tests__/build-list.spec.ts +284 -0
- package/src/intent/__tests__/build-mutation.spec.ts +108 -0
- package/src/intent/__tests__/build-output.spec.ts +55 -0
- package/src/intent/__tests__/build-raw.spec.ts +153 -0
- package/src/intent/__tests__/build-update-validation.spec.ts +134 -0
- package/src/intent/build-aggregate.ts +182 -0
- package/src/intent/build-create.ts +19 -0
- package/src/intent/build-delete.ts +62 -0
- package/src/intent/build-detail.ts +95 -0
- package/src/intent/build-discover.ts +199 -0
- package/src/intent/build-list.ts +91 -0
- package/src/intent/build-mutation.ts +75 -0
- package/src/intent/build-output.ts +72 -0
- package/src/intent/build-raw.ts +61 -0
- package/src/intent/build-update.ts +19 -0
- package/src/intent/get-schema-with-priming.ts +43 -0
- package/src/intent/select-child-relationship.ts +48 -0
- package/src/intent/types.ts +181 -0
- package/src/lib/__tests__/apply-command.parity.spec.ts +97 -0
- package/src/lib/__tests__/apply-command.spec.ts +171 -0
- package/src/lib/__tests__/auth.spec.ts +292 -0
- package/src/lib/__tests__/formatter.spec.ts +86 -0
- package/src/lib/__tests__/graphql-name.spec.ts +64 -0
- package/src/lib/__tests__/introspect.spec.ts +399 -0
- package/src/lib/__tests__/object-info.spec.ts +124 -0
- package/src/lib/__tests__/path-selection.spec.ts +219 -0
- package/src/lib/__tests__/prime-schema.spec.ts +269 -0
- package/src/lib/__tests__/query-builder.spec.ts +95 -0
- package/src/lib/__tests__/query-commands.spec.ts +74 -0
- package/src/lib/__tests__/session.spec.ts +292 -0
- package/src/lib/__tests__/tokenize.spec.ts +33 -0
- package/src/lib/__tests__/uiapi.spec.ts +192 -0
- package/src/lib/__tests__/variable-promotion.spec.ts +211 -0
- package/src/lib/__tests__/walker.spec.ts +250 -0
- package/src/lib/apply-command.ts +286 -0
- package/src/lib/auth.ts +157 -0
- package/src/lib/codegen.ts +899 -0
- package/src/lib/command-registry.ts +434 -0
- package/src/lib/formatter.ts +587 -0
- package/src/lib/fs-utils.ts +46 -0
- package/src/lib/graphql-name.ts +35 -0
- package/src/lib/interactive.ts +634 -0
- package/src/lib/introspect.ts +320 -0
- package/src/lib/object-info.ts +409 -0
- package/src/lib/path-selection.ts +162 -0
- package/src/lib/prime-schema.ts +195 -0
- package/src/lib/query-builder.ts +193 -0
- package/src/lib/query-commands.ts +290 -0
- package/src/lib/session.ts +1304 -0
- package/src/lib/tokenize.ts +43 -0
- package/src/lib/uiapi.ts +176 -0
- package/src/lib/validator.ts +143 -0
- package/src/lib/variable-promotion.ts +145 -0
- package/src/lib/walker.ts +975 -0
- package/src/mcp/__tests__/server.spec.ts +155 -0
- package/src/mcp/server.ts +38 -0
- package/src/mcp/stdio.ts +29 -0
- package/src/mcp/tools/__tests__/sf-gql-aggregate.spec.ts +173 -0
- package/src/mcp/tools/__tests__/sf-gql-create.spec.ts +235 -0
- package/src/mcp/tools/__tests__/sf-gql-delete.spec.ts +194 -0
- package/src/mcp/tools/__tests__/sf-gql-detail.spec.ts +246 -0
- package/src/mcp/tools/__tests__/sf-gql-discover.spec.ts +320 -0
- package/src/mcp/tools/__tests__/sf-gql-list.spec.ts +128 -0
- package/src/mcp/tools/__tests__/sf-gql-raw.spec.ts +141 -0
- package/src/mcp/tools/__tests__/sf-gql-update.spec.ts +207 -0
- package/src/mcp/tools/echo.ts +24 -0
- package/src/mcp/tools/sf-gql-aggregate.ts +102 -0
- package/src/mcp/tools/sf-gql-create.ts +55 -0
- package/src/mcp/tools/sf-gql-delete.ts +49 -0
- package/src/mcp/tools/sf-gql-detail.ts +85 -0
- package/src/mcp/tools/sf-gql-discover.ts +67 -0
- package/src/mcp/tools/sf-gql-list.ts +73 -0
- package/src/mcp/tools/sf-gql-raw.ts +56 -0
- package/src/mcp/tools/sf-gql-update.ts +55 -0
- package/src/mcp/tools/shared/zod-schemas.ts +55 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# graphiti
|
|
2
|
+
|
|
3
|
+
Progressive GraphQL query builder CLI for Salesforce orgs. Treats a query as a **projection of the schema** — navigate the schema like a filesystem, select leaf fields, set arguments, and compose queries without ever writing raw GraphQL.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npm run build
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Run via `node dist/cli.js` (built) or `npx tsx src/cli.ts` (dev).
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- [Salesforce CLI](https://developer.salesforce.com/tools/salesforcecli) installed and authenticated (`sf org login web --alias myorg`)
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1. Connect to an org (downloads GraphQL schema once)
|
|
22
|
+
graphiti connect myorg
|
|
23
|
+
|
|
24
|
+
# 2. Start a new query session
|
|
25
|
+
graphiti query new myorg
|
|
26
|
+
# → prints Session: s_xxxx and root fields
|
|
27
|
+
|
|
28
|
+
# 3. Navigate the schema like a directory
|
|
29
|
+
graphiti query s_xxxx ls # list available fields
|
|
30
|
+
graphiti query s_xxxx ls -l # long listing (type, args, selections)
|
|
31
|
+
graphiti query s_xxxx ls --search Account # filter by name
|
|
32
|
+
graphiti query s_xxxx cd accounts # enter a field directory
|
|
33
|
+
graphiti query s_xxxx cd accounts/edges/node # multi-level nav
|
|
34
|
+
graphiti query s_xxxx pwd # print current path
|
|
35
|
+
graphiti query s_xxxx cd .. # go up one level
|
|
36
|
+
graphiti query s_xxxx cd / # go back to root
|
|
37
|
+
|
|
38
|
+
# 4. Select leaf fields (parent path added automatically)
|
|
39
|
+
graphiti query s_xxxx select Id
|
|
40
|
+
graphiti query s_xxxx select Name --as accountName
|
|
41
|
+
|
|
42
|
+
# 5. Remove a selection
|
|
43
|
+
graphiti query s_xxxx rm Id
|
|
44
|
+
|
|
45
|
+
# 6. Navigate union/interface fragment directories
|
|
46
|
+
graphiti query s_xxxx cd search # field of union type
|
|
47
|
+
graphiti query s_xxxx cd [Account] # inline fragment directory
|
|
48
|
+
graphiti query s_xxxx select name
|
|
49
|
+
|
|
50
|
+
# 7. Set field arguments
|
|
51
|
+
graphiti query s_xxxx cd accounts
|
|
52
|
+
graphiti query s_xxxx args set first 10
|
|
53
|
+
graphiti query s_xxxx args set where '{"Status":{"eq":"Open"}}'
|
|
54
|
+
graphiti query s_xxxx args ls
|
|
55
|
+
graphiti query s_xxxx args rm first
|
|
56
|
+
|
|
57
|
+
# 8. Bind arguments to query variables
|
|
58
|
+
graphiti query s_xxxx vars set \$whereInput AccountWhere
|
|
59
|
+
graphiti query s_xxxx bind set where \$whereInput
|
|
60
|
+
|
|
61
|
+
# 9. Set directory-scoped default argument values
|
|
62
|
+
graphiti query s_xxxx defaults set first 20
|
|
63
|
+
|
|
64
|
+
# 10. Preview and execute
|
|
65
|
+
graphiti query s_xxxx show
|
|
66
|
+
graphiti query s_xxxx validate
|
|
67
|
+
graphiti query s_xxxx execute
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Interactive Mode
|
|
71
|
+
|
|
72
|
+
Start an interactive REPL session for faster iteration — same commands, no need to repeat the session ID each time. Bare commands like `select`, `cd`, and `args set` show guided pick-lists.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
graphiti query s_xxxx interactive
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
REPL prompt: `graphiti [s_xxxx] /accounts > `
|
|
79
|
+
|
|
80
|
+
Inside the REPL:
|
|
81
|
+
|
|
82
|
+
- Type any subcommand (`ls`, `cd edges`, `select Id`, etc.) without the `graphiti query s_xxxx` prefix.
|
|
83
|
+
- `select` with no argument → guided pick-list of available leaf fields.
|
|
84
|
+
- `cd` with no argument → guided pick-list of navigable directories.
|
|
85
|
+
- `args set` with no argument → guided pick-list of available field arguments.
|
|
86
|
+
- `vars set` with just `set` → guided prompts for name, type, and default value.
|
|
87
|
+
- `exit` or `Ctrl-C` to quit.
|
|
88
|
+
|
|
89
|
+
## Commands Reference
|
|
90
|
+
|
|
91
|
+
### Setup
|
|
92
|
+
|
|
93
|
+
| Command | Description |
|
|
94
|
+
| ------------------------------------------- | ----------------------------------------------------------- |
|
|
95
|
+
| `graphiti connect <org>` | Connect to a Salesforce org and download its GraphQL schema |
|
|
96
|
+
| `graphiti type <org> <TypeName>` | Inspect a named GraphQL type |
|
|
97
|
+
| `graphiti validate <org> <TypeName> <json>` | Validate a JSON value against an input type |
|
|
98
|
+
|
|
99
|
+
### Session lifecycle
|
|
100
|
+
|
|
101
|
+
| Command | Description |
|
|
102
|
+
| ----------------------------------------------------- | -------------------------------------------------- |
|
|
103
|
+
| `graphiti query new <org> [--mutation] [--aggregate]` | Create a new query, mutation, or aggregate session |
|
|
104
|
+
| `graphiti query help [topic]` | Show command help, optionally scoped to a topic |
|
|
105
|
+
|
|
106
|
+
### Navigation
|
|
107
|
+
|
|
108
|
+
| Command | Description |
|
|
109
|
+
| --------------------------------------------- | ----------------------------------------------------------- |
|
|
110
|
+
| `graphiti query <sid> pwd` | Print current schema navigation path |
|
|
111
|
+
| `graphiti query <sid> ls [-l] [--search pat]` | List schema entries at current path |
|
|
112
|
+
| `graphiti query <sid> cd <path>` | Navigate to a schema path (supports `..`, `/`, multi-level) |
|
|
113
|
+
|
|
114
|
+
### Projection
|
|
115
|
+
|
|
116
|
+
| Command | Description |
|
|
117
|
+
| ------------------------------------------------- | ---------------------------------------- |
|
|
118
|
+
| `graphiti query <sid> select <leaf> [--as alias]` | Select a leaf field (parents auto-added) |
|
|
119
|
+
| `graphiti query <sid> rm <field-or-alias>` | Remove a leaf selection |
|
|
120
|
+
|
|
121
|
+
### Multiple instances & aliases
|
|
122
|
+
|
|
123
|
+
| Command | Description |
|
|
124
|
+
| ---------------------------------------------- | ---------------------------------------------- |
|
|
125
|
+
| `graphiti query <sid> alias ls` | List all projection instances at current path |
|
|
126
|
+
| `graphiti query <sid> alias set <name>` | Set alias on the focused instance |
|
|
127
|
+
| `graphiti query <sid> alias new <name>` | Create a new projection instance with an alias |
|
|
128
|
+
| `graphiti query <sid> alias use <alias-or-id>` | Switch focus to a different instance |
|
|
129
|
+
| `graphiti query <sid> alias clear` | Remove alias from the focused instance |
|
|
130
|
+
|
|
131
|
+
Use `alias new` to select the same field multiple times with different arguments (e.g., two aliased versions of `accounts` with different `where` clauses).
|
|
132
|
+
|
|
133
|
+
### Arguments
|
|
134
|
+
|
|
135
|
+
| Command | Description |
|
|
136
|
+
| ---------------------------------------------- | -------------------------------------------- |
|
|
137
|
+
| `graphiti query <sid> args ls` | List args on the focused projection instance |
|
|
138
|
+
| `graphiti query <sid> args set <name> <value>` | Set an argument value |
|
|
139
|
+
| `graphiti query <sid> args rm <name>` | Remove an argument |
|
|
140
|
+
|
|
141
|
+
### Variable bindings
|
|
142
|
+
|
|
143
|
+
| Command | Description |
|
|
144
|
+
| -------------------------------------------- | ---------------------------------------------- |
|
|
145
|
+
| `graphiti query <sid> bind ls` | List variable bindings on the focused instance |
|
|
146
|
+
| `graphiti query <sid> bind set <arg> <$var>` | Bind an argument to a query variable |
|
|
147
|
+
| `graphiti query <sid> bind rm <arg>` | Remove a binding |
|
|
148
|
+
|
|
149
|
+
### Directory-scoped defaults
|
|
150
|
+
|
|
151
|
+
Default argument values applied to all projection instances under the current path unless an instance overrides them.
|
|
152
|
+
|
|
153
|
+
| Command | Description |
|
|
154
|
+
| -------------------------------------------------- | ----------------------------- |
|
|
155
|
+
| `graphiti query <sid> defaults ls` | List defaults at current path |
|
|
156
|
+
| `graphiti query <sid> defaults set <name> <value>` | Set a default |
|
|
157
|
+
| `graphiti query <sid> defaults rm <name>` | Remove a default |
|
|
158
|
+
|
|
159
|
+
### Variables
|
|
160
|
+
|
|
161
|
+
| Command | Description |
|
|
162
|
+
| -------------------------------------------------------- | ------------------------ |
|
|
163
|
+
| `graphiti query <sid> vars ls` | List all query variables |
|
|
164
|
+
| `graphiti query <sid> vars set <$name> <type> [default]` | Define a variable |
|
|
165
|
+
| `graphiti query <sid> vars value <$name> <value>` | Set the runtime value |
|
|
166
|
+
| `graphiti query <sid> vars rm <$name>` | Remove a variable |
|
|
167
|
+
|
|
168
|
+
### Query output
|
|
169
|
+
|
|
170
|
+
| Command | Description |
|
|
171
|
+
| ------------------------------- | -------------------------------------------- |
|
|
172
|
+
| `graphiti query <sid> show` | Print the current GraphQL query string |
|
|
173
|
+
| `graphiti query <sid> validate` | Validate the query against the schema |
|
|
174
|
+
| `graphiti query <sid> execute` | Execute the query against the Salesforce org |
|
|
175
|
+
|
|
176
|
+
### Interactive
|
|
177
|
+
|
|
178
|
+
| Command | Description |
|
|
179
|
+
| ---------------------------------- | --------------------------------- |
|
|
180
|
+
| `graphiti query <sid> interactive` | Start an interactive REPL session |
|
|
181
|
+
|
|
182
|
+
## Performance
|
|
183
|
+
|
|
184
|
+
Schema and auth data are cached aggressively to minimize cold-start overhead:
|
|
185
|
+
|
|
186
|
+
- **Auth memoization** — `sf org display` is called at most once per process; the result is cached for the lifetime of the command or interactive session.
|
|
187
|
+
- **Session-stored instanceUrl** — The Salesforce instance URL is resolved once at `query new` time and persisted in the session file. Subsequent commands use this URL to locate the cached schema file directly, completely bypassing `sf org display`.
|
|
188
|
+
- **SDL schema cache** — After a schema is first built from the introspection JSON, it is serialized to a compact SDL file (`~/.graphiti/schemas/<cacheKey>.graphql`) next to the introspection JSON. Subsequent cold starts load the SDL file instead, which is faster than re-processing the raw introspection format.
|
|
189
|
+
- **In-process schema cache** — Within a single interactive session, the parsed `GraphQLSchema` is kept in memory so every command after the first is near-instant.
|
|
190
|
+
|
|
191
|
+
## Schema storage
|
|
192
|
+
|
|
193
|
+
Schemas are stored in `~/.graphiti/schemas/` keyed by a hash of the normalized Salesforce instance URL. This means:
|
|
194
|
+
|
|
195
|
+
- Reusing an org alias for a different org will always pick up the correct schema.
|
|
196
|
+
- `graphiti connect <org>` re-downloads and refreshes the cache for that org.
|
|
197
|
+
- The companion SDL file (`<cacheKey>.graphql`) is regenerated automatically whenever the introspection JSON is updated.
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npm run build # compile TypeScript
|
|
203
|
+
npm test # build + run tests
|
|
204
|
+
```
|
package/TASK.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Graphiti CLI Evaluation Task
|
|
2
|
+
|
|
3
|
+
You are evaluating the **graphiti** CLI — a progressive GraphQL query builder for Salesforce orgs. Your job is to use it end-to-end to build every query two real-world apps would need, then reflect on the experience and ship improvements.
|
|
4
|
+
|
|
5
|
+
Start by reading `AGENT_GUIDE.md` in this repo to learn the full command surface.
|
|
6
|
+
|
|
7
|
+
Use `vscodeOrg` as the org alias for all commands. Run graphiti via `npx graphiti` from the repo root.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Phase 1: Build Queries for an Order Management App
|
|
12
|
+
|
|
13
|
+
**App prompt:**
|
|
14
|
+
|
|
15
|
+
> I need an order management app for sales and fulfillment staff. The home screen should show a breakdown of orders by status. There should be a product catalog page where staff can browse and search products. The orders page should let users filter by status and date. Clicking an order should open a detail view showing the order header and all line items with product name, quantity, and price. Staff should be able to update the order status, add line items, and remove line items. There should also be a way to create a new order by selecting an account and adding products.
|
|
16
|
+
|
|
17
|
+
### Instructions
|
|
18
|
+
|
|
19
|
+
1. Use `npx graphiti describe` to explore the relevant SObjects (Order, OrderItem, Product2, PricebookEntry, Account, etc.) and understand available fields, picklist values, and relationships.
|
|
20
|
+
2. For each screen / feature below, create a named session and build the complete query using graphiti CLI commands (`new`, `select`, `set`, `var`, `check`, `show`).
|
|
21
|
+
3. Run `check` on every query to validate it.
|
|
22
|
+
4. Run `show` on every query to capture the final GraphQL.
|
|
23
|
+
5. Run `codegen` on each query to generate TypeScript types.
|
|
24
|
+
|
|
25
|
+
**Queries to build:**
|
|
26
|
+
|
|
27
|
+
| # | Session Name | Purpose |
|
|
28
|
+
| --- | --------------------- | ----------------------------------------------------------------------- |
|
|
29
|
+
| 1 | `order-home-status` | Home screen — count/list orders grouped by status |
|
|
30
|
+
| 2 | `product-catalog` | Product catalog — browse/search products with pagination |
|
|
31
|
+
| 3 | `order-list` | Order list — filterable by status and date, sortable |
|
|
32
|
+
| 4 | `order-detail` | Order detail — header fields + line items with product name, qty, price |
|
|
33
|
+
| 5 | `order-update-status` | Mutation — update order status |
|
|
34
|
+
| 6 | `order-add-line` | Mutation — add a line item to an order |
|
|
35
|
+
| 7 | `order-remove-line` | Mutation — remove a line item |
|
|
36
|
+
| 8 | `order-create` | Mutation — create a new order for an account |
|
|
37
|
+
| 9 | `account-lookup` | Account lookup/search for order creation |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Phase 2: Build Queries for a Support Queue App
|
|
42
|
+
|
|
43
|
+
**App prompt:**
|
|
44
|
+
|
|
45
|
+
> I need a support queue React app for our support team. The home view should have tiles showing case counts by status — New, In Progress, Waiting on Customer, Resolved — plus a list of unassigned cases and a list of cases assigned to me. There should be a case list page with filters for status, priority, and owner, sortable by date and priority. Opening a case should show the full case details including the related account and contact. From the detail view, agents should be able to update the case status and priority, assign it to themselves, and add a note. There should also be a form to create a new case with account and contact lookups. Show the current user's name in the header.
|
|
46
|
+
|
|
47
|
+
### Instructions
|
|
48
|
+
|
|
49
|
+
Same workflow as Phase 1. Use `describe` to explore, then build each query.
|
|
50
|
+
|
|
51
|
+
**Queries to build:**
|
|
52
|
+
|
|
53
|
+
| # | Session Name | Purpose |
|
|
54
|
+
| --- | ---------------------- | -------------------------------------------------------------------------- |
|
|
55
|
+
| 1 | `case-home-new` | Home tile — cases with Status = New |
|
|
56
|
+
| 2 | `case-home-inprogress` | Home tile — cases with Status = In Progress |
|
|
57
|
+
| 3 | `case-home-waiting` | Home tile — cases with Status = Waiting on Customer |
|
|
58
|
+
| 4 | `case-home-resolved` | Home tile — cases with Status = Resolved |
|
|
59
|
+
| 5 | `case-home-unassigned` | Home — unassigned cases list |
|
|
60
|
+
| 6 | `case-home-mine` | Home — cases assigned to current user |
|
|
61
|
+
| 7 | `case-list` | Case list — filterable by status/priority/owner, sortable by date/priority |
|
|
62
|
+
| 8 | `case-detail` | Case detail — full case with related account and contact |
|
|
63
|
+
| 9 | `case-update` | Mutation — update case status and priority |
|
|
64
|
+
| 10 | `case-assign` | Mutation — assign case to current user |
|
|
65
|
+
| 11 | `case-add-note` | Mutation — add a CaseComment / note |
|
|
66
|
+
| 12 | `case-create` | Mutation — create a new case with account and contact |
|
|
67
|
+
| 13 | `account-search` | Account lookup for case creation |
|
|
68
|
+
| 14 | `contact-search` | Contact lookup for case creation |
|
|
69
|
+
| 15 | `current-user` | Header — current user's name |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Phase 3: Validate TypeScript Type Accuracy
|
|
74
|
+
|
|
75
|
+
The generated TypeScript types are consumed by agents and developers to build app code against query results. Type accuracy is critical — wrong types cause runtime errors, missing optionality causes null-pointer crashes, and weak types (`Record<string, unknown>`) force consumers to cast blindly.
|
|
76
|
+
|
|
77
|
+
Run `codegen` on every query built in Phases 1 and 2 and audit the output for the following:
|
|
78
|
+
|
|
79
|
+
### 3a. Result type accuracy
|
|
80
|
+
|
|
81
|
+
For each query, check that the generated `*Result` interface accurately reflects the GraphQL response shape:
|
|
82
|
+
|
|
83
|
+
1. **Field names match the query.** Aliased fields should use the alias name, not the underlying field name. If you selected `Subject.value:subject`, the type should have `subject: string`, not `value: string`.
|
|
84
|
+
2. **Nullability is correct.** Parent relationship fields (Account, Contact, Owner, CreatedBy) should be `| null`. Connection types (edges, pageInfo) should NOT be nullable. Scalar value wrappers (`{ value: string }`) should be `| null` when the field is optional in the schema.
|
|
85
|
+
3. **Picklist values are union types, not plain `string`.** `Status.value` should be `CaseStatus` (a union of the actual picklist values), not `string`. Verify picklist unions contain the correct values from the org.
|
|
86
|
+
4. **Union/polymorphic types are represented.** `Owner` (a union of `User | Group`) should have the inline fragment structure, not a flat object. Both branches should be present.
|
|
87
|
+
5. **Connection pagination types are present.** `pageInfo { hasNextPage, endCursor }` and `totalCount` should be typed when selected.
|
|
88
|
+
6. **Nested child relationships are typed.** `CaseComments` inside a Case should have its own connection structure with edges/node, not be flattened.
|
|
89
|
+
|
|
90
|
+
### 3b. Variable type accuracy
|
|
91
|
+
|
|
92
|
+
For each query with variables, check that the generated `*Variables` interface is strongly typed:
|
|
93
|
+
|
|
94
|
+
1. **Filter variables use the real filter type.** `$where` should be typed as `Case_Filter` (with all Case field names and operator types), NOT `Record<string, unknown>`.
|
|
95
|
+
2. **OrderBy variables use the real orderBy type.** `$orderBy` should be `Case_OrderBy` with field-level `OrderByClause` entries and `ResultOrder` (`"ASC" | "DESC"`) enums.
|
|
96
|
+
3. **Mutation input variables are fully expanded.** `$input: CaseCreateInput` should expand to show `{ Case: CaseCreateRepresentation }` with all settable fields (`Subject?: string`, `Status?: string`, `AccountId?: string`, etc.), NOT `Record<string, unknown>`.
|
|
97
|
+
4. **Operator types are expanded.** Filter fields should have concrete operator interfaces (`PicklistOperators { eq?: string; ne?: string; in?: string[]; ... }`), not `Record<string, unknown>`.
|
|
98
|
+
5. **Scalar variables use correct primitives.** `$first` should be `number`, `$after` should be `string`, `$caseId` should be `string`.
|
|
99
|
+
6. **Required vs optional is correct.** Variables with `!` in their GraphQL type (e.g. `$input: CaseCreateInput!`) should be required in the interface (no `?`). Variables without `!` should be optional (`?`) and include `| null`.
|
|
100
|
+
|
|
101
|
+
### 3c. Picklist type accuracy (inputs and results)
|
|
102
|
+
|
|
103
|
+
Picklists are a critical pain point. An agent building a case creation form, a status filter, or a priority dropdown needs to know the exact valid values — not just `string`. Audit every picklist field across both result types and input/variable types:
|
|
104
|
+
|
|
105
|
+
#### Result-side picklists
|
|
106
|
+
|
|
107
|
+
1. **Every picklist field in a result type should use a union type alias.** `Status.value` should be `CaseStatus` (not `string`), `Priority.value` should be `CasePriority`, `Origin.value` should be `CaseOrigin`, etc.
|
|
108
|
+
2. **The union type should contain the actual org values.** Run `describe <SObject>` and cross-reference the picklist values listed there against the generated union. They must match exactly. If the org has `Status` values `["New", "On Hold", "Escalated", "Closed"]`, the type must be `"New" | "On Hold" | "Escalated" | "Closed"` — not a subset, not a superset.
|
|
109
|
+
3. **Picklist fields on related objects should also be enriched.** If case-detail selects `Account.Industry.value`, that should be `AccountIndustry`, not `string`.
|
|
110
|
+
|
|
111
|
+
#### Input-side picklists
|
|
112
|
+
|
|
113
|
+
4. **Mutation input picklist fields should use the same union type or be constrained.** In `CaseCreateRepresentation`, `Status?: string` is too weak — an agent could pass `"Invalid"` and only discover the error at runtime. Verify whether codegen constrains these to the picklist union (e.g. `Status?: CaseStatus`). If it doesn't, document this as a type accuracy issue.
|
|
114
|
+
5. **Filter operator types for picklist fields should indicate valid values.** In `Case_Filter`, `Status?: PicklistOperators` where `PicklistOperators { eq?: string }` means an agent filtering by status has no autocomplete for valid values. Check whether the filter operator type carries the picklist constraint. If `eq` is just `string`, document this.
|
|
115
|
+
6. **OrderBy fields for picklists should be fine** (they only need `ASC`/`DESC`), but verify they aren't incorrectly typed.
|
|
116
|
+
|
|
117
|
+
#### What to test
|
|
118
|
+
|
|
119
|
+
Pick at least 3 queries that touch picklist fields and verify both sides:
|
|
120
|
+
|
|
121
|
+
- `case-list` or `case-home-*` — `Status` and `Priority` in both the result type AND the `$where` filter variable type
|
|
122
|
+
- `case-create` — `Status`, `Priority`, `Origin`, `Reason`, `Type` in the `CaseCreateRepresentation` input
|
|
123
|
+
- `case-detail` — `Status` and `Priority` in the result, plus any picklist on related Account or Contact
|
|
124
|
+
- `order-list` or `order-detail` — `Status` on Order (if it's a picklist)
|
|
125
|
+
|
|
126
|
+
For each picklist field, note:
|
|
127
|
+
|
|
128
|
+
- The field name and which SObject it belongs to
|
|
129
|
+
- Whether the result type uses a union alias (good) or plain `string` (bad)
|
|
130
|
+
- Whether the input/filter type uses a union alias, plain `string`, or `Record<string, unknown>`
|
|
131
|
+
- The actual valid values from `describe`
|
|
132
|
+
|
|
133
|
+
### 3d. @optional directive handling
|
|
134
|
+
|
|
135
|
+
For any fields selected with `@optional` (FLS safety), verify:
|
|
136
|
+
|
|
137
|
+
1. **The field is marked optional in the TypeScript type** (`fieldName?: Type | undefined`).
|
|
138
|
+
2. **Fields without `@optional` are NOT marked optional** (no spurious `?`).
|
|
139
|
+
3. If no fields use `@optional`, add it to at least 2 fields on one query (e.g. `optional Description Subject` on case-detail) and verify the codegen output changes correctly.
|
|
140
|
+
|
|
141
|
+
### 3e. Agent usability audit
|
|
142
|
+
|
|
143
|
+
Pretend you are an agent writing React components that consume these types. For each query, verify:
|
|
144
|
+
|
|
145
|
+
1. **Can you write a data access expression without `as` casts?** E.g. `data.uiapi.query.Case.edges[0].node.Status.value` should typecheck without casting. If you need `as unknown as X` anywhere, the type is wrong.
|
|
146
|
+
2. **Do mutation input types give autocomplete?** When constructing a `CreateCaseVariables` value, do you get field name suggestions for the input object, or is it `Record<string, unknown>` where anything goes?
|
|
147
|
+
3. **Are there any `unknown` types that should be concrete?** Scan for `unknown` in the output — each one is a place where an agent would have to guess or hardcode.
|
|
148
|
+
|
|
149
|
+
### What to do with findings
|
|
150
|
+
|
|
151
|
+
- Document every type accuracy issue in your reflection (Phase 4) under a new **"Type Accuracy Issues"** section.
|
|
152
|
+
- For each issue, note: the session name, the field/variable, what the type IS vs. what it SHOULD be.
|
|
153
|
+
- In Phase 5 (Improve), prioritize type accuracy fixes alongside other improvements. Type accuracy issues that affect agent usability should be treated as high priority.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Phase 4: Reflect
|
|
158
|
+
|
|
159
|
+
After completing Phases 1–3, write a structured reflection. Create or update a file called `REFLECTION.md` in the repo root with the following sections:
|
|
160
|
+
|
|
161
|
+
### What Went Well
|
|
162
|
+
|
|
163
|
+
- Which commands were intuitive and efficient?
|
|
164
|
+
- Where did the CLI save time vs. writing raw GraphQL?
|
|
165
|
+
- What patterns emerged that felt natural?
|
|
166
|
+
|
|
167
|
+
### What Was Painful
|
|
168
|
+
|
|
169
|
+
- Which tasks required too many commands or felt clunky?
|
|
170
|
+
- Where did you get stuck, hit confusing errors, or have to guess?
|
|
171
|
+
- What information was missing or hard to discover?
|
|
172
|
+
- Were there queries you couldn't build, or that required awkward workarounds?
|
|
173
|
+
|
|
174
|
+
### Type Accuracy Issues
|
|
175
|
+
|
|
176
|
+
For each codegen issue found in Phase 3, document:
|
|
177
|
+
|
|
178
|
+
- **Session:** which query/mutation
|
|
179
|
+
- **Field/Variable:** the specific field or variable name
|
|
180
|
+
- **Current type:** what codegen produces now
|
|
181
|
+
- **Expected type:** what it should produce for correct agent consumption
|
|
182
|
+
- **Impact:** how this would affect an agent writing code against the type (e.g. "agent would need an `as` cast", "agent gets no autocomplete for mutation fields", "agent could pass invalid status value")
|
|
183
|
+
|
|
184
|
+
### Missing Capabilities
|
|
185
|
+
|
|
186
|
+
- What commands or features would have made the process significantly easier?
|
|
187
|
+
- Are there common query patterns that should be first-class operations?
|
|
188
|
+
|
|
189
|
+
### Friction Log
|
|
190
|
+
|
|
191
|
+
For each moment of friction, note:
|
|
192
|
+
|
|
193
|
+
- The exact command(s) you ran
|
|
194
|
+
- What you expected to happen
|
|
195
|
+
- What actually happened
|
|
196
|
+
- How you worked around it (if you did)
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Phase 5: Improve the CLI
|
|
201
|
+
|
|
202
|
+
Based on your reflection, do the following:
|
|
203
|
+
|
|
204
|
+
1. **Prioritize**: Pick the top 3–5 improvements that would have the highest impact on making GraphQL query authoring seamless for both humans and AI agents. **Type accuracy issues that affect agent usability should be weighted heavily** — an agent that can trust the types will write correct code on the first try.
|
|
205
|
+
2. **Plan**: For each improvement, write a brief design (what changes, where in the codebase, any new commands or flags).
|
|
206
|
+
3. **Implement**: Make the code changes. Run `npm run build` and `npm test` after each change to verify nothing breaks.
|
|
207
|
+
4. **Verify**: Re-run `codegen` on the affected queries and confirm the types are now correct. For type accuracy fixes, write out the before/after comparison in the reflection.
|
|
208
|
+
|
|
209
|
+
### Simplicity discipline
|
|
210
|
+
|
|
211
|
+
Before adding ANY new flag, command, or shorthand, ask yourself:
|
|
212
|
+
|
|
213
|
+
1. **Can this be solved by better error messages or help text instead?** A self-documenting CLI that tells you what to do next when something goes wrong is more valuable than a shorthand that saves one command. If the existing commands can do the job in 2-3 steps and the error messages guide you there, that's good enough — don't add a flag.
|
|
214
|
+
|
|
215
|
+
2. **Can this be solved by improving existing command behavior?** Auto-correction (like auto-injecting `Record/` for mutations or `@args/` for set) is better than adding a new flag. Making the existing command smarter is always preferable to adding surface area.
|
|
216
|
+
|
|
217
|
+
3. **Does this new flag compose well, or does it create combinatorial complexity?** A command like `clone --set --unset --var --select --force` has 5 flags that interact with each other. Each new flag multiplies the testing/documentation burden. If a flag only makes sense in combination with other flags, it's probably not pulling its weight.
|
|
218
|
+
|
|
219
|
+
4. **Would better `--help` output or AGENT_GUIDE.md documentation solve this instead?** An agent reads `--help` before every command. Rich help text with examples is consumed every time; a niche flag is used rarely. Invest in documentation over features.
|
|
220
|
+
|
|
221
|
+
5. **Is this a real workflow pattern or a one-off?** If you only needed this once across 24 queries, it's not worth a flag. If you needed it on 10+ queries, it might be.
|
|
222
|
+
|
|
223
|
+
**Strongly prefer these improvement categories (in order):**
|
|
224
|
+
|
|
225
|
+
1. Better error messages that show the correct command to run
|
|
226
|
+
2. Smarter auto-correction in existing commands
|
|
227
|
+
3. Richer `--help` output with real examples
|
|
228
|
+
4. Better AGENT_GUIDE.md documentation
|
|
229
|
+
5. Type accuracy improvements in `codegen`
|
|
230
|
+
6. New flags or commands (last resort — justify why 1-5 aren't sufficient)
|
|
231
|
+
|
|
232
|
+
Remember the core tenets of this CLI:
|
|
233
|
+
|
|
234
|
+
- Make GraphQL query authoring as seamless as possible for humans
|
|
235
|
+
- Make GraphQL query authoring as seamless as possible for AI agents
|
|
236
|
+
- The schema is the source of truth — navigate it, don't memorize it
|
|
237
|
+
- Progressive disclosure — simple things should be simple, complex things should be possible
|
|
238
|
+
- **Generated types are a contract** — agents and developers depend on them being accurate
|
|
239
|
+
- **Simplicity is a feature** — every new flag is a maintenance burden and a learning burden. Fewer commands that work well beats many commands that overlap. Self-documenting behavior (good errors, good help) beats hidden shortcuts.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Rules
|
|
244
|
+
|
|
245
|
+
- Use only `npx graphiti` commands and the shell. Do not manually write GraphQL strings.
|
|
246
|
+
- Do not ask the user for input at any point. Make reasonable decisions and move forward.
|
|
247
|
+
- If a command fails, read the error, adjust, and retry. Document the failure in your reflection.
|
|
248
|
+
- If a mutation type doesn't exist in the schema, note it in the reflection and move on.
|
|
249
|
+
- Run `npm run build && npm test` before and after making any code changes.
|