@fieldwangai/agentflow 0.1.117 → 0.1.119

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.
@@ -3337,9 +3337,11 @@ function prdWorkflowShareLinkSummary(record, shareToken, publicBaseUrl, userId =
3337
3337
  workflowShare: token,
3338
3338
  });
3339
3339
  const base = String(publicBaseUrl || "").replace(/\/+$/, "");
3340
+ const shortUrl = `${base}/w/${encodeURIComponent(token)}`;
3340
3341
  return {
3341
3342
  tapdId: String(record.tapdId || ""),
3342
3343
  url: `${base}/workspace?${query.toString()}`,
3344
+ shortUrl,
3343
3345
  active: true,
3344
3346
  readOnly: true,
3345
3347
  createdAt: record.shareCreatedAt || "",
@@ -7776,7 +7778,7 @@ function prdWorkflowReviewIdFromRequest(tapdId, payload = {}, durability = "temp
7776
7778
  return prdWorkflowSafeStateId(["review", tapdId, stageHead, digest].filter(Boolean).join("-")).slice(0, 64);
7777
7779
  }
7778
7780
 
7779
- function prdWorkflowReviewArtifactKey(tapdId, payload = {}, durability = "temporary") {
7781
+ function prdWorkflowReviewArtifactKey(tapdId, payload = {}) {
7780
7782
  const explicit = String(payload.artifactKey || payload.artifact_key || "").trim();
7781
7783
  if (explicit) return explicit.slice(0, 500);
7782
7784
  const issueKey = prdWorkflowSafeStateId(
@@ -7798,7 +7800,6 @@ function prdWorkflowReviewArtifactKey(tapdId, payload = {}, durability = "tempor
7798
7800
  issueKey,
7799
7801
  platform,
7800
7802
  stage,
7801
- String(durability || "temporary").trim().toLowerCase() || "temporary",
7802
7803
  ].join(":").slice(0, 500);
7803
7804
  }
7804
7805
 
@@ -9094,16 +9095,54 @@ function prdWorkflowNormalizeStoredRuntimeEvent(tapdId, event = {}) {
9094
9095
  const kind = String(item.kind || "").trim();
9095
9096
  const hasReviewUrl = /\/api\/prd-workflow\/review\//.test(String(item.url || item.href || ""));
9096
9097
  if (!kind && !hasReviewUrl) return item;
9098
+ const originalKey = String(item.key || item.artifactKey || item.artifact_key || "").trim();
9099
+ const logicalKey = /^prd-review:/i.test(originalKey)
9100
+ ? originalKey.replace(/:(?:temporary|durable)$/i, "")
9101
+ : originalKey && !/^(?:temporary-)?review:https?:/i.test(originalKey)
9102
+ ? originalKey
9103
+ : prdWorkflowReviewArtifactKey(tapdId, out);
9097
9104
  return {
9098
9105
  ...item,
9106
+ key: logicalKey,
9099
9107
  persistence: item.persistence || "runtime",
9100
9108
  source: item.source && typeof item.source === "object" && !Array.isArray(item.source) ? item.source : sourceArtifact,
9101
9109
  };
9102
9110
  };
9103
9111
  if (Array.isArray(out.artifacts)) out.artifacts = out.artifacts.map(normalizeReviewRef);
9104
9112
  if (Array.isArray(out.links)) out.links = out.links.map(normalizeReviewRef);
9105
- }
9113
+ const reviewArtifactKey = String(
9114
+ out.artifacts?.[0]?.key
9115
+ || out.links?.[0]?.key
9116
+ || "",
9117
+ ).trim();
9118
+ out.aggregateByStage = false;
9119
+ if (reviewArtifactKey) out.id = `review-link:${reviewArtifactKey}`;
9120
+ }
9121
+ const canonicalStage = prdWorkflowRuntimeEventCanonicalStage(out);
9122
+ const canonicalAction = prdWorkflowRuntimeEventCanonicalAction(canonicalStage);
9123
+ const rawStage = String(out.stageKey || out.stage_key || out.stage || "").trim();
9124
+ if (canonicalStage) {
9125
+ if (rawStage && rawStage !== canonicalStage) out.sourceStage = out.sourceStage || rawStage;
9126
+ out.stage = canonicalStage;
9127
+ out.stageKey = canonicalStage;
9128
+ }
9129
+ if (canonicalAction && (type === "workflow-marker" || String(out.action || "") === "mark")) {
9130
+ out.action = canonicalAction;
9131
+ out.actionId = canonicalAction;
9132
+ }
9133
+ if (type === "workflow-report" && !out.action && (
9134
+ String(out.artifactScope || out.artifact_scope || out.scope || "").toLowerCase() === "global"
9135
+ || out.aggregateByStage === false
9136
+ || out.aggregate_by_stage === false
9137
+ )) {
9138
+ out.auxiliary = true;
9139
+ }
9140
+ out.artifacts = prdWorkflowRuntimeOwnedArtifacts(out.artifacts, canonicalStage);
9141
+ out.links = prdWorkflowRuntimeOwnedArtifacts(out.links, canonicalStage);
9106
9142
  out.tapdId = String(out.tapdId || out.tapd_id || tapdId || "");
9143
+ const canonicalId = prdWorkflowRuntimeEventId(out);
9144
+ if (out.id && out.id !== canonicalId) out.sourceEventId = out.sourceEventId || out.id;
9145
+ out.id = canonicalId;
9107
9146
  return out;
9108
9147
  }
9109
9148
 
@@ -9156,15 +9195,63 @@ function prdWorkflowRuntimeEventStatus(type, status) {
9156
9195
  function prdWorkflowRuntimeEventCanonicalStage(event = {}) {
9157
9196
  const issue = String(event.issueKey || event.issue_key || event.issue || "").trim();
9158
9197
  const action = String(event.action || event.actionId || event.action_id || "").trim();
9159
- const rawStage = String(event.stage || event.stageKey || event.stage_key || event.phase || event.code || action || "").trim();
9160
- const text = [rawStage, action, event.code, event.type, event.title].map((value) => String(value || "")).join(" ").toLowerCase();
9198
+ const rawStage = String(event.stageKey || event.stage_key || event.stage || event.phase || event.code || action || "").trim();
9199
+ const normalizedStage = rawStage.toLowerCase();
9200
+ const tokens = [rawStage, action, event.code, event.type]
9201
+ .map((value) => String(value || "").trim().toLowerCase())
9202
+ .filter(Boolean);
9161
9203
  if (issue) {
9162
- if (/plan_draft_local|submit-plan|plan-doc/.test(text)) return `issue-plan:${issue}`;
9163
- if (/gitlab_issue_missing|ensure-gitlab-issue/.test(text)) return `issue-gitlab:${issue}`;
9204
+ if (/^(?:issue-plan|issue-gitlab|implementation|bugfix|integration):/.test(normalizedStage)) return rawStage;
9205
+ if (tokens.some((value) => /^code-review(?::|$)/.test(value) || value === "code_review_completed")) {
9206
+ return `implementation:${issue}`;
9207
+ }
9208
+ if (tokens.some((value) => /plan_draft_local|submit-plan|plan-doc/.test(value) || ["plan_mr", "plan_approved", "issue-plan"].includes(value))) {
9209
+ return `issue-plan:${issue}`;
9210
+ }
9211
+ if (tokens.some((value) => /gitlab_issue_missing|ensure-gitlab-issue/.test(value) || value === "issue-gitlab")) {
9212
+ return `issue-gitlab:${issue}`;
9213
+ }
9214
+ if (tokens.some((value) => ["fix_mr", "bugfix"].includes(value))) return `bugfix:${issue}`;
9215
+ if (tokens.some((value) => ["integration_mr", "integrated", "integration"].includes(value))) return `integration:${issue}`;
9216
+ if (tokens.some((value) => [
9217
+ "implementation_mr",
9218
+ "implementation_done",
9219
+ "implementation_merged",
9220
+ "impl_mr",
9221
+ "impl_done",
9222
+ "impl_merged",
9223
+ "runtime_marker",
9224
+ "status",
9225
+ "implementation",
9226
+ ].includes(value))) {
9227
+ return `implementation:${issue}`;
9228
+ }
9164
9229
  }
9165
9230
  return rawStage || action;
9166
9231
  }
9167
9232
 
9233
+ function prdWorkflowRuntimeEventCanonicalAction(stage = "") {
9234
+ const prefix = String(stage || "").trim().split(":", 1)[0];
9235
+ return ["issue-plan", "issue-gitlab", "implementation", "bugfix", "integration"].includes(prefix)
9236
+ ? prefix
9237
+ : "";
9238
+ }
9239
+
9240
+ function prdWorkflowRuntimeOwnedArtifacts(values, stage = "") {
9241
+ if (!Array.isArray(values)) return values;
9242
+ const ownsOnlyChangedArtifact = /^(?:issue-plan|implementation|bugfix|integration):/.test(String(stage || ""));
9243
+ if (!ownsOnlyChangedArtifact) return values;
9244
+ return values.filter((item) => {
9245
+ if (!item || typeof item !== "object" || Array.isArray(item)) return true;
9246
+ const kind = String(item.kind || item.type || "").trim().toLowerCase();
9247
+ const key = String(item.key || item.artifactKey || item.artifact_key || "").trim().toLowerCase();
9248
+ return !(
9249
+ ["gitlab-epic", "gitlab-issue"].includes(kind)
9250
+ || /^(?:gitlab-epic|gitlab-issue):/.test(key)
9251
+ );
9252
+ });
9253
+ }
9254
+
9168
9255
  function prdWorkflowRuntimeEventId(event = {}) {
9169
9256
  const stage = prdWorkflowRuntimeEventCanonicalStage(event);
9170
9257
  const action = String(event.action || event.actionId || event.action_id || "").trim();
@@ -9201,8 +9288,13 @@ function prdWorkflowCompactRuntimeValue(value, maxChars = 24000) {
9201
9288
  function prdWorkflowNormalizeRuntimeEvent(tapdId, event = {}) {
9202
9289
  const now = new Date().toISOString();
9203
9290
  const type = String(event.type || event.kind || "workflow-event").trim().slice(0, 120);
9204
- const action = String(event.action || event.actionId || event.action_id || "").trim().slice(0, 160);
9205
- const stage = String(event.stage || event.stageKey || event.stage_key || event.phase || action || "").trim().slice(0, 160);
9291
+ const rawAction = String(event.action || event.actionId || event.action_id || "").trim().slice(0, 160);
9292
+ const rawStage = String(event.stageKey || event.stage_key || event.stage || event.phase || rawAction || "").trim().slice(0, 160);
9293
+ const stage = prdWorkflowRuntimeEventCanonicalStage(event).slice(0, 160);
9294
+ const canonicalAction = prdWorkflowRuntimeEventCanonicalAction(stage);
9295
+ const action = canonicalAction && (type === "workflow-marker" || rawAction === "mark")
9296
+ ? canonicalAction
9297
+ : rawAction;
9206
9298
  const scope = String(event.scope || "").trim().slice(0, 80);
9207
9299
  const platform = String(event.platform || "").trim().slice(0, 80);
9208
9300
  const status = prdWorkflowRuntimeEventStatus(type, event.status);
@@ -9228,6 +9320,7 @@ function prdWorkflowNormalizeRuntimeEvent(tapdId, event = {}) {
9228
9320
  authority,
9229
9321
  updatedAt: now,
9230
9322
  };
9323
+ if (rawStage && rawStage !== stage) entry.sourceStage = rawStage;
9231
9324
  const sourceEventId = String(event.id || event.eventId || event.event_id || "").trim();
9232
9325
  if (sourceEventId && sourceEventId !== entry.id) entry.sourceEventId = sourceEventId.slice(0, 160);
9233
9326
  if (action) {
@@ -9236,8 +9329,10 @@ function prdWorkflowNormalizeRuntimeEvent(tapdId, event = {}) {
9236
9329
  }
9237
9330
  if (stage) {
9238
9331
  entry.stage = stage;
9239
- entry.stageKey = String(event.stageKey || event.stage_key || stage);
9332
+ entry.stageKey = stage;
9240
9333
  }
9334
+ entry.artifacts = prdWorkflowRuntimeOwnedArtifacts(entry.artifacts, stage);
9335
+ entry.links = prdWorkflowRuntimeOwnedArtifacts(entry.links, stage);
9241
9336
  if (scope) entry.scope = scope;
9242
9337
  if (platform) entry.platform = platform;
9243
9338
  if (!entry.createdAt) entry.createdAt = event.startedAt || now;
@@ -9256,26 +9351,50 @@ function prdWorkflowNormalizeRuntimeEvent(tapdId, event = {}) {
9256
9351
  function prdWorkflowMergeRuntimeEventArrays(left, right) {
9257
9352
  const out = [];
9258
9353
  const seen = new Map();
9354
+ const aliasesFor = (value) => {
9355
+ if (typeof value === "string") return [`value:${value}`];
9356
+ if (!value || typeof value !== "object" || Array.isArray(value)) return [];
9357
+ const aliases = [];
9358
+ const explicitKey = String(value.key || value.artifactKey || value.artifact_key || "").trim();
9359
+ if (explicitKey) aliases.push(`key:${explicitKey}`);
9360
+ const rawUrl = String(
9361
+ value.canonicalUrl
9362
+ || value.canonical_url
9363
+ || value.href
9364
+ || value.url
9365
+ || "",
9366
+ ).trim();
9367
+ if (rawUrl) {
9368
+ try {
9369
+ const parsed = new URL(rawUrl, "http://agentflow.local");
9370
+ aliases.push(`url:${parsed.origin}${parsed.pathname.replace(/\/+$/, "") || "/"}`);
9371
+ } catch {
9372
+ aliases.push(`url:${rawUrl.split(/[?#]/)[0].replace(/\/+$/, "")}`);
9373
+ }
9374
+ }
9375
+ const itemPath = String(value.path || "").trim();
9376
+ if (itemPath) aliases.push(`path:${itemPath}`);
9377
+ if (!aliases.length) {
9378
+ try {
9379
+ aliases.push(`value:${JSON.stringify(value)}`);
9380
+ } catch {
9381
+ aliases.push(`value:${String(value)}`);
9382
+ }
9383
+ }
9384
+ return aliases;
9385
+ };
9259
9386
  for (const value of [...(Array.isArray(left) ? left : []), ...(Array.isArray(right) ? right : [])]) {
9260
9387
  if (value == null) continue;
9261
- const explicitKey = value && typeof value === "object" && !Array.isArray(value)
9262
- ? String(value.key || value.artifactKey || value.artifact_key || "").trim()
9263
- : "";
9264
- let key = explicitKey ? `key:${explicitKey}` : "";
9265
- try {
9266
- if (!key) key = typeof value === "string" ? value : JSON.stringify(value);
9267
- } catch {
9268
- key = String(value);
9269
- }
9270
- const index = seen.get(key);
9388
+ const aliases = aliasesFor(value);
9389
+ const index = aliases.map((alias) => seen.get(alias)).find((candidate) => candidate != null);
9271
9390
  if (index == null) {
9272
- seen.set(key, out.length);
9391
+ const nextIndex = out.length;
9273
9392
  out.push(value);
9393
+ aliases.forEach((alias) => seen.set(alias, nextIndex));
9274
9394
  continue;
9275
9395
  }
9276
- if (explicitKey) {
9277
- out[index] = value;
9278
- }
9396
+ out[index] = value;
9397
+ aliasesFor(value).forEach((alias) => seen.set(alias, index));
9279
9398
  }
9280
9399
  return out;
9281
9400
  }
@@ -11096,6 +11215,40 @@ export function startUiServer({
11096
11215
  isAdmin: Boolean(authUser.isAdmin),
11097
11216
  adminOwnerId: String(url.searchParams.get("adminOwnerId") || "").trim(),
11098
11217
  } : {};
11218
+ if (req.method === "GET" && url.pathname.startsWith("/w/")) {
11219
+ const parts = url.pathname.split("/").filter(Boolean);
11220
+ if (parts.length !== 2) {
11221
+ res.writeHead(404);
11222
+ res.end("Not found");
11223
+ return;
11224
+ }
11225
+ let shareToken = "";
11226
+ try {
11227
+ shareToken = decodeURIComponent(parts[1] || "");
11228
+ } catch {
11229
+ res.writeHead(404);
11230
+ res.end("Not found");
11231
+ return;
11232
+ }
11233
+ const record = getPrdWorkflowCollaborationByShareToken(shareToken);
11234
+ if (!record) {
11235
+ res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
11236
+ res.end("Workflow share link is invalid or has been revoked");
11237
+ return;
11238
+ }
11239
+ const query = new URLSearchParams({
11240
+ view: "workflow",
11241
+ tapdId: String(record.tapdId || ""),
11242
+ workflowShare: shareToken,
11243
+ });
11244
+ res.writeHead(302, {
11245
+ Location: `/workspace?${query.toString()}`,
11246
+ "Cache-Control": "no-store",
11247
+ "Referrer-Policy": "no-referrer",
11248
+ });
11249
+ res.end();
11250
+ return;
11251
+ }
11099
11252
  if (req.method === "GET" && url.pathname === "/api/auth/session-token") {
11100
11253
  if (!authUser?.userId) {
11101
11254
  json(res, 401, { error: "Unauthorized" });
@@ -11418,7 +11571,7 @@ export function startUiServer({
11418
11571
  json(res, 200, {
11419
11572
  ok: true,
11420
11573
  snapshot,
11421
- ...(workflowShare ? { workflowShare, shareUrl: workflowShare.url } : {}),
11574
+ ...(workflowShare ? { workflowShare, shareUrl: workflowShare.shortUrl || workflowShare.url } : {}),
11422
11575
  });
11423
11576
  } catch (e) {
11424
11577
  json(res, 500, { error: (e && e.message) || String(e) });
@@ -11607,7 +11760,7 @@ export function startUiServer({
11607
11760
  json(res, 200, {
11608
11761
  ok: true,
11609
11762
  snapshot: withDiagnostic,
11610
- ...(workflowShare ? { workflowShare, shareUrl: workflowShare.url } : {}),
11763
+ ...(workflowShare ? { workflowShare, shareUrl: workflowShare.shortUrl || workflowShare.url } : {}),
11611
11764
  });
11612
11765
  } catch (e) {
11613
11766
  json(res, 500, { error: (e && e.message) || String(e) });
@@ -12332,7 +12485,15 @@ export function startUiServer({
12332
12485
  const shortUrl = shortLink?.shortUrl || "";
12333
12486
  const displayUrl = shortUrl || reviewUrl;
12334
12487
  const durability = review.durability || "temporary";
12335
- const artifactKey = prdWorkflowReviewArtifactKey(tapdId, payload, durability);
12488
+ const artifactKey = prdWorkflowReviewArtifactKey(tapdId, payload);
12489
+ const reviewStageKey = prdWorkflowRuntimeEventCanonicalStage(payload)
12490
+ || payload.stageKey
12491
+ || payload.stage_key
12492
+ || payload.stage
12493
+ || "review";
12494
+ const reviewMrUrl = String(payload.mrUrl || payload.mr_url || "").trim();
12495
+ const reviewMrIid = String(payload.mrIid || payload.mr_iid || "").trim();
12496
+ const reviewCommitSha = String(payload.commitSha || payload.commit_sha || "").trim();
12336
12497
  const reviewSource = review.source && typeof review.source === "object" && !Array.isArray(review.source)
12337
12498
  ? review.source
12338
12499
  : { kind: durability === "durable" ? "ai-doc" : "local-draft", durability };
@@ -12351,19 +12512,32 @@ export function startUiServer({
12351
12512
  canonicalUrl: reviewUrl,
12352
12513
  shortUrl,
12353
12514
  expiresAt: review.expiresAt || "",
12515
+ issueKey: payload.issueKey || payload.issue_key || "",
12516
+ platform: payload.platform || "",
12517
+ stageKey: reviewStageKey,
12518
+ ...(reviewMrUrl ? { mrUrl: reviewMrUrl } : {}),
12519
+ ...(reviewMrIid ? { mrIid: reviewMrIid } : {}),
12520
+ ...(reviewCommitSha ? { commitSha: reviewCommitSha } : {}),
12354
12521
  };
12355
12522
  const event = prdWorkflowAppendRuntimeEvent(scopedRoot, tapdId, {
12523
+ id: `review-link:${artifactKey}`,
12356
12524
  type: "review-link",
12357
12525
  auxiliary: true,
12526
+ aggregateByStage: false,
12358
12527
  conflictOnArtifact: false,
12359
12528
  truth: "runtime_event",
12360
12529
  persistence: "runtime",
12361
12530
  action: payload.action || payload.actionId || "",
12362
- stage: payload.stage || payload.stageKey || payload.stage_key || "review",
12531
+ stage: reviewStageKey,
12532
+ stageKey: reviewStageKey,
12363
12533
  title: payload.title || "临时 Markdown Review",
12364
12534
  detail: "已生成临时 Markdown review 链接",
12365
12535
  status: "current",
12366
12536
  issueKey: payload.issueKey || payload.issue_key || "",
12537
+ platform: payload.platform || "",
12538
+ ...(reviewMrUrl ? { mrUrl: reviewMrUrl } : {}),
12539
+ ...(reviewMrIid ? { mrIid: reviewMrIid } : {}),
12540
+ ...(reviewCommitSha ? { commitSha: reviewCommitSha } : {}),
12367
12541
  idempotencyKey,
12368
12542
  durability,
12369
12543
  sourceArtifact: reviewSource,
@@ -12380,6 +12554,12 @@ export function startUiServer({
12380
12554
  durability,
12381
12555
  source: reviewSource,
12382
12556
  expiresAt: review.expiresAt || "",
12557
+ issueKey: artifact.issueKey,
12558
+ platform: artifact.platform,
12559
+ stageKey: artifact.stageKey,
12560
+ ...(artifact.mrUrl ? { mrUrl: artifact.mrUrl } : {}),
12561
+ ...(artifact.mrIid ? { mrIid: artifact.mrIid } : {}),
12562
+ ...(artifact.commitSha ? { commitSha: artifact.commitSha } : {}),
12383
12563
  }],
12384
12564
  reviewId: review.id,
12385
12565
  reviewShortCode: shortLink?.shortCode || "",
@@ -233,6 +233,7 @@ export function normalizeWorkflowReport(payload = {}) {
233
233
  workflow,
234
234
  workflowKey: workflow.key,
235
235
  aggregateByStage: Boolean(action),
236
+ auxiliary: !action,
236
237
  conflictOnArtifact: payload.conflictOnArtifact === true || payload.conflict_on_artifact === true,
237
238
  artifactScope: action ? "action" : "global",
238
239
  ...(action ? {
@@ -395,16 +396,51 @@ export function mergeWorkflowArtifacts(baseArtifacts = [], runtimeEvents = []) {
395
396
  export function mergeWorkflowArtifactLists(left = [], right = [], defaultScope = "action") {
396
397
  const out = [];
397
398
  const seen = new Map();
399
+ const normalizedUrl = (value) => {
400
+ const raw = cleanString(value, 4000);
401
+ if (!raw) return "";
402
+ try {
403
+ const parsed = new URL(raw, "http://agentflow.local");
404
+ return `${parsed.origin}${parsed.pathname.replace(/\/+$/, "") || "/"}`;
405
+ } catch {
406
+ return raw.split(/[?#]/)[0].replace(/\/+$/, "");
407
+ }
408
+ };
409
+ const artifactAliases = (artifact) => {
410
+ const aliases = [];
411
+ const explicitKey = cleanString(
412
+ artifact?.key || artifact?.artifactKey || artifact?.artifact_key,
413
+ 500,
414
+ );
415
+ if (explicitKey) aliases.push(`key:${explicitKey}`);
416
+ const url = normalizedUrl(
417
+ artifact?.canonicalUrl
418
+ || artifact?.canonical_url
419
+ || artifact?.href
420
+ || artifact?.url,
421
+ );
422
+ if (url) aliases.push(`url:${url}`);
423
+ const artifactPath = cleanString(artifact?.path, 4000);
424
+ if (artifactPath) aliases.push(`path:${artifactPath}`);
425
+ return aliases;
426
+ };
398
427
  const add = (artifact) => {
399
428
  if (!artifact || typeof artifact !== "object" || Array.isArray(artifact)) return;
429
+ const rawAliases = artifactAliases(artifact);
400
430
  const normalized = normalizeWorkflowArtifact(artifact, out.length, defaultScope);
401
- const key = normalized.key || normalized.url || `${normalized.type}:${normalized.title}`;
402
- const existing = seen.get(key);
431
+ const aliases = uniqueValues([
432
+ ...rawAliases,
433
+ ...artifactAliases(normalized),
434
+ ...(!rawAliases.length ? [`value:${normalized.type}:${normalized.title}`] : []),
435
+ ]);
436
+ const existing = aliases.map((alias) => seen.get(alias)).find((value) => value != null);
403
437
  if (existing == null) {
404
- seen.set(key, out.length);
438
+ const index = out.length;
405
439
  out.push(normalized);
440
+ aliases.forEach((alias) => seen.set(alias, index));
406
441
  } else {
407
442
  out[existing] = { ...out[existing], ...normalized };
443
+ artifactAliases(out[existing]).forEach((alias) => seen.set(alias, existing));
408
444
  }
409
445
  };
410
446
  for (const artifact of Array.isArray(left) ? left : []) add(artifact);