@rsktash/beads-ui 0.1.41 → 0.1.43

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsktash/beads-ui",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/rsktash/beads-ui.git"
@@ -89,24 +89,40 @@ function buildPagination(pagination) {
89
89
  */
90
90
  export async function enrichListItems(items) {
91
91
  const pool = getPool();
92
- if (!pool || items.length === 0) return items;
92
+ if (!pool || items.length === 0) {
93
+ log('enrichListItems skip: pool=%s items=%d', !!pool, items.length);
94
+ return items;
95
+ }
93
96
 
94
- const ids = items.map(i => i.id);
97
+ const ids = items.map(i => String(i.id));
98
+ log('enrichListItems: enriching %d items', ids.length);
95
99
 
96
100
  try {
97
- // Batch: resolve parent IDs from dependencies table
101
+ // 1. Resolve parent IDs — use the `parent` field already present from the
102
+ // LEFT JOIN in query functions, falling back to a dependencies lookup.
98
103
  /** @type {Map<string, string>} */
99
104
  const parentIdMap = new Map();
100
- const [parentDepRows] = await pool.query(
101
- `SELECT issue_id, depends_on_id FROM dependencies
102
- WHERE type = 'parent-child' AND issue_id IN (${ids.map(() => '?').join(',')})`,
103
- ids
104
- );
105
- for (const r of /** @type {any[]} */ (parentDepRows)) {
106
- parentIdMap.set(r.issue_id, r.depends_on_id);
105
+ for (const item of items) {
106
+ const p = item.parent ?? item.parent_id;
107
+ if (p && typeof p === 'string' && p.length > 0) {
108
+ parentIdMap.set(String(item.id), p);
109
+ }
110
+ }
111
+
112
+ // If items didn't carry a `parent` column, query dependencies table
113
+ if (parentIdMap.size === 0 && ids.length > 0) {
114
+ const [parentDepRows] = await pool.query(
115
+ `SELECT issue_id, depends_on_id FROM dependencies
116
+ WHERE type = 'parent-child' AND issue_id IN (${ids.map(() => '?').join(',')})`,
117
+ ids
118
+ );
119
+ for (const r of /** @type {any[]} */ (parentDepRows)) {
120
+ parentIdMap.set(r.issue_id, r.depends_on_id);
121
+ }
107
122
  }
123
+ log('enrichListItems: %d parent mappings found', parentIdMap.size);
108
124
 
109
- // Batch: parent titles
125
+ // 2. Batch parent titles
110
126
  const parentIds = [...new Set(parentIdMap.values())];
111
127
  /** @type {Map<string, string>} */
112
128
  const parentTitles = new Map();
@@ -118,49 +134,56 @@ export async function enrichListItems(items) {
118
134
  for (const r of /** @type {any[]} */ (rows)) parentTitles.set(r.id, r.title);
119
135
  }
120
136
 
121
- // Batch: children counts
122
- const [childRows] = await pool.query(
123
- `SELECT depends_on_id AS pid, COUNT(*) AS total,
124
- SUM(CASE WHEN i.status = 'closed' THEN 1 ELSE 0 END) AS closed
125
- FROM dependencies d JOIN issues i ON i.id = d.issue_id
126
- WHERE d.type = 'parent-child' AND d.depends_on_id IN (${ids.map(() => '?').join(',')})
127
- GROUP BY d.depends_on_id`,
128
- ids
129
- );
137
+ // 3. Batch children counts
130
138
  /** @type {Map<string, { total: number, closed: number }>} */
131
139
  const childCounts = new Map();
132
- for (const r of /** @type {any[]} */ (childRows)) {
133
- childCounts.set(r.pid, { total: Number(r.total), closed: Number(r.closed) });
140
+ if (ids.length > 0) {
141
+ const [childRows] = await pool.query(
142
+ `SELECT depends_on_id AS pid, COUNT(*) AS total,
143
+ SUM(CASE WHEN i.status = 'closed' THEN 1 ELSE 0 END) AS closed
144
+ FROM dependencies d JOIN issues i ON i.id = d.issue_id
145
+ WHERE d.type = 'parent-child' AND d.depends_on_id IN (${ids.map(() => '?').join(',')})
146
+ GROUP BY d.depends_on_id`,
147
+ ids
148
+ );
149
+ for (const r of /** @type {any[]} */ (childRows)) {
150
+ childCounts.set(r.pid, { total: Number(r.total), closed: Number(r.closed) });
151
+ }
134
152
  }
153
+ log('enrichListItems: %d items have children', childCounts.size);
135
154
 
136
- // Batch: blocked by (issues that block each item)
137
- const [blockRows] = await pool.query(
138
- `SELECT d.issue_id, d.depends_on_id, i.title AS blocker_title
139
- FROM dependencies d JOIN issues i ON i.id = d.depends_on_id
140
- WHERE d.type = 'blocks' AND d.issue_id IN (${ids.map(() => '?').join(',')})
141
- AND i.status != 'closed'`,
142
- ids
143
- );
155
+ // 4. Batch blocked-by (open issues that block each item)
144
156
  /** @type {Map<string, Array<{ id: string, title: string }>>} */
145
157
  const blockedBy = new Map();
146
- for (const r of /** @type {any[]} */ (blockRows)) {
147
- if (!blockedBy.has(r.issue_id)) blockedBy.set(r.issue_id, []);
148
- blockedBy.get(r.issue_id).push({ id: r.depends_on_id, title: r.blocker_title });
158
+ if (ids.length > 0) {
159
+ const [blockRows] = await pool.query(
160
+ `SELECT d.issue_id, d.depends_on_id, i.title AS blocker_title
161
+ FROM dependencies d JOIN issues i ON i.id = d.depends_on_id
162
+ WHERE d.type = 'blocks' AND d.issue_id IN (${ids.map(() => '?').join(',')})
163
+ AND i.status != 'closed'`,
164
+ ids
165
+ );
166
+ for (const r of /** @type {any[]} */ (blockRows)) {
167
+ if (!blockedBy.has(r.issue_id)) blockedBy.set(r.issue_id, []);
168
+ blockedBy.get(r.issue_id).push({ id: r.depends_on_id, title: r.blocker_title });
169
+ }
149
170
  }
171
+ log('enrichListItems: %d items have blockers', blockedBy.size);
150
172
 
151
173
  return items.map(item => {
174
+ const id = String(item.id);
152
175
  const enriched = { ...item };
153
- const pid = parentIdMap.get(/** @type {string} */ (enriched.id));
176
+ const pid = parentIdMap.get(id);
154
177
  if (pid) {
155
178
  enriched.parent_id = pid;
156
179
  if (parentTitles.has(pid)) enriched.parent_title = parentTitles.get(pid);
157
180
  }
158
- const cc = childCounts.get(/** @type {string} */ (enriched.id));
181
+ const cc = childCounts.get(id);
159
182
  if (cc) {
160
183
  enriched.total_children = cc.total;
161
184
  enriched.closed_children = cc.closed;
162
185
  }
163
- const bb = blockedBy.get(/** @type {string} */ (enriched.id));
186
+ const bb = blockedBy.get(id);
164
187
  if (bb && bb.length > 0) {
165
188
  enriched.blocked_by = bb;
166
189
  }
package/server/ws.js CHANGED
@@ -9,6 +9,7 @@ import { isRequest, makeError, makeOk } from '../app/protocol.js';
9
9
  import { isAuthEnabled, verifyToken } from './auth.js';
10
10
  import { getGitUserName, runBd, runBdJson } from './bd.js';
11
11
  import { resolveWorkspaceDatabase } from './db.js';
12
+ import { rebindDoltServer } from './dolt-pool.js';
12
13
  import {
13
14
  addComment,
14
15
  addDependency,
@@ -615,14 +616,20 @@ export function attachWsServer(http_server, options = {}) {
615
616
  DB_WATCHER.rebind({ root_dir: resolved_root });
616
617
  }
617
618
 
618
- // Clear existing registry entries and refresh all subscriptions
619
+ // Clear existing registry entries
619
620
  registry.clear();
620
621
 
621
622
  // Broadcast workspace-changed event to all clients
622
623
  broadcast('workspace-changed', CURRENT_WORKSPACE);
623
624
 
624
- // Schedule refresh of all active list subscriptions
625
- scheduleListRefresh();
625
+ // Rebind the Dolt SQL pool THEN refresh subscriptions
626
+ rebindDoltServer(resolved_root).then((pool) => {
627
+ log('Dolt pool rebound for %s: %s', resolved_root, pool ? 'connected' : 'unavailable');
628
+ scheduleListRefresh();
629
+ }).catch((err) => {
630
+ log('rebindDoltServer error: %o', err);
631
+ scheduleListRefresh();
632
+ });
626
633
  }
627
634
 
628
635
  return { changed, workspace: CURRENT_WORKSPACE };