@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 +1 -1
- package/server/dolt-queries.js +59 -36
- package/server/ws.js +10 -3
package/package.json
CHANGED
package/server/dolt-queries.js
CHANGED
|
@@ -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)
|
|
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
|
-
//
|
|
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
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
133
|
-
|
|
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
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
//
|
|
625
|
-
|
|
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 };
|