@noy-db/in-devtools-tui 0.2.0-pre.10

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vLannaAi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/bin.js ADDED
@@ -0,0 +1,389 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/bin.tsx
4
+ import { render } from "ink";
5
+ import { createInspector } from "@noy-db/in-devtools";
6
+ import { createNoydb } from "@noy-db/hub";
7
+ import { toMeter } from "@noy-db/to-meter";
8
+
9
+ // src/App.tsx
10
+ import React, { useState, useEffect } from "react";
11
+ import { Box as Box6, Text as Text6, useApp, useInput } from "ink";
12
+
13
+ // src/panes/VaultList.tsx
14
+ import { Box, Text } from "ink";
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ function VaultList({ vaults, activeName }) {
17
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginRight: 2, children: [
18
+ /* @__PURE__ */ jsx(Text, { bold: true, underline: true, children: "Vaults" }),
19
+ vaults.map((v) => v.id === activeName ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
20
+ "\u203A ",
21
+ v.id
22
+ ] }, v.id) : /* @__PURE__ */ jsxs(Text, { children: [
23
+ " ",
24
+ v.id
25
+ ] }, v.id))
26
+ ] });
27
+ }
28
+
29
+ // src/panes/CollectionList.tsx
30
+ import { Box as Box2, Text as Text2 } from "ink";
31
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
32
+ function CollectionList({ snapshot, selectedIdx }) {
33
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginRight: 2, children: [
34
+ /* @__PURE__ */ jsx2(Text2, { bold: true, underline: true, children: "Collections" }),
35
+ snapshot.collections.map((c, i) => i === selectedIdx ? /* @__PURE__ */ jsx2(Text2, { color: "cyan", inverse: true, children: c.name }, c.name) : /* @__PURE__ */ jsx2(Text2, { children: c.name }, c.name))
36
+ ] });
37
+ }
38
+
39
+ // src/panes/DetailPane.tsx
40
+ import { Box as Box3, Text as Text3 } from "ink";
41
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
42
+ function DetailPane({ collection }) {
43
+ if (!collection) return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Select a collection (\u21B5)" }) });
44
+ const fieldNames = Object.keys(collection.fields);
45
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
46
+ /* @__PURE__ */ jsx3(Text3, { bold: true, underline: true, children: collection.name }),
47
+ /* @__PURE__ */ jsxs3(Text3, { children: [
48
+ "records: ",
49
+ collection.stats?.records ?? "\u2014",
50
+ " bytes: ",
51
+ collection.stats?.bytes ?? "\u2014"
52
+ ] }),
53
+ /* @__PURE__ */ jsxs3(Text3, { children: [
54
+ "fields: ",
55
+ fieldNames.length ? fieldNames.join(", ") : "(none)"
56
+ ] })
57
+ ] });
58
+ }
59
+
60
+ // src/panes/RecordsPane.tsx
61
+ import { Box as Box4, Text as Text4 } from "ink";
62
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
63
+ function cell(value) {
64
+ if (value === null || value === void 0) return "\xB7";
65
+ if (typeof value === "object") return Array.isArray(value) ? `[${value.length}]` : "{\u2026}";
66
+ if (typeof value === "string") return value;
67
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
68
+ return "?";
69
+ }
70
+ function RecordsPane({ collection, page, error }) {
71
+ const schemaFields = Object.keys(collection.fields);
72
+ if (error) return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
73
+ /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
74
+ "records error: ",
75
+ error
76
+ ] }),
77
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "n/p retry \xB7 \u21E5 back" })
78
+ ] });
79
+ if (!page) return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "loading records\u2026" }) });
80
+ const firstRowKeys = page.rows.length > 0 && typeof page.rows[0] === "object" && page.rows[0] !== null ? Object.keys(page.rows[0]) : [];
81
+ const fields = schemaFields.length > 0 ? schemaFields : firstRowKeys;
82
+ const from = page.total === 0 ? 0 : page.offset + 1;
83
+ const to = Math.min(page.offset + page.limit, page.total);
84
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
85
+ /* @__PURE__ */ jsxs4(Text4, { bold: true, children: [
86
+ "rows ",
87
+ from,
88
+ "\u2013",
89
+ to,
90
+ " of ",
91
+ page.total,
92
+ " ",
93
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "(n/p page \xB7 \u21E5 back)" })
94
+ ] }),
95
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: fields.join(" ") }),
96
+ page.rows.map((row, i) => /* @__PURE__ */ jsx4(Text4, { children: fields.map((f) => cell(row?.[f])).join(" ") }, i))
97
+ ] });
98
+ }
99
+
100
+ // src/panes/WriteMonitor.tsx
101
+ import { Box as Box5, Text as Text5 } from "ink";
102
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
103
+ function WriteMonitor({ vaultName, rows, meter }) {
104
+ const m = meter?.byMethod;
105
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
106
+ /* @__PURE__ */ jsxs5(Text5, { bold: true, children: [
107
+ "Write Monitor \u2014 ",
108
+ vaultName,
109
+ " ",
110
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "(w/esc \xB7 c clear \xB7 q quit)" })
111
+ ] }),
112
+ meter && m && /* @__PURE__ */ jsxs5(Text5, { children: [
113
+ "store put p50 ",
114
+ m.put?.p50 ?? "\u2014",
115
+ "ms p99 ",
116
+ m.put?.p99 ?? "\u2014",
117
+ "ms",
118
+ meter.status === "degraded" ? " \u26A0degraded" : "",
119
+ " \xB7 del p50 ",
120
+ m.delete?.p50 ?? "\u2014",
121
+ " p99 ",
122
+ m.delete?.p99 ?? "\u2014",
123
+ " \xB7 get p50 ",
124
+ m.get?.p50 ?? "\u2014",
125
+ " p99 ",
126
+ m.get?.p99 ?? "\u2014"
127
+ ] }),
128
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "time user op collection/docId v" }),
129
+ rows.length === 0 && /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "(waiting for writes\u2026)" }),
130
+ rows.map((r, i) => r.conflict ? /* @__PURE__ */ jsxs5(Text5, { color: "yellow", children: [
131
+ r.time,
132
+ " ",
133
+ r.user.padEnd(6),
134
+ " ",
135
+ r.op,
136
+ " ",
137
+ r.target.padEnd(18),
138
+ " ",
139
+ r.versions,
140
+ " \u26A0 CONFLICT"
141
+ ] }, i) : /* @__PURE__ */ jsxs5(Text5, { children: [
142
+ r.time,
143
+ " ",
144
+ r.user.padEnd(6),
145
+ " ",
146
+ r.op,
147
+ " ",
148
+ r.target.padEnd(18),
149
+ " ",
150
+ r.versions
151
+ ] }, i))
152
+ ] });
153
+ }
154
+
155
+ // src/App.tsx
156
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
157
+ var PAGE = 20;
158
+ var BUFFER = 200;
159
+ function fmtTime(ts) {
160
+ return new Date(ts).toTimeString().slice(0, 8);
161
+ }
162
+ function rowOf(e) {
163
+ const op = e.op === "delete" ? "del" : "put";
164
+ const versions = e.op === "delete" ? `${e.baseVersion}\u2192\xB7` : `${e.baseVersion}\u2192${e.version}`;
165
+ return { time: fmtTime(e.timestamp), user: e.userId, op, target: `${e.collection}/${e.docId}`, versions, baseKey: `${e.collection}/${e.docId}@${e.baseVersion}`, conflict: false };
166
+ }
167
+ function App({ inspector, vault, vaultName, initial }) {
168
+ const { exit } = useApp();
169
+ const vaults = initial?.vaults ?? [];
170
+ const snapshot = initial?.snapshot;
171
+ const collections = snapshot?.collections ?? [];
172
+ const [selectedIdx, setSelectedIdx] = useState(0);
173
+ const [drilled, setDrilled] = useState(false);
174
+ const [tab, setTab] = useState("schema");
175
+ const [offset, setOffset] = useState(0);
176
+ const [page, setPage] = useState(null);
177
+ const [recErr, setRecErr] = useState(void 0);
178
+ const current = collections[selectedIdx];
179
+ const [view, setView] = useState("structure");
180
+ const [feed, setFeed] = useState([]);
181
+ const conflictKeys = React.useRef(/* @__PURE__ */ new Set());
182
+ const [started, setStarted] = useState(false);
183
+ const [meter, setMeter] = useState(null);
184
+ useEffect(() => {
185
+ if (view !== "monitor") return;
186
+ const tick = () => setMeter(inspector.meterSnapshot());
187
+ tick();
188
+ const id = setInterval(tick, 1e3);
189
+ return () => clearInterval(id);
190
+ }, [view, inspector]);
191
+ useEffect(() => {
192
+ if (!drilled || tab !== "records" || !current) return;
193
+ let live = true;
194
+ setPage(null);
195
+ setRecErr(void 0);
196
+ inspector.records(vault, current.name, { limit: PAGE, offset }).then((p) => {
197
+ if (live) setPage(p);
198
+ }).catch((e) => {
199
+ if (live) setRecErr(e instanceof Error ? e.message : String(e));
200
+ });
201
+ return () => {
202
+ live = false;
203
+ };
204
+ }, [drilled, tab, current, offset, inspector, vault]);
205
+ useEffect(() => {
206
+ if (!started) return;
207
+ const offW = inspector.subscribe((e) => {
208
+ setFeed((prev) => {
209
+ const row = rowOf(e);
210
+ if (conflictKeys.current.has(row.baseKey)) row.conflict = true;
211
+ const overlap = prev.some((r) => r.baseKey === row.baseKey && r.user !== row.user);
212
+ if (overlap) row.conflict = true;
213
+ const next = [row, ...prev.map((r) => r.baseKey === row.baseKey && r.user !== row.user ? { ...r, conflict: true } : r)];
214
+ return next.slice(0, BUFFER);
215
+ });
216
+ });
217
+ const offC = inspector.subscribeConflicts((c) => {
218
+ const key = `${c.collection}/${c.docId}@${c.baseVersion}`;
219
+ conflictKeys.current.add(key);
220
+ setFeed((prev) => prev.map((r) => r.baseKey === key ? { ...r, conflict: true } : r));
221
+ });
222
+ return () => {
223
+ offW();
224
+ offC();
225
+ };
226
+ }, [started, inspector]);
227
+ useInput((input, key) => {
228
+ if (input === "q" || key.ctrl && input === "c") {
229
+ exit();
230
+ return;
231
+ }
232
+ if (view === "monitor") {
233
+ if (key.escape) setView("structure");
234
+ if (input === "c") setFeed([]);
235
+ return;
236
+ }
237
+ if (input === "w") {
238
+ setView("monitor");
239
+ setStarted(true);
240
+ return;
241
+ }
242
+ if (!drilled) {
243
+ if (key.downArrow) setSelectedIdx((i) => Math.min(collections.length - 1, i + 1));
244
+ if (key.upArrow) setSelectedIdx((i) => Math.max(0, i - 1));
245
+ if (key.return) {
246
+ setDrilled(true);
247
+ setTab("schema");
248
+ }
249
+ return;
250
+ }
251
+ if (key.escape) {
252
+ setDrilled(false);
253
+ setTab("schema");
254
+ setOffset(0);
255
+ }
256
+ if (key.tab) {
257
+ setTab((t) => t === "schema" ? "records" : "schema");
258
+ setOffset(0);
259
+ }
260
+ if (tab === "records") {
261
+ if (input === "n" && (!page || offset + PAGE < page.total)) setOffset((o) => o + PAGE);
262
+ if (input === "p" && offset - PAGE >= 0) setOffset((o) => o - PAGE);
263
+ }
264
+ });
265
+ if (view === "monitor") return /* @__PURE__ */ jsx6(WriteMonitor, { vaultName, rows: feed, meter });
266
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
267
+ /* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
268
+ "noy-db inspector \u2014 ",
269
+ vaultName,
270
+ " ",
271
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "(\u2191/\u2193 \xB7 \u21B5 drill \xB7 \u21E5 tab \xB7 q quit)" })
272
+ ] }),
273
+ /* @__PURE__ */ jsxs6(Box6, { marginTop: 1, children: [
274
+ /* @__PURE__ */ jsx6(VaultList, { vaults, activeName: vaultName }),
275
+ /* @__PURE__ */ jsx6(CollectionList, { snapshot: snapshot ?? { vault: vaultName, collections: [] }, selectedIdx }),
276
+ drilled && tab === "records" && current ? /* @__PURE__ */ jsx6(RecordsPane, { collection: current, page, ...recErr !== void 0 ? { error: recErr } : {} }) : /* @__PURE__ */ jsx6(DetailPane, { collection: drilled ? current : void 0 })
277
+ ] })
278
+ ] });
279
+ }
280
+
281
+ // src/load-options.ts
282
+ import { resolve as resolvePath } from "path";
283
+ import { pathToFileURL } from "url";
284
+ async function loadOptionsFromFile(filePath) {
285
+ const abs = resolvePath(filePath);
286
+ if (/\.[mc]?ts$/.test(abs)) {
287
+ throw new Error(
288
+ `TypeScript config files are not directly loadable. Compile it first, or use a .mjs/.js config.`
289
+ );
290
+ }
291
+ const mod = await import(pathToFileURL(abs).href);
292
+ const value = mod.default ?? mod;
293
+ return typeof value === "function" ? await value() : value;
294
+ }
295
+ function resolvePassphrase(argv, env) {
296
+ const flag = argv.find((a) => a.startsWith("--passphrase="));
297
+ if (flag) return flag.slice("--passphrase=".length);
298
+ if (env.NOYDB_PASSPHRASE) return env.NOYDB_PASSPHRASE;
299
+ return void 0;
300
+ }
301
+
302
+ // src/prompt-passphrase.ts
303
+ function promptMasked(question, streams = {}) {
304
+ const input = streams.input ?? process.stdin;
305
+ const output = streams.output ?? process.stdout;
306
+ return new Promise((resolve, reject) => {
307
+ output.write(question);
308
+ input.setRawMode?.(true);
309
+ let buf = "";
310
+ const onData = (chunk) => {
311
+ const s = chunk.toString();
312
+ for (const ch of s) {
313
+ if (ch === "\r" || ch === "\n") {
314
+ output.write("\n");
315
+ input.setRawMode?.(false);
316
+ input.removeListener("data", onData);
317
+ resolve(buf);
318
+ return;
319
+ }
320
+ if (ch === "") {
321
+ output.write("\n");
322
+ input.setRawMode?.(false);
323
+ input.removeListener("data", onData);
324
+ reject(new Error("aborted"));
325
+ return;
326
+ }
327
+ if (ch === "\x7F" || ch === "\b") {
328
+ if (buf.length > 0) {
329
+ buf = buf.slice(0, -1);
330
+ output.write("\b \b");
331
+ }
332
+ continue;
333
+ }
334
+ buf += ch;
335
+ output.write("\u2022");
336
+ }
337
+ };
338
+ input.on("data", onData);
339
+ });
340
+ }
341
+
342
+ // src/bin.tsx
343
+ import { jsx as jsx7 } from "react/jsx-runtime";
344
+ function fail(msg) {
345
+ process.stderr.write(`noydb-inspect: ${msg}
346
+ `);
347
+ process.exit(2);
348
+ }
349
+ async function main() {
350
+ const argv = process.argv.slice(2);
351
+ const configPath = argv.find((a) => !a.startsWith("--"));
352
+ if (!configPath) fail("usage: noydb-inspect <config.js|mjs> --vault=<name> [--passphrase=\u2026]");
353
+ const vaultFlag = argv.find((a) => a.startsWith("--vault="));
354
+ if (!vaultFlag) fail("missing --vault=<name>");
355
+ const vaultName = vaultFlag.slice("--vault=".length);
356
+ let passphrase = resolvePassphrase(argv, process.env);
357
+ if (passphrase === void 0) {
358
+ if (!process.stdin.isTTY) fail("no passphrase \u2014 pass --passphrase=\u2026 or set NOYDB_PASSPHRASE");
359
+ passphrase = await promptMasked("Passphrase: ");
360
+ }
361
+ let baseOptions;
362
+ try {
363
+ baseOptions = await loadOptionsFromFile(configPath);
364
+ } catch (err) {
365
+ fail(`failed to load config "${configPath}": ${err instanceof Error ? err.message : String(err)}`);
366
+ }
367
+ let meter;
368
+ if (argv.includes("--meter") && baseOptions.store) {
369
+ const metered = toMeter(baseOptions.store);
370
+ baseOptions = { ...baseOptions, store: metered.store };
371
+ meter = metered.meter;
372
+ }
373
+ const options = { ...baseOptions, secret: passphrase };
374
+ const db = await createNoydb(options);
375
+ let vault;
376
+ try {
377
+ vault = await db.openVault(vaultName);
378
+ } catch (err) {
379
+ fail(`failed to open vault "${vaultName}": ${err instanceof Error ? err.message : String(err)}`);
380
+ }
381
+ const inspector = createInspector(db, meter ? { meter } : void 0);
382
+ const initial = { vaults: await inspector.listVaults(), snapshot: await inspector.snapshot(vault) };
383
+ const { waitUntilExit } = render(
384
+ /* @__PURE__ */ jsx7(App, { inspector, vault, vaultName, initial })
385
+ );
386
+ await waitUntilExit();
387
+ }
388
+ main().catch((err) => fail(err instanceof Error ? err.message : String(err)));
389
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bin.tsx","../src/App.tsx","../src/panes/VaultList.tsx","../src/panes/CollectionList.tsx","../src/panes/DetailPane.tsx","../src/panes/RecordsPane.tsx","../src/panes/WriteMonitor.tsx","../src/load-options.ts","../src/prompt-passphrase.ts"],"sourcesContent":["import React from 'react'\nimport { render } from 'ink'\nimport { createInspector } from '@noy-db/in-devtools'\nimport { createNoydb } from '@noy-db/hub'\nimport type { NoydbOptions } from '@noy-db/hub'\nimport { toMeter } from '@noy-db/to-meter'\nimport { App } from './App.js'\nimport { loadOptionsFromFile, resolvePassphrase } from './load-options.js'\nimport { promptMasked } from './prompt-passphrase.js'\n\nfunction fail(msg: string): never {\n process.stderr.write(`noydb-inspect: ${msg}\\n`)\n process.exit(2)\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2)\n const configPath = argv.find((a) => !a.startsWith('--'))\n if (!configPath) fail('usage: noydb-inspect <config.js|mjs> --vault=<name> [--passphrase=…]')\n const vaultFlag = argv.find((a) => a.startsWith('--vault='))\n if (!vaultFlag) fail('missing --vault=<name>')\n const vaultName = vaultFlag.slice('--vault='.length)\n\n let passphrase = resolvePassphrase(argv, process.env)\n if (passphrase === undefined) {\n if (!process.stdin.isTTY) fail('no passphrase — pass --passphrase=… or set NOYDB_PASSPHRASE')\n passphrase = await promptMasked('Passphrase: ')\n }\n\n let baseOptions: NoydbOptions\n try {\n baseOptions = (await loadOptionsFromFile(configPath)) as NoydbOptions\n } catch (err) {\n fail(`failed to load config \"${configPath}\": ${err instanceof Error ? err.message : String(err)}`)\n }\n\n let meter\n if (argv.includes('--meter') && baseOptions.store) {\n const metered = toMeter(baseOptions.store)\n baseOptions = { ...baseOptions, store: metered.store }\n meter = metered.meter\n }\n\n // Passphrase lives in NoydbOptions.secret — openVault(name) picks it up internally.\n const options: NoydbOptions = { ...baseOptions, secret: passphrase }\n const db = await createNoydb(options)\n\n let vault\n try {\n vault = await db.openVault(vaultName)\n } catch (err) {\n fail(`failed to open vault \"${vaultName}\": ${err instanceof Error ? err.message : String(err)}`)\n }\n\n const inspector = createInspector(db, meter ? { meter } : undefined)\n const initial = { vaults: await inspector.listVaults(), snapshot: await inspector.snapshot(vault) }\n const { waitUntilExit } = render(\n <App inspector={inspector} vault={vault} vaultName={vaultName} initial={initial} />,\n )\n await waitUntilExit()\n}\n\nmain().catch((err) => fail(err instanceof Error ? err.message : String(err)))\n","import React, { useState, useEffect } from 'react'\nimport { Box, Text, useApp, useInput } from 'ink'\nimport type { AppProps, DetailTab, View, FeedRow } from './types.js'\nimport type { RecordPage } from '@noy-db/in-devtools'\nimport type { InspectorWriteEvent } from '@noy-db/in-devtools'\nimport type { MeterSnapshot } from '@noy-db/to-meter'\nimport { VaultList } from './panes/VaultList.js'\nimport { CollectionList } from './panes/CollectionList.js'\nimport { DetailPane } from './panes/DetailPane.js'\nimport { RecordsPane } from './panes/RecordsPane.js'\nimport { WriteMonitor } from './panes/WriteMonitor.js'\n\nconst PAGE = 20\nconst BUFFER = 200\n\nfunction fmtTime(ts: number): string { return new Date(ts).toTimeString().slice(0, 8) }\n\nfunction rowOf(e: InspectorWriteEvent): FeedRow {\n const op = e.op === 'delete' ? 'del' : 'put'\n const versions = e.op === 'delete' ? `${e.baseVersion}→·` : `${e.baseVersion}→${e.version}`\n return { time: fmtTime(e.timestamp), user: e.userId, op, target: `${e.collection}/${e.docId}`, versions, baseKey: `${e.collection}/${e.docId}@${e.baseVersion}`, conflict: false }\n}\n\nexport function App({ inspector, vault, vaultName, initial }: AppProps) {\n const { exit } = useApp()\n const vaults = initial?.vaults ?? []\n const snapshot = initial?.snapshot\n const collections = snapshot?.collections ?? []\n const [selectedIdx, setSelectedIdx] = useState(0)\n const [drilled, setDrilled] = useState(false)\n const [tab, setTab] = useState<DetailTab>('schema')\n const [offset, setOffset] = useState(0)\n const [page, setPage] = useState<RecordPage | null>(null)\n const [recErr, setRecErr] = useState<string | undefined>(undefined)\n const current = collections[selectedIdx]\n\n const [view, setView] = useState<View>('structure')\n const [feed, setFeed] = useState<ReadonlyArray<FeedRow>>([])\n const conflictKeys = React.useRef<Set<string>>(new Set())\n const [started, setStarted] = useState(false)\n const [meter, setMeter] = useState<MeterSnapshot | null>(null)\n useEffect(() => {\n if (view !== 'monitor') return\n const tick = () => setMeter(inspector.meterSnapshot())\n tick()\n const id = setInterval(tick, 1000)\n return () => clearInterval(id)\n }, [view, inspector])\n\n useEffect(() => {\n if (!drilled || tab !== 'records' || !current) return\n let live = true\n setPage(null); setRecErr(undefined)\n inspector.records(vault, current.name, { limit: PAGE, offset })\n .then((p) => { if (live) setPage(p) })\n .catch((e) => { if (live) setRecErr(e instanceof Error ? e.message : String(e)) })\n return () => { live = false }\n }, [drilled, tab, current, offset, inspector, vault])\n\n useEffect(() => {\n if (!started) return\n const offW = inspector.subscribe((e) => {\n setFeed((prev) => {\n const row = rowOf(e)\n if (conflictKeys.current.has(row.baseKey)) row.conflict = true\n const overlap = prev.some((r) => r.baseKey === row.baseKey && r.user !== row.user)\n if (overlap) row.conflict = true\n const next = [row, ...prev.map((r) => (r.baseKey === row.baseKey && r.user !== row.user ? { ...r, conflict: true } : r))]\n return next.slice(0, BUFFER)\n })\n })\n const offC = inspector.subscribeConflicts((c) => {\n const key = `${c.collection}/${c.docId}@${c.baseVersion}`\n conflictKeys.current.add(key)\n setFeed((prev) => prev.map((r) => (r.baseKey === key ? { ...r, conflict: true } : r)))\n })\n return () => { offW(); offC() }\n }, [started, inspector])\n\n useInput((input, key) => {\n if (input === 'q' || (key.ctrl && input === 'c')) { exit(); return }\n if (view === 'monitor') {\n if (key.escape) setView('structure')\n if (input === 'c') setFeed([])\n return\n }\n if (input === 'w') { setView('monitor'); setStarted(true); return }\n if (!drilled) {\n if (key.downArrow) setSelectedIdx((i) => Math.min(collections.length - 1, i + 1))\n if (key.upArrow) setSelectedIdx((i) => Math.max(0, i - 1))\n if (key.return) { setDrilled(true); setTab('schema') }\n return\n }\n if (key.escape) { setDrilled(false); setTab('schema'); setOffset(0) }\n if (key.tab) { setTab((t) => (t === 'schema' ? 'records' : 'schema')); setOffset(0) }\n if (tab === 'records') {\n if (input === 'n' && (!page || offset + PAGE < page.total)) setOffset((o) => o + PAGE)\n if (input === 'p' && offset - PAGE >= 0) setOffset((o) => o - PAGE)\n }\n })\n\n if (view === 'monitor') return <WriteMonitor vaultName={vaultName} rows={feed} meter={meter} />\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>noy-db inspector — {vaultName} <Text dimColor>(↑/↓ · ↵ drill · ⇥ tab · q quit)</Text></Text>\n <Box marginTop={1}>\n <VaultList vaults={vaults} activeName={vaultName} />\n <CollectionList snapshot={snapshot ?? { vault: vaultName, collections: [] }} selectedIdx={selectedIdx} />\n {drilled && tab === 'records' && current\n ? <RecordsPane collection={current} page={page} {...(recErr !== undefined ? { error: recErr } : {})} />\n : <DetailPane collection={drilled ? current : undefined} />}\n </Box>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { VaultInfo } from '@noy-db/in-devtools'\n\nexport function VaultList({ vaults, activeName }: { vaults: ReadonlyArray<VaultInfo>; activeName: string }) {\n return (\n <Box flexDirection=\"column\" marginRight={2}>\n <Text bold underline>Vaults</Text>\n {vaults.map((v) => (\n v.id === activeName\n ? <Text key={v.id} color=\"green\">{'› '}{v.id}</Text>\n : <Text key={v.id}>{' '}{v.id}</Text>\n ))}\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { InspectorSnapshot } from '@noy-db/in-devtools'\n\nexport function CollectionList({ snapshot, selectedIdx }: { snapshot: InspectorSnapshot; selectedIdx: number }) {\n return (\n <Box flexDirection=\"column\" marginRight={2}>\n <Text bold underline>Collections</Text>\n {snapshot.collections.map((c, i) => (\n i === selectedIdx\n ? <Text key={c.name} color=\"cyan\" inverse>{c.name}</Text>\n : <Text key={c.name}>{c.name}</Text>\n ))}\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { InspectorCollection } from '@noy-db/in-devtools'\n\nexport function DetailPane({ collection }: { collection: InspectorCollection | undefined }) {\n if (!collection) return (\n <Box flexDirection=\"column\"><Text dimColor>Select a collection (↵)</Text></Box>\n )\n const fieldNames = Object.keys(collection.fields)\n return (\n <Box flexDirection=\"column\">\n <Text bold underline>{collection.name}</Text>\n <Text>records: {collection.stats?.records ?? '—'} bytes: {collection.stats?.bytes ?? '—'}</Text>\n <Text>fields: {fieldNames.length ? fieldNames.join(', ') : '(none)'}</Text>\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { RecordPage, InspectorCollection } from '@noy-db/in-devtools'\n\nfunction cell(value: unknown): string {\n if (value === null || value === undefined) return '·'\n if (typeof value === 'object') return Array.isArray(value) ? `[${(value as unknown[]).length}]` : '{…}'\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value)\n return '?'\n}\n\nexport function RecordsPane({ collection, page, error }: {\n collection: InspectorCollection\n page: RecordPage | null\n error?: string\n}) {\n const schemaFields = Object.keys(collection.fields)\n if (error) return <Box flexDirection=\"column\"><Text color=\"red\">records error: {error}</Text><Text dimColor>n/p retry · ⇥ back</Text></Box>\n if (!page) return <Box flexDirection=\"column\"><Text dimColor>loading records…</Text></Box>\n // Fall back to first-row keys when schema has no declared fields\n const firstRowKeys = page.rows.length > 0 && typeof page.rows[0] === 'object' && page.rows[0] !== null\n ? Object.keys(page.rows[0] as Record<string, unknown>)\n : []\n const fields = schemaFields.length > 0 ? schemaFields : firstRowKeys\n const from = page.total === 0 ? 0 : page.offset + 1\n const to = Math.min(page.offset + page.limit, page.total)\n return (\n <Box flexDirection=\"column\">\n <Text bold>rows {from}–{to} of {page.total} <Text dimColor>(n/p page · ⇥ back)</Text></Text>\n <Text dimColor>{fields.join(' ')}</Text>\n {page.rows.map((row, i) => (\n <Text key={i}>{fields.map((f) => cell((row as Record<string, unknown>)?.[f])).join(' ')}</Text>\n ))}\n </Box>\n )\n}\n","import React from 'react'\nimport { Box, Text } from 'ink'\nimport type { FeedRow } from '../types.js'\nimport type { MeterSnapshot } from '@noy-db/to-meter'\n\nexport function WriteMonitor({ vaultName, rows, meter }: { vaultName: string; rows: ReadonlyArray<FeedRow>; meter: MeterSnapshot | null }) {\n const m = meter?.byMethod\n return (\n <Box flexDirection=\"column\">\n <Text bold>Write Monitor — {vaultName} <Text dimColor>(w/esc · c clear · q quit)</Text></Text>\n {meter && m && (\n <Text>\n store put p50 {m.put?.p50 ?? '—'}ms p99 {m.put?.p99 ?? '—'}ms{meter.status === 'degraded' ? ' ⚠degraded' : ''} · del p50 {m.delete?.p50 ?? '—'} p99 {m.delete?.p99 ?? '—'} · get p50 {m.get?.p50 ?? '—'} p99 {m.get?.p99 ?? '—'}\n </Text>\n )}\n <Text dimColor>time user op collection/docId v</Text>\n {rows.length === 0 && <Text dimColor>(waiting for writes…)</Text>}\n {rows.map((r, i) => (\n r.conflict\n ? <Text key={i} color=\"yellow\">{r.time} {r.user.padEnd(6)} {r.op} {r.target.padEnd(18)} {r.versions} ⚠ CONFLICT</Text>\n : <Text key={i}>{r.time} {r.user.padEnd(6)} {r.op} {r.target.padEnd(18)} {r.versions}</Text>\n ))}\n </Box>\n )\n}\n","import { resolve as resolvePath } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\n/**\n * Load a NoydbOptions object from a `.js`/`.mjs` config (default export, or a\n * factory function). Mirrors @noy-db/cli's loader; reimplemented here to avoid\n * coupling the TUI bin to the whole cli package. `.ts` is unsupported (Node has\n * no native loader) — compile first or use a `.mjs` config.\n */\nexport async function loadOptionsFromFile(filePath: string): Promise<unknown> {\n const abs = resolvePath(filePath)\n if (/\\.[mc]?ts$/.test(abs)) {\n throw new Error(\n `TypeScript config files are not directly loadable. Compile it first, or use a .mjs/.js config.`,\n )\n }\n const mod = (await import(pathToFileURL(abs).href)) as { default?: unknown }\n const value = mod.default ?? mod\n return typeof value === 'function' ? await (value as () => Promise<unknown>)() : value\n}\n\n/** Resolve the passphrase from `--passphrase=…` or `NOYDB_PASSPHRASE`; undefined → caller prompts. */\nexport function resolvePassphrase(argv: readonly string[], env: NodeJS.ProcessEnv): string | undefined {\n const flag = argv.find((a) => a.startsWith('--passphrase='))\n if (flag) return flag.slice('--passphrase='.length)\n if (env.NOYDB_PASSPHRASE) return env.NOYDB_PASSPHRASE\n return undefined\n}\n","import type { Readable, Writable } from 'node:stream'\n\nexport interface PromptStreams {\n input?: Readable & { setRawMode?: (mode: boolean) => void }\n output?: Writable\n}\n\n/**\n * Read a line from `input` without echoing the plaintext; emit one `•` per\n * character to `output`. Returns the typed string (Enter terminates).\n * Never logs/persists the value.\n */\nexport function promptMasked(question: string, streams: PromptStreams = {}): Promise<string> {\n const input = (streams.input ?? process.stdin) as Readable & { setRawMode?: (m: boolean) => void }\n const output = streams.output ?? process.stdout\n return new Promise<string>((resolve, reject) => {\n output.write(question)\n input.setRawMode?.(true)\n let buf = ''\n const onData = (chunk: Buffer | string) => {\n const s = chunk.toString()\n for (const ch of s) {\n if (ch === '\\r' || ch === '\\n') {\n output.write('\\n')\n input.setRawMode?.(false)\n input.removeListener('data', onData)\n resolve(buf)\n return\n }\n if (ch === '\\x03') { // Ctrl-C — abort\n output.write('\\n')\n input.setRawMode?.(false)\n input.removeListener('data', onData)\n reject(new Error('aborted'))\n return\n }\n if (ch === '\\x7f' || ch === '\\x08') { // DEL / Backspace\n if (buf.length > 0) { buf = buf.slice(0, -1); output.write('\\b \\b') }\n continue\n }\n buf += ch\n output.write('•')\n }\n }\n input.on('data', onData)\n })\n}\n"],"mappings":";;;AACA,SAAS,cAAc;AACvB,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAE5B,SAAS,eAAe;;;ACLxB,OAAO,SAAS,UAAU,iBAAiB;AAC3C,SAAS,OAAAA,MAAK,QAAAC,OAAM,QAAQ,gBAAgB;;;ACA5C,SAAS,KAAK,YAAY;AAMpB,cAGM,YAHN;AAHC,SAAS,UAAU,EAAE,QAAQ,WAAW,GAA6D;AAC1G,SACE,qBAAC,OAAI,eAAc,UAAS,aAAa,GACvC;AAAA,wBAAC,QAAK,MAAI,MAAC,WAAS,MAAC,oBAAM;AAAA,IAC1B,OAAO,IAAI,CAAC,MACX,EAAE,OAAO,aACL,qBAAC,QAAgB,OAAM,SAAS;AAAA;AAAA,MAAM,EAAE;AAAA,SAA7B,EAAE,EAA8B,IAC3C,qBAAC,QAAiB;AAAA;AAAA,MAAM,EAAE;AAAA,SAAf,EAAE,EAAgB,CAClC;AAAA,KACH;AAEJ;;;ACdA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAKtB,SACE,OAAAC,MADF,QAAAC,aAAA;AAFG,SAAS,eAAe,EAAE,UAAU,YAAY,GAAyD;AAC9G,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,aAAa,GACvC;AAAA,oBAAAE,KAACD,OAAA,EAAK,MAAI,MAAC,WAAS,MAAC,yBAAW;AAAA,IAC/B,SAAS,YAAY,IAAI,CAAC,GAAG,MAC5B,MAAM,cACF,gBAAAC,KAACD,OAAA,EAAkB,OAAM,QAAO,SAAO,MAAE,YAAE,QAAhC,EAAE,IAAmC,IAChD,gBAAAC,KAACD,OAAA,EAAmB,YAAE,QAAX,EAAE,IAAc,CAChC;AAAA,KACH;AAEJ;;;ACdA,SAAS,OAAAG,MAAK,QAAAC,aAAY;AAKM,gBAAAC,MAM1B,QAAAC,aAN0B;AAFzB,SAAS,WAAW,EAAE,WAAW,GAAoD;AAC1F,MAAI,CAAC,WAAY,QACf,gBAAAD,KAACF,MAAA,EAAI,eAAc,UAAS,0BAAAE,KAACD,OAAA,EAAK,UAAQ,MAAC,0CAAuB,GAAO;AAE3E,QAAM,aAAa,OAAO,KAAK,WAAW,MAAM;AAChD,SACE,gBAAAE,MAACH,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAE,KAACD,OAAA,EAAK,MAAI,MAAC,WAAS,MAAE,qBAAW,MAAK;AAAA,IACtC,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MAAU,WAAW,OAAO,WAAW;AAAA,MAAI;AAAA,MAAU,WAAW,OAAO,SAAS;AAAA,OAAI;AAAA,IAC1F,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MAAS,WAAW,SAAS,WAAW,KAAK,IAAI,IAAI;AAAA,OAAS;AAAA,KACtE;AAEJ;;;ACfA,SAAS,OAAAG,MAAK,QAAAC,aAAY;AAiBsB,SAA+C,OAAAC,MAA/C,QAAAC,aAAA;AAdhD,SAAS,KAAK,OAAwB;AACpC,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,QAAQ,KAAK,IAAI,IAAK,MAAoB,MAAM,MAAM;AAClG,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,aAAa,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK;AAC7G,SAAO;AACT;AAEO,SAAS,YAAY,EAAE,YAAY,MAAM,MAAM,GAInD;AACD,QAAM,eAAe,OAAO,KAAK,WAAW,MAAM;AAClD,MAAI,MAAO,QAAO,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS;AAAA,oBAAAG,MAACF,OAAA,EAAK,OAAM,OAAM;AAAA;AAAA,MAAgB;AAAA,OAAM;AAAA,IAAO,gBAAAC,KAACD,OAAA,EAAK,UAAQ,MAAC,wCAAkB;AAAA,KAAO;AACrI,MAAI,CAAC,KAAM,QAAO,gBAAAC,KAACF,MAAA,EAAI,eAAc,UAAS,0BAAAE,KAACD,OAAA,EAAK,UAAQ,MAAC,mCAAgB,GAAO;AAEpF,QAAM,eAAe,KAAK,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK,CAAC,MAAM,YAAY,KAAK,KAAK,CAAC,MAAM,OAC9F,OAAO,KAAK,KAAK,KAAK,CAAC,CAA4B,IACnD,CAAC;AACL,QAAM,SAAS,aAAa,SAAS,IAAI,eAAe;AACxD,QAAM,OAAO,KAAK,UAAU,IAAI,IAAI,KAAK,SAAS;AAClD,QAAM,KAAK,KAAK,IAAI,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK;AACxD,SACE,gBAAAE,MAACH,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAG,MAACF,OAAA,EAAK,MAAI,MAAC;AAAA;AAAA,MAAM;AAAA,MAAK;AAAA,MAAE;AAAA,MAAG;AAAA,MAAK,KAAK;AAAA,MAAM;AAAA,MAAC,gBAAAC,KAACD,OAAA,EAAK,UAAQ,MAAC,yCAAmB;AAAA,OAAO;AAAA,IACrF,gBAAAC,KAACD,OAAA,EAAK,UAAQ,MAAE,iBAAO,KAAK,IAAI,GAAE;AAAA,IACjC,KAAK,KAAK,IAAI,CAAC,KAAK,MACnB,gBAAAC,KAACD,OAAA,EAAc,iBAAO,IAAI,CAAC,MAAM,KAAM,MAAkC,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,KAA5E,CAA8E,CAC1F;AAAA,KACH;AAEJ;;;ACnCA,SAAS,OAAAG,MAAK,QAAAC,aAAY;AAQpB,SAAuC,OAAAC,MAAvC,QAAAC,aAAA;AAJC,SAAS,aAAa,EAAE,WAAW,MAAM,MAAM,GAAqF;AACzI,QAAM,IAAI,OAAO;AACjB,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAG,MAACF,OAAA,EAAK,MAAI,MAAC;AAAA;AAAA,MAAiB;AAAA,MAAU;AAAA,MAAC,gBAAAC,KAACD,OAAA,EAAK,UAAQ,MAAC,8CAA0B;AAAA,OAAO;AAAA,IACtF,SAAS,KACR,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MACY,EAAE,KAAK,OAAO;AAAA,MAAI;AAAA,MAAQ,EAAE,KAAK,OAAO;AAAA,MAAI;AAAA,MAAG,MAAM,WAAW,aAAa,oBAAe;AAAA,MAAG;AAAA,MAAY,EAAE,QAAQ,OAAO;AAAA,MAAI;AAAA,MAAM,EAAE,QAAQ,OAAO;AAAA,MAAI;AAAA,MAAY,EAAE,KAAK,OAAO;AAAA,MAAI;AAAA,MAAM,EAAE,KAAK,OAAO;AAAA,OAC/N;AAAA,IAEF,gBAAAC,KAACD,OAAA,EAAK,UAAQ,MAAC,0DAA4C;AAAA,IAC1D,KAAK,WAAW,KAAK,gBAAAC,KAACD,OAAA,EAAK,UAAQ,MAAC,wCAAqB;AAAA,IACzD,KAAK,IAAI,CAAC,GAAG,MACZ,EAAE,WACE,gBAAAE,MAACF,OAAA,EAAa,OAAM,UAAU;AAAA,QAAE;AAAA,MAAK;AAAA,MAAG,EAAE,KAAK,OAAO,CAAC;AAAA,MAAE;AAAA,MAAG,EAAE;AAAA,MAAG;AAAA,MAAG,EAAE,OAAO,OAAO,EAAE;AAAA,MAAE;AAAA,MAAE,EAAE;AAAA,MAAS;AAAA,SAA1F,CAAsG,IACjH,gBAAAE,MAACF,OAAA,EAAc;AAAA,QAAE;AAAA,MAAK;AAAA,MAAG,EAAE,KAAK,OAAO,CAAC;AAAA,MAAE;AAAA,MAAG,EAAE;AAAA,MAAG;AAAA,MAAG,EAAE,OAAO,OAAO,EAAE;AAAA,MAAE;AAAA,MAAE,EAAE;AAAA,SAAlE,CAA2E,CAC3F;AAAA,KACH;AAEJ;;;AL6EiC,gBAAAG,MAI3B,QAAAC,aAJ2B;AAzFjC,IAAM,OAAO;AACb,IAAM,SAAS;AAEf,SAAS,QAAQ,IAAoB;AAAE,SAAO,IAAI,KAAK,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,CAAC;AAAE;AAEtF,SAAS,MAAM,GAAiC;AAC9C,QAAM,KAAK,EAAE,OAAO,WAAW,QAAQ;AACvC,QAAM,WAAW,EAAE,OAAO,WAAW,GAAG,EAAE,WAAW,eAAO,GAAG,EAAE,WAAW,SAAI,EAAE,OAAO;AACzF,SAAO,EAAE,MAAM,QAAQ,EAAE,SAAS,GAAG,MAAM,EAAE,QAAQ,IAAI,QAAQ,GAAG,EAAE,UAAU,IAAI,EAAE,KAAK,IAAI,UAAU,SAAS,GAAG,EAAE,UAAU,IAAI,EAAE,KAAK,IAAI,EAAE,WAAW,IAAI,UAAU,MAAM;AACnL;AAEO,SAAS,IAAI,EAAE,WAAW,OAAO,WAAW,QAAQ,GAAa;AACtE,QAAM,EAAE,KAAK,IAAI,OAAO;AACxB,QAAM,SAAS,SAAS,UAAU,CAAC;AACnC,QAAM,WAAW,SAAS;AAC1B,QAAM,cAAc,UAAU,eAAe,CAAC;AAC9C,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,CAAC;AAChD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,CAAC,KAAK,MAAM,IAAI,SAAoB,QAAQ;AAClD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,CAAC;AACtC,QAAM,CAAC,MAAM,OAAO,IAAI,SAA4B,IAAI;AACxD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA6B,MAAS;AAClE,QAAM,UAAU,YAAY,WAAW;AAEvC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAe,WAAW;AAClD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAiC,CAAC,CAAC;AAC3D,QAAM,eAAe,MAAM,OAAoB,oBAAI,IAAI,CAAC;AACxD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA+B,IAAI;AAC7D,YAAU,MAAM;AACd,QAAI,SAAS,UAAW;AACxB,UAAM,OAAO,MAAM,SAAS,UAAU,cAAc,CAAC;AACrD,SAAK;AACL,UAAM,KAAK,YAAY,MAAM,GAAI;AACjC,WAAO,MAAM,cAAc,EAAE;AAAA,EAC/B,GAAG,CAAC,MAAM,SAAS,CAAC;AAEpB,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,QAAQ,aAAa,CAAC,QAAS;AAC/C,QAAI,OAAO;AACX,YAAQ,IAAI;AAAG,cAAU,MAAS;AAClC,cAAU,QAAQ,OAAO,QAAQ,MAAM,EAAE,OAAO,MAAM,OAAO,CAAC,EAC3D,KAAK,CAAC,MAAM;AAAE,UAAI,KAAM,SAAQ,CAAC;AAAA,IAAE,CAAC,EACpC,MAAM,CAAC,MAAM;AAAE,UAAI,KAAM,WAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,IAAE,CAAC;AACnF,WAAO,MAAM;AAAE,aAAO;AAAA,IAAM;AAAA,EAC9B,GAAG,CAAC,SAAS,KAAK,SAAS,QAAQ,WAAW,KAAK,CAAC;AAEpD,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,UAAU,UAAU,CAAC,MAAM;AACtC,cAAQ,CAAC,SAAS;AAChB,cAAM,MAAM,MAAM,CAAC;AACnB,YAAI,aAAa,QAAQ,IAAI,IAAI,OAAO,EAAG,KAAI,WAAW;AAC1D,cAAM,UAAU,KAAK,KAAK,CAAC,MAAM,EAAE,YAAY,IAAI,WAAW,EAAE,SAAS,IAAI,IAAI;AACjF,YAAI,QAAS,KAAI,WAAW;AAC5B,cAAM,OAAO,CAAC,KAAK,GAAG,KAAK,IAAI,CAAC,MAAO,EAAE,YAAY,IAAI,WAAW,EAAE,SAAS,IAAI,OAAO,EAAE,GAAG,GAAG,UAAU,KAAK,IAAI,CAAE,CAAC;AACxH,eAAO,KAAK,MAAM,GAAG,MAAM;AAAA,MAC7B,CAAC;AAAA,IACH,CAAC;AACD,UAAM,OAAO,UAAU,mBAAmB,CAAC,MAAM;AAC/C,YAAM,MAAM,GAAG,EAAE,UAAU,IAAI,EAAE,KAAK,IAAI,EAAE,WAAW;AACvD,mBAAa,QAAQ,IAAI,GAAG;AAC5B,cAAQ,CAAC,SAAS,KAAK,IAAI,CAAC,MAAO,EAAE,YAAY,MAAM,EAAE,GAAG,GAAG,UAAU,KAAK,IAAI,CAAE,CAAC;AAAA,IACvF,CAAC;AACD,WAAO,MAAM;AAAE,WAAK;AAAG,WAAK;AAAA,IAAE;AAAA,EAChC,GAAG,CAAC,SAAS,SAAS,CAAC;AAEvB,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,UAAU,OAAQ,IAAI,QAAQ,UAAU,KAAM;AAAE,WAAK;AAAG;AAAA,IAAO;AACnE,QAAI,SAAS,WAAW;AACtB,UAAI,IAAI,OAAQ,SAAQ,WAAW;AACnC,UAAI,UAAU,IAAK,SAAQ,CAAC,CAAC;AAC7B;AAAA,IACF;AACA,QAAI,UAAU,KAAK;AAAE,cAAQ,SAAS;AAAG,iBAAW,IAAI;AAAG;AAAA,IAAO;AAClE,QAAI,CAAC,SAAS;AACZ,UAAI,IAAI,UAAW,gBAAe,CAAC,MAAM,KAAK,IAAI,YAAY,SAAS,GAAG,IAAI,CAAC,CAAC;AAChF,UAAI,IAAI,QAAS,gBAAe,CAAC,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;AACzD,UAAI,IAAI,QAAQ;AAAE,mBAAW,IAAI;AAAG,eAAO,QAAQ;AAAA,MAAE;AACrD;AAAA,IACF;AACA,QAAI,IAAI,QAAQ;AAAE,iBAAW,KAAK;AAAG,aAAO,QAAQ;AAAG,gBAAU,CAAC;AAAA,IAAE;AACpE,QAAI,IAAI,KAAK;AAAE,aAAO,CAAC,MAAO,MAAM,WAAW,YAAY,QAAS;AAAG,gBAAU,CAAC;AAAA,IAAE;AACpF,QAAI,QAAQ,WAAW;AACrB,UAAI,UAAU,QAAQ,CAAC,QAAQ,SAAS,OAAO,KAAK,OAAQ,WAAU,CAAC,MAAM,IAAI,IAAI;AACrF,UAAI,UAAU,OAAO,SAAS,QAAQ,EAAG,WAAU,CAAC,MAAM,IAAI,IAAI;AAAA,IACpE;AAAA,EACF,CAAC;AAED,MAAI,SAAS,UAAW,QAAO,gBAAAD,KAAC,gBAAa,WAAsB,MAAM,MAAM,OAAc;AAE7F,SACE,gBAAAC,MAACC,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAD,MAACE,OAAA,EAAK,MAAI,MAAC;AAAA;AAAA,MAAoB;AAAA,MAAU;AAAA,MAAC,gBAAAH,KAACG,OAAA,EAAK,UAAQ,MAAC,2EAAgC;AAAA,OAAO;AAAA,IAChG,gBAAAF,MAACC,MAAA,EAAI,WAAW,GACd;AAAA,sBAAAF,KAAC,aAAU,QAAgB,YAAY,WAAW;AAAA,MAClD,gBAAAA,KAAC,kBAAe,UAAU,YAAY,EAAE,OAAO,WAAW,aAAa,CAAC,EAAE,GAAG,aAA0B;AAAA,MACtG,WAAW,QAAQ,aAAa,UAC7B,gBAAAA,KAAC,eAAY,YAAY,SAAS,MAAa,GAAI,WAAW,SAAY,EAAE,OAAO,OAAO,IAAI,CAAC,GAAI,IACnG,gBAAAA,KAAC,cAAW,YAAY,UAAU,UAAU,QAAW;AAAA,OAC7D;AAAA,KACF;AAEJ;;;AMnHA,SAAS,WAAW,mBAAmB;AACvC,SAAS,qBAAqB;AAQ9B,eAAsB,oBAAoB,UAAoC;AAC5E,QAAM,MAAM,YAAY,QAAQ;AAChC,MAAI,aAAa,KAAK,GAAG,GAAG;AAC1B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,MAAO,MAAM,OAAO,cAAc,GAAG,EAAE;AAC7C,QAAM,QAAQ,IAAI,WAAW;AAC7B,SAAO,OAAO,UAAU,aAAa,MAAO,MAAiC,IAAI;AACnF;AAGO,SAAS,kBAAkB,MAAyB,KAA4C;AACrG,QAAM,OAAO,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,eAAe,CAAC;AAC3D,MAAI,KAAM,QAAO,KAAK,MAAM,gBAAgB,MAAM;AAClD,MAAI,IAAI,iBAAkB,QAAO,IAAI;AACrC,SAAO;AACT;;;ACfO,SAAS,aAAa,UAAkB,UAAyB,CAAC,GAAoB;AAC3F,QAAM,QAAS,QAAQ,SAAS,QAAQ;AACxC,QAAM,SAAS,QAAQ,UAAU,QAAQ;AACzC,SAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,WAAO,MAAM,QAAQ;AACrB,UAAM,aAAa,IAAI;AACvB,QAAI,MAAM;AACV,UAAM,SAAS,CAAC,UAA2B;AACzC,YAAM,IAAI,MAAM,SAAS;AACzB,iBAAW,MAAM,GAAG;AAClB,YAAI,OAAO,QAAQ,OAAO,MAAM;AAC9B,iBAAO,MAAM,IAAI;AACjB,gBAAM,aAAa,KAAK;AACxB,gBAAM,eAAe,QAAQ,MAAM;AACnC,kBAAQ,GAAG;AACX;AAAA,QACF;AACA,YAAI,OAAO,KAAQ;AACjB,iBAAO,MAAM,IAAI;AACjB,gBAAM,aAAa,KAAK;AACxB,gBAAM,eAAe,QAAQ,MAAM;AACnC,iBAAO,IAAI,MAAM,SAAS,CAAC;AAC3B;AAAA,QACF;AACA,YAAI,OAAO,UAAU,OAAO,MAAQ;AAClC,cAAI,IAAI,SAAS,GAAG;AAAE,kBAAM,IAAI,MAAM,GAAG,EAAE;AAAG,mBAAO,MAAM,OAAO;AAAA,UAAE;AACpE;AAAA,QACF;AACA,eAAO;AACP,eAAO,MAAM,QAAG;AAAA,MAClB;AAAA,IACF;AACA,UAAM,GAAG,QAAQ,MAAM;AAAA,EACzB,CAAC;AACH;;;ARWI,gBAAAI,YAAA;AA/CJ,SAAS,KAAK,KAAoB;AAChC,UAAQ,OAAO,MAAM,kBAAkB,GAAG;AAAA,CAAI;AAC9C,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,OAAsB;AACnC,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,QAAM,aAAa,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,IAAI,CAAC;AACvD,MAAI,CAAC,WAAY,MAAK,2EAAsE;AAC5F,QAAM,YAAY,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,UAAU,CAAC;AAC3D,MAAI,CAAC,UAAW,MAAK,wBAAwB;AAC7C,QAAM,YAAY,UAAU,MAAM,WAAW,MAAM;AAEnD,MAAI,aAAa,kBAAkB,MAAM,QAAQ,GAAG;AACpD,MAAI,eAAe,QAAW;AAC5B,QAAI,CAAC,QAAQ,MAAM,MAAO,MAAK,uEAA6D;AAC5F,iBAAa,MAAM,aAAa,cAAc;AAAA,EAChD;AAEA,MAAI;AACJ,MAAI;AACF,kBAAe,MAAM,oBAAoB,UAAU;AAAA,EACrD,SAAS,KAAK;AACZ,SAAK,0BAA0B,UAAU,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,EACnG;AAEA,MAAI;AACJ,MAAI,KAAK,SAAS,SAAS,KAAK,YAAY,OAAO;AACjD,UAAM,UAAU,QAAQ,YAAY,KAAK;AACzC,kBAAc,EAAE,GAAG,aAAa,OAAO,QAAQ,MAAM;AACrD,YAAQ,QAAQ;AAAA,EAClB;AAGA,QAAM,UAAwB,EAAE,GAAG,aAAa,QAAQ,WAAW;AACnE,QAAM,KAAK,MAAM,YAAY,OAAO;AAEpC,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,GAAG,UAAU,SAAS;AAAA,EACtC,SAAS,KAAK;AACZ,SAAK,yBAAyB,SAAS,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,EACjG;AAEA,QAAM,YAAY,gBAAgB,IAAI,QAAQ,EAAE,MAAM,IAAI,MAAS;AACnE,QAAM,UAAU,EAAE,QAAQ,MAAM,UAAU,WAAW,GAAG,UAAU,MAAM,UAAU,SAAS,KAAK,EAAE;AAClG,QAAM,EAAE,cAAc,IAAI;AAAA,IACxB,gBAAAA,KAAC,OAAI,WAAsB,OAAc,WAAsB,SAAkB;AAAA,EACnF;AACA,QAAM,cAAc;AACtB;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,CAAC;","names":["Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","jsx","jsxs","Box","Text","jsx"]}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@noy-db/in-devtools-tui",
3
+ "version": "0.2.0-pre.10",
4
+ "description": "Interactive terminal inspector for a live noy-db (ink TUI over @noy-db/in-devtools).",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "noydb-inspect": "./dist/bin.js"
9
+ },
10
+ "main": "./dist/bin.js",
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "dependencies": {
15
+ "ink": "^5.0.1",
16
+ "react": "^18.3.1",
17
+ "@noy-db/to-meter": "0.2.0-pre.10"
18
+ },
19
+ "peerDependencies": {
20
+ "@noy-db/in-devtools": "0.2.0-pre.10",
21
+ "@noy-db/hub": "0.2.0-pre.10"
22
+ },
23
+ "devDependencies": {
24
+ "ink-testing-library": "^4.0.0",
25
+ "@types/node": "^22.0.0",
26
+ "@types/react": "^18.3.12",
27
+ "@noy-db/hub": "0.2.0-pre.10",
28
+ "@noy-db/in-devtools": "0.2.0-pre.10"
29
+ },
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "test": "vitest run",
33
+ "lint": "eslint src/",
34
+ "typecheck": "tsc --noEmit"
35
+ }
36
+ }