graphvault-studio 0.1.2 → 0.1.3
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/CHANGELOG.md +14 -0
- package/README.md +38 -2
- package/dist/admin-cli.d.ts +3 -0
- package/dist/admin-cli.js +52 -3
- package/dist/admin-cli.js.map +1 -1
- package/dist/admin-client.d.ts +20 -3
- package/dist/admin-client.js +206 -55
- package/dist/admin-client.js.map +1 -1
- package/dist/admin-compatibility.d.ts +2 -0
- package/dist/admin-compatibility.js +53 -0
- package/dist/admin-compatibility.js.map +1 -0
- package/dist/admin-integrity.d.ts +26 -0
- package/dist/admin-integrity.js +76 -0
- package/dist/admin-integrity.js.map +1 -0
- package/dist/admin-server.d.ts +6 -0
- package/dist/admin-server.js +102 -6
- package/dist/admin-server.js.map +1 -1
- package/dist/admin-storage-io.d.ts +10 -0
- package/dist/admin-storage-io.js +80 -0
- package/dist/admin-storage-io.js.map +1 -0
- package/dist/admin-subtree.d.ts +8 -0
- package/dist/admin-subtree.js +92 -0
- package/dist/admin-subtree.js.map +1 -0
- package/dist/admin-types.d.ts +71 -0
- package/dist/admin-ui.js +100 -12
- package/dist/admin-ui.js.map +1 -1
- package/docs/PUBLISHING.md +5 -2
- package/docs/RELEASE_NOTES_0.1.0.md +2 -0
- package/docs/REMOTE_STORAGE.md +19 -1
- package/package.json +5 -3
package/dist/admin-ui.js
CHANGED
|
@@ -45,6 +45,8 @@ export const ADMIN_HTML = `<!doctype html>
|
|
|
45
45
|
.sidebar-card { display: grid; align-content: start; gap: 8px; padding: 10px; border-radius: 8px; background: rgba(255, 255, 255, 0.06); }
|
|
46
46
|
nav .hint { align-self: end; }
|
|
47
47
|
.row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; }
|
|
48
|
+
.row-split { grid-template-columns: minmax(0, 1fr) 82px; }
|
|
49
|
+
.mini-labels { color: #9fb1b9; font-size: 12px; padding: 2px 2px 0; }
|
|
48
50
|
.topbar { min-height: 74px; padding: 16px 24px; display: grid; grid-template-columns: 1fr auto auto; gap: 14px; align-items: center; background: linear-gradient(180deg, #ffffff 0%, #fbfdfd 100%); border-bottom: 1px solid var(--line); }
|
|
49
51
|
.topbar h1 { margin: 0; font-family: "Avenir Next", "SF Pro Display", "Aptos Display", "Inter", ui-sans-serif, system-ui, sans-serif; font-size: 24px; letter-spacing: 0; font-weight: 600; }
|
|
50
52
|
.auth { display: none; grid-template-columns: minmax(160px, 280px) auto; gap: 8px; }
|
|
@@ -62,6 +64,7 @@ export const ADMIN_HTML = `<!doctype html>
|
|
|
62
64
|
.mutation summary { cursor: pointer; font-weight: 600; }
|
|
63
65
|
.mutation[open] { display: grid; gap: 8px; }
|
|
64
66
|
.mutation-grid { display: grid; grid-template-columns: 90px 1fr 1fr 1fr; gap: 8px; }
|
|
67
|
+
.audit-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
|
|
65
68
|
.gvql-box { display: none; padding: 12px 14px; border-bottom: 1px solid var(--line); background: #f8fafb; gap: 8px; }
|
|
66
69
|
.gvql-box.active { display: grid; }
|
|
67
70
|
.gvql-examples { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
@@ -115,7 +118,7 @@ export const ADMIN_HTML = `<!doctype html>
|
|
|
115
118
|
.field-actions { grid-area: actions; justify-content: flex-start; }
|
|
116
119
|
}
|
|
117
120
|
@media (max-width: 980px) {
|
|
118
|
-
.shell, .kpis, .mutation-grid, .topbar, .fields-head { grid-template-columns: 1fr; }
|
|
121
|
+
.shell, .kpis, .mutation-grid, .audit-grid, .topbar, .fields-head { grid-template-columns: 1fr; }
|
|
119
122
|
.gvql-actions, .gvql-parameter-row { grid-template-columns: 1fr; }
|
|
120
123
|
nav { grid-template-rows: auto; gap: 10px; padding: 12px; }
|
|
121
124
|
.brand { grid-template-columns: 42px 1fr; padding-bottom: 2px; }
|
|
@@ -141,6 +144,7 @@ export const ADMIN_HTML = `<!doctype html>
|
|
|
141
144
|
<button class="nav-btn" data-view="objects" onclick="showObjects()">Objects</button>
|
|
142
145
|
<button class="nav-btn" data-view="graph" onclick="showGraph()">Graph</button>
|
|
143
146
|
<button class="nav-btn" data-view="gvql" onclick="showGvql()">GVQL</button>
|
|
147
|
+
<button class="nav-btn" data-view="operations" onclick="showOperations()">Operations</button>
|
|
144
148
|
<button class="nav-btn" data-view="types" onclick="showTypes()">Type Dictionary</button>
|
|
145
149
|
<button class="nav-btn" data-view="transactions" onclick="showTransactions()">Transactions</button>
|
|
146
150
|
<button class="nav-btn" data-view="journal" onclick="showJournal()">Journal</button>
|
|
@@ -148,6 +152,9 @@ export const ADMIN_HTML = `<!doctype html>
|
|
|
148
152
|
</div>
|
|
149
153
|
<div class="sidebar-card">
|
|
150
154
|
<div class="row"><input id="q" placeholder="Search paths, values, types, IDs" /><button onclick="showSearch()">Search</button></div>
|
|
155
|
+
<div class="row row-split mini-labels"><span>Graph root ID</span><span>Depth</span></div>
|
|
156
|
+
<div class="row row-split"><input id="graphRoot" placeholder="Graph root object ID" /><input id="graphDepth" type="number" min="0" max="12" value="2" /></div>
|
|
157
|
+
<button onclick="showGraph()">Load Graph Slice</button>
|
|
151
158
|
<div class="row"><input id="backupPath" placeholder="Backup destination" /><button onclick="runBackup()">Backup</button></div>
|
|
152
159
|
<button onclick="runMaintenance()">Run Maintenance</button>
|
|
153
160
|
</div>
|
|
@@ -172,6 +179,11 @@ export const ADMIN_HTML = `<!doctype html>
|
|
|
172
179
|
<input id="mvalue" placeholder='New JSON value, e.g. "active"' />
|
|
173
180
|
<input id="confirmToken" placeholder="Confirm token" />
|
|
174
181
|
</div>
|
|
182
|
+
<div class="audit-grid">
|
|
183
|
+
<input id="auditActor" placeholder="Actor, e.g. ops@example.com" />
|
|
184
|
+
<input id="auditReason" placeholder="Reason for change" />
|
|
185
|
+
<input id="auditTraceId" placeholder="Trace ID" />
|
|
186
|
+
</div>
|
|
175
187
|
<div class="actions"><button onclick="preview()">Preview</button><button class="danger" onclick="mutate()">Commit Change</button></div>
|
|
176
188
|
</details>
|
|
177
189
|
<div class="gvql-box" id="gvqlPanel">
|
|
@@ -185,6 +197,8 @@ OFFSET 0</textarea>
|
|
|
185
197
|
<input id="gvqlParams" type="hidden" />
|
|
186
198
|
<div class="gvql-actions">
|
|
187
199
|
<input id="gvqlConfirmToken" placeholder="Confirm token" />
|
|
200
|
+
<input id="gvqlAuditActor" placeholder="Actor" />
|
|
201
|
+
<input id="gvqlAuditReason" placeholder="Reason" />
|
|
188
202
|
<button onclick="runGvql(true)">Run / Preview</button>
|
|
189
203
|
<button class="danger" onclick="runGvql(false)">Commit GVQL</button>
|
|
190
204
|
</div>
|
|
@@ -223,8 +237,9 @@ OFFSET 0</textarea>
|
|
|
223
237
|
hierarchy: ['Object Hierarchy', 'Root-first graph view with references you can follow.'],
|
|
224
238
|
overview: ['Storage Overview', 'Health, object count, latest transaction, and current snapshot.'],
|
|
225
239
|
objects: ['Objects', 'Browse graph records with type, preview, and transaction metadata.'],
|
|
226
|
-
graph: ['Object Graph', '
|
|
240
|
+
graph: ['Object Graph', 'Depth-limited graph slice for large stores and API-style inspection.'],
|
|
227
241
|
gvql: ['GVQL Query', 'Run graph pattern queries and preview batch updates.'],
|
|
242
|
+
operations: ['Operations', 'Storage hardening, WAL state, and recovery readiness.'],
|
|
228
243
|
types: ['Type Dictionary', 'Registered runtime types and schema metadata.'],
|
|
229
244
|
transactions: ['Transactions', 'Newest commits first.'],
|
|
230
245
|
journal: ['Journal', 'Append-only storage activity log.'],
|
|
@@ -308,7 +323,12 @@ OFFSET 0</textarea>
|
|
|
308
323
|
}
|
|
309
324
|
async function refreshKpis() {
|
|
310
325
|
const summary = await requestJson('/api/summary?verify=false');
|
|
311
|
-
|
|
326
|
+
const hardening = summary.hardening || {};
|
|
327
|
+
const ops = summary.operations || {};
|
|
328
|
+
const library = summary.library || {};
|
|
329
|
+
const walLabel = (hardening.transactionLog || '-') + (typeof ops.pendingWalCommits === 'number' ? ' / ' + ops.pendingWalCommits + ' pending' : '');
|
|
330
|
+
const libraryLabel = (library.installedVersion || '-') + (library.status === 'warning' ? ' warning' : '');
|
|
331
|
+
kpis.innerHTML = kpi('Objects', summary.objectCount) + kpi('Transaction', summary.transactionId) + kpi('Library', libraryLabel) + kpi('WAL', walLabel) + kpi('Lock', hardening.writerLock || '-') + kpi('Ops', ops.status || '-') + kpi('Snapshot', summary.currentSnapshot || '-');
|
|
312
332
|
return summary;
|
|
313
333
|
}
|
|
314
334
|
async function showHierarchy() {
|
|
@@ -326,6 +346,7 @@ OFFSET 0</textarea>
|
|
|
326
346
|
show({ root: root.rootObjectId, note: 'Only the visible branch is loaded. Click a row to drill into its children.' });
|
|
327
347
|
}
|
|
328
348
|
async function showObjectWithChildren(id, label, parentPath) {
|
|
349
|
+
document.getElementById('graphRoot').value = id;
|
|
329
350
|
const record = await apiJson('/api/objects/' + encodeURIComponent(id));
|
|
330
351
|
hierarchyPath = parentPath.concat([{ id, label, record }]);
|
|
331
352
|
renderEditableFields(record);
|
|
@@ -345,6 +366,7 @@ OFFSET 0</textarea>
|
|
|
345
366
|
setRows(rows, 'No children');
|
|
346
367
|
}
|
|
347
368
|
async function showObjectInHierarchyPath(id) {
|
|
369
|
+
document.getElementById('graphRoot').value = id;
|
|
348
370
|
setView('hierarchy');
|
|
349
371
|
listTitle.textContent = 'Object hierarchy';
|
|
350
372
|
listHint.textContent = 'Search context';
|
|
@@ -396,7 +418,17 @@ OFFSET 0</textarea>
|
|
|
396
418
|
listHint.textContent = 'Overview';
|
|
397
419
|
const summary = await refreshKpis();
|
|
398
420
|
show(summary);
|
|
399
|
-
|
|
421
|
+
const rows = [];
|
|
422
|
+
if (summary.operations) {
|
|
423
|
+
rows.push({ columns: [summary.operations.status, 'operations', summary.operations.pendingWalCommits + ' pending WAL commits', summary.operations.walCommitFiles + ' WAL commits'], onclick: () => show(summary.operations) });
|
|
424
|
+
}
|
|
425
|
+
if (summary.library) {
|
|
426
|
+
rows.push({ columns: [summary.library.installedVersion || '-', 'library', 'recommended ' + summary.library.recommendedVersion, summary.library.status], onclick: () => show(summary.library) });
|
|
427
|
+
}
|
|
428
|
+
if (summary.latestTransaction) {
|
|
429
|
+
rows.push({ columns: ['#' + summary.latestTransaction.transactionId, summary.latestTransaction.mode, summary.latestTransaction.snapshotFile, summary.latestTransaction.objectIds.length + ' ids'], onclick: () => show(summary.latestTransaction) });
|
|
430
|
+
}
|
|
431
|
+
setRows(rows, 'No transactions yet');
|
|
400
432
|
}
|
|
401
433
|
async function showObjects() {
|
|
402
434
|
setView('objects');
|
|
@@ -427,6 +459,7 @@ OFFSET 0</textarea>
|
|
|
427
459
|
list.appendChild(pager);
|
|
428
460
|
}
|
|
429
461
|
async function showObject(id) {
|
|
462
|
+
document.getElementById('graphRoot').value = id;
|
|
430
463
|
document.getElementById('mid').value = id;
|
|
431
464
|
document.getElementById('detailHint').textContent = 'Object #' + id;
|
|
432
465
|
const record = await requestJson('/api/objects/' + encodeURIComponent(id));
|
|
@@ -565,11 +598,39 @@ OFFSET 0</textarea>
|
|
|
565
598
|
}
|
|
566
599
|
async function showGraph() {
|
|
567
600
|
setView('graph');
|
|
568
|
-
listTitle.textContent = 'Graph
|
|
569
|
-
|
|
570
|
-
const
|
|
601
|
+
listTitle.textContent = 'Graph slice';
|
|
602
|
+
const depth = graphDepth();
|
|
603
|
+
const rootObjectId = document.getElementById('graphRoot').value.trim();
|
|
604
|
+
listHint.textContent = (rootObjectId ? 'Root #' + rootObjectId : 'Store root') + ', depth ' + depth;
|
|
605
|
+
const graph = await requestJson(graphSubtreeUrl(rootObjectId, depth));
|
|
571
606
|
renderGraph(graph);
|
|
572
|
-
|
|
607
|
+
const rows = [
|
|
608
|
+
{
|
|
609
|
+
columns: [graph.complete ? 'complete' : 'partial', 'subtree', graph.objectIds.length + ' objects', graph.truncatedReferences.length + ' boundary refs'],
|
|
610
|
+
onclick: () => show(graph)
|
|
611
|
+
},
|
|
612
|
+
...graph.nodes.map(node => ({
|
|
613
|
+
columns: ['#' + node.objectId, node.type || node.kind, node.objectId === graph.rootObjectId ? 'subtree root' : 'loaded node', 'node'],
|
|
614
|
+
onclick: () => showObject(node.objectId)
|
|
615
|
+
})),
|
|
616
|
+
...graph.edges.map(e => ({
|
|
617
|
+
columns: [e.from + ' -> ' + e.to, 'edge', e.path, 'loaded'],
|
|
618
|
+
onclick: () => showObject(e.to)
|
|
619
|
+
})),
|
|
620
|
+
...(graph.truncatedReferences || []).map(ref => ({
|
|
621
|
+
columns: [ref.fromObjectId + ' -> ' + ref.toObjectId, 'boundary', ref.path, 'depth ' + ref.depth],
|
|
622
|
+
onclick: () => showObjectInHierarchyPath(ref.toObjectId)
|
|
623
|
+
}))
|
|
624
|
+
];
|
|
625
|
+
setRows(rows, 'No graph objects found');
|
|
626
|
+
}
|
|
627
|
+
function graphDepth() {
|
|
628
|
+
const value = Number.parseInt(document.getElementById('graphDepth').value || '2', 10);
|
|
629
|
+
return Number.isInteger(value) && value >= 0 ? Math.min(value, 12) : 2;
|
|
630
|
+
}
|
|
631
|
+
function graphSubtreeUrl(rootObjectId, depth) {
|
|
632
|
+
const query = '?depth=' + encodeURIComponent(String(depth));
|
|
633
|
+
return rootObjectId ? '/api/objects/' + encodeURIComponent(rootObjectId) + '/subtree' + query : '/api/subtree' + query;
|
|
573
634
|
}
|
|
574
635
|
function renderGraph(graph) {
|
|
575
636
|
const width = 900;
|
|
@@ -607,8 +668,8 @@ OFFSET 0</textarea>
|
|
|
607
668
|
circle.setAttribute('cx', pos.x);
|
|
608
669
|
circle.setAttribute('cy', pos.y);
|
|
609
670
|
circle.setAttribute('r', '26');
|
|
610
|
-
circle.setAttribute('fill', node.objectId ===
|
|
611
|
-
circle.setAttribute('stroke', node.objectId ===
|
|
671
|
+
circle.setAttribute('fill', node.objectId === graph.rootObjectId ? '#f3c969' : '#d9f1ef');
|
|
672
|
+
circle.setAttribute('stroke', node.objectId === graph.rootObjectId ? '#ab7622' : '#0f8b8d');
|
|
612
673
|
circle.setAttribute('stroke-width', '2');
|
|
613
674
|
const label = document.createElementNS(ns, 'text');
|
|
614
675
|
label.setAttribute('x', pos.x);
|
|
@@ -624,6 +685,20 @@ OFFSET 0</textarea>
|
|
|
624
685
|
}
|
|
625
686
|
const showTypes = async () => { setView('types'); listTitle.textContent = 'Types'; listHint.textContent = 'Dictionary'; const value = await requestJson('/api/types'); setRows((value && value.entries || []).map(e => ({ columns: [String(e.id || '-'), e.name || 'type', e.handler || e.name || '-', 'type'], onclick: () => show(e) })), 'No type dictionary found'); };
|
|
626
687
|
const showTransactions = async () => { setView('transactions'); listTitle.textContent = 'Transactions'; listHint.textContent = 'Newest first'; const rows = await requestJson('/api/transactions'); setRows(rows.map(t => ({ columns: ['#' + t.transactionId, t.mode, t.snapshotFile, t.objectIds.length + ' ids'], onclick: () => show(t) })), 'No transactions found'); };
|
|
688
|
+
const showOperations = async () => {
|
|
689
|
+
setView('operations');
|
|
690
|
+
listTitle.textContent = 'Storage operations';
|
|
691
|
+
listHint.textContent = 'WAL and hardening';
|
|
692
|
+
const ops = await requestJson('/api/operations');
|
|
693
|
+
setRows([
|
|
694
|
+
{ columns: [ops.status, 'health', ops.pendingWalCommits + ' pending WAL commits', ops.latestWalTransactionId ? 'wal tx ' + ops.latestWalTransactionId : 'no wal'], onclick: () => show(ops) },
|
|
695
|
+
{ columns: [ops.transactionLog, 'wal', ops.walPrepareFiles + ' prepares', ops.walCommitFiles + ' commits'], onclick: () => show(ops) },
|
|
696
|
+
{ columns: [String(ops.checkedIntegrityHashes || 0), 'integrity hashes', 'transaction chain', ops.checkedIntegrityHashes ? 'present' : 'not recorded'], onclick: () => show(ops) },
|
|
697
|
+
{ columns: [ops.mutationsAllowed ? 'enabled' : 'disabled', 'mutations', 'lock timeout ' + ops.lockTimeoutMs + 'ms', ops.staleLockTimeoutMs ? 'stale ' + ops.staleLockTimeoutMs + 'ms' : 'no stale recovery'], onclick: () => show(ops) },
|
|
698
|
+
{ columns: ['#' + ops.publishedTransactionId, 'published', ops.latestJournalTransactionId ? 'journal tx ' + ops.latestJournalTransactionId : 'no journal', 'manifest'], onclick: () => show(ops) }
|
|
699
|
+
], 'No operations status');
|
|
700
|
+
return ops;
|
|
701
|
+
};
|
|
627
702
|
const showJournal = async () => {
|
|
628
703
|
setView('journal');
|
|
629
704
|
listTitle.textContent = 'Journal';
|
|
@@ -637,7 +712,8 @@ OFFSET 0</textarea>
|
|
|
637
712
|
listHint.textContent = 'Integrity';
|
|
638
713
|
const result = await requestJson('/api/verify');
|
|
639
714
|
setRows([
|
|
640
|
-
{ columns: [result.ok ? 'OK' : 'FAIL', 'health', result.errors.length ? result.errors.join('; ') : 'No errors', result.checkedObjects + ' objects'], onclick: () => show(result) }
|
|
715
|
+
{ columns: [result.ok ? 'OK' : 'FAIL', 'health', result.errors.length ? result.errors.join('; ') : 'No errors', result.checkedObjects + ' objects'], onclick: () => show(result) },
|
|
716
|
+
{ columns: [String(result.checkedIntegrityHashes || 0), 'integrity hashes', result.checkedWalRecords + ' WAL checks', result.pendingWalCommits + ' pending WAL'], onclick: () => show(result) }
|
|
641
717
|
], 'Verification has not run');
|
|
642
718
|
return result;
|
|
643
719
|
};
|
|
@@ -821,6 +897,7 @@ OFFSET 0</textarea>
|
|
|
821
897
|
dryRun
|
|
822
898
|
};
|
|
823
899
|
if (!dryRun && confirmRequired) payload.confirmToken = document.getElementById('gvqlConfirmToken').value;
|
|
900
|
+
if (!dryRun) payload.metadata = readAuditMetadata('gvql');
|
|
824
901
|
const result = await postJson('/api/gvql', payload);
|
|
825
902
|
const rows = [];
|
|
826
903
|
if (result.plan) {
|
|
@@ -872,8 +949,19 @@ OFFSET 0</textarea>
|
|
|
872
949
|
}
|
|
873
950
|
const confirmRequired = __CONFIRM_REQUIRED__;
|
|
874
951
|
if (!confirmRequired) document.getElementById('confirmToken').style.display = 'none';
|
|
952
|
+
if (!confirmRequired) document.getElementById('gvqlConfirmToken').style.display = 'none';
|
|
953
|
+
function readAuditMetadata(prefix) {
|
|
954
|
+
const actor = document.getElementById(prefix === 'gvql' ? 'gvqlAuditActor' : 'auditActor')?.value.trim();
|
|
955
|
+
const reason = document.getElementById(prefix === 'gvql' ? 'gvqlAuditReason' : 'auditReason')?.value.trim();
|
|
956
|
+
const traceId = prefix === 'gvql' ? '' : document.getElementById('auditTraceId')?.value.trim();
|
|
957
|
+
const metadata = { source: prefix === 'gvql' ? 'graphvault-studio:gvql' : 'graphvault-studio:direct-edit' };
|
|
958
|
+
if (actor) metadata.actor = actor;
|
|
959
|
+
if (reason) metadata.reason = reason;
|
|
960
|
+
if (traceId) metadata.traceId = traceId;
|
|
961
|
+
return metadata;
|
|
962
|
+
}
|
|
875
963
|
function mutationPayload(includeConfirm) {
|
|
876
|
-
const payload = { objectId: document.getElementById('mid').value, path: document.getElementById('mpath').value, value: JSON.parse(document.getElementById('mvalue').value) };
|
|
964
|
+
const payload = { objectId: document.getElementById('mid').value, path: document.getElementById('mpath').value, value: JSON.parse(document.getElementById('mvalue').value), metadata: readAuditMetadata('direct') };
|
|
877
965
|
if (includeConfirm) payload.confirmToken = document.getElementById('confirmToken').value;
|
|
878
966
|
return payload;
|
|
879
967
|
}
|
package/dist/admin-ui.js.map
CHANGED
|
@@ -1 +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
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAmOC,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAwuBvD,CAAC"}
|
package/docs/PUBLISHING.md
CHANGED
|
@@ -18,9 +18,12 @@ npm ci
|
|
|
18
18
|
npm test
|
|
19
19
|
npm run demo:store
|
|
20
20
|
npm run pack:dry-run
|
|
21
|
+
npm run package:smoke
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
`npm test` includes `npm run compat:check`, which verifies that Studio's `@sprengmeister/graphvault` dependency range accepts the runtime version recommended by the current Studio build. If the check warns that the lockfile resolves an older GraphVault Library, publish or install the newer library before a production Studio release.
|
|
25
|
+
|
|
26
|
+
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. `npm run package:smoke` installs the generated tarball into a fresh temporary project, verifies the CLI help output, imports the public programmatic API, creates a real GraphVault store, and exercises search plus bounded subtree loading through the installed package.
|
|
24
27
|
|
|
25
28
|
## Tagging
|
|
26
29
|
|
|
@@ -35,7 +38,7 @@ git push origin v0.1.0
|
|
|
35
38
|
|
|
36
39
|
Use the GitHub Actions `Release` workflow with the matching tag input, for example `v0.1.0`.
|
|
37
40
|
|
|
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.
|
|
41
|
+
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`, performs the fresh-install package smoke test, and publishes with npm provenance.
|
|
39
42
|
|
|
40
43
|
## Repository Visibility
|
|
41
44
|
|
|
@@ -8,8 +8,10 @@ GraphVault Studio 0.1.0 is the first public graphical admin client for GraphVaul
|
|
|
8
8
|
- Search across paths, values, ids, types, references, and encoded object data.
|
|
9
9
|
- Parent-path lookup from an object back toward the root, including multiple direct parents.
|
|
10
10
|
- Paged object browser for large stores.
|
|
11
|
+
- Depth-limited subtree API and graph slice view for large stores.
|
|
11
12
|
- GVQL console for graph queries and dry-run batch mutation previews.
|
|
12
13
|
- Controlled primitive-field editing with confirmation-token safety.
|
|
14
|
+
- Visible GraphVault Library compatibility status.
|
|
13
15
|
- Verification, maintenance, backup, transaction, journal, graph, type dictionary, and object detail views.
|
|
14
16
|
- CLI and programmatic server startup.
|
|
15
17
|
- Local filesystem, custom, remote, S3-compatible, HTTP, and SQL-backed storage through GraphVault storage targets.
|
package/docs/REMOTE_STORAGE.md
CHANGED
|
@@ -53,4 +53,22 @@ await startAdminServer({
|
|
|
53
53
|
});
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
For mutation endpoints, always set `allowMutations: true` and a `mutationConfirmToken`. For exposed or shared environments, also set `authToken
|
|
56
|
+
For mutation endpoints, always set `allowMutations: true` and a `mutationConfirmToken`. For exposed or shared environments, also set authentication. The legacy `authToken` option grants admin access. Prefer role tokens when several people or tools use Studio:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
await startAdminServer({
|
|
60
|
+
storageDirectory: "main",
|
|
61
|
+
storageTarget,
|
|
62
|
+
allowMutations: true,
|
|
63
|
+
mutationConfirmToken: process.env.GRAPHVAULT_ADMIN_CONFIRM_TOKEN,
|
|
64
|
+
accessTokens: [
|
|
65
|
+
{ token: process.env.GRAPHVAULT_VIEWER_TOKEN!, role: "viewer" },
|
|
66
|
+
{ token: process.env.GRAPHVAULT_OPERATOR_TOKEN!, role: "operator" },
|
|
67
|
+
{ token: process.env.GRAPHVAULT_ADMIN_ROLE_TOKEN!, role: "admin" },
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- `viewer`: read-only inspection, search, verification, transactions, journal, and GVQL dry-runs.
|
|
73
|
+
- `operator`: viewer plus maintenance and backup.
|
|
74
|
+
- `admin`: operator plus committed direct edits and GVQL mutations. Admin writes should still use a confirmation token.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphvault-studio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Graphical admin client for GraphVault object graph stores.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,9 +37,11 @@
|
|
|
37
37
|
],
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsc -p tsconfig.json",
|
|
40
|
-
"
|
|
40
|
+
"compat:check": "node scripts/check-compatibility.mjs",
|
|
41
|
+
"test": "npm run compat:check && npm run build && node tests/smoke.mjs",
|
|
41
42
|
"demo:store": "npm run build && node examples/create-demo-store.mjs",
|
|
42
43
|
"pack:dry-run": "npm pack --dry-run --cache ./.npm-cache",
|
|
44
|
+
"package:smoke": "node scripts/package-smoke.mjs",
|
|
43
45
|
"prepack": "npm run build"
|
|
44
46
|
},
|
|
45
47
|
"keywords": [
|
|
@@ -58,7 +60,7 @@
|
|
|
58
60
|
"node": ">=20"
|
|
59
61
|
},
|
|
60
62
|
"dependencies": {
|
|
61
|
-
"@sprengmeister/graphvault": "0.1.0"
|
|
63
|
+
"@sprengmeister/graphvault": ">=0.1.0 <0.3.0"
|
|
62
64
|
},
|
|
63
65
|
"devDependencies": {
|
|
64
66
|
"@types/node": "^22.15.3",
|