@h-rig/core 0.0.6-alpha.90 → 0.0.6-alpha.91
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/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +292 -16
- package/dist/src/load-config.js +130 -6
- package/dist/src/plugin-runtime.d.ts +7 -1
- package/dist/src/rigSelectors.d.ts +27 -1
- package/dist/src/rigSelectors.js +125 -4
- package/dist/src/taskGraph.d.ts +39 -26
- package/dist/src/taskGraph.js +164 -8
- package/dist/src/taskGraphLayout.js +22 -3
- package/package.json +3 -15
package/dist/src/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type { PluginHost } from "./plugin-host";
|
|
|
6
6
|
export { buildRigInitConfigSource, type RigInitConfigInput, } from "./rig-init-builder";
|
|
7
7
|
export type { RegisteredValidator, RigPluginRuntime, RigPluginWithRuntime, TaskSourceFactoryContext, TaskSourceFactoryEntry, ValidatorContext, ValidatorResult, } from "./plugin-runtime";
|
|
8
8
|
export { applyEngineEvent, applyEngineEvents, applyEngineEvent as applyRigEvent, pruneQueueEntries, type ApplyStatus, type EngineEventApplyResult, type EngineEventApplyResult as RigEventApplyResult, } from "./engineReadModelReducer";
|
|
9
|
-
export { pickDefaultWorkspaceId, selectAdhocRuns, selectAdhocRunsForWorkspace, selectApprovalsForRun, selectApprovalsForWorkspace, selectGraphsForWorkspace, selectPendingApprovals, selectPrimaryWorkspace, selectQueueForWorkspace, selectRun, selectRunsByTask, selectRunsForTask, selectRunsForWorkspace, selectTask, selectTasksByStatus, selectTasksByWorkspace, selectTasksForWorkspace, selectTasksGroupedByStatus, selectUserInputsForRun, selectUserInputsForWorkspace, selectWorkspace, selectWorkspaces, } from "./rigSelectors";
|
|
10
|
-
export { buildTaskReferenceIndex, computeTaskBlockingDepths, readTaskMetadataStringList, readTaskSourceIssueId, resolveTaskReference, } from "./taskGraph";
|
|
9
|
+
export { pickDefaultWorkspaceId, projectRunStatusForTaskGrouping, projectTaskStatusForGrouping, projectTaskStatusWithSessions, normalizeTaskAssigneeFilter, readTaskAssigneeLogins, selectAdhocRuns, selectAdhocRunsForWorkspace, selectApprovalsForRun, selectApprovalsForWorkspace, selectGraphsForWorkspace, selectPendingApprovals, selectPrimaryWorkspace, selectQueueForWorkspace, selectRun, selectRunsByTask, selectRunsForTask, selectRunsForWorkspace, selectTask, selectTasksByStatus, selectTasksByWorkspace, selectTasksForWorkspace, selectTasksGroupedByStatus, selectTasksAssignedTo, selectTasksAssignedToMe, selectUserInputsForRun, selectUserInputsForWorkspace, selectWorkspace, selectWorkspaces, type RigTaskSessionProjection, type RigTaskStatusGroup, type RigTaskStatusGroupingInput, } from "./rigSelectors";
|
|
10
|
+
export { buildTaskReferenceIndex, computeTaskBlockingDepths, computeTaskDependencyBadges, isTaskTerminalStatus, readTaskDependencyRefs, readTaskMetadataStringList, readTaskSourceIssueId, resolveTaskReference, selectNextReadyTaskByPriority, type TaskDependencyBadge, type TaskDependencyBadgeKind, type TaskDependencyBadgeSummary, type TaskDependencyProjection, } from "./taskGraph";
|
|
11
11
|
export { extractTaskCode, extractTaskGroupKey, stripTaskCode, } from "./taskGraphCodes";
|
|
12
12
|
export { buildTaskGraphLayout, type TaskGraphEdge, type TaskGraphLane, type TaskGraphLayout, type TaskGraphNode, type TaskGraphStage, } from "./taskGraphLayout";
|
package/dist/src/index.js
CHANGED
|
@@ -2140,10 +2140,40 @@ function projectTaskStatusForGrouping(status) {
|
|
|
2140
2140
|
case "in_progress":
|
|
2141
2141
|
case "under_review":
|
|
2142
2142
|
return "running";
|
|
2143
|
+
case null:
|
|
2144
|
+
case undefined:
|
|
2145
|
+
case "":
|
|
2146
|
+
return "unknown";
|
|
2143
2147
|
default:
|
|
2144
2148
|
return status;
|
|
2145
2149
|
}
|
|
2146
2150
|
}
|
|
2151
|
+
function projectRunStatusForTaskGrouping(status) {
|
|
2152
|
+
switch (status) {
|
|
2153
|
+
case "created":
|
|
2154
|
+
case "queued":
|
|
2155
|
+
case "preparing":
|
|
2156
|
+
return "queued";
|
|
2157
|
+
case "running":
|
|
2158
|
+
case "waiting-approval":
|
|
2159
|
+
case "waiting-user-input":
|
|
2160
|
+
case "paused":
|
|
2161
|
+
case "validating":
|
|
2162
|
+
case "reviewing":
|
|
2163
|
+
case "closing-out":
|
|
2164
|
+
return "running";
|
|
2165
|
+
case "needs-attention":
|
|
2166
|
+
return "blocked";
|
|
2167
|
+
case "failed":
|
|
2168
|
+
case "stopped":
|
|
2169
|
+
return "ready";
|
|
2170
|
+
case "completed":
|
|
2171
|
+
return "completed";
|
|
2172
|
+
case null:
|
|
2173
|
+
case undefined:
|
|
2174
|
+
return null;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2147
2177
|
var STATUS_PRIORITY = [
|
|
2148
2178
|
"running",
|
|
2149
2179
|
"blocked",
|
|
@@ -2155,11 +2185,31 @@ var STATUS_PRIORITY = [
|
|
|
2155
2185
|
"cancelled",
|
|
2156
2186
|
"completed"
|
|
2157
2187
|
];
|
|
2158
|
-
function
|
|
2159
|
-
|
|
2188
|
+
function taskIdFromSession(session) {
|
|
2189
|
+
return session.taskId ?? session.record.taskId ?? null;
|
|
2190
|
+
}
|
|
2191
|
+
function latestSessionByTask(sessions) {
|
|
2192
|
+
const byTask = new Map;
|
|
2193
|
+
for (const session of sessions) {
|
|
2194
|
+
const taskId = taskIdFromSession(session);
|
|
2195
|
+
if (!taskId)
|
|
2196
|
+
continue;
|
|
2197
|
+
const existing = byTask.get(taskId);
|
|
2198
|
+
if (!existing || (session.lastEventAt ?? "").localeCompare(existing.lastEventAt ?? "") >= 0) {
|
|
2199
|
+
byTask.set(taskId, session);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
return byTask;
|
|
2203
|
+
}
|
|
2204
|
+
function projectTaskStatusWithSessions(task, sessionsByTask = new Map) {
|
|
2205
|
+
const sessionStatus = projectRunStatusForTaskGrouping(sessionsByTask.get(task.id)?.status);
|
|
2206
|
+
return sessionStatus ?? projectTaskStatusForGrouping(task.status);
|
|
2207
|
+
}
|
|
2208
|
+
function groupTasksByProjectedStatus(tasks, sessions = []) {
|
|
2209
|
+
const sessionsByTask = latestSessionByTask(sessions);
|
|
2160
2210
|
const byStatus = new Map;
|
|
2161
2211
|
for (const task of tasks) {
|
|
2162
|
-
const projectedStatus =
|
|
2212
|
+
const projectedStatus = projectTaskStatusWithSessions(task, sessionsByTask);
|
|
2163
2213
|
const group = byStatus.get(projectedStatus);
|
|
2164
2214
|
if (group) {
|
|
2165
2215
|
group.push(task);
|
|
@@ -2183,6 +2233,69 @@ function selectTasksGroupedByStatus(snapshot, workspaceId) {
|
|
|
2183
2233
|
}
|
|
2184
2234
|
return result;
|
|
2185
2235
|
}
|
|
2236
|
+
function isProjectionGroupingInput(value) {
|
|
2237
|
+
return Boolean(value && typeof value === "object" && !("snapshotSequence" in value) && Array.isArray(value.tasks));
|
|
2238
|
+
}
|
|
2239
|
+
function selectTasksGroupedByStatus(input, workspaceId) {
|
|
2240
|
+
if (isProjectionGroupingInput(input)) {
|
|
2241
|
+
const filteredTasks = input.workspaceId ? input.tasks.filter((task) => {
|
|
2242
|
+
const workspaceTask = task;
|
|
2243
|
+
return workspaceTask.workspaceId === input.workspaceId;
|
|
2244
|
+
}) : input.tasks;
|
|
2245
|
+
return groupTasksByProjectedStatus(filteredTasks, input.sessions);
|
|
2246
|
+
}
|
|
2247
|
+
const tasks = selectTasksByWorkspace(input, workspaceId ?? null);
|
|
2248
|
+
return groupTasksByProjectedStatus(tasks, []);
|
|
2249
|
+
}
|
|
2250
|
+
function isObjectRecord(value) {
|
|
2251
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2252
|
+
}
|
|
2253
|
+
function normalizeLogin(value) {
|
|
2254
|
+
return value.trim().replace(/^@+/, "").toLowerCase();
|
|
2255
|
+
}
|
|
2256
|
+
function assigneeLoginsFromValue(value) {
|
|
2257
|
+
if (!Array.isArray(value))
|
|
2258
|
+
return [];
|
|
2259
|
+
return value.flatMap((entry) => {
|
|
2260
|
+
if (typeof entry === "string" && entry.trim())
|
|
2261
|
+
return [normalizeLogin(entry)];
|
|
2262
|
+
if (isObjectRecord(entry) && typeof entry.login === "string" && entry.login.trim()) {
|
|
2263
|
+
return [normalizeLogin(entry.login)];
|
|
2264
|
+
}
|
|
2265
|
+
return [];
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
function normalizeTaskAssigneeFilter(assignee, currentUserLogin) {
|
|
2269
|
+
const trimmed = assignee?.trim();
|
|
2270
|
+
if (!trimmed)
|
|
2271
|
+
return null;
|
|
2272
|
+
if (trimmed === "@me" || trimmed.toLowerCase() === "me") {
|
|
2273
|
+
return currentUserLogin?.trim() ? normalizeLogin(currentUserLogin) : null;
|
|
2274
|
+
}
|
|
2275
|
+
return normalizeLogin(trimmed);
|
|
2276
|
+
}
|
|
2277
|
+
function readTaskAssigneeLogins(task) {
|
|
2278
|
+
const taskRecord = task;
|
|
2279
|
+
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
2280
|
+
const raw = isObjectRecord(metadata?.raw) ? metadata.raw : null;
|
|
2281
|
+
return Array.from(new Set([
|
|
2282
|
+
...assigneeLoginsFromValue(taskRecord.assignees),
|
|
2283
|
+
...assigneeLoginsFromValue(metadata?.assignees),
|
|
2284
|
+
...assigneeLoginsFromValue(raw?.assignees)
|
|
2285
|
+
]));
|
|
2286
|
+
}
|
|
2287
|
+
function taskMatchesAssigneeFilter(task, assignee, options = {}) {
|
|
2288
|
+
const normalized = normalizeTaskAssigneeFilter(assignee, options.currentUserLogin);
|
|
2289
|
+
if (!normalized)
|
|
2290
|
+
return false;
|
|
2291
|
+
return readTaskAssigneeLogins(task).includes(normalized);
|
|
2292
|
+
}
|
|
2293
|
+
function selectTasksAssignedTo(tasks, assignee, options = {}) {
|
|
2294
|
+
return tasks.filter((task) => taskMatchesAssigneeFilter(task, assignee, options));
|
|
2295
|
+
}
|
|
2296
|
+
function selectTasksAssignedToMe(tasks, currentUserLogin) {
|
|
2297
|
+
return selectTasksAssignedTo(tasks, "@me", currentUserLogin === undefined ? {} : { currentUserLogin });
|
|
2298
|
+
}
|
|
2186
2299
|
function selectRun(snapshot, runId) {
|
|
2187
2300
|
if (!runId)
|
|
2188
2301
|
return null;
|
|
@@ -2244,17 +2357,45 @@ function selectUserInputsForWorkspace(snapshot, workspaceId) {
|
|
|
2244
2357
|
return (snapshot.userInputs ?? []).filter((request) => runIds.has(request.runId));
|
|
2245
2358
|
}
|
|
2246
2359
|
// packages/core/src/taskGraph.ts
|
|
2247
|
-
function
|
|
2360
|
+
function isObjectRecord2(value) {
|
|
2248
2361
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2249
2362
|
}
|
|
2250
|
-
function
|
|
2251
|
-
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
2252
|
-
const value = metadata?.[key];
|
|
2363
|
+
function readStringList(value) {
|
|
2253
2364
|
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
2254
2365
|
}
|
|
2366
|
+
function unique(values) {
|
|
2367
|
+
return Array.from(new Set(values));
|
|
2368
|
+
}
|
|
2369
|
+
function readTaskMetadataStringList(task, key) {
|
|
2370
|
+
const taskRecord = task;
|
|
2371
|
+
const topLevel = readStringList(taskRecord[key]);
|
|
2372
|
+
if (topLevel.length > 0)
|
|
2373
|
+
return topLevel;
|
|
2374
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
2375
|
+
const metadataList = readStringList(metadata?.[key]);
|
|
2376
|
+
if (metadataList.length > 0)
|
|
2377
|
+
return metadataList;
|
|
2378
|
+
if (key === "dependencies") {
|
|
2379
|
+
return readStringList(metadata?.deps);
|
|
2380
|
+
}
|
|
2381
|
+
return [];
|
|
2382
|
+
}
|
|
2383
|
+
function readTaskDependencyRefs(task) {
|
|
2384
|
+
return unique([
|
|
2385
|
+
...readTaskMetadataStringList(task, "dependencies"),
|
|
2386
|
+
...readTaskMetadataStringList(task, "parentChildDeps")
|
|
2387
|
+
]);
|
|
2388
|
+
}
|
|
2255
2389
|
function readTaskSourceIssueId(task) {
|
|
2256
|
-
|
|
2257
|
-
|
|
2390
|
+
if (typeof task.sourceIssueId === "string" && task.sourceIssueId.length > 0) {
|
|
2391
|
+
return task.sourceIssueId;
|
|
2392
|
+
}
|
|
2393
|
+
const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
|
|
2394
|
+
if (typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0) {
|
|
2395
|
+
return metadata.sourceIssueId;
|
|
2396
|
+
}
|
|
2397
|
+
const rigMetadata = isObjectRecord2(metadata?._rig) ? metadata._rig : null;
|
|
2398
|
+
return typeof rigMetadata?.sourceIssueId === "string" && rigMetadata.sourceIssueId.length > 0 ? rigMetadata.sourceIssueId : null;
|
|
2258
2399
|
}
|
|
2259
2400
|
function resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
|
|
2260
2401
|
if (tasksById.has(ref))
|
|
@@ -2284,11 +2425,7 @@ function computeTaskBlockingDepths(tasks) {
|
|
|
2284
2425
|
if (!task)
|
|
2285
2426
|
return 0;
|
|
2286
2427
|
stack.add(taskId);
|
|
2287
|
-
const
|
|
2288
|
-
...readTaskMetadataStringList(task, "dependencies"),
|
|
2289
|
-
...readTaskMetadataStringList(task, "parentChildDeps")
|
|
2290
|
-
];
|
|
2291
|
-
const blockers = refs.map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
|
|
2428
|
+
const blockers = readTaskDependencyRefs(task).map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
|
|
2292
2429
|
const depth = blockers.length === 0 ? 0 : Math.max(...blockers.map((blockerId) => visit(blockerId, stack) + 1));
|
|
2293
2430
|
stack.delete(taskId);
|
|
2294
2431
|
memo.set(taskId, depth);
|
|
@@ -2299,6 +2436,134 @@ function computeTaskBlockingDepths(tasks) {
|
|
|
2299
2436
|
}
|
|
2300
2437
|
return memo;
|
|
2301
2438
|
}
|
|
2439
|
+
function isTaskTerminalStatus(status) {
|
|
2440
|
+
switch (status) {
|
|
2441
|
+
case "closed":
|
|
2442
|
+
case "completed":
|
|
2443
|
+
case "done":
|
|
2444
|
+
case "cancelled":
|
|
2445
|
+
case "canceled":
|
|
2446
|
+
return true;
|
|
2447
|
+
default:
|
|
2448
|
+
return false;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
function isTaskBlockedStatus(status) {
|
|
2452
|
+
return status === "blocked";
|
|
2453
|
+
}
|
|
2454
|
+
function isTaskRunnableStatus(status) {
|
|
2455
|
+
if (status === null || status === undefined || status === "")
|
|
2456
|
+
return true;
|
|
2457
|
+
if (isTaskTerminalStatus(status) || isTaskBlockedStatus(status))
|
|
2458
|
+
return false;
|
|
2459
|
+
switch (status) {
|
|
2460
|
+
case "ready":
|
|
2461
|
+
case "open":
|
|
2462
|
+
case "failed":
|
|
2463
|
+
return true;
|
|
2464
|
+
default:
|
|
2465
|
+
return false;
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
function priorityValue(task) {
|
|
2469
|
+
return typeof task.priority === "number" && Number.isFinite(task.priority) ? task.priority : Number.MAX_SAFE_INTEGER;
|
|
2470
|
+
}
|
|
2471
|
+
function computeTaskDependencyBadges(tasks) {
|
|
2472
|
+
const index = buildTaskReferenceIndex(tasks);
|
|
2473
|
+
const blockingDepths = computeTaskBlockingDepths(tasks);
|
|
2474
|
+
const dependencyIdsByTask = new Map;
|
|
2475
|
+
const unresolvedRefsByTask = new Map;
|
|
2476
|
+
const blocksByTask = new Map;
|
|
2477
|
+
for (const task of tasks) {
|
|
2478
|
+
const dependencyIds = [];
|
|
2479
|
+
const unresolvedRefs = [];
|
|
2480
|
+
for (const ref of readTaskDependencyRefs(task)) {
|
|
2481
|
+
const dependencyId = resolveTaskReference(ref, index.tasksById, index.taskIdByExternalRef, index.taskIdBySourceIssueId);
|
|
2482
|
+
if (dependencyId && dependencyId !== task.id) {
|
|
2483
|
+
dependencyIds.push(dependencyId);
|
|
2484
|
+
const blocks = blocksByTask.get(dependencyId);
|
|
2485
|
+
if (blocks) {
|
|
2486
|
+
blocks.push(task.id);
|
|
2487
|
+
} else {
|
|
2488
|
+
blocksByTask.set(dependencyId, [task.id]);
|
|
2489
|
+
}
|
|
2490
|
+
} else {
|
|
2491
|
+
unresolvedRefs.push(ref);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
dependencyIdsByTask.set(task.id, unique(dependencyIds));
|
|
2495
|
+
unresolvedRefsByTask.set(task.id, unique(unresolvedRefs));
|
|
2496
|
+
}
|
|
2497
|
+
const summaries = new Map;
|
|
2498
|
+
for (const task of tasks) {
|
|
2499
|
+
const dependencyIds = dependencyIdsByTask.get(task.id) ?? [];
|
|
2500
|
+
const unresolvedDependencyRefs = unresolvedRefsByTask.get(task.id) ?? [];
|
|
2501
|
+
const blockedBy = dependencyIds.filter((dependencyId) => {
|
|
2502
|
+
const dependency = index.tasksById.get(dependencyId);
|
|
2503
|
+
return dependency ? !isTaskTerminalStatus(dependency.status) : false;
|
|
2504
|
+
});
|
|
2505
|
+
const blocks = unique(blocksByTask.get(task.id) ?? []);
|
|
2506
|
+
const blocked = isTaskBlockedStatus(task.status) || blockedBy.length > 0;
|
|
2507
|
+
const ready = isTaskRunnableStatus(task.status) && !blocked;
|
|
2508
|
+
const badges = [];
|
|
2509
|
+
if (blocked) {
|
|
2510
|
+
badges.push({
|
|
2511
|
+
kind: "blocked",
|
|
2512
|
+
label: blockedBy.length > 0 ? `blocked \xD7${blockedBy.length}` : "blocked",
|
|
2513
|
+
description: blockedBy.length > 0 ? `Waiting on ${blockedBy.join(", ")}.` : "Task source marks this task blocked.",
|
|
2514
|
+
...blockedBy.length > 0 ? { count: blockedBy.length } : {},
|
|
2515
|
+
taskIds: blockedBy
|
|
2516
|
+
});
|
|
2517
|
+
} else if (ready) {
|
|
2518
|
+
badges.push({
|
|
2519
|
+
kind: "ready",
|
|
2520
|
+
label: "ready",
|
|
2521
|
+
description: "No open dependencies block this task."
|
|
2522
|
+
});
|
|
2523
|
+
}
|
|
2524
|
+
if (dependencyIds.length > 0 || blocks.length > 0 || unresolvedDependencyRefs.length > 0) {
|
|
2525
|
+
badges.push({
|
|
2526
|
+
kind: "dependency",
|
|
2527
|
+
label: `deps ${dependencyIds.length}/${blocks.length}`,
|
|
2528
|
+
description: [
|
|
2529
|
+
dependencyIds.length > 0 ? `Depends on ${dependencyIds.join(", ")}.` : null,
|
|
2530
|
+
blocks.length > 0 ? `Blocks ${blocks.join(", ")}.` : null,
|
|
2531
|
+
unresolvedDependencyRefs.length > 0 ? `Unresolved refs: ${unresolvedDependencyRefs.join(", ")}.` : null
|
|
2532
|
+
].filter((part) => part !== null).join(" "),
|
|
2533
|
+
count: dependencyIds.length + blocks.length,
|
|
2534
|
+
taskIds: unique([...dependencyIds, ...blocks])
|
|
2535
|
+
});
|
|
2536
|
+
}
|
|
2537
|
+
summaries.set(task.id, {
|
|
2538
|
+
taskId: task.id,
|
|
2539
|
+
blockingDepth: blockingDepths.get(task.id) ?? 0,
|
|
2540
|
+
dependencyIds,
|
|
2541
|
+
unresolvedDependencyRefs,
|
|
2542
|
+
blockedBy,
|
|
2543
|
+
blocks,
|
|
2544
|
+
blocked,
|
|
2545
|
+
ready,
|
|
2546
|
+
dependencyCount: dependencyIds.length,
|
|
2547
|
+
dependentCount: blocks.length,
|
|
2548
|
+
badges
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
return summaries;
|
|
2552
|
+
}
|
|
2553
|
+
function selectNextReadyTaskByPriority(tasks, options = {}) {
|
|
2554
|
+
const excluded = new Set(options.excludeTaskIds ?? []);
|
|
2555
|
+
const badges = computeTaskDependencyBadges(tasks);
|
|
2556
|
+
const candidates = tasks.filter((task) => !excluded.has(task.id)).filter((task) => options.filter?.(task) ?? true).filter((task) => badges.get(task.id)?.ready === true).toSorted((left, right) => {
|
|
2557
|
+
const priorityDelta = priorityValue(left) - priorityValue(right);
|
|
2558
|
+
if (priorityDelta !== 0)
|
|
2559
|
+
return priorityDelta;
|
|
2560
|
+
const createdDelta = (left.createdAt ?? "").localeCompare(right.createdAt ?? "");
|
|
2561
|
+
if (createdDelta !== 0)
|
|
2562
|
+
return createdDelta;
|
|
2563
|
+
return left.id.localeCompare(right.id);
|
|
2564
|
+
});
|
|
2565
|
+
return candidates[0] ?? null;
|
|
2566
|
+
}
|
|
2302
2567
|
// packages/core/src/taskGraphCodes.ts
|
|
2303
2568
|
var TASK_CODE_RE = /^\[([A-Z0-9]+(?:-[A-Z0-9]+)*)\]\s*/;
|
|
2304
2569
|
function extractTaskCode(title) {
|
|
@@ -2338,11 +2603,11 @@ var PALETTE = [
|
|
|
2338
2603
|
{ bg: "#132c35", border: "#1783a6", edge: "#53c4e5" },
|
|
2339
2604
|
{ bg: "#26310f", border: "#6d9a19", edge: "#a7da42" }
|
|
2340
2605
|
];
|
|
2341
|
-
function
|
|
2606
|
+
function isObjectRecord3(value) {
|
|
2342
2607
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2343
2608
|
}
|
|
2344
2609
|
function readIssueType(task) {
|
|
2345
|
-
const metadata =
|
|
2610
|
+
const metadata = isObjectRecord3(task.metadata) ? task.metadata : null;
|
|
2346
2611
|
return typeof metadata?.issueType === "string" ? metadata.issueType : null;
|
|
2347
2612
|
}
|
|
2348
2613
|
function isGraphTask(task) {
|
|
@@ -2637,6 +2902,8 @@ export {
|
|
|
2637
2902
|
selectTasksForWorkspace,
|
|
2638
2903
|
selectTasksByWorkspace,
|
|
2639
2904
|
selectTasksByStatus,
|
|
2905
|
+
selectTasksAssignedToMe,
|
|
2906
|
+
selectTasksAssignedTo,
|
|
2640
2907
|
selectTask,
|
|
2641
2908
|
selectRunsForWorkspace,
|
|
2642
2909
|
selectRunsForTask,
|
|
@@ -2645,6 +2912,7 @@ export {
|
|
|
2645
2912
|
selectQueueForWorkspace,
|
|
2646
2913
|
selectPrimaryWorkspace,
|
|
2647
2914
|
selectPendingApprovals,
|
|
2915
|
+
selectNextReadyTaskByPriority,
|
|
2648
2916
|
selectGraphsForWorkspace,
|
|
2649
2917
|
selectApprovalsForWorkspace,
|
|
2650
2918
|
selectApprovalsForRun,
|
|
@@ -2653,13 +2921,21 @@ export {
|
|
|
2653
2921
|
resolveTaskReference,
|
|
2654
2922
|
readTaskSourceIssueId,
|
|
2655
2923
|
readTaskMetadataStringList,
|
|
2924
|
+
readTaskDependencyRefs,
|
|
2925
|
+
readTaskAssigneeLogins,
|
|
2656
2926
|
pruneQueueEntries,
|
|
2927
|
+
projectTaskStatusWithSessions,
|
|
2928
|
+
projectTaskStatusForGrouping,
|
|
2929
|
+
projectRunStatusForTaskGrouping,
|
|
2657
2930
|
pickDefaultWorkspaceId,
|
|
2931
|
+
normalizeTaskAssigneeFilter,
|
|
2932
|
+
isTaskTerminalStatus,
|
|
2658
2933
|
extractTaskGroupKey,
|
|
2659
2934
|
extractTaskCode,
|
|
2660
2935
|
definePlugin,
|
|
2661
2936
|
defineConfig,
|
|
2662
2937
|
createPluginHost,
|
|
2938
|
+
computeTaskDependencyBadges,
|
|
2663
2939
|
computeTaskBlockingDepths,
|
|
2664
2940
|
buildTaskReferenceIndex,
|
|
2665
2941
|
buildTaskGraphLayout,
|
package/dist/src/load-config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/core/src/load-config.ts
|
|
3
|
-
import { existsSync, mkdtempSync, readFileSync, rmSync } from "fs";
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from "fs";
|
|
4
4
|
import { tmpdir } from "os";
|
|
5
5
|
import { dirname, join } from "path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
@@ -38,6 +38,118 @@ function isModuleResolutionError(error) {
|
|
|
38
38
|
function runningFromCompiledBinary() {
|
|
39
39
|
return import.meta.url.includes("$bunfs");
|
|
40
40
|
}
|
|
41
|
+
function packageNameAndSubpath(specifier) {
|
|
42
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || /^[a-zA-Z]+:/.test(specifier))
|
|
43
|
+
return null;
|
|
44
|
+
const parts = specifier.split("/");
|
|
45
|
+
const packageName = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
46
|
+
if (!packageName)
|
|
47
|
+
return null;
|
|
48
|
+
const rest = parts.slice(specifier.startsWith("@") ? 2 : 1).join("/");
|
|
49
|
+
return { packageName, subpath: rest ? `./${rest}` : "." };
|
|
50
|
+
}
|
|
51
|
+
function exportTargetFromEntry(entry) {
|
|
52
|
+
if (typeof entry === "string")
|
|
53
|
+
return entry;
|
|
54
|
+
if (entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
55
|
+
const conditions = entry;
|
|
56
|
+
for (const key of ["bun", "import", "default", "require"]) {
|
|
57
|
+
if (typeof conditions[key] === "string")
|
|
58
|
+
return conditions[key];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function patternExportTarget(record, subpath) {
|
|
64
|
+
for (const [pattern, entry] of Object.entries(record)) {
|
|
65
|
+
if (!pattern.includes("*"))
|
|
66
|
+
continue;
|
|
67
|
+
const [prefix = "", suffix = ""] = pattern.split("*");
|
|
68
|
+
if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix))
|
|
69
|
+
continue;
|
|
70
|
+
const replacement = subpath.slice(prefix.length, subpath.length - suffix.length);
|
|
71
|
+
const target = exportTargetFromEntry(entry);
|
|
72
|
+
if (target)
|
|
73
|
+
return target.replace("*", replacement);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function exportTargetFromPackageJson(pkg, subpath) {
|
|
78
|
+
const exportsField = pkg.exports;
|
|
79
|
+
const target = (() => {
|
|
80
|
+
if (typeof exportsField === "string" && subpath === ".")
|
|
81
|
+
return exportsField;
|
|
82
|
+
if (!exportsField || typeof exportsField !== "object" || Array.isArray(exportsField))
|
|
83
|
+
return null;
|
|
84
|
+
const record = exportsField;
|
|
85
|
+
return exportTargetFromEntry(record[subpath] ?? (subpath === "." ? record["."] : undefined)) ?? patternExportTarget(record, subpath);
|
|
86
|
+
})();
|
|
87
|
+
if (target)
|
|
88
|
+
return target;
|
|
89
|
+
return subpath === "." && typeof pkg.module === "string" ? pkg.module : subpath === "." && typeof pkg.main === "string" ? pkg.main : null;
|
|
90
|
+
}
|
|
91
|
+
function resolvePackageDirFromBunStore(packageName, nodeModulesDir) {
|
|
92
|
+
const storeDir = join(nodeModulesDir, ".bun");
|
|
93
|
+
if (!existsSync(storeDir))
|
|
94
|
+
return null;
|
|
95
|
+
const encoded = packageName.replace("/", "+");
|
|
96
|
+
try {
|
|
97
|
+
for (const entry of readdirSync(storeDir).sort()) {
|
|
98
|
+
if (!entry.startsWith(`${encoded}@`))
|
|
99
|
+
continue;
|
|
100
|
+
const candidate = join(storeDir, entry, "node_modules", packageName);
|
|
101
|
+
if (existsSync(join(candidate, "package.json")))
|
|
102
|
+
return candidate;
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function resolvePackageExportFromDir(packageDir, subpath) {
|
|
110
|
+
const packageJsonPath = join(packageDir, "package.json");
|
|
111
|
+
if (!existsSync(packageJsonPath))
|
|
112
|
+
return null;
|
|
113
|
+
try {
|
|
114
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
115
|
+
const target = exportTargetFromPackageJson(pkg, subpath);
|
|
116
|
+
if (target) {
|
|
117
|
+
const resolved = join(packageDir, target);
|
|
118
|
+
if (existsSync(resolved))
|
|
119
|
+
return resolved;
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
function resolveBarePackageExport(specifier, parentDir) {
|
|
127
|
+
const parsed = packageNameAndSubpath(specifier);
|
|
128
|
+
if (!parsed)
|
|
129
|
+
return null;
|
|
130
|
+
const packageNames = parsed.packageName.startsWith("@rig/") ? [parsed.packageName, parsed.packageName.replace(/^@rig\//, "@h-rig/")] : [parsed.packageName];
|
|
131
|
+
let current = parentDir;
|
|
132
|
+
while (true) {
|
|
133
|
+
const nodeModulesDir = join(current, "node_modules");
|
|
134
|
+
for (const packageName of packageNames) {
|
|
135
|
+
const directPackageDir = join(nodeModulesDir, packageName);
|
|
136
|
+
const resolvedDirect = resolvePackageExportFromDir(directPackageDir, parsed.subpath);
|
|
137
|
+
if (resolvedDirect)
|
|
138
|
+
return resolvedDirect;
|
|
139
|
+
const bunStorePackageDir = resolvePackageDirFromBunStore(packageName, nodeModulesDir);
|
|
140
|
+
if (bunStorePackageDir) {
|
|
141
|
+
const resolvedFromStore = resolvePackageExportFromDir(bunStorePackageDir, parsed.subpath);
|
|
142
|
+
if (resolvedFromStore)
|
|
143
|
+
return resolvedFromStore;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const next = dirname(current);
|
|
147
|
+
if (next === current)
|
|
148
|
+
break;
|
|
149
|
+
current = next;
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
41
153
|
var runtimeBundleQueue = Promise.resolve();
|
|
42
154
|
function enqueueRuntimeBundle(operation) {
|
|
43
155
|
const next = runtimeBundleQueue.then(operation, operation);
|
|
@@ -67,16 +179,20 @@ async function importConfigViaRuntimeBundleUnserialized(configPath) {
|
|
|
67
179
|
setup(build) {
|
|
68
180
|
build.onResolve({ filter: /^@rig\// }, (args) => {
|
|
69
181
|
const candidates = [
|
|
70
|
-
[args.path, configDir],
|
|
71
182
|
[args.path, hostDir],
|
|
72
|
-
[args.path.replace(/^@rig\//, "@h-rig/"), hostDir]
|
|
183
|
+
[args.path.replace(/^@rig\//, "@h-rig/"), hostDir],
|
|
184
|
+
[args.path, configDir]
|
|
73
185
|
];
|
|
74
186
|
for (const [specifier, parent] of candidates) {
|
|
75
187
|
try {
|
|
76
|
-
const resolved = bun.resolveSync?.(specifier, parent);
|
|
188
|
+
const resolved = bun.resolveSync?.(specifier, parent) ?? resolveBarePackageExport(specifier, parent);
|
|
77
189
|
if (resolved)
|
|
78
190
|
return { path: resolved };
|
|
79
|
-
} catch {
|
|
191
|
+
} catch {
|
|
192
|
+
const resolved = resolveBarePackageExport(specifier, parent);
|
|
193
|
+
if (resolved)
|
|
194
|
+
return { path: resolved };
|
|
195
|
+
}
|
|
80
196
|
}
|
|
81
197
|
return;
|
|
82
198
|
});
|
|
@@ -85,10 +201,13 @@ async function importConfigViaRuntimeBundleUnserialized(configPath) {
|
|
|
85
201
|
return;
|
|
86
202
|
const parent = args.importer ? dirname(args.importer) : configDir;
|
|
87
203
|
try {
|
|
88
|
-
const resolved = bun.resolveSync?.(args.path, parent);
|
|
204
|
+
const resolved = bun.resolveSync?.(args.path, parent) ?? resolveBarePackageExport(args.path, parent);
|
|
89
205
|
if (resolved)
|
|
90
206
|
return { path: resolved };
|
|
91
207
|
} catch {
|
|
208
|
+
const resolved = resolveBarePackageExport(args.path, parent);
|
|
209
|
+
if (resolved)
|
|
210
|
+
return { path: resolved };
|
|
92
211
|
return { path: args.path, namespace: UNRESOLVED_NAMESPACE };
|
|
93
212
|
}
|
|
94
213
|
return;
|
|
@@ -132,6 +251,11 @@ async function loadConfig(cwd) {
|
|
|
132
251
|
if (runningFromCompiledBinary()) {
|
|
133
252
|
return importConfigViaRuntimeBundleUnserialized(p);
|
|
134
253
|
}
|
|
254
|
+
const source = readFileSync(p, "utf8");
|
|
255
|
+
const importsRigHostPackages = /(?:import\s+[^;]*?from\s*|import\s*\()\s*["']@rig\//.test(source);
|
|
256
|
+
if (importsRigHostPackages) {
|
|
257
|
+
return importConfigViaRuntimeBundleUnserialized(p);
|
|
258
|
+
}
|
|
135
259
|
try {
|
|
136
260
|
return await import(pathToFileURL(p).href);
|
|
137
261
|
} catch (error) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HookImplementation, RegisteredTaskSource, RigPlugin, TaskSourceConfig, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
|
|
1
|
+
import type { HookImplementation, RegisteredTaskSource, RigConfig, RigPlugin, TaskSourceConfig, TaskSourceRegistration, ValidatorRegistration } from "@rig/contracts";
|
|
2
2
|
export interface ValidatorResult {
|
|
3
3
|
id: string;
|
|
4
4
|
passed: boolean;
|
|
@@ -25,6 +25,12 @@ export interface RegisteredValidator extends ValidatorRegistration {
|
|
|
25
25
|
*/
|
|
26
26
|
export interface TaskSourceFactoryContext {
|
|
27
27
|
projectRoot: string;
|
|
28
|
+
/**
|
|
29
|
+
* Full project config for adapters that need source-adjacent top-level
|
|
30
|
+
* settings (for example github.projects). Factories should still treat
|
|
31
|
+
* TaskSourceConfig as the primary adapter config.
|
|
32
|
+
*/
|
|
33
|
+
rigConfig?: RigConfig;
|
|
28
34
|
}
|
|
29
35
|
/**
|
|
30
36
|
* Executable task-source factory contributed by a plugin. A plugin
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ArtifactSummary, ApprovalSummary, EngineReadModel, GraphSummary, QueueEntry, RemoteConnectionSummary, RemoteEndpoint, RunId, RunSummary, TaskId, TaskStatus, TaskSummary, UserInputRequestSummary, WorkspaceId, WorkspaceSummary } from "@rig/contracts";
|
|
1
|
+
import type { ArtifactSummary, ApprovalSummary, EngineReadModel, GraphSummary, QueueEntry, RemoteConnectionSummary, RemoteEndpoint, RunId, RunJournalProjection, RunStatus, RunSummary, TaskId, TaskStatus, TaskSummary, UserInputRequestSummary, WorkspaceId, WorkspaceSummary } from "@rig/contracts";
|
|
2
|
+
import type { TaskDependencyProjection } from "./taskGraph";
|
|
2
3
|
export declare function selectWorkspaces(snapshot: EngineReadModel | null): readonly WorkspaceSummary[];
|
|
3
4
|
export declare function selectPrimaryWorkspace(snapshot: EngineReadModel | null): WorkspaceSummary | null;
|
|
4
5
|
export declare function pickDefaultWorkspaceId(snapshot: EngineReadModel | null): WorkspaceId | null;
|
|
@@ -7,10 +8,35 @@ export declare function selectTask(snapshot: EngineReadModel | null, taskId: Tas
|
|
|
7
8
|
export declare function selectTasksByWorkspace(snapshot: EngineReadModel | null, workspaceId: WorkspaceId | null): TaskSummary[];
|
|
8
9
|
export declare const selectTasksForWorkspace: typeof selectTasksByWorkspace;
|
|
9
10
|
export declare function selectTasksByStatus(snapshot: EngineReadModel | null, status: TaskStatus): TaskSummary[];
|
|
11
|
+
export type RigTaskSessionProjection = Pick<RunJournalProjection, "record" | "status" | "lastEventAt"> & {
|
|
12
|
+
readonly taskId?: string | null;
|
|
13
|
+
};
|
|
14
|
+
export interface RigTaskStatusGroupingInput<T extends TaskDependencyProjection = TaskDependencyProjection> {
|
|
15
|
+
readonly tasks: readonly T[];
|
|
16
|
+
readonly sessions?: readonly RigTaskSessionProjection[];
|
|
17
|
+
readonly workspaceId?: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface RigTaskStatusGroup<T extends TaskDependencyProjection = TaskDependencyProjection> {
|
|
20
|
+
readonly status: string;
|
|
21
|
+
readonly tasks: readonly T[];
|
|
22
|
+
}
|
|
23
|
+
export declare function projectTaskStatusForGrouping(status: string | null | undefined): string;
|
|
24
|
+
export declare function projectRunStatusForTaskGrouping(status: RunStatus | null | undefined): string | null;
|
|
25
|
+
export declare function projectTaskStatusWithSessions(task: TaskDependencyProjection, sessionsByTask?: ReadonlyMap<string, RigTaskSessionProjection>): string;
|
|
10
26
|
export declare function selectTasksGroupedByStatus(snapshot: EngineReadModel | null, workspaceId: WorkspaceId | null): Array<{
|
|
11
27
|
status: string;
|
|
12
28
|
tasks: readonly TaskSummary[];
|
|
13
29
|
}>;
|
|
30
|
+
export declare function selectTasksGroupedByStatus<T extends TaskDependencyProjection>(input: RigTaskStatusGroupingInput<T>): Array<RigTaskStatusGroup<T>>;
|
|
31
|
+
export declare function normalizeTaskAssigneeFilter(assignee: string | null | undefined, currentUserLogin?: string | null): string | null;
|
|
32
|
+
export declare function readTaskAssigneeLogins(task: TaskDependencyProjection): readonly string[];
|
|
33
|
+
export declare function taskMatchesAssigneeFilter(task: TaskDependencyProjection, assignee: string | null | undefined, options?: {
|
|
34
|
+
readonly currentUserLogin?: string | null;
|
|
35
|
+
}): boolean;
|
|
36
|
+
export declare function selectTasksAssignedTo<T extends TaskDependencyProjection>(tasks: readonly T[], assignee: string | null | undefined, options?: {
|
|
37
|
+
readonly currentUserLogin?: string | null;
|
|
38
|
+
}): T[];
|
|
39
|
+
export declare function selectTasksAssignedToMe<T extends TaskDependencyProjection>(tasks: readonly T[], currentUserLogin: string | null | undefined): T[];
|
|
14
40
|
export declare function selectRun(snapshot: EngineReadModel | null, runId: RunId | null): RunSummary | null;
|
|
15
41
|
export declare function selectRunsByTask(snapshot: EngineReadModel | null, taskId: TaskId | null): RunSummary[];
|
|
16
42
|
export declare const selectRunsForTask: typeof selectRunsByTask;
|
package/dist/src/rigSelectors.js
CHANGED
|
@@ -39,10 +39,40 @@ function projectTaskStatusForGrouping(status) {
|
|
|
39
39
|
case "in_progress":
|
|
40
40
|
case "under_review":
|
|
41
41
|
return "running";
|
|
42
|
+
case null:
|
|
43
|
+
case undefined:
|
|
44
|
+
case "":
|
|
45
|
+
return "unknown";
|
|
42
46
|
default:
|
|
43
47
|
return status;
|
|
44
48
|
}
|
|
45
49
|
}
|
|
50
|
+
function projectRunStatusForTaskGrouping(status) {
|
|
51
|
+
switch (status) {
|
|
52
|
+
case "created":
|
|
53
|
+
case "queued":
|
|
54
|
+
case "preparing":
|
|
55
|
+
return "queued";
|
|
56
|
+
case "running":
|
|
57
|
+
case "waiting-approval":
|
|
58
|
+
case "waiting-user-input":
|
|
59
|
+
case "paused":
|
|
60
|
+
case "validating":
|
|
61
|
+
case "reviewing":
|
|
62
|
+
case "closing-out":
|
|
63
|
+
return "running";
|
|
64
|
+
case "needs-attention":
|
|
65
|
+
return "blocked";
|
|
66
|
+
case "failed":
|
|
67
|
+
case "stopped":
|
|
68
|
+
return "ready";
|
|
69
|
+
case "completed":
|
|
70
|
+
return "completed";
|
|
71
|
+
case null:
|
|
72
|
+
case undefined:
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
46
76
|
var STATUS_PRIORITY = [
|
|
47
77
|
"running",
|
|
48
78
|
"blocked",
|
|
@@ -54,11 +84,31 @@ var STATUS_PRIORITY = [
|
|
|
54
84
|
"cancelled",
|
|
55
85
|
"completed"
|
|
56
86
|
];
|
|
57
|
-
function
|
|
58
|
-
|
|
87
|
+
function taskIdFromSession(session) {
|
|
88
|
+
return session.taskId ?? session.record.taskId ?? null;
|
|
89
|
+
}
|
|
90
|
+
function latestSessionByTask(sessions) {
|
|
91
|
+
const byTask = new Map;
|
|
92
|
+
for (const session of sessions) {
|
|
93
|
+
const taskId = taskIdFromSession(session);
|
|
94
|
+
if (!taskId)
|
|
95
|
+
continue;
|
|
96
|
+
const existing = byTask.get(taskId);
|
|
97
|
+
if (!existing || (session.lastEventAt ?? "").localeCompare(existing.lastEventAt ?? "") >= 0) {
|
|
98
|
+
byTask.set(taskId, session);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return byTask;
|
|
102
|
+
}
|
|
103
|
+
function projectTaskStatusWithSessions(task, sessionsByTask = new Map) {
|
|
104
|
+
const sessionStatus = projectRunStatusForTaskGrouping(sessionsByTask.get(task.id)?.status);
|
|
105
|
+
return sessionStatus ?? projectTaskStatusForGrouping(task.status);
|
|
106
|
+
}
|
|
107
|
+
function groupTasksByProjectedStatus(tasks, sessions = []) {
|
|
108
|
+
const sessionsByTask = latestSessionByTask(sessions);
|
|
59
109
|
const byStatus = new Map;
|
|
60
110
|
for (const task of tasks) {
|
|
61
|
-
const projectedStatus =
|
|
111
|
+
const projectedStatus = projectTaskStatusWithSessions(task, sessionsByTask);
|
|
62
112
|
const group = byStatus.get(projectedStatus);
|
|
63
113
|
if (group) {
|
|
64
114
|
group.push(task);
|
|
@@ -82,6 +132,69 @@ function selectTasksGroupedByStatus(snapshot, workspaceId) {
|
|
|
82
132
|
}
|
|
83
133
|
return result;
|
|
84
134
|
}
|
|
135
|
+
function isProjectionGroupingInput(value) {
|
|
136
|
+
return Boolean(value && typeof value === "object" && !("snapshotSequence" in value) && Array.isArray(value.tasks));
|
|
137
|
+
}
|
|
138
|
+
function selectTasksGroupedByStatus(input, workspaceId) {
|
|
139
|
+
if (isProjectionGroupingInput(input)) {
|
|
140
|
+
const filteredTasks = input.workspaceId ? input.tasks.filter((task) => {
|
|
141
|
+
const workspaceTask = task;
|
|
142
|
+
return workspaceTask.workspaceId === input.workspaceId;
|
|
143
|
+
}) : input.tasks;
|
|
144
|
+
return groupTasksByProjectedStatus(filteredTasks, input.sessions);
|
|
145
|
+
}
|
|
146
|
+
const tasks = selectTasksByWorkspace(input, workspaceId ?? null);
|
|
147
|
+
return groupTasksByProjectedStatus(tasks, []);
|
|
148
|
+
}
|
|
149
|
+
function isObjectRecord(value) {
|
|
150
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
151
|
+
}
|
|
152
|
+
function normalizeLogin(value) {
|
|
153
|
+
return value.trim().replace(/^@+/, "").toLowerCase();
|
|
154
|
+
}
|
|
155
|
+
function assigneeLoginsFromValue(value) {
|
|
156
|
+
if (!Array.isArray(value))
|
|
157
|
+
return [];
|
|
158
|
+
return value.flatMap((entry) => {
|
|
159
|
+
if (typeof entry === "string" && entry.trim())
|
|
160
|
+
return [normalizeLogin(entry)];
|
|
161
|
+
if (isObjectRecord(entry) && typeof entry.login === "string" && entry.login.trim()) {
|
|
162
|
+
return [normalizeLogin(entry.login)];
|
|
163
|
+
}
|
|
164
|
+
return [];
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function normalizeTaskAssigneeFilter(assignee, currentUserLogin) {
|
|
168
|
+
const trimmed = assignee?.trim();
|
|
169
|
+
if (!trimmed)
|
|
170
|
+
return null;
|
|
171
|
+
if (trimmed === "@me" || trimmed.toLowerCase() === "me") {
|
|
172
|
+
return currentUserLogin?.trim() ? normalizeLogin(currentUserLogin) : null;
|
|
173
|
+
}
|
|
174
|
+
return normalizeLogin(trimmed);
|
|
175
|
+
}
|
|
176
|
+
function readTaskAssigneeLogins(task) {
|
|
177
|
+
const taskRecord = task;
|
|
178
|
+
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
179
|
+
const raw = isObjectRecord(metadata?.raw) ? metadata.raw : null;
|
|
180
|
+
return Array.from(new Set([
|
|
181
|
+
...assigneeLoginsFromValue(taskRecord.assignees),
|
|
182
|
+
...assigneeLoginsFromValue(metadata?.assignees),
|
|
183
|
+
...assigneeLoginsFromValue(raw?.assignees)
|
|
184
|
+
]));
|
|
185
|
+
}
|
|
186
|
+
function taskMatchesAssigneeFilter(task, assignee, options = {}) {
|
|
187
|
+
const normalized = normalizeTaskAssigneeFilter(assignee, options.currentUserLogin);
|
|
188
|
+
if (!normalized)
|
|
189
|
+
return false;
|
|
190
|
+
return readTaskAssigneeLogins(task).includes(normalized);
|
|
191
|
+
}
|
|
192
|
+
function selectTasksAssignedTo(tasks, assignee, options = {}) {
|
|
193
|
+
return tasks.filter((task) => taskMatchesAssigneeFilter(task, assignee, options));
|
|
194
|
+
}
|
|
195
|
+
function selectTasksAssignedToMe(tasks, currentUserLogin) {
|
|
196
|
+
return selectTasksAssignedTo(tasks, "@me", currentUserLogin === undefined ? {} : { currentUserLogin });
|
|
197
|
+
}
|
|
85
198
|
function selectRun(snapshot, runId) {
|
|
86
199
|
if (!runId)
|
|
87
200
|
return null;
|
|
@@ -247,6 +360,7 @@ function selectConnectedRemoteCount(snapshot) {
|
|
|
247
360
|
return selectRemoteConnections(snapshot).filter((connection) => connection.status === "connected").length;
|
|
248
361
|
}
|
|
249
362
|
export {
|
|
363
|
+
taskMatchesAssigneeFilter,
|
|
250
364
|
selectWorkspaces,
|
|
251
365
|
selectWorkspace,
|
|
252
366
|
selectValidationsForTask,
|
|
@@ -258,6 +372,8 @@ export {
|
|
|
258
372
|
selectTasksForWorkspace,
|
|
259
373
|
selectTasksByWorkspace,
|
|
260
374
|
selectTasksByStatus,
|
|
375
|
+
selectTasksAssignedToMe,
|
|
376
|
+
selectTasksAssignedTo,
|
|
261
377
|
selectTask,
|
|
262
378
|
selectRuntimeForRun,
|
|
263
379
|
selectRunsForWorkspace,
|
|
@@ -289,5 +405,10 @@ export {
|
|
|
289
405
|
selectAdhocRunsForWorkspace,
|
|
290
406
|
selectAdhocRuns,
|
|
291
407
|
selectActionsForTask,
|
|
292
|
-
|
|
408
|
+
readTaskAssigneeLogins,
|
|
409
|
+
projectTaskStatusWithSessions,
|
|
410
|
+
projectTaskStatusForGrouping,
|
|
411
|
+
projectRunStatusForTaskGrouping,
|
|
412
|
+
pickDefaultWorkspaceId,
|
|
413
|
+
normalizeTaskAssigneeFilter
|
|
293
414
|
};
|
package/dist/src/taskGraph.d.ts
CHANGED
|
@@ -1,28 +1,41 @@
|
|
|
1
1
|
import type { TaskSummary } from "@rig/contracts";
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
export declare function resolveTaskReference(ref: string, tasksById: ReadonlyMap<string, TaskSummary>, taskIdByExternalRef: ReadonlyMap<string, string>, taskIdBySourceIssueId: ReadonlyMap<string, string>): string | null;
|
|
5
|
-
export declare function buildTaskReferenceIndex(tasks: readonly TaskSummary[]): {
|
|
6
|
-
tasksById: Map<string, {
|
|
7
|
-
readonly id: string & import("effect/Brand").Brand<"TaskId">;
|
|
8
|
-
readonly title: string;
|
|
9
|
-
readonly createdAt: string;
|
|
10
|
-
readonly updatedAt: string;
|
|
11
|
-
readonly role: string | null;
|
|
12
|
-
readonly status: "running" | "ready" | "completed" | "failed" | "draft" | "open" | "queued" | "in_progress" | "under_review" | "blocked" | "unknown" | "cancelled" | "closed";
|
|
13
|
-
readonly metadata: unknown;
|
|
14
|
-
readonly description: string;
|
|
15
|
-
readonly workspaceId: string & import("effect/Brand").Brand<"WorkspaceId">;
|
|
16
|
-
readonly graphId: (string & import("effect/Brand").Brand<"GraphId">) | null;
|
|
17
|
-
readonly externalId: string | null;
|
|
18
|
-
readonly priority: number | null;
|
|
19
|
-
readonly scope: readonly string[];
|
|
20
|
-
readonly validationKeys: readonly string[];
|
|
21
|
-
readonly dependencies?: readonly string[] | undefined;
|
|
22
|
-
readonly sourceIssueId?: string | null | undefined;
|
|
23
|
-
readonly parentChildDeps?: readonly string[] | undefined;
|
|
24
|
-
}>;
|
|
25
|
-
taskIdByExternalRef: Map<string, string & import("effect/Brand").Brand<"TaskId">>;
|
|
26
|
-
taskIdBySourceIssueId: Map<string, string & import("effect/Brand").Brand<"TaskId">>;
|
|
2
|
+
export type TaskDependencyProjection = Pick<TaskSummary, "id" | "status" | "priority" | "metadata"> & Partial<Pick<TaskSummary, "externalId" | "sourceIssueId" | "dependencies" | "parentChildDeps" | "createdAt" | "updatedAt">> & {
|
|
3
|
+
readonly title?: string | null;
|
|
27
4
|
};
|
|
28
|
-
export
|
|
5
|
+
export type TaskDependencyBadgeKind = "blocked" | "ready" | "dependency";
|
|
6
|
+
export interface TaskDependencyBadge {
|
|
7
|
+
readonly kind: TaskDependencyBadgeKind;
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly description: string;
|
|
10
|
+
readonly count?: number;
|
|
11
|
+
readonly taskIds?: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export interface TaskDependencyBadgeSummary {
|
|
14
|
+
readonly taskId: string;
|
|
15
|
+
readonly blockingDepth: number;
|
|
16
|
+
readonly dependencyIds: readonly string[];
|
|
17
|
+
readonly unresolvedDependencyRefs: readonly string[];
|
|
18
|
+
readonly blockedBy: readonly string[];
|
|
19
|
+
readonly blocks: readonly string[];
|
|
20
|
+
readonly blocked: boolean;
|
|
21
|
+
readonly ready: boolean;
|
|
22
|
+
readonly dependencyCount: number;
|
|
23
|
+
readonly dependentCount: number;
|
|
24
|
+
readonly badges: readonly TaskDependencyBadge[];
|
|
25
|
+
}
|
|
26
|
+
export declare function readTaskMetadataStringList(task: TaskDependencyProjection, key: "dependencies" | "parentChildDeps" | "labels"): string[];
|
|
27
|
+
export declare function readTaskDependencyRefs(task: TaskDependencyProjection): string[];
|
|
28
|
+
export declare function readTaskSourceIssueId(task: TaskDependencyProjection): string | null;
|
|
29
|
+
export declare function resolveTaskReference(ref: string, tasksById: ReadonlyMap<string, TaskDependencyProjection>, taskIdByExternalRef: ReadonlyMap<string, string>, taskIdBySourceIssueId: ReadonlyMap<string, string>): string | null;
|
|
30
|
+
export declare function buildTaskReferenceIndex<T extends TaskDependencyProjection>(tasks: readonly T[]): {
|
|
31
|
+
readonly tasksById: Map<string, T>;
|
|
32
|
+
readonly taskIdByExternalRef: Map<string, string>;
|
|
33
|
+
readonly taskIdBySourceIssueId: Map<string, string>;
|
|
34
|
+
};
|
|
35
|
+
export declare function computeTaskBlockingDepths<T extends TaskDependencyProjection>(tasks: readonly T[]): Map<string, number>;
|
|
36
|
+
export declare function isTaskTerminalStatus(status: string | null | undefined): boolean;
|
|
37
|
+
export declare function computeTaskDependencyBadges(tasks: readonly TaskDependencyProjection[]): Map<string, TaskDependencyBadgeSummary>;
|
|
38
|
+
export declare function selectNextReadyTaskByPriority<T extends TaskDependencyProjection>(tasks: readonly T[], options?: {
|
|
39
|
+
readonly excludeTaskIds?: Iterable<string>;
|
|
40
|
+
readonly filter?: (task: T) => boolean;
|
|
41
|
+
}): T | null;
|
package/dist/src/taskGraph.js
CHANGED
|
@@ -3,14 +3,42 @@
|
|
|
3
3
|
function isObjectRecord(value) {
|
|
4
4
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
5
|
}
|
|
6
|
+
function readStringList(value) {
|
|
7
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
8
|
+
}
|
|
9
|
+
function unique(values) {
|
|
10
|
+
return Array.from(new Set(values));
|
|
11
|
+
}
|
|
6
12
|
function readTaskMetadataStringList(task, key) {
|
|
13
|
+
const taskRecord = task;
|
|
14
|
+
const topLevel = readStringList(taskRecord[key]);
|
|
15
|
+
if (topLevel.length > 0)
|
|
16
|
+
return topLevel;
|
|
7
17
|
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
8
|
-
const
|
|
9
|
-
|
|
18
|
+
const metadataList = readStringList(metadata?.[key]);
|
|
19
|
+
if (metadataList.length > 0)
|
|
20
|
+
return metadataList;
|
|
21
|
+
if (key === "dependencies") {
|
|
22
|
+
return readStringList(metadata?.deps);
|
|
23
|
+
}
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
function readTaskDependencyRefs(task) {
|
|
27
|
+
return unique([
|
|
28
|
+
...readTaskMetadataStringList(task, "dependencies"),
|
|
29
|
+
...readTaskMetadataStringList(task, "parentChildDeps")
|
|
30
|
+
]);
|
|
10
31
|
}
|
|
11
32
|
function readTaskSourceIssueId(task) {
|
|
33
|
+
if (typeof task.sourceIssueId === "string" && task.sourceIssueId.length > 0) {
|
|
34
|
+
return task.sourceIssueId;
|
|
35
|
+
}
|
|
12
36
|
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
13
|
-
|
|
37
|
+
if (typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0) {
|
|
38
|
+
return metadata.sourceIssueId;
|
|
39
|
+
}
|
|
40
|
+
const rigMetadata = isObjectRecord(metadata?._rig) ? metadata._rig : null;
|
|
41
|
+
return typeof rigMetadata?.sourceIssueId === "string" && rigMetadata.sourceIssueId.length > 0 ? rigMetadata.sourceIssueId : null;
|
|
14
42
|
}
|
|
15
43
|
function resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
|
|
16
44
|
if (tasksById.has(ref))
|
|
@@ -40,11 +68,7 @@ function computeTaskBlockingDepths(tasks) {
|
|
|
40
68
|
if (!task)
|
|
41
69
|
return 0;
|
|
42
70
|
stack.add(taskId);
|
|
43
|
-
const
|
|
44
|
-
...readTaskMetadataStringList(task, "dependencies"),
|
|
45
|
-
...readTaskMetadataStringList(task, "parentChildDeps")
|
|
46
|
-
];
|
|
47
|
-
const blockers = refs.map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
|
|
71
|
+
const blockers = readTaskDependencyRefs(task).map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
|
|
48
72
|
const depth = blockers.length === 0 ? 0 : Math.max(...blockers.map((blockerId) => visit(blockerId, stack) + 1));
|
|
49
73
|
stack.delete(taskId);
|
|
50
74
|
memo.set(taskId, depth);
|
|
@@ -55,10 +79,142 @@ function computeTaskBlockingDepths(tasks) {
|
|
|
55
79
|
}
|
|
56
80
|
return memo;
|
|
57
81
|
}
|
|
82
|
+
function isTaskTerminalStatus(status) {
|
|
83
|
+
switch (status) {
|
|
84
|
+
case "closed":
|
|
85
|
+
case "completed":
|
|
86
|
+
case "done":
|
|
87
|
+
case "cancelled":
|
|
88
|
+
case "canceled":
|
|
89
|
+
return true;
|
|
90
|
+
default:
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function isTaskBlockedStatus(status) {
|
|
95
|
+
return status === "blocked";
|
|
96
|
+
}
|
|
97
|
+
function isTaskRunnableStatus(status) {
|
|
98
|
+
if (status === null || status === undefined || status === "")
|
|
99
|
+
return true;
|
|
100
|
+
if (isTaskTerminalStatus(status) || isTaskBlockedStatus(status))
|
|
101
|
+
return false;
|
|
102
|
+
switch (status) {
|
|
103
|
+
case "ready":
|
|
104
|
+
case "open":
|
|
105
|
+
case "failed":
|
|
106
|
+
return true;
|
|
107
|
+
default:
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function priorityValue(task) {
|
|
112
|
+
return typeof task.priority === "number" && Number.isFinite(task.priority) ? task.priority : Number.MAX_SAFE_INTEGER;
|
|
113
|
+
}
|
|
114
|
+
function computeTaskDependencyBadges(tasks) {
|
|
115
|
+
const index = buildTaskReferenceIndex(tasks);
|
|
116
|
+
const blockingDepths = computeTaskBlockingDepths(tasks);
|
|
117
|
+
const dependencyIdsByTask = new Map;
|
|
118
|
+
const unresolvedRefsByTask = new Map;
|
|
119
|
+
const blocksByTask = new Map;
|
|
120
|
+
for (const task of tasks) {
|
|
121
|
+
const dependencyIds = [];
|
|
122
|
+
const unresolvedRefs = [];
|
|
123
|
+
for (const ref of readTaskDependencyRefs(task)) {
|
|
124
|
+
const dependencyId = resolveTaskReference(ref, index.tasksById, index.taskIdByExternalRef, index.taskIdBySourceIssueId);
|
|
125
|
+
if (dependencyId && dependencyId !== task.id) {
|
|
126
|
+
dependencyIds.push(dependencyId);
|
|
127
|
+
const blocks = blocksByTask.get(dependencyId);
|
|
128
|
+
if (blocks) {
|
|
129
|
+
blocks.push(task.id);
|
|
130
|
+
} else {
|
|
131
|
+
blocksByTask.set(dependencyId, [task.id]);
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
unresolvedRefs.push(ref);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
dependencyIdsByTask.set(task.id, unique(dependencyIds));
|
|
138
|
+
unresolvedRefsByTask.set(task.id, unique(unresolvedRefs));
|
|
139
|
+
}
|
|
140
|
+
const summaries = new Map;
|
|
141
|
+
for (const task of tasks) {
|
|
142
|
+
const dependencyIds = dependencyIdsByTask.get(task.id) ?? [];
|
|
143
|
+
const unresolvedDependencyRefs = unresolvedRefsByTask.get(task.id) ?? [];
|
|
144
|
+
const blockedBy = dependencyIds.filter((dependencyId) => {
|
|
145
|
+
const dependency = index.tasksById.get(dependencyId);
|
|
146
|
+
return dependency ? !isTaskTerminalStatus(dependency.status) : false;
|
|
147
|
+
});
|
|
148
|
+
const blocks = unique(blocksByTask.get(task.id) ?? []);
|
|
149
|
+
const blocked = isTaskBlockedStatus(task.status) || blockedBy.length > 0;
|
|
150
|
+
const ready = isTaskRunnableStatus(task.status) && !blocked;
|
|
151
|
+
const badges = [];
|
|
152
|
+
if (blocked) {
|
|
153
|
+
badges.push({
|
|
154
|
+
kind: "blocked",
|
|
155
|
+
label: blockedBy.length > 0 ? `blocked \xD7${blockedBy.length}` : "blocked",
|
|
156
|
+
description: blockedBy.length > 0 ? `Waiting on ${blockedBy.join(", ")}.` : "Task source marks this task blocked.",
|
|
157
|
+
...blockedBy.length > 0 ? { count: blockedBy.length } : {},
|
|
158
|
+
taskIds: blockedBy
|
|
159
|
+
});
|
|
160
|
+
} else if (ready) {
|
|
161
|
+
badges.push({
|
|
162
|
+
kind: "ready",
|
|
163
|
+
label: "ready",
|
|
164
|
+
description: "No open dependencies block this task."
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (dependencyIds.length > 0 || blocks.length > 0 || unresolvedDependencyRefs.length > 0) {
|
|
168
|
+
badges.push({
|
|
169
|
+
kind: "dependency",
|
|
170
|
+
label: `deps ${dependencyIds.length}/${blocks.length}`,
|
|
171
|
+
description: [
|
|
172
|
+
dependencyIds.length > 0 ? `Depends on ${dependencyIds.join(", ")}.` : null,
|
|
173
|
+
blocks.length > 0 ? `Blocks ${blocks.join(", ")}.` : null,
|
|
174
|
+
unresolvedDependencyRefs.length > 0 ? `Unresolved refs: ${unresolvedDependencyRefs.join(", ")}.` : null
|
|
175
|
+
].filter((part) => part !== null).join(" "),
|
|
176
|
+
count: dependencyIds.length + blocks.length,
|
|
177
|
+
taskIds: unique([...dependencyIds, ...blocks])
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
summaries.set(task.id, {
|
|
181
|
+
taskId: task.id,
|
|
182
|
+
blockingDepth: blockingDepths.get(task.id) ?? 0,
|
|
183
|
+
dependencyIds,
|
|
184
|
+
unresolvedDependencyRefs,
|
|
185
|
+
blockedBy,
|
|
186
|
+
blocks,
|
|
187
|
+
blocked,
|
|
188
|
+
ready,
|
|
189
|
+
dependencyCount: dependencyIds.length,
|
|
190
|
+
dependentCount: blocks.length,
|
|
191
|
+
badges
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return summaries;
|
|
195
|
+
}
|
|
196
|
+
function selectNextReadyTaskByPriority(tasks, options = {}) {
|
|
197
|
+
const excluded = new Set(options.excludeTaskIds ?? []);
|
|
198
|
+
const badges = computeTaskDependencyBadges(tasks);
|
|
199
|
+
const candidates = tasks.filter((task) => !excluded.has(task.id)).filter((task) => options.filter?.(task) ?? true).filter((task) => badges.get(task.id)?.ready === true).toSorted((left, right) => {
|
|
200
|
+
const priorityDelta = priorityValue(left) - priorityValue(right);
|
|
201
|
+
if (priorityDelta !== 0)
|
|
202
|
+
return priorityDelta;
|
|
203
|
+
const createdDelta = (left.createdAt ?? "").localeCompare(right.createdAt ?? "");
|
|
204
|
+
if (createdDelta !== 0)
|
|
205
|
+
return createdDelta;
|
|
206
|
+
return left.id.localeCompare(right.id);
|
|
207
|
+
});
|
|
208
|
+
return candidates[0] ?? null;
|
|
209
|
+
}
|
|
58
210
|
export {
|
|
211
|
+
selectNextReadyTaskByPriority,
|
|
59
212
|
resolveTaskReference,
|
|
60
213
|
readTaskSourceIssueId,
|
|
61
214
|
readTaskMetadataStringList,
|
|
215
|
+
readTaskDependencyRefs,
|
|
216
|
+
isTaskTerminalStatus,
|
|
217
|
+
computeTaskDependencyBadges,
|
|
62
218
|
computeTaskBlockingDepths,
|
|
63
219
|
buildTaskReferenceIndex
|
|
64
220
|
};
|
|
@@ -39,14 +39,33 @@ function stripTaskCode(label) {
|
|
|
39
39
|
function isObjectRecord(value) {
|
|
40
40
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
41
|
}
|
|
42
|
+
function readStringList(value) {
|
|
43
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
44
|
+
}
|
|
42
45
|
function readTaskMetadataStringList(task, key) {
|
|
46
|
+
const taskRecord = task;
|
|
47
|
+
const topLevel = readStringList(taskRecord[key]);
|
|
48
|
+
if (topLevel.length > 0)
|
|
49
|
+
return topLevel;
|
|
43
50
|
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
44
|
-
const
|
|
45
|
-
|
|
51
|
+
const metadataList = readStringList(metadata?.[key]);
|
|
52
|
+
if (metadataList.length > 0)
|
|
53
|
+
return metadataList;
|
|
54
|
+
if (key === "dependencies") {
|
|
55
|
+
return readStringList(metadata?.deps);
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
46
58
|
}
|
|
47
59
|
function readTaskSourceIssueId(task) {
|
|
60
|
+
if (typeof task.sourceIssueId === "string" && task.sourceIssueId.length > 0) {
|
|
61
|
+
return task.sourceIssueId;
|
|
62
|
+
}
|
|
48
63
|
const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
|
|
49
|
-
|
|
64
|
+
if (typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0) {
|
|
65
|
+
return metadata.sourceIssueId;
|
|
66
|
+
}
|
|
67
|
+
const rigMetadata = isObjectRecord(metadata?._rig) ? metadata._rig : null;
|
|
68
|
+
return typeof rigMetadata?.sourceIssueId === "string" && rigMetadata.sourceIssueId.length > 0 ? rigMetadata.sourceIssueId : null;
|
|
50
69
|
}
|
|
51
70
|
function resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
|
|
52
71
|
if (tasksById.has(ref))
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/core",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.91",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Rig
|
|
5
|
+
"description": "Config and plugin composition library for Rig's OMP extension ecosystem; not a product host/runtime.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
@@ -16,18 +16,6 @@
|
|
|
16
16
|
"./load-config": {
|
|
17
17
|
"types": "./dist/src/load-config.d.ts",
|
|
18
18
|
"import": "./dist/src/load-config.js"
|
|
19
|
-
},
|
|
20
|
-
"./engineReadModelReducer": {
|
|
21
|
-
"types": "./dist/src/engineReadModelReducer.d.ts",
|
|
22
|
-
"import": "./dist/src/engineReadModelReducer.js"
|
|
23
|
-
},
|
|
24
|
-
"./rigSelectors": {
|
|
25
|
-
"types": "./dist/src/rigSelectors.d.ts",
|
|
26
|
-
"import": "./dist/src/rigSelectors.js"
|
|
27
|
-
},
|
|
28
|
-
"./taskGraph": {
|
|
29
|
-
"types": "./dist/src/taskGraph.d.ts",
|
|
30
|
-
"import": "./dist/src/taskGraph.js"
|
|
31
19
|
}
|
|
32
20
|
},
|
|
33
21
|
"engines": {
|
|
@@ -37,7 +25,7 @@
|
|
|
37
25
|
"module": "./dist/src/index.js",
|
|
38
26
|
"types": "./dist/src/index.d.ts",
|
|
39
27
|
"dependencies": {
|
|
40
|
-
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.
|
|
28
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.91",
|
|
41
29
|
"effect": "4.0.0-beta.78"
|
|
42
30
|
}
|
|
43
31
|
}
|