graphvault-studio 0.1.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/CONTRIBUTING.md +27 -0
  3. package/LICENSE +21 -0
  4. package/README.md +119 -0
  5. package/assets/graphvault-logo.png +0 -0
  6. package/assets/studio-screenshot.png +0 -0
  7. package/dist/admin-cli.d.ts +12 -0
  8. package/dist/admin-cli.js +132 -0
  9. package/dist/admin-cli.js.map +1 -0
  10. package/dist/admin-client.d.ts +49 -0
  11. package/dist/admin-client.js +352 -0
  12. package/dist/admin-client.js.map +1 -0
  13. package/dist/admin-inspection.d.ts +4 -0
  14. package/dist/admin-inspection.js +70 -0
  15. package/dist/admin-inspection.js.map +1 -0
  16. package/dist/admin-mutation.d.ts +4 -0
  17. package/dist/admin-mutation.js +83 -0
  18. package/dist/admin-mutation.js.map +1 -0
  19. package/dist/admin-parent-index.d.ts +4 -0
  20. package/dist/admin-parent-index.js +72 -0
  21. package/dist/admin-parent-index.js.map +1 -0
  22. package/dist/admin-server.d.ts +14 -0
  23. package/dist/admin-server.js +132 -0
  24. package/dist/admin-server.js.map +1 -0
  25. package/dist/admin-types.d.ts +97 -0
  26. package/dist/admin-types.js +2 -0
  27. package/dist/admin-types.js.map +1 -0
  28. package/dist/admin-ui.d.ts +1 -0
  29. package/dist/admin-ui.js +886 -0
  30. package/dist/admin-ui.js.map +1 -0
  31. package/dist/admin.d.ts +4 -0
  32. package/dist/admin.js +3 -0
  33. package/dist/admin.js.map +1 -0
  34. package/dist/gvql-examples.d.ts +6 -0
  35. package/dist/gvql-examples.js +45 -0
  36. package/dist/gvql-examples.js.map +1 -0
  37. package/docs/GVQL.md +217 -0
  38. package/docs/PUBLISHING.md +46 -0
  39. package/docs/RELEASE_NOTES_0.1.0.md +41 -0
  40. package/docs/REMOTE_STORAGE.md +56 -0
  41. package/examples/create-demo-store.mjs +85 -0
  42. package/package.json +67 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-ui.js","sourceRoot":"","sources":["../src/admin-ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAqNC,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8pBvD,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { StorageAdminClient } from "./admin-client.js";
2
+ export type { AdminGraph, AdminGraphEdge, AdminGraphNode, AdminHierarchyPath, AdminHierarchyPathItem, AdminMutation, AdminMutationPreview, GvqlResult, AdminObjectChild, AdminObjectListItem, AdminObjectPage, AdminObjectParent, AdminRootReference, AdminSearchResult, AdminSummary, StorageAdminClientOptions, } from "./admin-client.js";
3
+ export { startAdminServer } from "./admin-server.js";
4
+ export type { AdminServerOptions, RunningAdminServer } from "./admin-server.js";
package/dist/admin.js ADDED
@@ -0,0 +1,3 @@
1
+ export { StorageAdminClient } from "./admin-client.js";
2
+ export { startAdminServer } from "./admin-server.js";
3
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAmBvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface StudioGvqlExample {
2
+ name: string;
3
+ query: string;
4
+ parameters?: Record<string, unknown>;
5
+ }
6
+ export declare const STUDIO_GVQL_EXAMPLES: StudioGvqlExample[];
@@ -0,0 +1,45 @@
1
+ export const STUDIO_GVQL_EXAMPLES = [
2
+ { name: "Inspect nodes", query: "MATCH (node) RETURN node.$id AS objectId, node.$type AS type, node.$kind AS kind ORDER BY node.$id ASC LIMIT 25 OFFSET 0" },
3
+ { name: "Filter types", query: 'MATCH (node) WHERE node.$type IN ["Document", "Workspace"] RETURN node.$id AS objectId, node.$type AS type ORDER BY node.$type ASC, node.$id ASC' },
4
+ { name: "Distinct values", query: "MATCH (item) RETURN DISTINCT item.status AS status ORDER BY status ASC" },
5
+ { name: "Type count", query: "MATCH (node) WHERE node.$type IS NOT NULL RETURN count(DISTINCT node.$type) AS types" },
6
+ { name: "Nullable fields", query: "MATCH (item) WHERE item.archivedAt IS NULL AND item.status IS NOT NULL RETURN item.id AS id, item.title AS title ORDER BY item.id ASC" },
7
+ { name: "Indexed IN", query: 'MATCH (item) WHERE item.status IN ["draft", "published"] AND item.id IN ["doc-1", "doc-2"] RETURN item.id AS id, item.status AS status ORDER BY item.id ASC' },
8
+ { name: "Indexed OR", query: 'MATCH (item) WHERE item.id = "missing" OR item.status = "published" RETURN item.id AS id, item.status AS status' },
9
+ { name: "Parentheses", query: 'MATCH (item) WHERE (item.status = "draft" OR item.status = "published") AND item.views > 20 RETURN item.id AS id, item.status AS status, item.views AS views' },
10
+ { name: "NOT filter", query: 'MATCH (item) WHERE NOT (item.status = "published" OR item.views < 10) RETURN item.id AS id, item.status AS status, item.views AS views' },
11
+ { name: "Computed score", query: "MATCH (item) RETURN item.id AS id, (item.views + $bonus) * 2 AS score ORDER BY score DESC LIMIT 25", parameters: { bonus: 5 } },
12
+ { name: "Aggregate", query: "MATCH (item) RETURN item.status AS status, count(*) AS count GROUP BY item.status HAVING count > 1 ORDER BY count DESC, status ASC" },
13
+ { name: "Aggregate NOT", query: 'MATCH (item) RETURN item.status AS status, count(*) AS count, avg(item.views) AS avgViews GROUP BY item.status HAVING NOT (status = "published" OR avgViews < 10) ORDER BY status ASC' },
14
+ {
15
+ name: "WITH pipeline",
16
+ query: "MATCH (item) WHERE item.status IS NOT NULL WITH item.status AS status, count(*) AS count, avg(item.views) AS avgViews GROUP BY item.status HAVING count > 0 RETURN status, count, avgViews ORDER BY count DESC",
17
+ },
18
+ {
19
+ name: "WITH filter",
20
+ query: 'MATCH (item) WHERE item.status IS NOT NULL WITH item.id AS id, CASE WHEN item.views >= 100 THEN "hot" WHEN item.archivedAt IS NOT NULL THEN "archived" ELSE "active" END AS bucket WHERE bucket = "hot" RETURN id, bucket ORDER BY id ASC',
21
+ },
22
+ { name: "Traverse owner", query: 'MATCH (item)-[:owner]->(owner) WHERE owner.name = "Platform Team" RETURN item.title AS title' },
23
+ { name: "Join patterns", query: 'MATCH (item)-[:owner]->(owner), (item)-[:category]->(category) WHERE owner.name = "Platform Team" AND category.slug = "guides" RETURN item.id AS id, item.title AS title, category.label AS category ORDER BY item.title ASC LIMIT 25' },
24
+ { name: "Optional match", query: "MATCH (item) OPTIONAL MATCH (item)-[:related]->(items)-[:*]->(related) RETURN item.id AS id, related.id AS relatedId ORDER BY item.id ASC LIMIT 25" },
25
+ {
26
+ name: "Scalar functions",
27
+ query: 'MATCH (item) WHERE lower(item.title) CONTAINS lower($needle) RETURN item.id AS id, upper(trim(item.title)) AS title, length(item.title) AS titleLength, coalesce(item.archivedAt, "none") AS archived ORDER BY item.id ASC',
28
+ parameters: { needle: "storage" },
29
+ },
30
+ {
31
+ name: "CASE buckets",
32
+ query: 'MATCH (item) WHERE item.status IS NOT NULL RETURN item.id AS id, CASE WHEN item.views >= 100 THEN "hot" WHEN item.archivedAt IS NOT NULL THEN "archived" ELSE "active" END AS bucket ORDER BY item.id ASC LIMIT 25',
33
+ },
34
+ { name: "Preview SET", query: 'MATCH (item) WHERE item.status = "draft" SET item.status = "archived" RETURN count(*) AS changed' },
35
+ {
36
+ name: "CASE update",
37
+ query: 'MATCH (item) WHERE item.status IS NOT NULL SET item.status = CASE WHEN item.views >= 100 THEN "featured" WHEN item.archivedAt IS NOT NULL THEN "archived" ELSE item.status END RETURN item.id AS id, item.status AS status',
38
+ },
39
+ { name: "Arithmetic SET", query: "MATCH (item) WHERE item.status = \"published\" SET item.views = (item.views + $increment) * 2 RETURN item.id AS id, item.views AS views", parameters: { increment: 5 } },
40
+ { name: "REMOVE field", query: "MATCH (item) WHERE item.archivedAt IS NOT NULL REMOVE item.archivedAt RETURN count(*) AS changed" },
41
+ { name: "DELETE object", query: 'MATCH (item) WHERE item.status = "archived" DELETE item RETURN item.id AS id' },
42
+ { name: "CREATE into collection", query: 'MATCH (workspace:Workspace) WHERE workspace.name = "Developer docs" CREATE (item:Document { id: "doc-4", title: "Release checklist", status: "draft", views: 0 }) INTO workspace.documents RETURN item.id AS id, item.title AS title' },
43
+ { name: "MERGE into collection", query: 'MATCH (workspace:Workspace) WHERE workspace.name = "Developer docs" MERGE (item:Document { id: "doc-4", title: "Release checklist", status: "draft", views: 0 }) INTO workspace.documents ON item.id RETURN item.id AS id, item.title AS title' },
44
+ ];
45
+ //# sourceMappingURL=gvql-examples.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gvql-examples.js","sourceRoot":"","sources":["../src/gvql-examples.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,oBAAoB,GAAwB;IACvD,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,0HAA0H,EAAE;IAC5J,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,kJAAkJ,EAAE;IACnL,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,wEAAwE,EAAE;IAC5G,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,sFAAsF,EAAE;IACrH,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,uIAAuI,EAAE;IAC3K,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,6JAA6J,EAAE;IAC5L,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,iHAAiH,EAAE;IAChJ,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,8JAA8J,EAAE;IAC9L,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,wIAAwI,EAAE;IACvK,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,oGAAoG,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACjK,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,oIAAoI,EAAE;IAClK,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,uLAAuL,EAAE;IACzN;QACE,IAAI,EAAE,eAAe;QACrB,KAAK,EAAE,gNAAgN;KACxN;IACD;QACE,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,2OAA2O;KACnP;IACD,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,8FAA8F,EAAE;IACjI,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,uOAAuO,EAAE;IACzQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,oJAAoJ,EAAE;IACvL;QACE,IAAI,EAAE,kBAAkB;QACxB,KAAK,EAAE,4NAA4N;QACnO,UAAU,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;KAClC;IACD;QACE,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,oNAAoN;KAC5N;IACD,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,kGAAkG,EAAE;IAClI;QACE,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,4NAA4N;KACpO;IACD,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,yIAAyI,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE;IAC1M,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,kGAAkG,EAAE;IACnI,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,8EAA8E,EAAE;IAChH,EAAE,IAAI,EAAE,wBAAwB,EAAE,KAAK,EAAE,sOAAsO,EAAE;IACjR,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,gPAAgP,EAAE;CAC3R,CAAC"}
package/docs/GVQL.md ADDED
@@ -0,0 +1,217 @@
1
+ # GraphVault Studio GVQL Examples
2
+
3
+ ## GVQL
4
+
5
+ Studio includes a GVQL console for GraphVault stores. It supports read queries and safe batch-update previews:
6
+
7
+ ```sql
8
+ MATCH (node)
9
+ RETURN node.$id AS objectId, node.$type AS type, node.$kind AS kind
10
+ ORDER BY node.$id ASC
11
+ LIMIT 25
12
+ OFFSET 0
13
+ ```
14
+
15
+ ```sql
16
+ MATCH (node)
17
+ WHERE node.$type IN ["Document", "Workspace"]
18
+ RETURN node.$id AS objectId, node.$type AS type
19
+ ORDER BY node.$type ASC, node.$id ASC
20
+ ```
21
+
22
+ ```sql
23
+ MATCH (doc:Document)-[:owner]->(owner:Owner)
24
+ WHERE owner.name = "Platform Team"
25
+ RETURN doc.id AS id, doc.title AS title
26
+ ORDER BY doc.title ASC, doc.id ASC
27
+ LIMIT 25
28
+ OFFSET 0
29
+ ```
30
+
31
+ ```sql
32
+ MATCH (doc:Document)-[:owner]->(owner:Owner), (doc)-[:category]->(category:Category)
33
+ WHERE owner.name = "Platform Team" AND category.slug = "guides"
34
+ RETURN doc.id AS id, doc.title AS title, category.label AS category
35
+ ORDER BY doc.title ASC
36
+ LIMIT 25
37
+ ```
38
+
39
+ ```sql
40
+ MATCH (doc:Document)
41
+ OPTIONAL MATCH (doc)-[:related]->(items)-[:*]->(related:Document)
42
+ RETURN doc.id AS id, related.id AS relatedId
43
+ ORDER BY doc.id ASC
44
+ LIMIT 25
45
+ ```
46
+
47
+ ```sql
48
+ MATCH (doc:Document)
49
+ WHERE lower(doc.title) CONTAINS lower($needle)
50
+ RETURN doc.id AS id, upper(trim(doc.title)) AS title, length(doc.title) AS titleLength, coalesce(doc.archivedAt, "none") AS archived
51
+ ORDER BY doc.id ASC
52
+ ```
53
+
54
+ ```sql
55
+ MATCH (doc:Document)
56
+ RETURN DISTINCT doc.status AS status
57
+ ORDER BY status ASC
58
+ ```
59
+
60
+ ```sql
61
+ MATCH (node)
62
+ WHERE node.$type IS NOT NULL
63
+ RETURN count(DISTINCT node.$type) AS types
64
+ ```
65
+
66
+ ```sql
67
+ MATCH (doc:Document)
68
+ WHERE doc.archivedAt IS NULL AND doc.status IS NOT NULL
69
+ RETURN doc.id AS id, doc.title AS title
70
+ ORDER BY doc.id ASC
71
+ ```
72
+
73
+ ```sql
74
+ MATCH (doc:Document)
75
+ WHERE doc.status IN ["draft", "published"] AND doc.id IN ["doc-1", "doc-2"]
76
+ RETURN doc.id AS id, doc.status AS status
77
+ ORDER BY doc.id ASC
78
+ ```
79
+
80
+ ```sql
81
+ MATCH (doc:Document)
82
+ WHERE doc.id = "missing" OR doc.status = "published"
83
+ RETURN doc.id AS id, doc.status AS status
84
+ ```
85
+
86
+ ```sql
87
+ MATCH (doc:Document)
88
+ WHERE (doc.status = "draft" OR doc.status = "published") AND doc.views > 20
89
+ RETURN doc.id AS id, doc.status AS status, doc.views AS views
90
+ ```
91
+
92
+ ```sql
93
+ MATCH (doc:Document)
94
+ WHERE NOT (doc.status = "published" OR doc.views < 10)
95
+ RETURN doc.id AS id, doc.status AS status, doc.views AS views
96
+ ```
97
+
98
+ ```sql
99
+ MATCH (doc:Document)
100
+ RETURN doc.id AS id, (doc.views + $bonus) * 2 AS score
101
+ ORDER BY score DESC
102
+ LIMIT 25
103
+ ```
104
+
105
+ ```sql
106
+ MATCH (doc:Document)
107
+ WHERE doc.status IS NOT NULL
108
+ RETURN doc.id AS id,
109
+ CASE
110
+ WHEN doc.views >= 100 THEN "hot"
111
+ WHEN doc.archivedAt IS NOT NULL THEN "archived"
112
+ ELSE "active"
113
+ END AS bucket
114
+ ORDER BY doc.id ASC
115
+ LIMIT 25
116
+ ```
117
+
118
+ ```sql
119
+ MATCH (doc:Document)
120
+ WHERE doc.status IS NOT NULL
121
+ WITH doc.status AS status, count(*) AS count, avg(doc.views) AS avgViews
122
+ GROUP BY doc.status
123
+ HAVING count > 0
124
+ RETURN status, count, avgViews
125
+ ORDER BY count DESC
126
+ ```
127
+
128
+ ```sql
129
+ MATCH (doc:Document)
130
+ WHERE doc.status IS NOT NULL
131
+ WITH doc.id AS id,
132
+ CASE
133
+ WHEN doc.views >= 100 THEN "hot"
134
+ WHEN doc.archivedAt IS NOT NULL THEN "archived"
135
+ ELSE "active"
136
+ END AS bucket
137
+ WHERE bucket = "hot"
138
+ RETURN id, bucket
139
+ ORDER BY id ASC
140
+ ```
141
+
142
+ ```sql
143
+ MATCH (doc:Document)
144
+ WHERE doc.status = "draft"
145
+ SET doc.status = "archived"
146
+ RETURN count(*) AS changed
147
+ ```
148
+
149
+ ```sql
150
+ MATCH (doc:Document)
151
+ WHERE doc.status IS NOT NULL
152
+ SET doc.status =
153
+ CASE
154
+ WHEN doc.views >= 100 THEN "featured"
155
+ WHEN doc.archivedAt IS NOT NULL THEN "archived"
156
+ ELSE doc.status
157
+ END
158
+ RETURN doc.id AS id, doc.status AS status
159
+ ```
160
+
161
+ ```sql
162
+ MATCH (doc:Document)
163
+ WHERE doc.status = "published"
164
+ SET doc.views = (doc.views + $increment) * 2
165
+ RETURN doc.id AS id, doc.views AS views
166
+ ```
167
+
168
+ ```sql
169
+ MATCH (doc:Document)
170
+ WHERE doc.archivedAt IS NOT NULL
171
+ REMOVE doc.archivedAt
172
+ RETURN count(*) AS changed
173
+ ```
174
+
175
+ ```sql
176
+ MATCH (doc:Document)
177
+ WHERE doc.status = "archived"
178
+ DELETE doc
179
+ RETURN doc.id AS id
180
+ ```
181
+
182
+ ```sql
183
+ MATCH (workspace:Workspace)
184
+ WHERE workspace.name = "Developer docs"
185
+ CREATE (doc:Document { id: "doc-4", title: "Release checklist", status: "draft", views: 0 }) INTO workspace.documents
186
+ RETURN doc.id AS id, doc.title AS title
187
+ ```
188
+
189
+ ```sql
190
+ MATCH (workspace:Workspace)
191
+ WHERE workspace.name = "Developer docs"
192
+ MERGE (doc:Document { id: "doc-4", title: "Release checklist", status: "draft", views: 0 }) INTO workspace.documents ON doc.id
193
+ RETURN doc.id AS id, doc.title AS title
194
+ ```
195
+
196
+ Aggregate queries can be inspected directly in the same console:
197
+
198
+ ```sql
199
+ MATCH (item)
200
+ RETURN item.status AS status, count(*) AS count
201
+ GROUP BY item.status
202
+ HAVING count > 1
203
+ ORDER BY count DESC, status ASC
204
+ ```
205
+
206
+ ```sql
207
+ MATCH (item)
208
+ RETURN item.status AS status, count(*) AS count, avg(item.views) AS avgViews
209
+ GROUP BY item.status
210
+ HAVING NOT (status = "published" OR avgViews < 10)
211
+ ORDER BY status ASC
212
+ ```
213
+
214
+ `Run / Preview` executes read queries and dry-runs updates. `Commit GVQL` applies update statements only when Studio was started with mutation support and the confirmation token matches.
215
+
216
+ Each GVQL run includes an execution-plan row that shows whether Studio used a type index, primitive-property index, indexed `OR` union, or full scan, plus candidate and returned-row counts. That makes slow queries much easier to tune before they become production habits.
217
+ Use `LIMIT` and `OFFSET` in the console for predictable paging through large result sets. Query parameters such as `$needle` or `$increment` are detected automatically and shown as typed input fields, so you do not need to hand-edit a JSON payload for common runs. Comma-separated `MATCH` patterns let you express join-like graph queries where shared aliases must resolve to the same object. `OPTIONAL MATCH` keeps the primary rows visible when a relationship is missing. `WITH` pipelines let you name intermediate values, aggregate them, and filter row aliases before the final `RETURN`. Computed `RETURN` expressions, `CASE` buckets, and scalar functions such as `lower`, `upper`, `trim`, `length`, and `coalesce` are useful for quick scores, projections, normalization, and sanity checks without changing stored data. `RETURN DISTINCT` and `count(DISTINCT path)` are useful when you want compact lists or cardinality checks for values such as statuses, tenants, regions, types, or object categories. Parentheses and `NOT` in `WHERE` and `HAVING` make mixed filters predictable. `CREATE ... INTO`, idempotent `MERGE ... INTO ... ON`, arithmetic or conditional `SET` expressions, `IS NULL`, `IS NOT NULL`, `REMOVE`, and parent-aware `DELETE` make graph manipulation previewable before commit.
@@ -0,0 +1,46 @@
1
+ # Publishing GraphVault Studio
2
+
3
+ This checklist keeps the first public admin-client release repeatable.
4
+
5
+ ## Preconditions
6
+
7
+ - `@sprengmeister/graphvault` has already been released.
8
+ - `package.json` has the intended `name`, `version`, `repository`, `homepage`, `bugs`, `license`, `engines`, `bin`, `exports`, and `files`.
9
+ - `CHANGELOG.md` and `docs/RELEASE_NOTES_0.1.0.md` describe the release.
10
+ - `NPM_TOKEN` is configured as a GitHub Actions repository secret for npm publishing.
11
+
12
+ ## Local Release Check
13
+
14
+ Run these from the repository root:
15
+
16
+ ```bash
17
+ npm ci
18
+ npm test
19
+ npm run demo:store
20
+ npm run pack:dry-run
21
+ ```
22
+
23
+ Inspect the dry-run file list and confirm it contains `README.md`, `LICENSE`, `CHANGELOG.md`, `CONTRIBUTING.md`, `docs`, `dist`, `examples`, logo/screenshot assets, and the `graphvault-studio` CLI entry point.
24
+
25
+ ## Tagging
26
+
27
+ Create the release tag only after the local release check passes:
28
+
29
+ ```bash
30
+ git tag -a v0.1.0 -m "GraphVault Studio 0.1.0"
31
+ git push origin v0.1.0
32
+ ```
33
+
34
+ ## Publishing
35
+
36
+ Use the GitHub Actions `Release` workflow with the matching tag input, for example `v0.1.0`.
37
+
38
+ The workflow checks out the tag, installs with `npm ci`, runs tests, creates the demo store, validates the npm tarball with `npm run pack:dry-run`, and publishes with npm provenance.
39
+
40
+ ## Repository Visibility
41
+
42
+ Recommended GitHub topics:
43
+
44
+ ```text
45
+ typescript, admin-ui, database-admin, graph-database, object-graph, storage-browser, gvql, developer-tools
46
+ ```
@@ -0,0 +1,41 @@
1
+ # GraphVault Studio 0.1.0 Release Notes
2
+
3
+ GraphVault Studio 0.1.0 is the first public graphical admin client for GraphVault object graph stores.
4
+
5
+ ## What Is Included
6
+
7
+ - Root-first hierarchy browser for object graphs.
8
+ - Search across paths, values, ids, types, references, and encoded object data.
9
+ - Parent-path lookup from an object back toward the root, including multiple direct parents.
10
+ - Paged object browser for large stores.
11
+ - GVQL console for graph queries and dry-run batch mutation previews.
12
+ - Controlled primitive-field editing with confirmation-token safety.
13
+ - Verification, maintenance, backup, transaction, journal, graph, type dictionary, and object detail views.
14
+ - CLI and programmatic server startup.
15
+ - Local filesystem, custom, remote, S3-compatible, HTTP, and SQL-backed storage through GraphVault storage targets.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm install graphvault-studio
21
+ ```
22
+
23
+ The package is ready for npm registry publishing with the CLI binary:
24
+
25
+ ```bash
26
+ npx graphvault-studio --dir ./data --port 4177
27
+ ```
28
+
29
+ ## Demo
30
+
31
+ ```bash
32
+ npm ci
33
+ npm run demo:store
34
+ npx graphvault-studio --dir ./graphvault-studio-demo-store --port 4177 --allow-mutations --confirm-token confirm
35
+ ```
36
+
37
+ Open `http://127.0.0.1:4177`.
38
+
39
+ ## Recommended GitHub Topics
40
+
41
+ `typescript`, `admin-ui`, `developer-tools`, `graph-database`, `object-graph`, `storage-browser`, `database-admin`, `gvql`, `local-first`, `nestjs`
@@ -0,0 +1,56 @@
1
+ # Remote And Custom Storage
2
+
3
+ ### Remote Or Custom Storage
4
+
5
+ For remote storage, start Studio programmatically and pass the same `storageTarget` adapter your app uses. `storageDirectory` is still required; for remote targets it acts as the key prefix or logical root path inside the target.
6
+
7
+ ```ts
8
+ import { S3StorageTarget } from "@sprengmeister/graphvault";
9
+ import { startAdminServer } from "graphvault-studio";
10
+
11
+ await startAdminServer({
12
+ storageDirectory: "prod/app-store",
13
+ storageTarget: new S3StorageTarget({
14
+ bucket: "graphvault-prod",
15
+ prefix: "stores",
16
+ client: s3ClientAdapter,
17
+ }),
18
+ port: 4177,
19
+ authToken: process.env.GRAPHVAULT_ADMIN_TOKEN,
20
+ });
21
+ ```
22
+
23
+ HTTP-backed storage works the same way:
24
+
25
+ ```ts
26
+ import { HttpStorageTarget } from "@sprengmeister/graphvault";
27
+ import { startAdminServer } from "graphvault-studio";
28
+
29
+ await startAdminServer({
30
+ storageDirectory: "main",
31
+ storageTarget: new HttpStorageTarget({
32
+ baseUrl: "https://storage.example.com/graphvault",
33
+ headers: { authorization: `Bearer ${process.env.STORAGE_TOKEN}` },
34
+ }),
35
+ port: 4177,
36
+ });
37
+ ```
38
+
39
+ SQL-backed storage uses an adapter around your database client:
40
+
41
+ ```ts
42
+ import { SqlStorageTarget } from "@sprengmeister/graphvault";
43
+ import { startAdminServer } from "graphvault-studio";
44
+
45
+ await startAdminServer({
46
+ storageDirectory: "main",
47
+ storageTarget: new SqlStorageTarget({
48
+ client: sqlClientAdapter,
49
+ tableName: "graphvault_objects",
50
+ lockTableName: "graphvault_locks",
51
+ }),
52
+ port: 4177,
53
+ });
54
+ ```
55
+
56
+ For mutation endpoints, always set `allowMutations: true` and a `mutationConfirmToken`. For exposed or shared environments, also set `authToken`.
@@ -0,0 +1,85 @@
1
+ import { rm } from "node:fs/promises";
2
+ import { EmbeddedStorage } from "@sprengmeister/graphvault";
3
+
4
+ class Workspace {
5
+ constructor(name) {
6
+ this.name = name;
7
+ this.documents = [];
8
+ this.owners = [];
9
+ this.categories = [];
10
+ }
11
+ }
12
+
13
+ class Owner {
14
+ constructor(id, name, region) {
15
+ this.id = id;
16
+ this.name = name;
17
+ this.region = region;
18
+ }
19
+ }
20
+
21
+ class Category {
22
+ constructor(slug, label) {
23
+ this.slug = slug;
24
+ this.label = label;
25
+ }
26
+ }
27
+
28
+ class Document {
29
+ constructor(id, title, owner, category, status, views) {
30
+ this.id = id;
31
+ this.title = title;
32
+ this.owner = owner;
33
+ this.category = category;
34
+ this.status = status;
35
+ this.views = views;
36
+ this.tags = new Set();
37
+ this.related = [];
38
+ this.metrics = { score: views / 100, revisions: Math.max(1, Math.round(views / 30)) };
39
+ this.createdAt = new Date(1_765_000_000_000 + views * 60_000);
40
+ }
41
+ }
42
+
43
+ const storageDirectory = process.argv[2] ?? "./graphvault-studio-demo-store";
44
+ await rm(storageDirectory, { recursive: true, force: true });
45
+
46
+ const root = new Workspace("GraphVault product docs");
47
+ root.owners.push(new Owner("owner-platform", "Platform Team", "EU"));
48
+ root.owners.push(new Owner("owner-tools", "Developer Tools", "US"));
49
+ root.categories.push(new Category("architecture", "Architecture"));
50
+ root.categories.push(new Category("operations", "Operations"));
51
+ root.categories.push(new Category("admin", "Admin tooling"));
52
+
53
+ for (let index = 0; index < 36; index++) {
54
+ const owner = root.owners[index % root.owners.length];
55
+ const category = root.categories[index % root.categories.length];
56
+ const status = index % 11 === 0 ? "archived" : index % 5 === 0 ? "review" : "published";
57
+ const document = new Document(`doc-${String(index + 1).padStart(3, "0")}`, `GraphVault guide ${index + 1}`, owner, category, status, 20 + index * 17);
58
+ document.tags.add(index % 2 === 0 ? "typescript" : "storage");
59
+ document.tags.add(category.slug);
60
+ root.documents.push(document);
61
+ }
62
+
63
+ for (let index = 1; index < root.documents.length; index++) {
64
+ root.documents[index].related.push(root.documents[index - 1]);
65
+ if (index > 4 && index % 4 === 0) {
66
+ root.documents[index].related.push(root.documents[index - 4]);
67
+ }
68
+ }
69
+
70
+ const storage = await EmbeddedStorage.start({
71
+ storageDirectory,
72
+ root,
73
+ types: [
74
+ { name: "Workspace", ctor: Workspace },
75
+ { name: "Owner", ctor: Owner },
76
+ { name: "Category", ctor: Category },
77
+ { name: "Document", ctor: Document },
78
+ ],
79
+ });
80
+
81
+ await storage.storeRoot();
82
+ await storage.shutdown();
83
+
84
+ console.log(`Demo store created at ${storageDirectory}`);
85
+ console.log("Run: npx graphvault-studio --dir " + storageDirectory + " --port 4177 --allow-mutations --confirm-token confirm");
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "graphvault-studio",
3
+ "version": "0.1.2",
4
+ "description": "Graphical admin client for GraphVault object graph stores.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Sprengmeister-dev/graphvault-studio.git"
8
+ },
9
+ "homepage": "https://github.com/Sprengmeister-dev/graphvault-studio#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/Sprengmeister-dev/graphvault-studio/issues"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "type": "module",
17
+ "main": "./dist/admin.js",
18
+ "types": "./dist/admin.d.ts",
19
+ "bin": {
20
+ "graphvault-studio": "./dist/admin-cli.js"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/admin.d.ts",
25
+ "import": "./dist/admin.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "assets",
30
+ "CHANGELOG.md",
31
+ "CONTRIBUTING.md",
32
+ "docs",
33
+ "dist",
34
+ "examples",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json",
40
+ "test": "npm run build && node tests/smoke.mjs",
41
+ "demo:store": "npm run build && node examples/create-demo-store.mjs",
42
+ "pack:dry-run": "npm pack --dry-run --cache ./.npm-cache",
43
+ "prepack": "npm run build"
44
+ },
45
+ "keywords": [
46
+ "graphvault",
47
+ "admin-ui",
48
+ "database-admin",
49
+ "graph-database",
50
+ "object-graph",
51
+ "storage-browser",
52
+ "typescript",
53
+ "gvql",
54
+ "developer-tools"
55
+ ],
56
+ "license": "MIT",
57
+ "engines": {
58
+ "node": ">=20"
59
+ },
60
+ "dependencies": {
61
+ "@sprengmeister/graphvault": "0.1.0"
62
+ },
63
+ "devDependencies": {
64
+ "@types/node": "^22.15.3",
65
+ "typescript": "^5.8.3"
66
+ }
67
+ }