@thingd/cli 0.53.0 → 0.53.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AA0mEA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqEvD"}
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AAolEA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA0FvD"}
@@ -92,6 +92,7 @@ let viewerScroll = 0;
92
92
  let loadedItemId = "";
93
93
  let loadTimer = null;
94
94
  let pollTimer = null;
95
+ let polling = false;
95
96
  let keypressHandler = null;
96
97
  let formState = null;
97
98
  function openForm(title, fields, onSubmit, keepViewer) {
@@ -198,75 +199,47 @@ function formatUptime(ms) {
198
199
  }
199
200
  async function fetchResourcesFallback() {
200
201
  cloudError = null;
202
+ // Collections and streams — parallel fetch
203
+ let nativeCollections;
204
+ let nativeStreams;
201
205
  try {
202
- const nativeCollections = await db.listCollections();
203
- const nativeStreams = await db.listStreams();
204
- // Maintain default collections/streams for UI if none exist
205
- const defaultCols = new Set();
206
- const defaultStrs = new Set();
207
- for (const c of nativeCollections) {
208
- defaultCols.add(c);
209
- }
210
- for (const s of nativeStreams) {
211
- defaultStrs.add(s);
212
- }
213
- collections = Array.from(defaultCols).sort();
214
- streams = Array.from(defaultStrs).sort();
206
+ [nativeCollections, nativeStreams] = await Promise.all([
207
+ db.listCollections(),
208
+ db.listStreams(),
209
+ ]);
215
210
  }
216
211
  catch (err) {
217
212
  const msg = err instanceof Error ? err.message : String(err);
218
213
  cloudError = `Failed to load resources: ${msg}`;
219
- collections = [];
220
- streams = [];
214
+ nativeCollections = [];
215
+ nativeStreams = [];
221
216
  if (viewerLines.length === 1 && viewerLines[0] === "Select an item to view details.") {
222
217
  viewerLines = [pc.yellow(cloudError), pc.dim("Press 'r' to retry or 's' to switch driver.")];
223
218
  }
224
219
  }
225
- // Counts independent try/catch so one failure doesn't clear collections
226
- try {
227
- totalObjects = await db.countObjects();
228
- }
229
- catch {
230
- totalObjects = 0;
231
- }
232
- try {
233
- totalEventsCount = await db.countEvents();
234
- }
235
- catch {
236
- totalEventsCount = 0;
237
- }
238
- try {
239
- totalActiveJobsCount = await db.countActiveJobs();
240
- }
241
- catch {
242
- totalActiveJobsCount = 0;
243
- }
244
- try {
245
- totalDeadJobsCount = await db.countDeadJobs();
246
- }
247
- catch {
248
- totalDeadJobsCount = 0;
249
- }
250
- // Queues
251
- try {
252
- const listed = await db.listQueues();
253
- queues = (listed ?? []).sort();
254
- }
255
- catch (err) {
256
- const msg = err instanceof Error ? err.message : String(err);
257
- cloudError = cloudError ?? `Failed to load queues: ${msg}`;
258
- queues = [];
259
- }
260
- // Link count
261
- try {
262
- totalLinksCount = await db.countLinks();
263
- }
264
- catch {
265
- totalLinksCount = 0;
266
- }
267
- // Populate objectsByCollection from live data
220
+ collections = [...new Set(nativeCollections)].sort();
221
+ streams = [...new Set(nativeStreams)].sort();
222
+ // Counts, queues, links — all parallel, each with independent error handling
223
+ const [objCount, evtCount, activeCount, deadCount, listedQueues, linkCount] = await Promise.all([
224
+ db.countObjects().catch(() => 0),
225
+ db.countEvents().catch(() => 0),
226
+ db.countActiveJobs().catch(() => 0),
227
+ db.countDeadJobs().catch(() => 0),
228
+ db.listQueues().catch(() => {
229
+ cloudError = cloudError ?? "Failed to load queues";
230
+ return [];
231
+ }),
232
+ db.countLinks().catch(() => 0),
233
+ ]);
234
+ totalObjects = objCount;
235
+ totalEventsCount = evtCount;
236
+ totalActiveJobsCount = activeCount;
237
+ totalDeadJobsCount = deadCount;
238
+ queues = [...new Set(listedQueues ?? [])].sort();
239
+ totalLinksCount = linkCount;
240
+ // Objects per collection — parallel
268
241
  objectsByCollection.clear();
269
- for (const col of collections) {
242
+ await Promise.all(collections.map(async (col) => {
270
243
  try {
271
244
  const list = await db.listObjects(col);
272
245
  objectsByCollection.set(col, list.map((o) => o.id));
@@ -274,7 +247,7 @@ async function fetchResourcesFallback() {
274
247
  catch {
275
248
  objectsByCollection.set(col, []);
276
249
  }
277
- }
250
+ }));
278
251
  }
279
252
  async function fetchResources() {
280
253
  if (driver === "native" && dbPath) {
@@ -327,9 +300,8 @@ async function fetchResources() {
327
300
  const prevEvents = eventsHistory.length > 0
328
301
  ? (eventsHistory[eventsHistory.length - 1] ?? totalEventsCount)
329
302
  : totalEventsCount;
330
- // Polling is every 2000ms. Operations per second = delta / 2
331
- const objectWriteRate = Math.max(0, Math.round((totalObjects - prevObjects) / 2));
332
- const eventAppendRate = Math.max(0, Math.round((totalEventsCount - prevEvents) / 2));
303
+ const objectWriteRate = Math.max(0, Math.round((totalObjects - prevObjects) / 10));
304
+ const eventAppendRate = Math.max(0, Math.round((totalEventsCount - prevEvents) / 10));
333
305
  // Push Histories with Initial Pre-population to prevent misleading growth wiggles
334
306
  if (objectsHistory.length === 0) {
335
307
  objectsHistory = new Array(60).fill(totalObjects);
@@ -1986,21 +1958,42 @@ export async function runInteractiveCli() {
1986
1958
  setupKeypress();
1987
1959
  // Background polling loop for real-time updates
1988
1960
  pollTimer = setInterval(async () => {
1989
- if (connected && !formState?.active) {
1990
- try {
1991
- const snapItemId = loadedItemId;
1992
- await fetchResources();
1993
- draw();
1994
- const tree = buildTree();
1995
- const n = tree[cursorIndex];
1996
- // Silently reload content if the same node is still actively viewed
1997
- if (n && snapItemId === n.id && n.type !== "category") {
1998
- await loadContent(n).catch(() => { });
1999
- }
1961
+ if (!connected || formState?.active || polling) {
1962
+ return;
1963
+ }
1964
+ polling = true;
1965
+ try {
1966
+ const snapItemId = loadedItemId;
1967
+ const snapshot = JSON.stringify([
1968
+ totalObjects,
1969
+ totalEventsCount,
1970
+ totalActiveJobsCount,
1971
+ totalDeadJobsCount,
1972
+ totalLinksCount,
1973
+ ]);
1974
+ await fetchResources();
1975
+ const changed = snapshot !==
1976
+ JSON.stringify([
1977
+ totalObjects,
1978
+ totalEventsCount,
1979
+ totalActiveJobsCount,
1980
+ totalDeadJobsCount,
1981
+ totalLinksCount,
1982
+ ]);
1983
+ const tree = buildTree();
1984
+ const n = tree[cursorIndex];
1985
+ if (n && snapItemId === n.id && n.type !== "category") {
1986
+ await loadContent(n).catch(() => { });
2000
1987
  }
2001
- catch {
2002
- // Prevent unhandled rejection from killing the process
1988
+ if (changed) {
1989
+ draw();
2003
1990
  }
2004
1991
  }
1992
+ catch {
1993
+ // Prevent unhandled rejection from killing the process
1994
+ }
1995
+ finally {
1996
+ polling = false;
1997
+ }
2005
1998
  }, 10_000);
2006
1999
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thingd/cli",
3
- "version": "0.53.0",
3
+ "version": "0.53.1",
4
4
  "description": "CLI, Interactive TUI Dashboard, and MCP server for thingd — a fast object-first data engine for applications and AI agents.",
5
5
  "type": "module",
6
6
  "homepage": "https://engine.thingd.cloud",
@@ -45,7 +45,7 @@
45
45
  "cli-table3": "^0.6.5",
46
46
  "picocolors": "^1.1.1",
47
47
  "zod": "^4.4.3",
48
- "@thingd/sdk": "0.53.0"
48
+ "@thingd/sdk": "0.53.1"
49
49
  },
50
50
  "engines": {
51
51
  "node": ">=24.0.0"