@sqaoss/flowy 1.6.1 → 1.8.0

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.
@@ -0,0 +1,331 @@
1
+ /**
2
+ * Canonical GraphQL operations the Flowy CLI sends.
3
+ *
4
+ * Every operation the CLI relies on lives here, copied verbatim from the
5
+ * command that issues it. Commands import these constants instead of inlining
6
+ * the query/mutation text, so there is a single source of truth for the
7
+ * contract the CLI expects a backend to satisfy.
8
+ *
9
+ * Two consumers share this module:
10
+ * 1. `src/commands/*.ts` — the runtime CLI commands.
11
+ * 2. `server/src/contract.test.ts` — the contract guard that executes each
12
+ * operation against the bundled local server and asserts it is satisfied.
13
+ *
14
+ * If the bundled local server (or the SaaS server) renames an operation, a
15
+ * field, or an argument the CLI uses, the contract test fails — catching drift
16
+ * before it ships. See `server/src/contract.test.ts` for the documented list
17
+ * of intentional local/SaaS divergences (SaaS-only `whoami`, `register`,
18
+ * `rotateApiKey`, `createCheckout`, `auditLog`, `ancestors`).
19
+ */
20
+
21
+ // --- Nodes: read --------------------------------------------------------------
22
+
23
+ /** project.ts `show`, feature.ts `show` — fetch a single node by id. */
24
+ export const GET_NODE = `query GetNode($id: String!) {
25
+ node(id: $id) {
26
+ id type title description status metadata createdAt updatedAt
27
+ }
28
+ }`
29
+
30
+ /** project.ts `show` — same shape as GET_NODE, distinct operation name. */
31
+ export const GET_PROJECT = `query GetProject($id: String!) {
32
+ node(id: $id) {
33
+ id type title description status metadata createdAt updatedAt
34
+ }
35
+ }`
36
+
37
+ /** project.ts `set` — list nodes of a type, minimal fields for name matching. */
38
+ export const LIST_PROJECTS_FOR_SET = `query ListProjects($type: String) {
39
+ nodes(type: $type) {
40
+ id title
41
+ }
42
+ }`
43
+
44
+ /** project.ts `list` — list nodes of a type with display fields. */
45
+ export const LIST_PROJECTS = `query ListProjects($type: String) {
46
+ nodes(type: $type) {
47
+ id type title description status createdAt updatedAt
48
+ }
49
+ }`
50
+
51
+ /** task.ts `list --all` — every node of a type. */
52
+ export const ALL_TASKS = `query AllTasks($type: String!) {
53
+ nodes(type: $type) {
54
+ id type title status createdAt
55
+ }
56
+ }`
57
+
58
+ /** feature.ts `list` — direct children via relation, full display fields. */
59
+ export const DESCENDANTS = `query Descendants($nodeId: String!, $relation: String, $maxDepth: Int) {
60
+ descendants(nodeId: $nodeId, relation: $relation, maxDepth: $maxDepth) {
61
+ id type title description status createdAt updatedAt
62
+ }
63
+ }`
64
+
65
+ /** feature.ts `set` — direct children via relation, brief fields for matching. */
66
+ export const DESCENDANTS_BRIEF = `query Descendants($nodeId: String!, $relation: String, $maxDepth: Int) {
67
+ descendants(nodeId: $nodeId, relation: $relation, maxDepth: $maxDepth) {
68
+ id type title status
69
+ }
70
+ }`
71
+
72
+ /** task.ts `list` (active feature) — children with status only. */
73
+ export const LIST_TASKS = `query ListTasks($nodeId: String!, $relation: String!, $maxDepth: Int) {
74
+ descendants(nodeId: $nodeId, relation: $relation, maxDepth: $maxDepth) {
75
+ id type title status createdAt
76
+ }
77
+ }`
78
+
79
+ /** tree.ts — full subtree from any root. */
80
+ export const SUBTREE = `query Subtree($nodeId: String!, $maxDepth: Int) {
81
+ subtree(nodeId: $nodeId, maxDepth: $maxDepth) {
82
+ id type title status
83
+ }
84
+ }`
85
+
86
+ /** task.ts `list --ready` — actionable tasks (server-side dependency logic). */
87
+ export const READY_TASKS = `query ReadyTasks($projectId: String) {
88
+ readyTasks(projectId: $projectId) {
89
+ id type title status createdAt
90
+ }
91
+ }`
92
+
93
+ /** task.ts `show` — node plus its incoming/outgoing `blocks` edges. */
94
+ export const SHOW_TASK = `query ShowTask($id: String!) {
95
+ node(id: $id) {
96
+ id type title description status metadata createdAt updatedAt
97
+ }
98
+ blockedBy: edges(nodeId: $id, relation: "blocks", direction: "incoming") {
99
+ id type title status
100
+ }
101
+ blocks: edges(nodeId: $id, relation: "blocks", direction: "outgoing") {
102
+ id type title status
103
+ }
104
+ }`
105
+
106
+ /** task.ts `deps` — incoming/outgoing `blocks` edges only. */
107
+ export const TASK_DEPS = `query TaskDeps($id: String!) {
108
+ blockedBy: edges(nodeId: $id, relation: "blocks", direction: "incoming") {
109
+ id type title status
110
+ }
111
+ blocks: edges(nodeId: $id, relation: "blocks", direction: "outgoing") {
112
+ id type title status
113
+ }
114
+ }`
115
+
116
+ /** search.ts — full-text search with optional type/status/limit filters. */
117
+ export const SEARCH = `query Search($query: String!, $type: String, $status: String, $limit: Int) {
118
+ search(query: $query, type: $type, status: $status, limit: $limit) {
119
+ id type title description status
120
+ }
121
+ }`
122
+
123
+ // --- Nodes: write -------------------------------------------------------------
124
+
125
+ /** project.ts `create`, init.ts — create a node by type/title. */
126
+ export const CREATE_PROJECT = `mutation CreateProject($type: String!, $title: String!) {
127
+ createNode(type: $type, title: $title) {
128
+ id type title description status metadata createdAt updatedAt
129
+ }
130
+ }`
131
+
132
+ /** feature.ts `create` — create a node with a description. */
133
+ export const CREATE_NODE = `mutation CreateNode($type: String!, $title: String!, $description: String) {
134
+ createNode(type: $type, title: $title, description: $description) {
135
+ id type title description status createdAt updatedAt
136
+ }
137
+ }`
138
+
139
+ /** task.ts `create` — create a task node. */
140
+ export const CREATE_TASK = `mutation CreateTask($type: String!, $title: String!, $description: String) {
141
+ createNode(type: $type, title: $title, description: $description) {
142
+ id type title description status createdAt
143
+ }
144
+ }`
145
+
146
+ /** project/feature/task `update` — title/description/metadata. */
147
+ export const UPDATE_NODE = `mutation UpdateNode($id: String!, $title: String, $description: String, $metadata: String) {
148
+ updateNode(id: $id, title: $title, description: $description, metadata: $metadata) {
149
+ id type title description status metadata createdAt updatedAt
150
+ }
151
+ }`
152
+
153
+ /** status.ts — status-only shorthand update. */
154
+ export const UPDATE_STATUS = `mutation UpdateStatus($id: String!, $status: String) {
155
+ updateNode(id: $id, status: $status) {
156
+ id type title status updatedAt
157
+ }
158
+ }`
159
+
160
+ /** approve.ts — promote a pending_review node to approved. */
161
+ export const APPROVE_NODE = `mutation ApproveNode($id: String!) {
162
+ approveNode(id: $id) { id type title status updatedAt }
163
+ }`
164
+
165
+ /** project/feature/task `delete` — remove a node (and its edges). */
166
+ export const DELETE_NODE = `mutation DeleteNode($id: String!) {
167
+ deleteNode(id: $id)
168
+ }`
169
+
170
+ // --- Edges --------------------------------------------------------------------
171
+
172
+ /** feature.ts `create` — link feature under project. */
173
+ export const CREATE_EDGE = `mutation CreateEdge($sourceId: String!, $targetId: String!, $relation: String!) {
174
+ createEdge(sourceId: $sourceId, targetId: $targetId, relation: $relation) {
175
+ sourceId targetId relation createdAt
176
+ }
177
+ }`
178
+
179
+ /** task.ts `create` — link task under feature (no createdAt selected). */
180
+ export const LINK_TASK = `mutation LinkTask($sourceId: String!, $targetId: String!, $relation: String!) {
181
+ createEdge(sourceId: $sourceId, targetId: $targetId, relation: $relation) {
182
+ sourceId targetId relation
183
+ }
184
+ }`
185
+
186
+ /** task.ts `block` — create a `blocks` edge between two tasks. */
187
+ export const BLOCK_TASK = `mutation BlockTask($sourceId: String!, $targetId: String!, $relation: String!) {
188
+ createEdge(sourceId: $sourceId, targetId: $targetId, relation: $relation) {
189
+ sourceId targetId relation createdAt
190
+ }
191
+ }`
192
+
193
+ /** task.ts `unblock` — remove a `blocks` edge. */
194
+ export const UNBLOCK_TASK = `mutation UnblockTask($sourceId: String!, $targetId: String!, $relation: String!) {
195
+ removeEdge(sourceId: $sourceId, targetId: $targetId, relation: $relation)
196
+ }`
197
+
198
+ // --- Import / export ----------------------------------------------------------
199
+
200
+ /** import.ts — read existing nodes of a type to dedup by client-key. */
201
+ export const IMPORT_EXISTING = `query ImportExisting($type: String) {
202
+ nodes(type: $type) { id type title metadata }
203
+ }`
204
+
205
+ /** import.ts — outgoing edges of a node, used to dedup edge creation. */
206
+ export const IMPORT_EDGES = `query ImportEdges($nodeId: String!, $relation: String!) {
207
+ edges(nodeId: $nodeId, relation: $relation, direction: "outgoing") { id }
208
+ }`
209
+
210
+ /** import.ts — create a node (full arg surface incl. status/metadata). */
211
+ export const IMPORT_CREATE = `mutation ImportCreate($type: String!, $title: String!, $description: String, $status: String, $metadata: String) {
212
+ createNode(type: $type, title: $title, description: $description, status: $status, metadata: $metadata) { id }
213
+ }`
214
+
215
+ /** import.ts — update a node (full arg surface incl. status/metadata). */
216
+ export const IMPORT_UPDATE = `mutation ImportUpdate($id: String!, $title: String, $description: String, $status: String, $metadata: String) {
217
+ updateNode(id: $id, title: $title, description: $description, status: $status, metadata: $metadata) { id }
218
+ }`
219
+
220
+ /** import.ts — create an edge during materialization. */
221
+ export const IMPORT_EDGE = `mutation ImportEdge($sourceId: String!, $targetId: String!, $relation: String!) {
222
+ createEdge(sourceId: $sourceId, targetId: $targetId, relation: $relation) { sourceId targetId relation }
223
+ }`
224
+
225
+ /** export.ts — fetch the root project node. */
226
+ export const EXPORT_PROJECT = `query ExportProject($id: String!) {
227
+ node(id: $id) { id type title description status metadata }
228
+ }`
229
+
230
+ /** export.ts — all descendants of the project for the dump. */
231
+ export const EXPORT_DESCENDANTS = `query ExportDescendants($nodeId: String!, $relation: String, $maxDepth: Int) {
232
+ descendants(nodeId: $nodeId, relation: $relation, maxDepth: $maxDepth) {
233
+ id type title description status metadata
234
+ }
235
+ }`
236
+
237
+ /** export.ts — outgoing edges of a node, read back through the edge model. */
238
+ export const EXPORT_EDGES = `query ExportEdges($nodeId: String!, $relation: String!) {
239
+ edges(nodeId: $nodeId, relation: $relation, direction: "outgoing") {
240
+ id metadata
241
+ }
242
+ }`
243
+
244
+ // --- SaaS-only operations (NOT served by the bundled local server) ------------
245
+ //
246
+ // These belong to the hosted `flowy-saas` backend (auth, billing, audit). The
247
+ // bundled local server intentionally does not implement them — see the
248
+ // divergence list in `server/src/contract.test.ts`. They are exported here so
249
+ // the CLI commands share the same single source of truth and the SaaS contract
250
+ // test (flowy-saas `test/helpers/cli-queries.ts`) can mirror them.
251
+
252
+ /** setup.ts remote — register a hosted account. */
253
+ export const REGISTER = `mutation Register($email: String!, $tier: String) {
254
+ register(email: $email, tier: $tier) {
255
+ user { id email tier createdAt graceEndsAt }
256
+ apiKey
257
+ checkoutUrl
258
+ }
259
+ }`
260
+
261
+ /** whoami.ts — current hosted user. */
262
+ export const WHOAMI = `query Whoami {
263
+ whoami {
264
+ id email tier createdAt graceEndsAt
265
+ }
266
+ }`
267
+
268
+ /** key.ts — rotate the hosted API key. */
269
+ export const ROTATE_API_KEY = `mutation RotateApiKey {
270
+ rotateApiKey {
271
+ user { id email tier createdAt graceEndsAt }
272
+ apiKey
273
+ }
274
+ }`
275
+
276
+ /** billing.ts — create a checkout session for a tier. */
277
+ export const CREATE_CHECKOUT = `mutation CreateCheckout($tier: String!) {
278
+ createCheckout(tier: $tier) {
279
+ url
280
+ }
281
+ }`
282
+
283
+ /**
284
+ * Operations the bundled local server is contractually required to satisfy.
285
+ * The contract test executes each of these against a live local server.
286
+ */
287
+ export const LOCAL_CONTRACT_OPERATIONS = {
288
+ GET_NODE,
289
+ GET_PROJECT,
290
+ LIST_PROJECTS_FOR_SET,
291
+ LIST_PROJECTS,
292
+ ALL_TASKS,
293
+ DESCENDANTS,
294
+ DESCENDANTS_BRIEF,
295
+ LIST_TASKS,
296
+ SUBTREE,
297
+ READY_TASKS,
298
+ SHOW_TASK,
299
+ TASK_DEPS,
300
+ SEARCH,
301
+ CREATE_PROJECT,
302
+ CREATE_NODE,
303
+ CREATE_TASK,
304
+ UPDATE_NODE,
305
+ UPDATE_STATUS,
306
+ APPROVE_NODE,
307
+ DELETE_NODE,
308
+ CREATE_EDGE,
309
+ LINK_TASK,
310
+ BLOCK_TASK,
311
+ UNBLOCK_TASK,
312
+ IMPORT_EXISTING,
313
+ IMPORT_EDGES,
314
+ IMPORT_CREATE,
315
+ IMPORT_UPDATE,
316
+ IMPORT_EDGE,
317
+ EXPORT_PROJECT,
318
+ EXPORT_DESCENDANTS,
319
+ EXPORT_EDGES,
320
+ } as const
321
+
322
+ /**
323
+ * SaaS-only operations the bundled local server intentionally does NOT serve.
324
+ * Documented here so the divergence is explicit and discoverable.
325
+ */
326
+ export const SAAS_ONLY_OPERATIONS = {
327
+ REGISTER,
328
+ WHOAMI,
329
+ ROTATE_API_KEY,
330
+ CREATE_CHECKOUT,
331
+ } as const